Import v6.12.6 of the Inform 6 standard library
[informlib.git] / verblib.h
1 ! ==============================================================================
2 !   VERBLIB:  Front end to standard verbs library.
3 !
4 !   Supplied for use with Inform 6 -- Release 6.12.6 -- Serial number 220219
5 !
6 !   Copyright Graham Nelson 1993-2004 and David Griffith 2012-2022
7 !
8 !   This file is free software: you can redistribute it and/or modify
9 !   it under the terms of the GNU Affero General Public License as
10 !   published by the Free Software Foundation, either version 3 of the
11 !   License, or (at your option) any later version.
12 !
13 !   This file is distributed in the hope that it will be useful, but
14 !   WITHOUT ANY WARRANTY; without even the implied warranty of
15 !   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 !   Affero General Public License for more details.
17 !
18 !   You should have received a copy of the GNU Affero General Public
19 !   License along with this program. If not, see
20 !   https://gnu.org/licenses/
21 !
22 !   In your game file, Include three library files in this order:
23 !       Include "parser";
24 !       Include "verblib";
25 !       Include "grammar";
26 ! ==============================================================================
27
28 System_file;
29
30 #Ifdef          LIBRARY_STAGE;
31 #Iffalse        LIBRARY_STAGE >= AFTER_VERBLIB; ! if not already included
32 #Iftrue         LIBRARY_STAGE == AFTER_PARSER;  ! if okay to include it
33
34 ! ------------------------------------------------------------------------------
35
36 Default AMUSING_PROVIDED    1;
37 Default MAX_CARRIED         100;
38 Default MAX_SCORE           0;
39 Default NUMBER_TASKS        1;
40 Default OBJECT_SCORE        4;
41 Default ROOM_SCORE          5;
42 Default SACK_OBJECT         0;
43 Default TASKS_PROVIDED      1;
44
45 #Ifndef task_scores;
46 Array  task_scores -> 0 0 0 0;
47 #Endif;
48
49 Array  task_done -> NUMBER_TASKS;
50
51 #Ifndef LibraryMessages;
52 Object LibraryMessages;
53 #Endif;
54
55 #Ifndef NO_PLACES;
56 [ ObjectsSub; Objects1Sub(); ];
57 [ ObjectsTallSub; Objects1TallSub(); ];
58 [ ObjectsWideSub; Objects1WideSub(); ];
59 [ PlacesSub;  Places1Sub(); ];
60 [ PlacesTallSub; Places1TallSub(); ];
61 [ PlacesWideSub; Places1WideSub(); ];
62 #Endif; ! NO_PLACES
63
64 ! ------------------------------------------------------------------------------
65 ! Banner(), VersionSub(), and RunTimeError() are preempted by LanguageBanner(),
66 ! LanguageVersionSub(), and LanguageError() respectfully.  When converting
67 ! this library to support a different natural language, these three latter
68 ! functions should be created rather than editing this file.
69 ! ------------------------------------------------------------------------------
70
71 [ Banner i;
72     #Ifdef LanguageBanner;
73     LanguageBanner();
74     i = 0;  ! suppress warning
75     #Ifnot;
76     if (Story) {
77         #Ifdef TARGET_ZCODE;
78         #IfV5; style bold; #Endif;
79         print "^", (string) Story;
80         #IfV5; style roman; #Endif;
81         #Ifnot; ! TARGET_GLULX;
82         glk_set_style(style_Header);
83         print "^", (string) Story;
84         glk_set_style(style_Normal);
85         #Endif; ! TARGET_
86     } else
87         new_line;
88
89     if (Headline) print (string) Headline;
90     else
91         new_line;
92
93     #Ifdef TARGET_ZCODE;
94     print (string) RELEASE__TX, (HDR_GAMERELEASE-->0) & $03ff, " / ", (string) SERNUM__TX;
95     for (i=0 : i<6 : i++) print (char) HDR_GAMESERIAL->i;
96     #Ifnot; ! TARGET_GLULX;
97     print (string) RELEASE__TX;
98     @aloads ROM_GAMERELEASE 0 i;
99     print i;
100     print " / ", (string) SERNUM__TX;
101     for (i=0 : i<6 : i++) print (char) ROM_GAMESERIAL->i;
102     #Endif; ! TARGET_
103     new_line;
104     #Endif; ! LanguageBanner
105 ];
106
107 [ VersionSub ix;
108     #Ifdef LanguageVersionSub;
109     LanguageVersionSub();
110     ix = 0;  ! suppress warning
111     #Ifnot;
112     Banner();
113     #Ifdef TARGET_ZCODE;
114     ix = 0; ! shut up compiler warning
115     if (standard_interpreter > 0) {
116         print (string) STDTERP__TX, standard_interpreter/256, ".", standard_interpreter%256,
117             " (", HDR_TERPNUMBER->0;
118         #Iftrue (#version_number == 6);
119         print (char) '.', HDR_TERPVERSION->0;
120         #Ifnot;
121         print (char) HDR_TERPVERSION->0;
122         #Endif;
123         print ") / ";
124         }
125     else {
126         print (string) TERP__TX, HDR_TERPNUMBER->0, (string) VER__TX;
127         #Iftrue (#version_number == 6);
128         print HDR_TERPVERSION->0;
129         #Ifnot;
130         print (char) HDR_TERPVERSION->0;
131         #Endif;
132         print " / ";
133     }
134
135     #Ifnot; ! TARGET_GLULX;
136     @gestalt 1 0 ix;
137     print (string) TERPVER__TX, ix / $10000, ".", (ix & $FF00) / $100,
138       ".", ix & $FF, " / ";
139     @gestalt 0 0 ix;
140     print (string) VM__TX, ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / ";
141     #Endif; ! TARGET_;
142     print (string) LIBSER__TX, (string) LibSerial, "^";
143     #Ifdef LanguageVersion;
144     print (string) LanguageVersion, "^";
145     #Endif; ! LanguageVersion
146     #Endif; ! LanguageVersionSub
147 ];
148
149 [ RunTimeError n p1 p2;
150     #Ifdef LanguageError;
151     LanguageError(n, p1, p2);
152     #Ifnot;
153
154     print "** ", (string) LIBERROR__TX, n, " (", p1, ", ", p2, ") **";
155     #Ifdef DEBUG;
156     L__M(##RunTimeError, n, p1, p2);
157     #Endif;
158     print "^";
159
160     #Endif; ! LanguageError
161 ];
162
163 ! ----------------------------------------------------------------------------
164 !  The WriteListFrom routine, a flexible object-lister taking care of
165 !  plurals, inventory information, various formats and so on.  This is used
166 !  by everything in the library which ever wants to list anything.
167 !
168 !  If there were no objects to list, it prints nothing and returns false;
169 !  otherwise it returns true.
170 !
171 !  o is the object, and style is a bitmap, whose bits are given by:
172 ! ----------------------------------------------------------------------------
173
174
175 Constant NEWLINE_BIT   $0001;       ! New-line after each entry
176 Constant INDENT_BIT    $0002;       ! Indent each entry by depth
177 Constant FULLINV_BIT   $0004;       ! Full inventory information after entry
178 Constant ENGLISH_BIT   $0008;       ! English sentence style, with commas and and
179 Constant RECURSE_BIT   $0010;       ! Recurse downwards with usual rules
180 Constant ALWAYS_BIT    $0020;       ! Always recurse downwards
181 Constant TERSE_BIT     $0040;       ! More terse English style
182 Constant PARTINV_BIT   $0080;       ! Only brief inventory information after entry
183 Constant DEFART_BIT    $0100;       ! Use the definite article in list
184 Constant WORKFLAG_BIT  $0200;       ! At top level (only), only list objects
185                                     ! which have the "workflag" attribute
186 Constant ISARE_BIT     $0400;       ! Print " is" or " are" before list
187 Constant CONCEAL_BIT   $0800;       ! Omit objects with "concealed" or "scenery":
188                                     ! if WORKFLAG_BIT also set, then does _not_
189                                     ! apply at top level, but does lower down
190 Constant NOARTICLE_BIT $1000;       ! Print no articles, definite or not
191 Constant ID_BIT        $2000;       ! Print object id after each entry
192
193 [ NextEntry o odepth;
194     for (::) {
195         o = sibling(o);
196         if (o == 0) return 0;
197         if (lt_value && o.list_together ~= lt_value) continue;
198         if (c_style & WORKFLAG_BIT && odepth==0 && o hasnt workflag) continue;
199         if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) continue;
200         return o;
201     }
202 ];
203
204 [ WillRecurs o;
205     if (c_style & ALWAYS_BIT) rtrue;
206     if (c_style & RECURSE_BIT == 0) rfalse;
207     if ((o has transparent or supporter) || (o has container && o has open)) rtrue;
208     rfalse;
209 ];
210
211 [ ListEqual o1 o2;
212     if (child(o1) && WillRecurs(o1)) rfalse;
213     if (child(o2) && WillRecurs(o2)) rfalse;
214     if (c_style & (FULLINV_BIT + PARTINV_BIT)) {
215         if ((o1 hasnt worn && o2 has worn) || (o2 hasnt worn && o1 has worn)) rfalse;
216         if ((o1 hasnt light && o2 has light) || (o2 hasnt light && o1 has light)) rfalse;
217         if (o1 has container) {
218             if (o2 hasnt container) rfalse;
219             if ((o1 has open && o2 hasnt open) || (o2 has open && o1 hasnt open))
220                 rfalse;
221         }
222         else if (o2 has container)
223             rfalse;
224     }
225     return Identical(o1, o2);
226 ];
227
228 [ SortTogether obj value;
229     ! print "Sorting together possessions of ", (object) obj, " by value ", value, "^";
230     ! for (x=child(obj) : x : x=sibling(x))
231     !     print (the) x, " no: ", x, " lt: ", x.list_together, "^";
232     while (child(obj)) {
233         if (child(obj).list_together ~= value) move child(obj) to out_obj;
234         else                                   move child(obj) to in_obj;
235     }
236     while (child(in_obj))  move child(in_obj) to obj;
237     while (child(out_obj)) move child(out_obj) to obj;
238 ];
239
240 [ SortOutList obj i k l;
241     !  print "^^Sorting out list from ", (name) obj, "^  ";
242     !  for (i=child(location) : i : i=sibling(i))
243     !      print (name) i, " --> ";
244     !  new_line;
245
246   .AP_SOL;
247
248     for (i=obj : i : i=sibling(i)) {
249         k = i.list_together;
250         if (k ~= 0) {
251             ! print "Scanning ", (name) i, " with lt=", k, "^";
252             for (i=sibling(i) : i && i.list_together == k :) i = sibling(i);
253             if (i == 0) rfalse;
254             ! print "First not in block is ", (name) i, " with lt=", i.list_together, "^";
255             for (l=sibling(i) : l : l=sibling(l))
256                 if (l.list_together == k) {
257                     SortTogether(parent(obj), k);
258                     ! print "^^After ST:^  ";
259                     ! for (i=child(location) : i : i=sibling(i))
260                     !     print (name) i, " --> ";
261                     ! new_line;
262                     obj = child(parent(obj));
263                     jump AP_SOL;
264                 }
265         }
266     }
267 ];
268
269 #Ifdef TARGET_ZCODE;
270
271 [ Print__Spaces n;         ! To avoid a bug occurring in Inform 6.01 to 6.10
272     if (n == 0) return;
273     spaces n;
274 ];
275
276 #Ifnot; ! TARGET_GLULX;
277
278 [ Print__Spaces n;
279     while (n > 0) {
280         @streamchar ' ';
281         n = n - 1;
282     }
283 ];
284
285 #Endif; ! TARGET_
286
287 [ WriteListFrom o style depth
288     s1 s2 s3 s4 s5 s6;
289
290     if (o == nothing) return 0;
291
292     s1 = c_style;      s2 = lt_value;   s3 = listing_together;
293     s4 = listing_size; s5 = wlf_indent; s6 = inventory_stage;
294
295     if (o == child(parent(o))) {
296         SortOutList(o);
297         o = child(parent(o));
298     }
299     c_style = style;
300     wlf_indent = 0;
301     if (WriteListR(o, depth) == 0) return 0;
302
303     c_style = s1;      lt_value = s2;   listing_together = s3;
304     listing_size = s4; wlf_indent = s5; inventory_stage = s6;
305     rtrue;
306 ];
307
308 [ WriteListR o depth stack_pointer  classes_p sizes_p i j k k2 l m n q senc mr;
309     if (depth > 0 && o == child(parent(o))) {
310         SortOutList(o);
311         o = child(parent(o));
312     }
313     for (::) {
314         if (o == 0) rfalse;
315         if (c_style & WORKFLAG_BIT && depth==0 && o hasnt workflag) {
316             o = sibling(o);
317             continue;
318         }
319         if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) {
320             o = sibling(o);
321             continue;
322         }
323         break;
324     }
325     classes_p = match_classes + stack_pointer;
326     sizes_p   = match_list + stack_pointer;
327
328     for (i=o,j=0 : i && (j+stack_pointer)<128 : i=NextEntry(i,depth),j++) {
329         classes_p->j = 0;
330         if (i.plural) k++;
331     }
332
333     if (c_style & ISARE_BIT) {
334         if (j == 1 && o hasnt pluralname) Tense(IS__TX, WAS__TX);
335         else                              Tense(ARE__TX, WERE__TX);
336         if (c_style & NEWLINE_BIT)   print (string) COLON__TX, "^";
337         else                         print (char) ' ';
338         c_style = c_style - ISARE_BIT;
339     }
340
341     stack_pointer = stack_pointer+j+1;
342
343     if (k < 2) jump EconomyVersion;   ! It takes two to plural
344     n = 1;
345     for (i=o,k=0 : k<j : i=NextEntry(i,depth),k++)
346         if (classes_p->k == 0) {
347             classes_p->k = n; sizes_p->n = 1;
348             for (l=NextEntry(i,depth),m=k+1 : l && m<j : l=NextEntry(l,depth),m++)
349                 if (classes_p->m == 0 && i.plural && l.plural ~= 0) {
350                     if (ListEqual(i, l) == 1) {
351                         sizes_p->n = sizes_p->n + 1;
352                         classes_p->m = n;
353                     }
354                 }
355             n++;
356         }
357     n--;
358
359     for (i=1,j=o,k=0 : i<=n : i++,senc++) {
360         while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) {
361             k++; j=NextEntry(j, depth);
362         }
363         m = sizes_p->i;
364         if (j == 0) mr = 0;
365         else {
366             if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String &&
367                 j.list_together == mr) senc--;
368             mr = j.list_together;
369         }
370     }
371     senc--;
372
373     for (i=1,j=o,k=0,mr=0 : senc>=0 : i++,senc--) {
374         while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) {
375             k++; j=NextEntry(j, depth);
376         }
377         if (j.list_together ~= 0 or lt_value) {
378             if (j.list_together == mr) {
379                 senc++;
380                 jump Omit_FL2;
381             }
382             k2 = NextEntry(j, depth);
383             if (k2 == 0 || k2.list_together ~= j.list_together) jump Omit_WL2;
384             k2 = metaclass(j.list_together);
385             if (k2 == Routine or String) {
386                 q = j; listing_size = 1; l = k; m = i;
387                 while (m < n && q.list_together == j.list_together) {
388                     m++;
389                     while (((classes_p->l) ~= m) && ((classes_p->l) ~= -m)) {
390                         l++; q = NextEntry(q, depth);
391                      }
392                     if (q.list_together == j.list_together) listing_size++;
393                 }
394                 ! print " [", listing_size, "] ";
395                 if (listing_size == 1) jump Omit_WL2;
396                 if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent));
397                 if (k2 == String) {
398                     q = 0;
399                     for (l=0 : l<listing_size : l++) q = q+sizes_p->(l+i);
400                     EnglishNumber(q); print " ";
401                     print (string) j.list_together;
402                     if (c_style & ENGLISH_BIT) print " (";
403                     if (c_style & INDENT_BIT)  print (string) COLON__TX, "^";
404                 }
405                 q = c_style;
406                 if (k2 ~= String) {
407                     inventory_stage = 1;
408                     parser_one = j; parser_two = depth+wlf_indent;
409                     if (RunRoutines(j, list_together) == 1) jump Omit__Sublist2;
410                 }
411
412                 #Ifdef TARGET_ZCODE;
413                 @push lt_value;    @push listing_together;    @push listing_size;
414                 #Ifnot; ! TARGET_GLULX;
415                 @copy lt_value sp; @copy listing_together sp; @copy listing_size sp;
416                 #Endif; ! TARGET_;
417
418                 lt_value = j.list_together; listing_together = j; wlf_indent++;
419                 WriteListR(j, depth, stack_pointer); wlf_indent--;
420
421                 #Ifdef TARGET_ZCODE;
422                 @pull listing_size; @pull listing_together; @pull lt_value;
423                 #Ifnot; ! TARGET_GLULX;
424                 @copy sp listing_size;
425                 @copy sp listing_together;
426                 @copy sp lt_value;
427                 #Endif; ! TARGET_;
428
429                 if (k2 == String) {
430                     if (q & ENGLISH_BIT) print ")";
431                 }
432                 else {
433                     inventory_stage = 2;
434                     parser_one = j; parser_two = depth+wlf_indent;
435                     RunRoutines(j, list_together);
436                 }
437
438               .Omit__Sublist2;
439
440                 if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line;
441                 c_style = q;
442                 mr = j.list_together;
443                 jump Omit_EL2;
444             }
445         }
446
447       .Omit_WL2;
448
449         if (WriteBeforeEntry(j, depth, 0, senc) == 1) jump Omit_FL2;
450         if (sizes_p->i == 1) {
451             if (c_style & NOARTICLE_BIT)  print (name) j;
452             else {
453                 if (c_style & DEFART_BIT) print (the) j;
454                 else                      print (a) j;
455             }
456             if (c_style & ID_BIT)         print " (", j, ")";
457         }
458         else {
459             if (c_style & DEFART_BIT) PrefaceByArticle(j, 1, sizes_p->i);
460             print (number) sizes_p->i, " ";
461             PrintOrRun(j, plural, 1);
462         }
463         if (sizes_p->i > 1 && j hasnt pluralname) {
464             give j pluralname;
465             WriteAfterEntry(j, depth, stack_pointer);
466             give j ~pluralname;
467         }
468         else {
469             WriteAfterEntry(j,depth,stack_pointer);
470         }
471       .Omit_EL2;
472
473         if (c_style & ENGLISH_BIT) {
474             if (senc == 1) print (SerialComma) i+senc, (string) AND__TX;
475             if (senc > 1)  print (string) COMMA__TX;
476         }
477      .Omit_FL2;
478     }
479     rtrue;
480
481   .EconomyVersion;
482
483     n = j;
484     for (i=1,j=o : i<=n : j=NextEntry(j,depth),i++,senc++) {
485         if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String &&
486             j.list_together==mr) senc--;
487         mr = j.list_together;
488     }
489
490     for (i=1,j=o,mr=0 : i<=senc : j=NextEntry(j,depth),i++) {
491         if (j.list_together ~= 0 or lt_value) {
492             if (j.list_together == mr) {
493                 i--;
494                 jump Omit_FL;
495             }
496             k = NextEntry(j, depth);
497             if (k == 0 || k.list_together ~= j.list_together) jump Omit_WL;
498             k = metaclass(j.list_together);
499             if (k == Routine or String) {
500                 if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent));
501                 if (k == String) {
502                     q = j; l = 0;
503                     do {
504                         q = NextEntry(q, depth); l++;
505                     } until (q == 0 || q.list_together ~= j.list_together);
506                     EnglishNumber(l); print " ";
507                     print (string) j.list_together;
508                     if (c_style & ENGLISH_BIT) print " (";
509                     if (c_style & INDENT_BIT) print (string) COLON__TX, "^";
510                 }
511                 q = c_style;
512                 if (k ~= String) {
513                     inventory_stage = 1;
514                     parser_one = j; parser_two = depth+wlf_indent;
515                     if (RunRoutines(j, list_together) == 1) jump Omit__Sublist;
516                 }
517
518                 #Ifdef TARGET_ZCODE;
519                 @push lt_value; @push listing_together; @push listing_size;
520                 #Ifnot; ! TARGET_GLULX;
521                 @copy lt_value sp; @copy listing_together sp; @copy listing_size sp;
522                 #Endif; ! TARGET_;
523
524                 lt_value = j.list_together; listing_together = j; wlf_indent++;
525                 WriteListR(j, depth, stack_pointer); wlf_indent--;
526
527                 #Ifdef TARGET_ZCODE;
528                 @pull listing_size; @pull listing_together; @pull lt_value;
529                 #Ifnot; ! TARGET_GLULX;
530                 @copy sp listing_size; @copy sp listing_together; @copy sp lt_value;
531                 #Endif; ! TARGET_;
532
533                 if (k == String) {
534                     if (q & ENGLISH_BIT) print ")";
535                 }
536                 else {
537                     inventory_stage = 2;
538                     parser_one = j; parser_two = depth+wlf_indent;
539                     RunRoutines(j, list_together);
540                 }
541
542               .Omit__Sublist;
543
544                 if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line;
545                 c_style = q;
546                 mr = j.list_together;
547                 jump Omit_EL;
548             }
549         }
550
551       .Omit_WL;
552
553         if (WriteBeforeEntry(j, depth, i, senc) == 1) jump Omit_FL;
554         if (c_style & NOARTICLE_BIT)  print (name) j;
555         else {
556             if (c_style & DEFART_BIT) print (the) j;
557             else                      print (a) j;
558         }
559         if (c_style & ID_BIT)         print " (", j, ")";
560         WriteAfterEntry(j, depth, stack_pointer);
561
562       .Omit_EL;
563
564         if (c_style & ENGLISH_BIT) {
565             if (i == senc-1) print (SerialComma) senc, (string) AND__TX;
566             if (i < senc-1) print (string) COMMA__TX;
567         }
568
569   .Omit_FL;
570
571     }
572 ]; ! end of WriteListR
573
574 [ WriteBeforeEntry o depth ipos sentencepos
575     flag;
576
577     inventory_stage = 1;
578     if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent));
579     if (o.invent && (c_style & (PARTINV_BIT|FULLINV_BIT))) {
580         flag = PrintOrRun(o, invent, 1);
581         if (flag) {
582             if (c_style & ENGLISH_BIT) {
583                 if (ipos == sentencepos-1)
584                     print (SerialComma) sentencepos, (string) AND__TX;
585                 if (ipos < sentencepos-1)
586                     print (string) COMMA__TX;
587             }
588             if (c_style & NEWLINE_BIT) new_line;
589         }
590     }
591     return flag;
592 ];
593
594 [ WriteAfterEntry o depth stack_p
595     p recurse_flag parenth_flag eldest_child child_count combo i j;
596
597     inventory_stage = 2;
598     if (c_style & PARTINV_BIT) {
599         if (o.invent && RunRoutines(o, invent))
600             if (c_style & NEWLINE_BIT) ""; else rtrue;
601
602         combo = 0;
603         if (o has light && location hasnt light) combo=combo+1;
604         if (o has container && o hasnt open)     combo=combo+2;
605         if ((o has container && (o has open || o has transparent))) {
606             objectloop(i in o) {
607                 if (i hasnt concealed && i hasnt scenery) {
608                     j = true; break;
609                 }
610             }
611             if (~~j) combo=combo+4;
612         }
613         if (combo) L__M(##ListMiscellany, combo, o);
614     }   ! end of PARTINV_BIT processing
615
616     if (c_style & FULLINV_BIT) {
617         if (o.invent && RunRoutines(o, invent))
618             if (c_style & NEWLINE_BIT) ""; else rtrue;
619
620         if (o has light && o has worn) { L__M(##ListMiscellany, 8, o);  parenth_flag = true; }
621         else {
622             if (o has light)           { L__M(##ListMiscellany, 9, o);  parenth_flag = true; }
623             if (o has worn)            { L__M(##ListMiscellany, 10, o); parenth_flag = true; }
624         }
625
626         if (o has container)
627             if (o has openable) {
628                 if (parenth_flag) print (string) AND__TX;
629                 else              L__M(##ListMiscellany, 11, o);
630                 if (o has open)
631                     if (child(o)) L__M(##ListMiscellany, 12, o);
632                     else          L__M(##ListMiscellany, 13, o);
633                 else
634                     if (o has lockable && o has locked) L__M(##ListMiscellany, 15, o);
635                     else                                L__M(##ListMiscellany, 14, o);
636                 parenth_flag = true;
637             }
638             else
639                 if (child(o)==0 && o has transparent)
640                     if (parenth_flag) L__M(##ListMiscellany, 16, o);
641                     else              L__M(##ListMiscellany, 17, o);
642
643         if (parenth_flag) print ")";
644     }   ! end of FULLINV_BIT processing
645
646     if (c_style & CONCEAL_BIT) {
647         child_count = 0;
648         objectloop (p in o)
649             if (p hasnt concealed && p hasnt scenery) { child_count++; eldest_child = p; }
650     }
651     else { child_count = children(o); eldest_child = child(o); }
652
653     if (child_count && (c_style & ALWAYS_BIT)) {
654         if (c_style & ENGLISH_BIT) L__M(##ListMiscellany, 18, o);
655         recurse_flag = true;
656     }
657
658     if (child_count && (c_style & RECURSE_BIT)) {
659         if (o has supporter) {
660             if (c_style & ENGLISH_BIT) {
661                 if (c_style & TERSE_BIT) L__M(##ListMiscellany, 19, o);
662                 else                     L__M(##ListMiscellany, 20, o);
663                 if (o has animate)       print (string) WHOM__TX;
664                 else                     print (string) WHICH__TX;
665             }
666             recurse_flag = true;
667         }
668         if (o has container && (o has open || o has transparent)) {
669             if (c_style & ENGLISH_BIT) {
670                 if (c_style & TERSE_BIT) L__M(##ListMiscellany, 21, o);
671                 else                     L__M(##ListMiscellany, 22, o);
672                 if (o has animate)       print (string) WHOM__TX;
673                 else                     print (string) WHICH__TX;
674                 }
675             recurse_flag = true;
676         }
677     }
678
679     if (recurse_flag && (c_style & ENGLISH_BIT))
680         if (child_count > 1 || eldest_child has pluralname) Tense(ARE2__TX, WERE2__TX);
681         else                                                Tense(IS2__TX, WAS2__TX);
682
683     if (c_style & NEWLINE_BIT) new_line;
684
685     if (recurse_flag) {
686         o = child(o);
687         #Ifdef TARGET_ZCODE;
688         @push lt_value; @push listing_together; @push listing_size;
689         #Ifnot; ! TARGET_GLULX;
690         @copy lt_value sp; @copy listing_together sp; @copy listing_size sp;
691         #Endif;
692         lt_value = 0;   listing_together = 0;   listing_size = 0;
693         WriteListR(o, depth+1, stack_p);
694         #Ifdef TARGET_ZCODE;
695         @pull listing_size; @pull listing_together; @pull lt_value;
696         #Ifnot; ! TARGET_GLULX;
697         @copy sp listing_size; @copy sp listing_together; @copy sp lt_value;
698         #Endif;
699         if (c_style & TERSE_BIT) print ")";
700     }
701 ];
702
703 ! ----------------------------------------------------------------------------
704 ! LoopWithinObject(rtn,obj,arg)
705 !
706 ! rtn is the address of a user-supplied routine.
707 ! obj is an optional parent object whose dependents are to be processed; the
708 ! default is the current actor (normally the player).
709 ! arg is an optional argument passed to the rtn; this can be a single variable
710 ! or constant, or the address of an array (which enables multiple values to be
711 ! passed and returned).
712 !
713 ! For each object o which is a child, grandchild, great-grandchild, etc, of the
714 ! original obj, LoopWithinObject() calls rtn(o,arg).
715 !
716 ! The rtn should perform any appropriate testing or processing on each object o,
717 ! using the optional arg value if necessary. If the rtn returns true (or any
718 ! positive value), the children of o, if any, are also tested; those children
719 ! are skipped if rtn returns false. To terminate the loop before all objects
720 ! have been processed, rtn should return a large negative number (eg -99).
721 !
722 ! To deal with supporters and open containers, so that objects are processed
723 ! only if they are accessible to the player, rtn might end with these
724 ! statements:
725 !   if ((o has transparent or supporter) || (o has container && o has open)) rtrue;
726 !   rfalse;
727 ! or alternatively with:
728 !   c_style = RECURSE_BIT; return WillRecurs(o);
729 !
730 ! LoopWithinObject() returns the number of objects which have been processed.
731 ! ----------------------------------------------------------------------------
732
733 [ LoopWithinObject rtn obj arg
734     n o x y;
735     if (obj == 0) obj = actor;
736     o = child(obj);
737     while (o) {
738         y = parent(o); n++;
739         x = rtn(o, arg);    ! user-supplied routine returning x.
740                             ! if x < 0: skip up to next parent
741                             ! if x = 0: jump across to next sibling
742                             ! if x > 0: continue down to child objects
743         if (y ~= parent(o)) { RunTimeError(15, o); rfalse; }
744         if (x > 0 && child(o)) o = child(o);
745         else
746             while (o) {
747                 if (++x > 0 && sibling(o)) { o = sibling(o); break; }
748                 o = parent(o);
749                 if (o == obj) return n;
750             }
751     }
752 ];
753
754
755 ! ----------------------------------------------------------------------------
756 !  Much better menus can be created using one of the optional library
757 !  extensions.  These are provided for compatibility with previous practice:
758 ! ----------------------------------------------------------------------------
759
760 [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i j;
761     menu_nesting++;
762
763   .LKRD;
764
765     menu_item = 0;
766     lines = EntryR();
767     main_title = item_name;
768
769     print "--- "; print (string) main_title; print " ---^^";
770
771     if (menu_choices ofclass Routine) menu_choices();
772     else                              print (string) menu_choices;
773
774     for (::) {
775         L__M(##Miscellany, 52, lines);
776         print "> ";
777
778         #Ifdef TARGET_ZCODE;
779         #IfV3;
780         read buffer parse;
781         #Ifnot;
782         read buffer parse DrawStatusLine;
783         #Endif; ! V3
784         j = parse->1; ! number of words
785         #Ifnot; ! TARGET_GLULX;
786         KeyboardPrimitive(buffer, parse);
787         j = parse-->0; ! number of words
788         #Endif; ! TARGET_
789
790         i = parse-->1;
791         if (j == 0 || (i == QUIT1__WD or QUIT2__WD)) {
792             menu_nesting--; if (menu_nesting > 0) rfalse;
793             if (deadflag == 0) <<Look>>;
794             rfalse;
795         }
796         i = TryNumber(1);
797         if (i == 0) jump LKRD;
798         if (i < 1 || i > lines) continue;
799         menu_item = i;
800         j = ChoiceR();
801         if (j == 2) jump LKRD;
802         if (j == 3) rfalse;
803     }
804 ];
805
806 #Ifdef TARGET_ZCODE;
807
808 #IfV3;
809
810 [ DoMenu menu_choices EntryR ChoiceR; LowKey_Menu(menu_choices, EntryR, ChoiceR); ];
811
812 #Endif; ! V3
813
814 #IfV5;
815
816 [ DoMenu menu_choices EntryR ChoiceR
817          lines main_title main_wid cl i j oldcl pkey ch cw y x;
818     if (pretty_flag == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR);
819     menu_nesting++;
820     menu_item = 0;
821     lines = EntryR();
822     main_title = item_name; main_wid = item_width;
823     cl = 7;
824
825   .ReDisplay;
826
827     oldcl = 0;
828     @erase_window $ffff;
829     #Iftrue (#version_number == 6);
830     @set_cursor -1;
831     ch = HDR_FONTWUNITS->0;
832     #Ifnot;
833     ch = 1;
834     #Endif;
835     i = ch * (lines+7);
836     @split_window i;
837     i = HDR_SCREENWCHARS->0;
838     if (i == 0) i = 80;
839     @set_window 1;
840     @set_cursor 1 1;
841
842     #Iftrue (#version_number == 6);
843     @set_font 4 -> cw;
844     cw = HDR_FONTHUNITS->0;
845     #Ifnot;
846     cw = 1;
847     #Endif;
848
849     style reverse;
850     spaces(i); j=1+(i/2-main_wid)*cw;
851     @set_cursor 1 j;
852     print (string) main_title;
853     y=1+ch; @set_cursor y 1; spaces(i);
854     x=1+cw; @set_cursor y x; print (string) NKEY__TX;
855     j=1+(i-13)*cw; @set_cursor y j; print (string) PKEY__TX;
856     y=y+ch; @set_cursor y 1; spaces(i);
857     @set_cursor y x; print (string) RKEY__TX;
858     j=1+(i-18)*cw; @set_cursor y j;
859
860     if (menu_nesting == 1) print (string) QKEY1__TX;
861     else                   print (string) QKEY2__TX;
862     style roman;
863     y = y+2*ch;
864     @set_cursor y x; font off;
865
866     if (menu_choices ofclass String) print (string) menu_choices;
867     else                             menu_choices();
868
869     x = 1+3*cw;
870     for (::) {
871         if (cl ~= oldcl) {
872             if (oldcl>0) {
873                 y=1+(oldcl-1)*ch; @set_cursor y x; print " ";
874             }
875             y=1+(cl-1)*ch; @set_cursor y x; print ">";
876         }
877
878         oldcl = cl;
879         @read_char 1 -> pkey;
880         if (pkey == NKEY1__KY or NKEY2__KY or 130) {
881             cl++; if (cl == 7+lines) cl = 7; continue;
882         }
883         if (pkey == PKEY1__KY or PKEY2__KY or 129) {
884             cl--; if (cl == 6) cl = 6+lines; continue;
885         }
886         if (pkey == QKEY1__KY or QKEY2__KY or 27 or 131) break;
887         if (pkey == 10 or 13 or 132) {
888             @set_window 0; font on;
889             new_line; new_line; new_line;
890
891             menu_item = cl-6;
892             EntryR();
893
894             @erase_window $ffff;
895             @split_window ch;
896             i = HDR_SCREENWCHARS->0; if ( i== 0) i = 80;
897             @set_window 1; @set_cursor 1 1; style reverse; spaces(i);
898             j=1+(i/2-item_width)*cw;
899             @set_cursor 1 j;
900             print (string) item_name;
901             style roman; @set_window 0; new_line;
902
903             i = ChoiceR();
904             if (i == 2) jump ReDisplay;
905             if (i == 3) break;
906
907             L__M(##Miscellany, 53);
908             @read_char 1 -> pkey; jump ReDisplay;
909         }
910     }
911
912     menu_nesting--; if (menu_nesting > 0) rfalse;
913     font on; @set_cursor 1 1;
914     @erase_window $ffff; @set_window 0;
915     #Iftrue (#version_number == 6);
916     @set_cursor -2;
917     #Endif;
918     new_line; new_line; new_line;
919     if (deadflag == 0) <<Look>>;
920 ];
921
922 #Endif; ! V5
923
924 #Ifnot; ! TARGET_GLULX
925
926 [ DoMenu menu_choices EntryR ChoiceR
927     winwid winhgt lines main_title main_wid cl i oldcl pkey;
928
929     if (pretty_flag == 0 || gg_statuswin == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR);
930
931     menu_nesting++;
932     menu_item = 0;
933     lines = EntryR();
934     main_title = item_name;
935     main_wid = item_width;
936
937     cl = 0;
938
939     ! If we printed "hit arrow keys" here, it would be appropriate to
940     ! check for the availability of Glk input keys. But we actually
941     ! print "hit N/P/Q". So it's reasonable to silently accept Glk
942     ! arrow key codes as secondary options.
943
944   .ReDisplay;
945
946     glk_window_clear(gg_statuswin);
947     glk_window_clear(gg_mainwin);
948     glk_set_window(gg_statuswin);
949     StatusLineHeight(lines+7);
950     glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4);
951     winwid = gg_arguments-->0;
952     winhgt = gg_arguments-->1;
953     glk_set_style(style_Subheader);
954     glk_window_move_cursor(gg_statuswin, winwid/2-main_wid, 0);
955     print (string) main_title;
956     glk_window_move_cursor(gg_statuswin, 1, 1);
957     print (string) NKEY__TX;
958     glk_window_move_cursor(gg_statuswin, winwid-13, 1);
959     print (string) PKEY__TX;
960     glk_window_move_cursor(gg_statuswin, 1, 2);
961     print (string) RKEY__TX;
962     glk_window_move_cursor(gg_statuswin, winwid-18, 2);
963     if (menu_nesting == 1) print (string) QKEY1__TX;
964     else                   print (string) QKEY2__TX;
965     glk_set_style(style_Normal);
966     glk_window_move_cursor(gg_statuswin, 1, 4);
967     if (menu_choices ofclass String) print (string) menu_choices;
968     else                             menu_choices();
969
970     oldcl = -1;
971
972     for (::) {
973         if (cl ~= oldcl) {
974             if (cl < 0 || cl >= lines) cl = 0;
975             if (oldcl >= 0) {
976                 glk_window_move_cursor(gg_statuswin, 3, oldcl+6);
977                 print (char) ' ';
978             }
979             oldcl = cl;
980             glk_window_move_cursor(gg_statuswin, 3, oldcl+6);
981             print (char) '>';
982         }
983         pkey = KeyCharPrimitive(gg_statuswin, true);
984         if (pkey == $80000000) jump ReDisplay;
985         if (pkey == NKEY1__KY or NKEY2__KY or $fffffffb) {
986             cl++;
987             if (cl >= lines) cl = 0;
988             continue;
989         }
990         if (pkey == PKEY1__KY or PKEY2__KY or $fffffffc) {
991             cl--;
992             if (cl < 0) cl = lines-1;
993             continue;
994         }
995         if (pkey == QKEY1__KY or QKEY2__KY or $fffffff8 or $fffffffe) break;
996         if (pkey == $fffffffa or $fffffffd) {
997             glk_set_window(gg_mainwin);
998             new_line; new_line; new_line;
999             menu_item = cl+1;
1000             EntryR();
1001             glk_window_clear(gg_statuswin);
1002             glk_window_clear(gg_mainwin);
1003             glk_set_window(gg_statuswin);
1004             StatusLineHeight(1);
1005             glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4);
1006             winwid = gg_arguments-->0;
1007             winhgt = gg_arguments-->1;
1008             glk_set_style(style_Subheader);
1009             glk_window_move_cursor(gg_statuswin, winwid/2-item_width, 0);
1010             print (string) item_name;
1011             glk_set_style(style_Normal);
1012             glk_set_window(gg_mainwin);
1013             new_line;
1014             i = ChoiceR();
1015             if (i == 2) jump ReDisplay;
1016             if (i == 3) break;
1017             L__M(##Miscellany, 53);
1018             pkey = KeyCharPrimitive(gg_mainwin, 1);
1019             jump ReDisplay;
1020         }
1021     }
1022
1023     ! done with this menu...
1024     menu_nesting--;
1025     if (menu_nesting > 0) rfalse;
1026     glk_set_window(gg_mainwin);
1027     glk_window_clear(gg_mainwin);
1028     new_line; new_line; new_line;
1029     if (deadflag == 0) <<Look>>;
1030 ];
1031
1032 #Endif; ! TARGET_
1033
1034 ! ----------------------------------------------------------------------------
1035 !   A cunning routine (which could have been a daemon, but isn't, for the
1036 !   sake of efficiency) to move objects which could be in many rooms about
1037 !   so that the player never catches one not in place
1038 ! ----------------------------------------------------------------------------
1039
1040 [ MoveFloatingObjects i k l m address flag;
1041     if (location == player or nothing) return;
1042     objectloop (i) {
1043         address = i.&found_in;
1044         if (address && i hasnt non_floating && ~~IndirectlyContains(player, i)) {
1045             if (metaclass(address-->0) == Routine)
1046                 flag = i.found_in();
1047             else {
1048                 flag = false;
1049                 k = i.#found_in/WORDSIZE;
1050                 for (l=0 : l<k : l++) {
1051                     m = address-->l;
1052                     if ((m in Class && location ofclass m) ||
1053                             m == location || m in location) {
1054                         flag = true;
1055                         break;
1056                     }
1057                 }
1058             }
1059             if (flag) {
1060                 if (i notin location) move i to location;
1061             } else {
1062                 if (parent(i)) remove i;
1063             }
1064         }
1065     }
1066 ];
1067
1068 ! ----------------------------------------------------------------------------
1069 !   Two little routines for moving the player safely.
1070 ! ----------------------------------------------------------------------------
1071
1072 [ PlayerTo newplace flag;
1073     NoteDeparture();
1074     move player to newplace;
1075     while (parent(newplace)) newplace = parent(newplace);
1076     location = real_location = newplace;
1077     MoveFloatingObjects(); AdjustLight(1);
1078     switch (flag) {
1079       0:    <Look>;
1080       1:    NoteArrival(); ScoreArrival();
1081       2:    LookSub(1);
1082     }
1083 ];
1084
1085 [ MovePlayer direc; <Go direc>; <Look>; ];
1086
1087 ! ----------------------------------------------------------------------------
1088 !   The handy YesOrNo routine, and some "meta" verbs
1089 ! ----------------------------------------------------------------------------
1090
1091 [ YesOrNo noStatusRedraw
1092     i j;
1093     for (::) {
1094         #Ifdef TARGET_ZCODE;
1095         if (location == nothing || parent(player) == nothing || noStatusRedraw)
1096             read buffer parse;
1097         else read buffer parse DrawStatusLine;
1098         j = parse->1;
1099         #Ifnot; ! TARGET_GLULX;
1100         noStatusRedraw = 0; ! suppress warning
1101         KeyboardPrimitive(buffer, parse);
1102         j = parse-->0;
1103         #Endif; ! TARGET_
1104         if (j) { ! at least one word entered
1105             i = parse-->1;
1106             if (i == YES1__WD or YES2__WD or YES3__WD) rtrue;
1107             if (i == NO1__WD  or NO2__WD  or NO3__WD) rfalse;
1108         }
1109         L__M(##Quit, 1); print "> ";
1110     }
1111 ];
1112
1113 #Ifdef TARGET_ZCODE;
1114
1115 [ QuitSub;
1116     L__M(##Quit, 2);
1117     if (YesOrNo()) quit;
1118 ];
1119
1120 [ RestartSub;
1121     L__M(##Restart, 1);
1122     if (YesOrNo()) { @restart; L__M(##Restart, 2); }
1123 ];
1124
1125 [ RestoreSub;
1126     restore Rmaybe;
1127     return L__M(##Restore, 1);
1128   .RMaybe;
1129     if (AfterRestore() == 1)
1130         LibraryExtensions.RunAll(ext_afterrestore);
1131     else
1132         L__M(##Restore, 2);
1133 ];
1134
1135 [ SaveSub flag;
1136     #IfV5;
1137     @save -> flag;
1138     switch (flag) {
1139       0: L__M(##Save, 1);
1140       1:
1141          if (AfterSave() == 1)
1142              LibraryExtensions.RunAll(ext_aftersave);
1143          else
1144              L__M(##Save, 2);
1145       2:
1146         RestoreColours();
1147         if (AfterRestore() == 1)
1148             LibraryExtensions.RunAll(ext_afterrestore);
1149         else
1150             L__M(##Restore, 2);
1151     }
1152     #Ifnot;
1153     save Smaybe;
1154     return L__M(##Save, 1);
1155   .SMaybe;
1156     if (AfterSave() == 1)
1157         LibraryExtensions.RunAll(ext_aftersave);
1158     else
1159         L__M(##Save, 2);
1160     #Endif; ! V5
1161 ];
1162
1163 [ VerifySub;
1164     @verify ?Vmaybe;
1165     jump Vwrong;
1166   .Vmaybe;
1167     return L__M(##Verify, 1);
1168   .Vwrong;
1169     L__M(##Verify, 2);
1170 ];
1171
1172 [ ScriptOnSub;
1173     transcript_mode = ((HDR_GAMEFLAGS-->0) & 1);
1174     if (transcript_mode) return L__M(##ScriptOn, 1);
1175     @output_stream 2;
1176     if (((HDR_GAMEFLAGS-->0) & 1) == 0) return L__M(##ScriptOn, 3);
1177     L__M(##ScriptOn, 2);
1178     transcript_mode = true;
1179 ];
1180
1181 [ ScriptOffSub;
1182     transcript_mode = ((HDR_GAMEFLAGS-->0) & 1);
1183     if (transcript_mode == false) return L__M(##ScriptOff, 1);
1184     L__M(##ScriptOff, 2);
1185     @output_stream -2;
1186     if ((HDR_GAMEFLAGS-->0) & 1) return L__M(##ScriptOff, 3);
1187     transcript_mode = false;
1188 ];
1189
1190 [ CommandsOnSub;
1191     @output_stream 4;
1192     xcommsdir = 1;
1193     L__M(##CommandsOn, 1);
1194 ];
1195
1196 [ CommandsOffSub;
1197     if (xcommsdir == 1) @output_stream -4;
1198     xcommsdir = 0;
1199     L__M(##CommandsOff, 1);
1200 ];
1201
1202 [ CommandsReadSub;
1203     @input_stream 1;
1204     xcommsdir = 2;
1205     L__M(##CommandsRead, 1);
1206 ];
1207
1208 #Ifnot; ! TARGET_GLULX;
1209
1210 [ QuitSub;
1211     L__M(##Quit, 2);
1212     if (YesOrNo()) quit;
1213 ];
1214
1215 [ RestartSub;
1216     L__M(##Restart,1);
1217     if (YesOrNo()) { @restart; L__M(##Restart, 2); }
1218 ];
1219
1220 [ RestoreSub res fref;
1221     fref = glk_fileref_create_by_prompt($01, $02, 0);
1222     if (fref == 0) jump RFailed;
1223     gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK);
1224     glk_fileref_destroy(fref);
1225     if (gg_savestr == 0) jump RFailed;
1226     @restore gg_savestr res;
1227     glk_stream_close(gg_savestr, 0);
1228     gg_savestr = 0;
1229   .RFailed;
1230     L__M(##Restore, 1);
1231 ];
1232
1233 [ SaveSub res fref retval;
1234     fref = glk_fileref_create_by_prompt($01, $01, 0);
1235     if (fref == 0) jump SFailed;
1236     gg_savestr = glk_stream_open_file(fref, $01, GG_SAVESTR_ROCK);
1237     glk_fileref_destroy(fref);
1238     if (gg_savestr == 0) jump SFailed;
1239     @save gg_savestr res;
1240     if (res == -1) {
1241         ! The player actually just typed "restore". We're going to print
1242         !  L__M(##Restore,2); the Z-Code Inform library does this correctly
1243         ! now. But first, we have to recover all the Glk objects; the values
1244         ! in our global variables are all wrong.
1245         GGRecoverObjects();
1246         glk_stream_close(gg_savestr, 0);
1247         gg_savestr = 0;
1248         if (AfterRestore() == 1)
1249             return LibraryExtensions.RunAll(ext_afterrestore);
1250         else
1251             return L__M(##Restore, 2);
1252         return retval;
1253     }
1254     glk_stream_close(gg_savestr, 0);
1255     gg_savestr = 0;
1256     if (res == 0) {
1257         if (AfterSave() == 1)
1258             return LibraryExtensions.RunAll(ext_aftersave);
1259         else
1260             return L__M(##Save, 2);
1261     }
1262   .SFailed;
1263     L__M(##Save, 1);
1264 ];
1265
1266 [ VerifySub res;
1267     @verify res;
1268     if (res == 0) return L__M(##Verify, 1);
1269     L__M(##Verify, 2);
1270 ];
1271
1272 [ ScriptOnSub;
1273     if (gg_scriptstr) return L__M(##ScriptOn, 1);
1274     if (gg_scriptfref == 0) {
1275         gg_scriptfref = glk_fileref_create_by_prompt($102, $05, GG_SCRIPTFREF_ROCK);
1276         if (gg_scriptfref == 0) jump S1Failed;
1277     }
1278     gg_scriptstr = glk_stream_open_file(gg_scriptfref, $05, GG_SCRIPTSTR_ROCK);
1279     if (gg_scriptstr == 0) jump S1Failed;
1280     glk_window_set_echo_stream(gg_mainwin, gg_scriptstr);
1281     L__M(##ScriptOn, 2);
1282     return;
1283   .S1Failed;
1284     L__M(##ScriptOn, 3);
1285 ];
1286
1287 [ ScriptOffSub;
1288     if (gg_scriptstr == 0) return L__M(##ScriptOff,1);
1289     L__M(##ScriptOff, 2);
1290     glk_stream_close(gg_scriptstr, 0);
1291     gg_scriptstr = 0;
1292 ];
1293
1294 [ CommandsOnSub fref;
1295     if (gg_commandstr) {
1296         if (gg_command_reading) return L__M(##CommandsOn, 2);
1297         else                    return L__M(##CommandsOn, 3);
1298     }
1299     fref = glk_fileref_create_by_prompt($103, $01, 0);
1300     if (fref == 0) return L__M(##CommandsOn, 4);
1301     gg_command_reading = false;
1302     gg_commandstr = glk_stream_open_file(fref, $01, GG_COMMANDWSTR_ROCK);
1303     glk_fileref_destroy(fref);
1304     if (gg_commandstr == 0) return L__M(##CommandsOn, 4);
1305     L__M(##CommandsOn, 1);
1306 ];
1307
1308 [ CommandsOffSub;
1309     if (gg_commandstr == 0) return L__M(##CommandsOff, 2);
1310     if (gg_command_reading) return L__M(##CommandsRead, 5);
1311     glk_stream_close(gg_commandstr, 0);
1312     gg_commandstr = 0;
1313     gg_command_reading = false;
1314     L__M(##CommandsOff, 1);
1315 ];
1316
1317 [ CommandsReadSub fref;
1318     if (gg_commandstr) {
1319         if (gg_command_reading) return L__M(##CommandsRead, 2);
1320         else                    return L__M(##CommandsRead, 3);
1321     }
1322     fref = glk_fileref_create_by_prompt($103, $02, 0);
1323     if (fref == 0) return L__M(##CommandsRead, 4);
1324     gg_command_reading = true;
1325     gg_commandstr = glk_stream_open_file(fref, $02, GG_COMMANDRSTR_ROCK);
1326     glk_fileref_destroy(fref);
1327     if (gg_commandstr == 0) return L__M(##CommandsRead, 4);
1328     L__M(##CommandsRead, 1);
1329 ];
1330
1331 #Endif; ! TARGET_;
1332
1333 [ NotifyOnSub;  notify_mode = true; L__M(##NotifyOn);  ];
1334 [ NotifyOffSub; notify_mode = false; L__M(##NotifyOff); ];
1335
1336 [ Places1TallSub;
1337         places_style = NEWLINE_BIT+INDENT_BIT+FULLINV_BIT;
1338         <Places>;
1339 ];
1340
1341 [ Places1WideSub;
1342         places_style = ENGLISH_BIT+FULLINV_BIT;
1343         <Places>;
1344 ];
1345
1346 [ Places1Sub i j k;
1347     objectloop (i has visited) j++;
1348     if (j == 0)
1349         return L__M(##Places, 3);
1350
1351     L__M(##Places, 1);
1352     if (places_style & INDENT_BIT) print (string) COLON__TX, "^";
1353     else print " ";
1354
1355     objectloop (i has visited) {
1356         if (places_style & INDENT_BIT) Print__Spaces(2);
1357         print (name) i; k++;
1358
1359         if (places_style & INDENT_BIT)
1360             new_line;
1361         else {
1362             if (k == j)
1363                 return L__M(##Places, 2);
1364             if (k == j-1)
1365                 print (SerialComma) j, (string) AND__TX;
1366             else
1367                 print (string) COMMA__TX;
1368         }
1369     }
1370 ];
1371
1372 [ Objects1TallSub;
1373         objects_style = NEWLINE_BIT+INDENT_BIT+FULLINV_BIT;
1374         <Objects>;
1375 ];
1376
1377 [ Objects1WideSub;
1378         objects_style = ENGLISH_BIT+FULLINV_BIT;
1379         <Objects>;
1380 ];
1381
1382 [ Objects1Sub i j f object_count;
1383     object_count = 0;
1384     objectloop (i has moved) {
1385         object_count++;
1386     }
1387     if (object_count == 0)
1388         return L__M(##Objects, 2);
1389
1390     L__M(##Objects, 1);
1391     if (objects_style & INDENT_BIT) print (string) COLON__TX, "^";
1392     else print " ";
1393
1394     objectloop (i has moved) {
1395       f++;
1396       if (objects_style & INDENT_BIT) Print__Spaces(2);
1397       print (the) i;
1398        j = parent(i);
1399         if (j) {
1400            if (j == player) {
1401                if (i has worn) L__M(##Objects, 3, j, i);
1402                else            L__M(##Objects, 4, j, i);
1403                 jump Obj__Ptd;
1404             }
1405             if (j has animate)   { L__M(##Objects, 5, j, i); jump Obj__Ptd; }
1406             if (j has visited)   { L__M(##Objects, 6, j, i); jump Obj__Ptd; }
1407             if (j has container) { L__M(##Objects, 8, j, i); jump Obj__Ptd; }
1408             if (j has supporter) { L__M(##Objects, 9, j, i); jump Obj__Ptd; }
1409             if (j has enterable) { L__M(##Objects, 7, j, i); jump Obj__Ptd; }
1410         }
1411         L__M(##Objects, 10, j, i);
1412
1413       .Obj__Ptd;
1414
1415         if ((object_count - f) >= 1) {
1416             if (objects_style & INDENT_BIT) {
1417                 Print__Spaces(2);
1418                 new_line;
1419              } else {
1420                  if ((object_count - f) == 1) {
1421                      SerialComma(object_count);
1422                      print " ", (address) AND1__WD, " ";
1423                  } else if (object_count > 2)
1424                      print (string) COMMA__TX;
1425              }
1426         }
1427     }
1428
1429     if ((objects_style & INDENT_BIT) == 0)
1430         print ".^";
1431     else
1432         print "^";
1433 ];
1434
1435 ! ----------------------------------------------------------------------------
1436 !   The scoring system
1437 ! ----------------------------------------------------------------------------
1438
1439 [ ScoreSub;
1440     #Ifdef NO_SCORE;
1441     if (deadflag == 0) L__M(##Score, 2);
1442     #Ifnot;
1443     if (deadflag) new_line;
1444     L__M(##Score, 1);
1445     if(PrintRank() == false) LibraryExtensions.RunAll(ext_printrank);
1446     #Endif; ! NO_SCORE
1447 ];
1448
1449 #Ifndef TaskScore;
1450 [ TaskScore i;
1451     return task_scores->i;
1452 ];
1453 #Endif;
1454
1455 [ Achieved num;
1456     if (task_done->num == 0) {
1457         task_done->num = 1;
1458         score = score + TaskScore(num);
1459     }
1460 ];
1461
1462 [ PANum m n;
1463     print "  ";
1464     n = m;
1465     if (n < 0)    { n = -m; n = n*10; }
1466     if (n < 10)   { print "   "; jump Panuml; }
1467     if (n < 100)  { print "  "; jump Panuml; }
1468     if (n < 1000) { print " "; }
1469
1470   .Panuml;
1471
1472     print m, " ";
1473 ];
1474
1475 [ FullScoreSub i;
1476     ScoreSub();
1477     if (score == 0 || TASKS_PROVIDED == 1) rfalse;
1478     new_line;
1479     L__M(##FullScore, 1);
1480     for (i=0 : i<NUMBER_TASKS : i++)
1481         if (task_done->i == 1) {
1482             PANum(TaskScore(i));
1483             if(PrintTaskName(i) == false)
1484                 LibraryExtensions.RunAll(ext_printtaskname,i);
1485         }
1486     if (things_score) {
1487         PANum(things_score);
1488         L__M(##FullScore, 2);
1489     }
1490     if (places_score) {
1491         PANum(places_score);
1492         L__M(##FullScore, 3);
1493     }
1494     new_line; PANum(score); L__M(##FullScore, 4);
1495 ];
1496
1497 ! ----------------------------------------------------------------------------
1498 !   Real verbs start here: Inventory
1499 ! ----------------------------------------------------------------------------
1500
1501 [ InvWideSub;
1502     if (actor == player)
1503         inventory_style = ENGLISH_BIT+FULLINV_BIT+RECURSE_BIT;
1504     else
1505         inventory_style = ENGLISH_BIT+PARTINV_BIT;
1506     <Inv, actor>;
1507 ];
1508
1509 [ InvTallSub;
1510     if (actor == player)
1511         inventory_style = NEWLINE_BIT+INDENT_BIT+FULLINV_BIT+RECURSE_BIT;
1512     else
1513         inventory_style = NEWLINE_BIT+INDENT_BIT+PARTINV_BIT;
1514     <Inv, actor>;
1515 ];
1516
1517 [ InvSub x;
1518     if (child(actor) == 0)   return L__M(##Inv, 1);
1519     if (inventory_style == 0)
1520         if (actor == player) return InvTallSub();
1521         else                 return InvWideSub();
1522     L__M(##Inv, 2);
1523     if (inventory_style & NEWLINE_BIT) L__M(##Inv, 3); else print " ";
1524
1525     WriteListFrom(child(actor), inventory_style, 1);
1526     if (inventory_style & ENGLISH_BIT) L__M(##Inv, 4);
1527
1528     #Ifndef MANUAL_PRONOUNS;
1529     objectloop (x in player) PronounNotice(x);
1530     #Endif;
1531     x = 0; ! To prevent a "not used" error
1532     AfterRoutines();
1533 ];
1534
1535 ! ----------------------------------------------------------------------------
1536 !   The object tree and determining the possibility of moves
1537 ! ----------------------------------------------------------------------------
1538
1539 [ CommonAncestor o1 o2 i j;
1540     ! Find the nearest object indirectly containing o1 and o2,
1541     ! or return 0 if there is no common ancestor.
1542     i = o1;
1543     while (i) {
1544         j = o2;
1545         while (j) {
1546             if (j == i) return i;
1547             j = parent(j);
1548         }
1549         i = parent(i);
1550     }
1551     return 0;
1552 ];
1553
1554 [ IndirectlyContains o1 o2;
1555     ! Does o1 indirectly contain o2?  (Same as testing if their common ancestor is o1.)
1556     while (o2) {
1557         if (o1 == o2) rtrue;
1558         if (o2 ofclass Class) rfalse;
1559         o2 = parent(o2);
1560     }
1561     rfalse;
1562 ];
1563
1564 [ ObjectScopedBySomething item i j k l m;
1565     i = item;
1566     objectloop (j .& add_to_scope) {
1567         l = j.&add_to_scope;
1568         k = (j.#add_to_scope)/WORDSIZE;
1569         if (l-->0 ofclass Routine) continue;
1570         for (m=0 : m<k : m++)
1571             if (l-->m == i) return j;
1572     }
1573     rfalse;
1574 ];
1575
1576 [ ObjectIsUntouchable item flag1 flag2 ancestor i;
1577     ! Determine if there's any barrier preventing the actor from moving
1578     ! things to "item".  Return false if no barrier; otherwise print a
1579     ! suitable message and return true.
1580     ! If flag1 is set, do not print any message.
1581     ! If flag2 is set, also apply Take/Remove restrictions.
1582
1583     ! If the item has been added to scope by something, it's first necessary
1584     ! for that something to be touchable.
1585
1586     ancestor = CommonAncestor(actor, item);
1587     if (ancestor == 0) {
1588         ancestor = item;
1589         while (ancestor && (i = ObjectScopedBySomething(ancestor)) == 0)
1590             ancestor = parent(ancestor);
1591         if (i) {
1592             if (ObjectIsUntouchable(i, flag1, flag2)) return;
1593             ! An item immediately added to scope
1594         }
1595     }
1596     else
1597
1598     ! First, a barrier between the actor and the ancestor.  The actor
1599     ! can only be in a sequence of enterable objects, and only closed
1600     ! containers form a barrier.
1601
1602     if (actor ~= ancestor) {
1603         i = parent(actor);
1604         while (i ~= ancestor) {
1605             if (i has container && i hasnt open) {
1606                 if (flag1) rtrue;
1607                 return L__M(##Take, 9, i, noun);
1608             }
1609             i = parent(i);
1610         }
1611     }
1612
1613     ! Second, a barrier between the item and the ancestor.  The item can
1614     ! be carried by someone, part of a piece of machinery, in or on top
1615     ! of something and so on.
1616
1617     i = parent(item);
1618     if (item ~= ancestor && i ~= player) {
1619         while (i ~= ancestor) {
1620             if (flag2 && i hasnt container && i hasnt supporter) {
1621                 if (i has animate) {
1622                     if (flag1) rtrue;
1623                     return L__M(##Take, 6, i, noun);
1624                 }
1625                 if (i has transparent) {
1626                     if (flag1) rtrue;
1627                     return L__M(##Take, 7, i, noun);
1628                 }
1629                 if (flag1) rtrue;
1630                 return L__M(##Take, 8, item, noun);
1631             }
1632             if (i has container && i hasnt open) {
1633                 if (flag1) rtrue;
1634                 return L__M(##Take, 9, i, noun);
1635             }
1636             i = parent(i);
1637         }
1638     }
1639     rfalse;
1640 ];
1641
1642 [ AttemptToTakeObject item
1643     ancestor after_recipient i k;
1644     ! Try to transfer the given item to the actor: return false
1645     ! if successful, true if unsuccessful, printing a suitable message
1646     ! in the latter case.
1647     ! People cannot ordinarily be taken.
1648     if (item == actor) return L__M(##Take, 2, noun);
1649     if (item has animate) return L__M(##Take, 3, item);
1650
1651     ancestor = CommonAncestor(actor, item);
1652
1653     if (ancestor == 0) {
1654         i = ObjectScopedBySomething(item);
1655         if (i) ancestor = CommonAncestor(actor, i);
1656     }
1657
1658     ! Is the actor indirectly inside the item?
1659     if (ancestor == item) return L__M(##Take, 4, item);
1660
1661     ! Does the actor already directly contain the item?
1662     if (item in actor) return L__M(##Take, 5, item);
1663
1664     ! Can the actor touch the item, or is there (e.g.) a closed container
1665     ! in the way?
1666     if (ObjectIsUntouchable(item, false, true)) rtrue;
1667
1668     ! The item is now known to be accessible.
1669
1670     ! Consult the immediate possessor of the item, if it's in a container
1671     ! which the actor is not in.
1672
1673     i = parent(item);
1674     if (i && i ~= ancestor && (i has container or supporter)) {
1675         after_recipient = i;
1676         k = action; action = ##LetGo;
1677         if (RunRoutines(i, before)) { action = k; rtrue; }
1678         action = k;
1679     }
1680
1681     if (item has static)  return L__M(##Take, 11, item);
1682     if (item has scenery) return L__M(##Take, 10, item);
1683
1684     ! The item is now known to be available for taking.  Is the player
1685     ! carrying too much?  If so, possibly juggle items into the rucksack
1686     ! to make room.
1687
1688     if (ObjectDoesNotFit(item, actor) ||
1689         LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, item, actor)) return;
1690     if (AtFullCapacity(item, actor)) return L__M(##Take, 12, item);
1691
1692     ! Transfer the item.
1693
1694     move item to actor; give item ~worn;
1695
1696     ! Send "after" message to the object letting go of the item, if any.
1697
1698     if (after_recipient) {
1699         k = action; action = ##LetGo;
1700         if (RunRoutines(after_recipient, after)) { action = k; rtrue; }
1701         action = k;
1702     }
1703     rfalse;
1704 ];
1705
1706 [ AtFullCapacity n s
1707     obj k;
1708     n = n; ! suppress compiler warning
1709     if (s == actor) {
1710         objectloop (obj in s)
1711             if (obj hasnt worn) k++;
1712     } else
1713         k = children(s);
1714
1715     if (k < RunRoutines(s, capacity) || (s == player && RoomInSack())) rfalse;
1716 ];
1717
1718 [ RoomInSack
1719     obj ks;
1720     if (SACK_OBJECT && SACK_OBJECT in player) {
1721         ks = keep_silent; keep_silent = 2;
1722         for (obj=youngest(player) : obj : obj=elder(obj))
1723             if (obj ~= SACK_OBJECT && obj hasnt worn or light) {
1724                 <Insert obj SACK_OBJECT>;
1725                 if (obj in SACK_OBJECT) {
1726                     keep_silent = ks;
1727                     return L__M(##Take, 13, obj, SACK_OBJECT);
1728                 }
1729             }
1730         keep_silent = ks;
1731     }
1732     rfalse;
1733 ];
1734
1735 ! ----------------------------------------------------------------------------
1736 !   Support for implicit actions
1737 ! ----------------------------------------------------------------------------
1738
1739 [ CheckImplicitAction act o1 o2
1740     sav_act sav_noun sav_sec res;
1741     if (o1 provides before_implicit) {
1742         sav_act  = action; action = act;
1743         sav_noun = noun;   noun   = o1;
1744         if (o2) { sav_sec  = second; second = o2; }
1745         res = RunRoutines(o1, before_implicit);
1746         action = sav_act; noun = sav_noun;
1747         if (sav_sec) second = sav_sec;
1748     }
1749     else {
1750         if (no_implicit_actions)
1751             res = 2;
1752         else
1753             res = 0;
1754     }
1755     return res;
1756 ];
1757
1758 [ ImplicitTake obj
1759     res ks supcon;
1760     switch (metaclass(obj)) { Class, String, Routine, nothing: rfalse; }
1761     if (obj in actor) rfalse;
1762     if (action_to_be == ##Drop && ~~IndirectlyContains(actor, obj)) rfalse;
1763     res = CheckImplicitAction(##Take, obj);
1764     ! 0 = Take object, Tell the user (normal default)
1765     ! 1 = Take object, don't Tell
1766     ! 2 = don't Take object  continue       (default with no_implicit_actions)
1767     ! 3 = don't Take object, don't continue
1768     if (res >= 2) rtrue;
1769     if (parent(obj) && parent(obj) has container or supporter) supcon = parent(obj);
1770     ks = keep_silent; keep_silent = 2; AttemptToTakeObject(obj); keep_silent = ks;
1771     if (obj notin actor) rtrue;
1772     if (res == 0 && ~~keep_silent)
1773         if (supcon) return L__M(##Miscellany, 58, obj, supcon);
1774         else        return L__M(##Miscellany, 26, obj);
1775     rfalse;
1776 ];
1777
1778 [ ImplicitExit obj
1779     res ks;
1780     if (parent(obj) == nothing) rfalse;
1781     res = CheckImplicitAction(##Exit, obj);
1782     ! 0 = Exit object, Tell the user (normal default)
1783     ! 1 = Exit object, don't Tell
1784     ! 2 = don't Exit object  continue       (default with no_implicit_actions)
1785     ! 3 = don't Exit object, don't continue
1786     if (res >= 2) rtrue;
1787     ks = keep_silent; keep_silent = 2; <Exit obj, actor>; keep_silent = ks;
1788     if (parent(actor) == obj) rtrue;
1789     if (res == 0 && ~~keep_silent) L__M(##Exit, 5, obj);
1790     rfalse;
1791 ];
1792
1793 [ ImplicitClose obj
1794     res ks;
1795     if (obj hasnt open) rfalse;
1796     res = CheckImplicitAction(##Close, obj);
1797     ! 0 = Close object, Tell the user (normal default)
1798     ! 1 = Close object, don't Tell
1799     ! 2 = don't Close object  continue       (default with no_implicit_actions)
1800     ! 3 = don't Close object, don't continue
1801     if (res >= 2) rtrue;
1802     ks = keep_silent; keep_silent = 2; <Close obj, actor>; keep_silent = ks;
1803     if (obj has open) rtrue;
1804     if (res == 0 && ~~keep_silent) L__M(##Close, 4, obj);
1805     rfalse;
1806 ];
1807
1808 [ ImplicitOpen obj
1809     res temp inp1temp;
1810     if (obj has open) rfalse;
1811     res = CheckImplicitAction(##Open, obj);
1812     ! 0 = Open object, Tell the user (normal default)
1813     ! 1 = Open object, don't Tell
1814     ! 2 = don't Open object  continue       (default with no_implicit_actions)
1815     ! 3 = don't Open object, don't continue
1816     if (res >= 2) rtrue;
1817     if (obj has locked) rtrue;
1818     temp = keep_silent; keep_silent = 2; <Open obj, actor>; keep_silent = temp;
1819     if (obj hasnt open) rtrue;
1820     if (res == 0 && ~~keep_silent) L__M(##Open, 6, obj);
1821     temp = action; action = ##Open;
1822     inp1temp = inp1; inp1 = obj;
1823     AfterRoutines();
1824     inp1 = inp1temp; action = temp;
1825     rfalse;
1826 ];
1827
1828 [ ImplicitUnlock obj;
1829     if (obj has locked) rtrue;
1830     rfalse;
1831 ];
1832
1833 [ ImplicitDisrobe obj
1834     res ks;
1835     if (obj hasnt worn) rfalse;
1836     res = CheckImplicitAction(##Disrobe, obj);
1837     ! 0 = Take off object, Tell the user (normal default)
1838     ! 1 = Take off object, don't Tell
1839     ! 2 = don't Take off object  continue       (default with no_implicit_actions)
1840     ! 3 = don't Take off object, don't continue
1841     if (res >= 2) rtrue;
1842     ks = keep_silent; keep_silent = 1; <Disrobe obj, actor>; keep_silent = ks;
1843     if (obj has worn && obj in actor) rtrue;
1844     if (res == 0 && ~~keep_silent) L__M(##Disrobe, 3, obj);
1845     rfalse;
1846 ];
1847
1848
1849 ! ----------------------------------------------------------------------------
1850 !   Object movement verbs
1851 ! ----------------------------------------------------------------------------
1852
1853 [ TakeSub;
1854     if (noun in actor && noun has worn) <<Disrobe noun, actor>>;
1855     if (onotheld_mode == 0 || noun notin actor)
1856         if (AttemptToTakeObject(noun)) return;
1857     if (AfterRoutines()) return;
1858     notheld_mode = onotheld_mode;
1859     if (notheld_mode == 1 || keep_silent) return;
1860     L__M(##Take, 1, noun);
1861 ];
1862
1863 [ RemoveSub i;
1864     i = parent(noun);
1865     if (i && i has container && i hasnt open && ImplicitOpen(i)) return L__M(##Remove, 1, i);
1866     if (noun has clothing) {
1867         if (noun has worn) <<Disrobe noun, actor>>;
1868         if (i && i has container or supporter)
1869             jump RSSkip;
1870         else
1871             if (i == actor) return L__M(##Disrobe, 1, noun);
1872     }
1873     if (i hasnt container && i hasnt supporter) return L__M(##Remove, 4, noun);
1874     if (i ~= second && second ~= nothing) return L__M(##Remove, 2, noun);
1875     if (i has animate) return L__M(##Take, 6, i, noun);
1876
1877     .RSSkip;
1878     if (AttemptToTakeObject(noun)) rtrue;
1879     action = ##Remove; if (AfterRoutines()) return;
1880     action = ##Take;   if (AfterRoutines()) return;
1881     if (keep_silent) return;
1882     L__M(##Remove, 3, noun);
1883 ];
1884
1885 [ DropSub;
1886     if (noun == actor)         return L__M(##PutOn, 4, noun);
1887     if (noun in parent(actor)) return L__M(##Drop, 1, noun);
1888     if (noun has worn && no_implicit_actions) return L__M(##Disrobe, 4, noun);
1889     if (noun has worn && ImplicitDisrobe(noun)) return;
1890     if (noun notin actor) {
1891         if (no_implicit_actions) return L__M(##Drop, 4, noun, parent(noun));
1892         L__M(##Miscellany, 58, noun, parent(noun));
1893     }
1894
1895     move noun to parent(actor);
1896     if (AfterRoutines() || keep_silent) return;
1897     L__M(##Drop, 3, noun);
1898 ];
1899
1900 [ PutOnSub ancestor;
1901     receive_action = ##PutOn;
1902     if (second == d_obj || actor in second) <<Drop noun, actor>>;
1903     if (parent(noun) == second) return L__M(##Drop, 1, noun);
1904     if (ImplicitTake(noun) && noun notin actor) return L__M(##PutOn, 1, noun);
1905
1906     ancestor = CommonAncestor(noun, second);
1907     if (ancestor == noun) return L__M(##PutOn, 2, noun);
1908     if (ObjectIsUntouchable(second)) return;
1909
1910     if (second ~= ancestor) {
1911         action = ##Receive;
1912         if (RunRoutines(second, before)) { action = ##PutOn; return; }
1913         action = ##PutOn;
1914     }
1915     if (second hasnt supporter) return L__M(##PutOn, 3, second);
1916
1917     if (noun has worn && no_implicit_actions) return L__M(##Disrobe, 4, noun);
1918     if (noun has worn && ImplicitDisrobe(noun)) return;
1919
1920     if (ObjectDoesNotFit(noun, second) ||
1921         LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return;
1922     if (AtFullCapacity(noun, second)) return L__M(##PutOn, 6, second);
1923
1924     move noun to second;
1925
1926     if (AfterRoutines()) return;
1927
1928     if (second ~= ancestor) {
1929         action = ##Receive;
1930         if (RunRoutines(second, after)) { action = ##PutOn; return; }
1931         action = ##PutOn;
1932     }
1933     if (keep_silent) return;
1934     if (multiflag) return L__M(##PutOn, 7);
1935     L__M(##PutOn, 8, noun, second);
1936 ];
1937
1938 [ InsertSub ancestor;
1939     receive_action = ##Insert;
1940     if (second == d_obj || actor in second) <<Drop noun, actor>>;
1941     if (parent(noun) == second) return L__M(##Drop, 1, noun);
1942     if (ImplicitTake(noun) && noun notin actor) return L__M(##Insert, 1, noun);
1943     ancestor = CommonAncestor(noun, second);
1944     if (ancestor == noun) return L__M(##Insert, 5, noun);
1945     if (ObjectIsUntouchable(second)) return;
1946     if (second ~= ancestor) {
1947         action = ##Receive;
1948         if (RunRoutines(second,before)) { action = ##Insert; rtrue; }
1949         action = ##Insert;
1950         if (second has container && second hasnt open && ImplicitOpen(second))
1951             return L__M(##Insert, 3, second);
1952     }
1953     if (second hasnt container) return L__M(##Insert, 2, second);
1954     if (noun has worn && no_implicit_actions) return L__M(##Disrobe, 4, noun);
1955     if (noun has worn && ImplicitDisrobe(noun)) return;
1956
1957     if (ObjectDoesNotFit(noun, second) ||
1958         LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return;
1959     if (AtFullCapacity(noun, second)) return L__M(##Insert, 7, second);
1960
1961     move noun to second;
1962
1963     if (AfterRoutines()) rtrue;
1964
1965     if (second ~= ancestor) {
1966         action = ##Receive;
1967         if (RunRoutines(second, after)) { action = ##Insert; rtrue; }
1968         action = ##Insert;
1969     }
1970     if (keep_silent) rtrue;
1971     if (multiflag) return L__M(##Insert, 8, noun);
1972     L__M(##Insert, 9, noun, second);
1973 ];
1974
1975 ! ----------------------------------------------------------------------------
1976 !   Empties and transfers are routed through the actions above
1977 ! ----------------------------------------------------------------------------
1978
1979 [ TransferSub;
1980     if (noun notin actor && AttemptToTakeObject(noun)) return;
1981     if (second has supporter) <<PutOn noun second, actor>>;
1982     if (second == d_obj) <<Drop noun, actor>>;
1983     <<Insert noun second, actor>>;
1984 ];
1985
1986 [ EmptySub; second = d_obj; EmptyTSub(); ];
1987
1988 [ EmptyTSub i j k flag;
1989     if (noun == second) return L__M(##EmptyT, 4, noun);
1990     if (ObjectIsUntouchable(noun)) return;
1991     if (noun hasnt container) return L__M(##EmptyT, 1, noun);
1992     if (noun hasnt open && ImplicitOpen(noun)) return L__M(##EmptyT, 2, noun);
1993     if (second ~= d_obj) {
1994         if (second hasnt supporter) {
1995             if (second hasnt container) return L__M(##EmptyT, 1, second);
1996             if (second hasnt open && ImplicitOpen(second))
1997                 return L__M(##EmptyT, 2, second);
1998         }
1999     }
2000     i = child(noun); k = children(noun);
2001     if (i == 0) return L__M(##EmptyT, 3, noun);
2002     while (i) {
2003         j = sibling(i);
2004         flag = false;
2005         if (ObjectIsUntouchable(noun)) flag = true;
2006         if (noun hasnt container) flag = true;
2007         if (noun hasnt open) flag = true;
2008         if (second ~= d_obj) {
2009             if (second hasnt supporter) {
2010                 if (second hasnt container) flag = true;
2011                 if (second hasnt open) flag = true;
2012             }
2013         }
2014         if (k-- == 0) flag = 1;
2015         if (flag) break;
2016         if (keep_silent == 0) print (name) i, (string) COLON__TX, " ";
2017         <Transfer i second, actor>;
2018         i = j;
2019     }
2020 ];
2021
2022 ! ----------------------------------------------------------------------------
2023 !   Gifts
2024 ! ----------------------------------------------------------------------------
2025
2026 [ GiveSub;
2027     if (noun notin actor && ImplicitTake(noun)) return L__M(##Give, 1, noun);
2028     if (second == actor) return L__M(##Give, 2, noun);
2029     if (noun has worn && ImplicitDisrobe(noun)) return;
2030     if (second == player) {
2031         move noun to player;
2032         return L__M(##Give, 4, noun);
2033     }
2034
2035     if (RunLife(second, ##Give)) return;
2036     L__M(##Give, 3, second);
2037 ];
2038
2039 [ GiveRSub; <Give second noun, actor>; ];
2040
2041 [ ShowSub;
2042     if (noun notin actor && ImplicitTake(noun)) return L__M(##Show, 1, noun);
2043     if (second == player) <<Examine noun, actor>>;
2044     if (RunLife(second, ##Show)) return;
2045     L__M(##Show, 2, second);
2046 ];
2047
2048 [ ShowRSub; <Show second noun, actor>; ];
2049
2050 ! ----------------------------------------------------------------------------
2051 !   Travelling around verbs
2052 ! ----------------------------------------------------------------------------
2053
2054 [ EnterSub ancestor j ks;
2055     if (noun has door || noun in Compass) {
2056         if (verb_word == STAND__TX or SIT__TX or LIE__TX)
2057             return L__M(##Enter, 2, noun, verb_word);
2058         <<Go noun, actor>>;
2059     }
2060
2061     if (actor in noun) return L__M(##Enter, 1, noun);
2062     if (noun hasnt enterable) return L__M(##Enter, 2, noun, verb_word);
2063
2064     if (parent(actor) ~= parent(noun)) {
2065         ancestor = CommonAncestor(actor, noun);
2066         if (ancestor == actor or 0) return L__M(##Enter, 4, noun);
2067         while (actor notin ancestor) {
2068             j = parent(actor);
2069             ks = keep_silent;
2070             if (parent(j) ~= ancestor || noun ~= ancestor) {
2071                 L__M(##Enter, 6, j);
2072                 keep_silent = 1;
2073             }
2074             <Exit, actor>;
2075             keep_silent = ks;
2076             if (actor in j) return;
2077         }
2078         if (actor in noun) return;
2079         if (noun notin ancestor) {
2080             j = parent(noun);
2081             while (parent(j) ~= ancestor) j = parent(j);
2082             L__M(##Enter, 7, j);
2083             ks = keep_silent; keep_silent = 1;
2084             <Enter j, actor>;
2085             keep_silent = ks;
2086             if (actor notin j) return;
2087             <<Enter noun, actor>>;
2088         }
2089     }
2090
2091     if (noun has container && noun hasnt open && ImplicitOpen(noun)) return L__M(##Enter, 3, noun);
2092     move actor to noun;
2093
2094     if (AfterRoutines() || keep_silent) return;
2095     L__M(##Enter, 5, noun);
2096     if (actor == player) Locale(noun);
2097 ];
2098
2099 [ GetOffSub;
2100     if (parent(actor) == noun) <<Exit, actor>>;
2101     L__M(##GetOff, 1, noun);
2102 ];
2103
2104 [ ExitSub p;
2105     p = parent(actor);
2106     if (noun ~= nothing && noun ~= p) return L__M(##Exit, 4 ,noun);
2107     if (p == location || (location == thedark && p == real_location)) {
2108         if (actor provides posture && actor.posture) {
2109             actor.posture = 0;
2110             return L__M(##Exit, 6);
2111         }
2112         if ((location.out_to) || (location == thedark && real_location.out_to))
2113             <<Go out_obj, actor>>;
2114         return L__M(##Exit, 1);
2115     }
2116     if (p has container && p hasnt open && ImplicitOpen(p))
2117         return L__M(##Exit, 2, p);
2118
2119     if (noun == nothing) {
2120         inp1 = p;
2121         if (RunRoutines(p, before)) return;
2122     }
2123
2124     move actor to parent(p);
2125     if (player provides posture) player.posture = 0;
2126
2127     if (AfterRoutines() || keep_silent) return;
2128     L__M(##Exit, 3, p);
2129     if (actor == player && p has container) LookSub(1);
2130 ];
2131
2132 [ VagueGoSub; L__M(##VagueGo); ];
2133
2134 [ GoInSub; <<Go in_obj, actor>>; ];
2135
2136 [ GoSub i j k movewith thedir next_loc;
2137
2138     ! first, check if any PushDir object is touchable
2139     if (second && second notin Compass && ObjectIsUntouchable(second)) return;
2140
2141     movewith = 0;
2142     i = parent(actor);
2143     if ((location ~= thedark && i ~= location) || (location == thedark && i ~= real_location)) {
2144         j = location;
2145         if (location == thedark) location = real_location;
2146         k = RunRoutines(i, before); if (k ~= 3) location = j;
2147         if (k == 1) {
2148            movewith = i; i = parent(i);
2149         }
2150         else {
2151             if (k) rtrue;
2152             if (ImplicitExit(i)) return L__M(##Go, 1, i);
2153             i = parent(actor);
2154         }
2155     }
2156
2157     if (noun.door_dir ~= nothing) {
2158         thedir = noun.door_dir;
2159         if (metaclass(thedir) == Routine)
2160             thedir = RunRoutines(noun, door_dir);
2161         next_loc = i.thedir;
2162     } else
2163         next_loc = noun;
2164
2165     k = metaclass(next_loc);
2166
2167     if (k == String) {
2168         print (string) next_loc;
2169         new_line; rfalse;
2170     }
2171
2172     if (k == Routine) {
2173         next_loc = RunRoutines(i, thedir);
2174         if (next_loc == 1) rtrue;
2175     }
2176
2177     if (k == nothing || next_loc == 0) {
2178         if (i.cant_go ~= 0 or CANTGO__TX) PrintOrRun(i, cant_go);
2179         else                              L__M(##Go, 2);
2180         rfalse;
2181     }
2182
2183     if (next_loc has door) {
2184         if (next_loc has concealed) return L__M(##Go, 2);
2185         if (next_loc hasnt open && ImplicitOpen(next_loc)) {
2186             if (noun == u_obj) return L__M(##Go, 3, next_loc);
2187             if (noun == d_obj) return L__M(##Go, 4, next_loc);
2188             return L__M(##Go, 5, next_loc);
2189         }
2190         k = RunRoutines(next_loc, door_to);
2191         if (k == 0) return L__M(##Go, 6, next_loc);
2192         if (k == 1) rtrue;
2193         next_loc = k;
2194     }
2195
2196     action = ##Going;
2197     if (RunRoutines(next_loc, before)) { action = ##Go; return; }
2198     action = ##Go;
2199
2200     if (movewith == 0) move actor to next_loc; else move movewith to next_loc;
2201     if (actor ~= player) return L__M(##Go, 7);
2202
2203     k = location; location = next_loc;
2204     MoveFloatingObjects();
2205     if (OffersLight(location))
2206         lightflag = true;
2207     else {
2208         lightflag = false;
2209         if (k == thedark) {
2210             if(DarkToDark() == false) ! From real_location To location
2211                 LibraryExtensions.RunAll(ext_darktodark);
2212             if (deadflag) rtrue;
2213         }
2214         location = thedark;
2215     }
2216     NoteDeparture(); real_location = next_loc;
2217     action = ##Going;
2218     if (RunRoutines(prev_location, after)) { action = ##Go; return; }
2219     action = ##Go;
2220     if (AfterRoutines() || keep_silent) return;
2221     LookSub(1);
2222 ];
2223
2224
2225
2226 ! ----------------------------------------------------------------------------
2227 !   Describing the world.  SayWhatsOn(object) does just that (producing
2228 !   no text if nothing except possibly "scenery" and "concealed" items are).
2229 !   Locale(object) runs through the "tail end" of a Look-style room
2230 !   description for the contents of the object, printing up suitable
2231 !   descriptions as it goes.
2232 ! ----------------------------------------------------------------------------
2233
2234
2235 [ SayWhatsOn descon j f;
2236     if (descon == parent(player)) rfalse;
2237     objectloop (j in descon)
2238         if (j hasnt concealed && j hasnt scenery) f = 1;
2239     if (f == 0) rfalse;
2240     L__M(##Look, 4, descon);
2241 ];
2242
2243
2244 [ NotSupportingThePlayer o i;
2245     i = parent(player);
2246     while (i && i ~= visibility_ceiling) {
2247         if (i == o) rfalse;
2248         i = parent(i);
2249         if (i && i hasnt supporter) rtrue;
2250     }
2251     rtrue;
2252 ];
2253
2254
2255 ! Determine if something is casually visible.  Includes things on
2256 ! supporters or in open/transparent containers that are scenery.  Static
2257 ! objects are not included.
2258 [ Locale descin text_without_ALSO text_with_ALSO
2259     o p num_objs must_print_ALSO x flag;
2260
2261     objectloop (o in descin) {
2262         if (o has scenery && (o has supporter || (o has container && o has open or transparent)) && children(o) > 0) {
2263             flag = 0;
2264             objectloop (x in o) {
2265                 if (x hasnt scenery && x hasnt concealed)
2266                     flag++;
2267             }
2268             if (flag > 0) {
2269                 if (num_objs == 0)
2270                     print "^";
2271                 else
2272                     print " ";
2273
2274                 if (o has supporter)
2275                     print "On ", (the) o;
2276                 else
2277                     print "In ", (the) o;
2278
2279                 WriteListFrom(child(o), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT);
2280                 print ".";
2281                 num_objs++;
2282             }
2283         }
2284     }
2285
2286     if (num_objs > 0)
2287         print "^";
2288
2289     objectloop (o in descin)
2290         give o ~workflag;
2291     num_objs = 0;
2292     objectloop (o in descin) {
2293         if (o hasnt concealed && NotSupportingThePlayer(o)) {
2294 #Ifndef MANUAL_PRONOUNS;
2295             PronounNotice(o);
2296 #Endif;
2297             if (o hasnt scenery) {
2298                 give o workflag;
2299                 num_objs++;
2300                 p = initial;
2301                 if ((o has door or container) && o has open && o provides when_open) {
2302                     p = when_open;
2303                     jump Prop_Chosen;
2304                 }
2305                 if ((o has door or container) && o hasnt open && o provides when_closed) {
2306                     p = when_closed;
2307                     jump Prop_Chosen;
2308                 }
2309                 if (o has switchable && o has on && o provides when_on) {
2310                     p = when_on;
2311                     jump Prop_Chosen;
2312                 }
2313                 if (o has switchable && o hasnt on && o provides when_off)
2314                     p = when_off;
2315 .Prop_Chosen;
2316                 if (o.&describe && RunRoutines(o, describe)) {
2317                     must_print_ALSO = true;
2318                     give o ~workflag;
2319                     num_objs--;
2320                     continue;
2321                 }
2322                 if (o.p && (o hasnt moved || p ~= initial)) {
2323                     new_line;
2324                     PrintOrRun(o, p);
2325                     must_print_ALSO = true;
2326                     give o ~workflag;
2327                     num_objs--;
2328                     if (o has supporter && child(o))
2329                         SayWhatsOn(o);
2330                 }
2331             }
2332         }
2333     }
2334
2335     if (num_objs == 0)
2336         return 0;
2337     if (actor ~= player)
2338         give actor concealed;
2339     if (text_without_ALSO) {
2340         new_line;
2341         if (must_print_ALSO)
2342             print (string) text_with_ALSO, " ";
2343         else
2344             print (string) text_without_ALSO, " ";
2345
2346         WriteListFrom(child(descin), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+WORKFLAG_BIT);
2347     } else {
2348         if (must_print_ALSO)
2349             L__M(##Look, 5, descin);
2350         else
2351             L__M(##Look, 6, descin);
2352     }
2353     if (actor ~= player)
2354         give actor ~concealed;
2355     return num_objs;
2356 ];
2357
2358
2359
2360 ! ----------------------------------------------------------------------------
2361 !   Looking.  LookSub(1) is allowed to abbreviate long descriptions, but
2362 !     LookSub(0) (which is what happens when the Look action is generated)
2363 !     isn't.  (Except that these are over-ridden by the player-set lookmode.)
2364 ! ----------------------------------------------------------------------------
2365
2366 [ LMode1Sub; lookmode=1; L__M(##LMode1); ];  ! Brief
2367
2368 [ LMode2Sub; lookmode=2; L__M(##LMode2); ];  ! Verbose
2369
2370 [ LMode3Sub; lookmode=3; L__M(##LMode3); ];  ! Superbrief
2371
2372 [ LModeNormalSub;       ! 'normal' value: the default, or as set in Initialise()
2373     switch (initial_lookmode) {
2374       1:       <<LMode1>>;
2375       3:       <<LMode3>>;
2376       default: <<LMode2>>;
2377     }
2378 ];
2379
2380 [ NoteArrival descin;
2381     if (location ~= lastdesc) {
2382         if (location.initial) PrintOrRun(location, initial);
2383         if (location == thedark) { lastdesc = thedark; return; }
2384         descin = location;
2385         if(NewRoom() == false) LibraryExtensions.RunAll(ext_newroom);
2386         lastdesc = descin;
2387     }
2388 ];
2389
2390 [ NoteDeparture;
2391     prev_location = real_location;
2392 ];
2393
2394 [ ScoreArrival;
2395     if (location hasnt visited) {
2396         give location visited;
2397         if (location has scored) {
2398             score = score + ROOM_SCORE;
2399             places_score = places_score + ROOM_SCORE;
2400         }
2401     }
2402 ];
2403
2404 [ FindVisibilityLevels visibility_levels;
2405     visibility_levels = 1;
2406     visibility_ceiling = parent(player);
2407     while ((parent(visibility_ceiling)) &&
2408                   (visibility_ceiling hasnt container || visibility_ceiling has open or transparent)) {
2409         visibility_ceiling = parent(visibility_ceiling);
2410         visibility_levels++;
2411     }
2412     return visibility_levels;
2413 ];
2414
2415 [ LookSub allow_abbrev  visibility_levels i j k nl_flag;
2416     if (parent(player) == 0) return RunTimeError(10);
2417
2418   .MovedByInitial;
2419
2420     if (location == thedark) { visibility_ceiling = thedark; NoteArrival(); }
2421     else {
2422         visibility_levels = FindVisibilityLevels();
2423         if (visibility_ceiling == location) {
2424             NoteArrival();
2425             if (visibility_ceiling ~= location) jump MovedByInitial;
2426         }
2427     }
2428     ! Printing the top line: e.g.
2429     ! Octagonal Room (on the table) (as Frodo)
2430     new_line;
2431     #Ifdef TARGET_ZCODE;
2432     style bold;
2433     #Ifnot; ! TARGET_GLULX;
2434     glk_set_style(style_Subheader);
2435     #Endif; ! TARGET_
2436     if (visibility_levels == 0) print (name) thedark;
2437     else {
2438         if (visibility_ceiling ~= location) print (The) visibility_ceiling;
2439         else print (name) visibility_ceiling;
2440     }
2441     #Ifdef TARGET_ZCODE;
2442     style roman;
2443     #Ifnot; ! TARGET_GLULX;
2444     glk_set_style(style_Normal);
2445     #Endif; ! TARGET_
2446
2447     for (j=1,i=parent(player) : j<visibility_levels : j++,i=parent(i))
2448         if (i has supporter) L__M(##Look, 1, i);
2449         else                 L__M(##Look, 2, i);
2450
2451     if (print_player_flag == 1) L__M(##Look, 3, player);
2452     new_line;
2453
2454     ! The room description (if visible)
2455
2456     if (lookmode < 3 && visibility_ceiling == location) {
2457         if ((allow_abbrev ~= 1) || (lookmode == 2) || (location hasnt visited)) {
2458             if (location.&describe) RunRoutines(location, describe);
2459             else {
2460                 if (location.description == 0) RunTimeError(11, location, description);
2461                 else PrintOrRun(location, description);
2462             }
2463         }
2464     }
2465
2466     if (visibility_ceiling == location) nl_flag = 1;
2467
2468     if (visibility_levels == 0) Locale(thedark);
2469     else {
2470         for (i=player,j=visibility_levels : j>0 : j--,i=parent(i)) give i workflag;
2471
2472         for (j=visibility_levels : j>0 : j--) {
2473             for (i=player,k=0 : k<j : k++) i=parent(i);
2474             if (i.inside_description) {
2475                 if (nl_flag) new_line; else nl_flag = 1;
2476                     PrintOrRun(i,inside_description);
2477                 }
2478             if (Locale(i)) nl_flag=1;
2479         }
2480     }
2481
2482     if(LookRoutine() == false) LibraryExtensions.RunAll(ext_lookroutine);
2483     ScoreArrival();
2484     action = ##Look;
2485     AfterRoutines();
2486 ];
2487
2488 [ ExamineSub i;
2489     if (location == thedark) return L__M(##Examine, 1, noun);
2490     i = noun.description;
2491     if (i == 0) {
2492         if (noun has container)
2493             if (noun has open or transparent) <<Search noun, actor>>;
2494             else return L__M(##Search, 5, noun);
2495         if (noun has switchable) { L__M(##Examine, 3, noun); rfalse; }
2496         return L__M(##Examine, 2, noun);
2497     }
2498     i = PrintOrRun(noun, description);
2499     if (i == 0 && noun has switchable) L__M(##Examine, 3, noun);
2500     AfterRoutines();
2501 ];
2502
2503 [ LookUnderSub;
2504     if (location == thedark) return L__M(##LookUnder, 1, noun);
2505     L__M(##LookUnder, 2);
2506 ];
2507
2508 [ VisibleContents o  i f;
2509     objectloop (i in o) if (i hasnt concealed or scenery) f++;
2510     return f;
2511 ];
2512
2513 [ SearchSub f;
2514     if (location == thedark) return L__M(##Search, 1, noun);
2515     if (ObjectIsUntouchable(noun)) return;
2516     f = VisibleContents(noun);
2517     if (noun has supporter) {
2518         if (f == 0) return L__M(##Search, 2, noun);
2519         return L__M(##Search, 3, noun);
2520     }
2521     if (noun hasnt container) return L__M(##Search, 4, noun);
2522     if (noun hasnt transparent or open && ImplicitOpen(noun)) return L__M(##Search, 5, noun);
2523     if (AfterRoutines()) return;
2524
2525     if (f == 0) return L__M(##Search, 6, noun);
2526     L__M(##Search, 7, noun);
2527 ];
2528
2529 ! ----------------------------------------------------------------------------
2530 !   Verbs which change the state of objects without moving them
2531 ! ----------------------------------------------------------------------------
2532
2533 [ UnlockSub;
2534     if (ObjectIsUntouchable(noun)) return;
2535     if (noun hasnt lockable)     return L__M(##Unlock, 1, noun);
2536     if (noun hasnt locked)       return L__M(##Unlock, 2, noun);
2537     if ((noun.with_key ofclass Object && noun.with_key ~= second) ||
2538                 (noun.with_key ofclass Routine && noun.with_key() ~= second) ||
2539                  noun.with_key == nothing)
2540                 return L__M(##Unlock, 3, second);
2541
2542     give noun ~locked;
2543
2544     if (AfterRoutines() || keep_silent) return;
2545     L__M(##Unlock, 4, noun);
2546 ];
2547
2548 [ LockSub;
2549     if (ObjectIsUntouchable(noun)) return;
2550     if (noun hasnt lockable) return L__M(##Lock, 1, noun);
2551     if (noun has locked)     return L__M(##Lock, 2 ,noun);
2552     if (noun has open && ImplicitClose(noun)) return L__M(##Lock, 3, noun);
2553     if ((noun.with_key ofclass Object && noun.with_key ~= second) ||
2554                 (noun.with_key ofclass Routine && noun.with_key() ~= second) ||
2555                  noun.with_key == nothing)
2556                 return L__M(##Lock, 4, second);
2557
2558     give noun locked;
2559     if (AfterRoutines() || keep_silent) return;
2560     L__M(##Lock, 5, noun);
2561 ];
2562
2563 [ SwitchOnSub;
2564     if (ObjectIsUntouchable(noun)) return;
2565     if (noun hasnt switchable) return L__M(##SwitchOn, 1, noun);
2566     if (noun has on)           return L__M(##SwitchOn, 2, noun);
2567
2568     give noun on;
2569     if (AfterRoutines() || keep_silent) return;
2570     L__M(##SwitchOn, 3, noun);
2571 ];
2572
2573 [ SwitchOffSub;
2574     if (ObjectIsUntouchable(noun)) return;
2575     if (noun hasnt switchable) return L__M(##SwitchOff, 1, noun);
2576     if (noun hasnt on)         return L__M(##SwitchOff, 2, noun);
2577
2578     give noun ~on;
2579     if (AfterRoutines() || keep_silent) return;
2580     L__M(##SwitchOff, 3, noun);
2581 ];
2582
2583 [ OpenSub;
2584     if (ObjectIsUntouchable(noun)) return;
2585     if (noun hasnt openable) return L__M(##Open, 1, noun);
2586     if (noun has locked && ImplicitUnlock(noun)) return L__M(##Open, 2, noun);
2587     if (noun has open)       return L__M(##Open, 3, noun);
2588     give noun open;
2589
2590     if (AfterRoutines() || keep_silent) return;
2591
2592     if (noun hasnt container)
2593         return L__M(##Open, 5, noun);
2594
2595     if ((noun has container && location ~= thedark && VisibleContents(noun)
2596          && IndirectlyContains(noun, player)) == 0) {
2597          if (noun hasnt transparent && noun hasnt door) return L__M(##Open, 4, noun);
2598     }
2599     L__M(##Open, 5, noun);
2600 ];
2601
2602 [ CloseSub;
2603     if (ObjectIsUntouchable(noun)) return;
2604     if (noun hasnt openable) return L__M(##Close, 1, noun);
2605     if (noun hasnt open)     return L__M(##Close, 2, noun);
2606
2607     give noun ~open;
2608     if (AfterRoutines() || keep_silent) return;
2609     L__M(##Close, 3, noun);
2610 ];
2611
2612 [ DisrobeSub;
2613     if (ObjectIsUntouchable(noun)) return;
2614     if (noun hasnt clothing) return L__M(##Disrobe,1, noun);
2615     if (noun hasnt worn && noun has clothing) return L__M(##Disrobe, 1, noun);
2616
2617     give noun ~worn;
2618     if (AfterRoutines() || keep_silent) return;
2619     L__M(##Disrobe, 2, noun);
2620 ];
2621
2622 [ WearSub;
2623     if (ObjectIsUntouchable(noun)) return;
2624     if (noun hasnt clothing)    return L__M(##Wear, 1, noun);
2625
2626     if (noun notin actor && noun hasnt worn) {
2627         if (IndirectlyContains(actor, noun) || parent(noun) has container or supporter) {
2628             if (no_implicit_actions) return L__M(##Wear, 5, noun, parent(noun));
2629             L__M(##Miscellany, 58, noun, parent(noun));
2630             AttemptToTakeObject(noun);
2631         } else
2632             return L__M(##Wear, 2, noun, parent(noun));
2633     }
2634     if (noun has worn)          return L__M(##Wear, 3, noun);
2635
2636     give noun worn;
2637     if (AfterRoutines() || keep_silent) return;
2638     L__M(##Wear, 4, noun);
2639 ];
2640
2641 [ EatSub;
2642     if (ObjectIsUntouchable(noun)) return;
2643     if (noun hasnt edible) return L__M(##Eat, 1, noun);
2644     if (noun has worn && ImplicitDisrobe(noun)) return;
2645
2646     remove noun;
2647     if (AfterRoutines() || keep_silent) return;
2648     L__M(##Eat, 2, noun);
2649 ];
2650
2651 ! ----------------------------------------------------------------------------
2652 !   Verbs which are really just stubs (anything which happens for these
2653 !   actions must happen in before rules)
2654 ! ----------------------------------------------------------------------------
2655
2656 [ AllowPushDir i;
2657     if (parent(second) ~= Compass) return L__M(##PushDir, 2, noun);
2658     if (second == u_obj or d_obj)  return L__M(##PushDir, 3, noun);
2659     AfterRoutines(); i = noun; move i to actor;
2660     <Go second, actor>;
2661     if (location == thedark) move i to real_location;
2662     else                     move i to location;
2663 ];
2664
2665 [ AnswerSub;
2666     if (second && RunLife(second,##Answer)) rfalse;
2667     L__M(##Answer, 1, noun);
2668 ];
2669
2670 [ AskSub;
2671     if (RunLife(noun,##Ask)) rfalse;
2672     L__M(##Ask, 1, noun);
2673 ];
2674
2675 [ AskForSub;
2676     if (noun == player) <<Inv, actor>>;
2677     L__M(##Order, 1, noun);
2678 ];
2679
2680 [ AskToSub; L__M(##Order, 1, noun); ];
2681
2682 [ AttackSub;
2683     if (ObjectIsUntouchable(noun)) return;
2684     if (noun has animate && RunLife(noun, ##Attack)) rfalse;
2685     L__M(##Attack, 1, noun);
2686 ];
2687
2688 [ BlowSub; L__M(##Blow, 1, noun); ];
2689
2690 [ BurnSub;
2691     if (noun has animate) return L__M(##Burn, 2, noun);
2692     L__M(##Burn, 1, noun);
2693 ];
2694
2695 [ BuySub; L__M(##Buy, 1, noun); ];
2696
2697 [ ClimbSub;
2698     if (noun has animate) return L__M(##Climb, 2, noun);
2699     L__M(##Climb, 1, noun);
2700 ];
2701
2702 [ ConsultSub; L__M(##Consult, 1, noun); ];
2703
2704 [ CutSub;
2705     if (noun has animate) return L__M(##Cut, 2, noun);
2706     L__M(##Cut, 1, noun);
2707 ];
2708
2709 [ DigSub; L__M(##Dig, 1, noun); ];
2710
2711 [ DrinkSub; L__M(##Drink, 1, noun); ];
2712
2713 [ FillSub;
2714     if (second == nothing) return L__M(##Fill, 1, noun);
2715     L__M(##Fill, 2, noun, second);
2716 ];
2717
2718 [ JumpSub; L__M(##Jump, 1, noun); ];
2719
2720 [ JumpInSub;
2721     if (noun has animate) return L__M(##JumpIn, 2, noun);
2722     if (noun has enterable) <<Enter noun>>;
2723     L__M(##JumpIn, 1, noun);
2724 ];
2725
2726 [ JumpOnSub;
2727     if (noun has animate) return L__M(##JumpOn, 2, noun);
2728     if (noun has enterable && noun has supporter) <<Enter noun>>;
2729     L__M(##JumpOn, 1, noun);
2730 ];
2731
2732 [ JumpOverSub;
2733     if (noun has animate) return L__M(##JumpOver, 2, noun);
2734     L__M(##JumpOver, 1, noun);
2735 ];
2736
2737 [ KissSub;
2738     if (ObjectIsUntouchable(noun)) return;
2739     if (RunLife(noun, ##Kiss)) return;
2740     if (noun == actor) return L__M(##Touch, 3, noun);
2741     L__M(##Kiss, 1, noun);
2742 ];
2743
2744 [ ListenSub; L__M(##Listen, 1, noun); ];
2745
2746 [ MildSub; L__M(##Mild, 1, noun); ];
2747
2748 [ NoSub; L__M(##No); ];
2749
2750 [ PraySub; L__M(##Pray, 1, noun); ];
2751
2752 [ PullSub;
2753     if (ObjectIsUntouchable(noun)) return;
2754     if (noun == player)   return L__M(##Pull, 1, noun);
2755     if (noun == actor)    return L__M(##Pull, 6, noun);
2756     if (noun has static)  return L__M(##Pull, 2, noun);
2757     if (noun has scenery) return L__M(##Pull, 3, noun);
2758     if (noun has animate) return L__M(##Pull, 5, noun);
2759     L__M(##Pull, 4, noun);
2760 ];
2761
2762 [ PushSub;
2763     if (ObjectIsUntouchable(noun)) return;
2764     if (noun == player)   return L__M(##Push, 1, noun);
2765     if (noun == actor)    return L__M(##Push, 5, noun);
2766     if (noun has static)  return L__M(##Push, 2, noun);
2767     if (noun has scenery) return L__M(##Push, 3, noun);
2768     if (noun has animate) return L__M(##Push, 5, noun);
2769     L__M(##Push, 4, noun);
2770 ];
2771
2772 [ PushDirSub; L__M(##PushDir, 1, noun); ];
2773
2774 [ RubSub;
2775     if (ObjectIsUntouchable(noun)) return;
2776     if (noun has animate) return L__M(##Rub, 2, noun);
2777     L__M(##Rub, 1, noun);
2778 ];
2779
2780 [ SetSub; L__M(##Set, 1, noun); ];
2781
2782 [ SetToSub; L__M(##SetTo, 1, noun); ];
2783
2784 [ SingSub; L__M(##Sing, 1, noun); ];
2785
2786 [ SleepSub; L__M(##Sleep, 1, noun); ];
2787
2788 [ SmellSub;
2789     if (noun ~= nothing && noun has animate) return L__M(##Smell, 2, noun);
2790     L__M(##Smell, 1, noun);
2791 ];
2792
2793 [ SorrySub; L__M(##Sorry, 1, noun); ];
2794
2795 [ SqueezeSub;
2796     if (ObjectIsUntouchable(noun)) return;
2797     if (noun has animate && noun ~= player) return L__M(##Squeeze, 1, noun);
2798     L__M(##Squeeze, 2, noun);
2799 ];
2800
2801 [ StrongSub; L__M(##Strong, 1, noun); ];
2802
2803 [ SwimSub; L__M(##Swim, 1, noun); ];
2804
2805 [ SwingSub; L__M(##Swing, 1, noun); ];
2806
2807 [ TasteSub;
2808     if (ObjectIsUntouchable(noun)) return;
2809     if (noun has animate) return L__M(##Taste, 2, noun);
2810     L__M(##Taste, 1, noun);
2811 ];
2812
2813 [ TellSub;
2814     if (noun == actor) return L__M(##Tell, 1, noun);
2815     if (RunLife(noun, ##Tell)) return;
2816     L__M(##Tell, 2, noun);
2817 ];
2818
2819 [ ThinkSub; L__M(##Think, 1, noun); ];
2820
2821 [ ThrowAtSub;
2822     if (ObjectIsUntouchable(noun)) return;
2823     if (second == nothing) return L__M(##ThrowAt, 1, noun);
2824     if (second > 1) {
2825         action = ##ThrownAt;
2826         if (RunRoutines(second, before)) { action = ##ThrowAt; rtrue; }
2827         action = ##ThrowAt;
2828     }
2829     if (noun has worn && ImplicitDisrobe(noun)) return;
2830     if (second hasnt animate) return L__M(##ThrowAt, 1, noun);
2831     if (RunLife(second, ##ThrowAt)) return;
2832     L__M(##ThrowAt, 2, noun);
2833 ];
2834
2835 [ TieSub;
2836     if (noun has animate) return L__M(##Tie, 2, noun);
2837     L__M(##Tie, 1, noun);
2838 ];
2839
2840 [ TouchSub;
2841     if (noun == actor)   return L__M(##Touch, 3, noun);
2842     if (ObjectIsUntouchable(noun)) return;
2843     if (noun has animate) return L__M(##Touch, 1, noun);
2844     L__M(##Touch, 2,noun);
2845 ];
2846
2847 [ TurnSub;
2848     if (ObjectIsUntouchable(noun)) return;
2849     if (noun == player)    return L__M(##Turn, 1, noun);
2850     if (noun == actor)     return L__M(##Turn, 5, noun);
2851     if (noun has static)   return L__M(##Turn, 2, noun);
2852     if (noun has scenery)  return L__M(##Turn, 3, noun);
2853     if (noun has animate)  return L__M(##Turn, 5, noun);
2854     L__M(##Turn, 4, noun);
2855 ];
2856
2857 [ WaitSub;
2858     if (AfterRoutines()) rtrue;
2859     L__M(##Wait, 1, noun);
2860 ];
2861
2862 [ WakeSub; L__M(##Wake, 1, noun); ];
2863
2864 [ WakeOtherSub;
2865     if (ObjectIsUntouchable(noun)) return;
2866     if (RunLife(noun, ##WakeOther)) return;
2867     L__M(##WakeOther, 1, noun);
2868 ];
2869
2870 [ WaveSub;
2871     if (noun == player) return L__M(##Wave, 2 ,noun, second);
2872     if (noun == actor) return L__M(##Wave, 3, noun, second);
2873     if (ImplicitTake(noun) && noun notin actor) return L__M(##Wave, 1, noun);
2874     L__M(##Wave, 2, noun, second);
2875 ];
2876
2877 [ WaveHandsSub;
2878     if (noun) return L__M(##WaveHands, 2, noun);
2879     L__M(##WaveHands, 1, noun); ];
2880
2881 [ YesSub; L__M(##Yes); ];
2882
2883 ! ----------------------------------------------------------------------------
2884 !   Debugging verbs
2885 ! ----------------------------------------------------------------------------
2886
2887 #Ifdef DEBUG;
2888
2889 [ TraceOnSub; parser_trace = 1; "[Trace on.]"; ];
2890
2891 [ TraceLevelSub;
2892     parser_trace = noun;
2893     print "[Parser tracing set to level ", parser_trace, ".]^";
2894 ];
2895
2896 [ TraceOffSub; parser_trace = 0; "Trace off."; ];
2897
2898 [ RoutinesOnSub;
2899     debug_flag = debug_flag |  DEBUG_MESSAGES;
2900     "[Message listing on.]";
2901 ];
2902
2903 [ RoutinesOffSub;
2904     debug_flag = debug_flag & ~DEBUG_MESSAGES;
2905     "[Message listing off.]";
2906 ];
2907
2908 [ RoutinesVerboseSub;
2909     debug_flag = debug_flag | (DEBUG_VERBOSE|DEBUG_MESSAGES);
2910     "[Verbose message listing on.]";
2911 ];
2912
2913 [ ActionsOnSub;
2914     debug_flag = debug_flag |  DEBUG_ACTIONS;
2915     "[Action listing on.]";
2916 ];
2917
2918 [ ActionsOffSub;
2919     debug_flag = debug_flag & ~DEBUG_ACTIONS;
2920     "[Action listing off.]";
2921 ];
2922
2923 [ TimersOnSub;
2924     debug_flag = debug_flag |  DEBUG_TIMERS;
2925     "[Timers listing on.]";
2926 ];
2927
2928 [ TimersOffSub;
2929     debug_flag = debug_flag & ~DEBUG_TIMERS;
2930     "[Timers listing off.]";
2931 ];
2932
2933 #Ifdef VN_1610;
2934
2935 [ ChangesOnSub;   debug_flag = debug_flag |  DEBUG_CHANGES;  "[Changes listing on.]"; ];
2936 [ ChangesOffSub;  debug_flag = debug_flag & ~DEBUG_CHANGES;  "[Changes listing off.]"; ];
2937
2938 #Ifnot;
2939
2940 [ ChangesOnSub; "[Changes listing available only from Inform 6.2 onwards.]"; ];
2941
2942 [ ChangesOffSub; "[Changes listing available only from Inform 6.2 onwards.]"; ];
2943
2944 #Endif; ! VN_1610
2945
2946 #Ifdef TARGET_ZCODE;
2947
2948 [ PredictableSub i;
2949     i = random(-100);
2950     "[Random number generator now predictable.]";
2951 ];
2952
2953 #Ifnot; ! TARGET_GLULX;
2954
2955 [ PredictableSub;
2956     @setrandom 100;
2957     "[Random number generator now predictable.]";
2958 ];
2959
2960 #Endif; ! TARGET_;
2961
2962 [ XTestMove obj dest;
2963     if (~~obj ofclass Object) "[Not an object.]";
2964     if (~~dest ofclass Object) "[Destination not an object.]";
2965     if ((obj <= InformLibrary) || (obj == LibraryMessages) || (obj in 1))
2966         "[Can't move ", (name) obj, ": it's a system object.]";
2967     while (dest) {
2968         if (dest == obj) "[Can't move ", (name) obj, ": it would contain itself.]";
2969         dest = parent(dest);
2970     }
2971     rfalse;
2972 ];
2973
2974 [ XPurloinSub;
2975     if (XTestMove(noun, player)) return;
2976     move noun to player; give noun moved ~concealed;
2977     "[Purloined.]";
2978 ];
2979
2980 [ XAbstractSub;
2981     if (XTestMove(noun, second)) return;
2982     move noun to second;
2983     "[Abstracted.]";
2984 ];
2985
2986 [ XObj obj f;
2987     if (parent(obj) == 0) print (name) obj; else print (a) obj;
2988     print " (", obj, ") ";
2989     if (f && parent(obj))
2990         print "in ~", (name) parent(obj), "~ (", parent(obj), ")";
2991     new_line;
2992     if (child(obj) == 0) rtrue;
2993     if (obj == Class) ! ???
2994         WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+NOARTICLE_BIT, 1);
2995     else
2996         WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+FULLINV_BIT, 1);
2997 ];
2998
2999 [ XTreeSub i;
3000     if (noun && ~~noun ofclass Object) "[Not an object.]";
3001     if (noun == 0) {
3002         objectloop (i)
3003             if (i ofclass Object && parent(i) == 0) XObj(i);
3004     }
3005     else XObj(noun, true);
3006 ];
3007
3008 [ GotoSub;
3009     if ((~~noun ofclass Object) || parent(noun)) "[Not a safe place.]";
3010     PlayerTo(noun);
3011 ];
3012
3013 [ GoNearSub x;
3014     if (~~noun ofclass Object) "[Not a safe place.]";
3015     x = noun;
3016     while (parent(x)) x = parent(x);
3017     PlayerTo(x);
3018 ];
3019
3020 [ Print_ScL obj; print_ret ++x_scope_count, ": ", (a) obj, " (", obj, ")"; ];
3021
3022 [ ScopeSub;
3023     if (noun && ~~noun ofclass Object) "[Not an object.]";
3024     x_scope_count = 0;
3025     LoopOverScope(Print_ScL, noun);
3026     if (x_scope_count == 0) "Nothing is in scope.";
3027 ];
3028
3029 #Ifdef TARGET_GLULX;
3030
3031 [ GlkListSub id val;
3032     id = glk_window_iterate(0, gg_arguments);
3033     while (id) {
3034         print "Window ", id, " (", gg_arguments-->0, "): ";
3035         val = glk_window_get_type(id);
3036         switch (val) {
3037           1: print "pair";
3038           2: print "blank";
3039           3: print "textbuffer";
3040           4: print "textgrid";
3041           5: print "graphics";
3042           default: print "unknown";
3043         }
3044         val = glk_window_get_parent(id);
3045         if (val) print ", parent is window ", val;
3046         else     print ", no parent (root)";
3047         val = glk_window_get_stream(id);
3048         print ", stream ", val;
3049         val = glk_window_get_echo_stream(id);
3050         if (val) print ", echo stream ", val;
3051         print "^";
3052         id = glk_window_iterate(id, gg_arguments);
3053     }
3054     id = glk_stream_iterate(0, gg_arguments);
3055     while (id) {
3056         print "Stream ", id, " (", gg_arguments-->0, ")^";
3057         id = glk_stream_iterate(id, gg_arguments);
3058     }
3059     id = glk_fileref_iterate(0, gg_arguments);
3060     while (id) {
3061         print "Fileref ", id, " (", gg_arguments-->0, ")^";
3062         id = glk_fileref_iterate(id, gg_arguments);
3063     }
3064     val = glk_gestalt(gestalt_Sound, 0);
3065     if (val) {
3066         id = glk_schannel_iterate(0, gg_arguments);
3067         while (id) {
3068             print "Soundchannel ", id, " (", gg_arguments-->0, ")^";
3069             id = glk_schannel_iterate(id, gg_arguments);
3070         }
3071     }
3072 ];
3073
3074 #Endif; ! TARGET_;
3075
3076 #Endif; ! DEBUG
3077
3078 ! ----------------------------------------------------------------------------
3079 !   Finally: the mechanism for library text (the text is in the language defn)
3080 ! ----------------------------------------------------------------------------
3081
3082 [ L__M act n x1 x2 s;
3083     if (keep_silent == 2) return;
3084     s = sw__var;
3085     sw__var = act;
3086     if (n == 0) n = 1;
3087     L___M(n, x1, x2);
3088     sw__var = s;
3089 ];
3090
3091 [ L___M n x1 x2 s;
3092     s = action;
3093     lm_n = n;
3094     lm_o = x1;
3095     lm_s = x2;
3096     action = sw__var;
3097     if (RunRoutines(LibraryMessages, before))             { action = s; rfalse; }
3098     if (LibraryExtensions.RunWhile(ext_messages, false )) { action = s; rfalse; }
3099     action = s;
3100     LanguageLM(n, x1, x2);
3101 ];
3102
3103 ! ==============================================================================
3104
3105 Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_VERBLIB;
3106
3107 #Ifnot;         ! LIBRARY_STAGE < AFTER_VERBLIB but ~= AFTER_PARSER
3108                         ! (this shouldn't happen because if 'parser' isn't there, LIBRARY_STAGE isn't defined)
3109 Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!";
3110 #Endif;
3111
3112 #Ifnot;         ! LIBRARY_STAGE >= AFTER_VERBLIB: already included
3113 Message "Warning: 'verblib' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)";
3114 #Endif;
3115
3116 #Ifnot;         ! LIBRARY_STAGE is not defined (likely, 'parser' hasn't been included)
3117 Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!";
3118 #Endif;
3119
3120 ! ==============================================================================