~ubuntu-branches/ubuntu/vivid/inform/vivid

« back to all changes in this revision

Viewing changes to inform-6.31.1/lib/parserm.h

  • Committer: Bazaar Package Importer
  • Author(s): Jan Christoph Nordholz
  • Date: 2008-05-26 22:09:44 UTC
  • mfrom: (2.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080526220944-ba7phz0d1k4vo7wx
Tags: 6.31.1+dfsg-1
* Remove a considerable number of files from the package
  due to unacceptable licensing terms.
* Repair library symlinks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
! ==============================================================================
 
2
!   PARSERM:  Core of parser.
 
3
!
 
4
!   Supplied for use with Inform 6 -- Release 6/11 -- Serial number 040227
 
5
!
 
6
!   Copyright Graham Nelson 1993-2004 but freely usable (see manuals)
 
7
!
 
8
!   This file is automatically Included in your game file by "Parser".
 
9
! ------------------------------------------------------------------------------
 
10
!   Inclusion of "linklpa" (which defines properties and attributes)
 
11
!   Global variables, constants and arrays
 
12
!       1: outside of the parser
 
13
!       2: used within the parser
 
14
!   Inclusion of natural language definition file
 
15
!       (which creates a compass and direction-objects)
 
16
!   Darkness and player objects
 
17
!   Definition of grammar token numbering system used by Inform
 
18
!
 
19
!   The InformParser object
 
20
!       keyboard reading
 
21
!       level 0: outer shell, conversation, errors
 
22
!             1: grammar lines
 
23
!             2: tokens
 
24
!             3: object lists
 
25
!             4: scope and ambiguity resolving
 
26
!             5: object comparisons
 
27
!             6: word comparisons
 
28
!             7: reading words and moving tables about
 
29
!       pronoun management
 
30
!
 
31
!   The InformLibrary object
 
32
!       main game loop
 
33
!       action processing
 
34
!       end of turn sequence
 
35
!       scope looping, before/after sequence, sending messages out
 
36
!       timers, daemons, time of day, score notification
 
37
!       light and darkness
 
38
!       changing player personality
 
39
!       tracing code (only present if DEBUG is set)
 
40
!
 
41
!   Status line printing, menu display
 
42
!   Printing object names with articles
 
43
!   Miscellaneous utility routines
 
44
!   Game banner, "version" verb, run-time errors
 
45
! ==============================================================================
 
46
 
 
47
System_file;
 
48
 
 
49
#Ifdef MODULE_MODE;
 
50
Constant DEBUG;
 
51
Constant Grammar__Version 2;
 
52
Include "linklpa";
 
53
#Endif; ! MODULE_MODE
 
54
 
 
55
! ------------------------------------------------------------------------------
 
56
!   Global variables and their associated Constant and Array declarations
 
57
! ------------------------------------------------------------------------------
 
58
 
 
59
Global location = InformLibrary;    ! Must be first global defined
 
60
Global sline1;                      ! Must be second
 
61
Global sline2;                      ! Must be third
 
62
                                    ! (for status line display)
 
63
! ------------------------------------------------------------------------------
 
64
!   Z-Machine and interpreter issues
 
65
! ------------------------------------------------------------------------------
 
66
 
 
67
#Ifdef TARGET_ZCODE;
 
68
Global top_object;                  ! Largest valid number of any tree object
 
69
! ### these globals are not meaningful... well, maybe standard_interpreter,
 
70
! but I'll decide that later (AP).
 
71
Constant INDIV_PROP_START 64;       ! Equivalent of a Glulx constant
 
72
 
 
73
#Endif; ! TARGET_ZCODE
 
74
 
 
75
Global standard_interpreter;        ! The version number of the Z-Machine Standard which the
 
76
                                    ! interpreter claims to support, in form (upper byte).(lower)
 
77
 
 
78
Global undo_flag;                   ! Can the interpreter provide "undo"?
 
79
Global just_undone;                 ! Can't have two successive UNDOs
 
80
 
 
81
Global transcript_mode;             ! true when game scripting is on
 
82
 
 
83
#Ifdef TARGET_ZCODE;
 
84
Global xcommsdir;                   ! true if command recording is on
 
85
#Endif; ! TARGET_ZCODE
 
86
 
 
87
#Ifdef TARGET_GLULX;
 
88
Constant GG_MAINWIN_ROCK     201;
 
89
Constant GG_STATUSWIN_ROCK   202;
 
90
Constant GG_QUOTEWIN_ROCK    203;
 
91
Constant GG_SAVESTR_ROCK     301;
 
92
Constant GG_SCRIPTSTR_ROCK   302;
 
93
Constant GG_COMMANDWSTR_ROCK 303;
 
94
Constant GG_COMMANDRSTR_ROCK 304;
 
95
Constant GG_SCRIPTFREF_ROCK  401;
 
96
Array gg_event --> 4;
 
97
#Ifdef VN_1630;
 
98
Array gg_arguments buffer 28;
 
99
#Ifnot;
 
100
Array gg_arguments --> 8;
 
101
#Endif; ! VN_
 
102
Global gg_mainwin = 0;
 
103
Global gg_statuswin = 0;
 
104
Global gg_quotewin = 0;
 
105
Global gg_scriptfref = 0;
 
106
Global gg_scriptstr = 0;
 
107
Global gg_savestr = 0;
 
108
Global gg_commandstr = 0;
 
109
Global gg_command_reading = 0;      ! true if gg_commandstr is being replayed
 
110
#Endif; ! TARGET_GLULX
 
111
 
 
112
Global gg_statuswin_cursize = 0;
 
113
Global gg_statuswin_size = 1;
 
114
 
 
115
! ------------------------------------------------------------------------------
 
116
!   Time and score
 
117
!   (for linkage reasons, the task_* arrays are created not here but in verblib.h)
 
118
! ------------------------------------------------------------------------------
 
119
 
 
120
#Ifndef sys_statusline_flag;
 
121
Global sys_statusline_flag = 0;     ! non-zero if status line displays time
 
122
#Endif;
 
123
#Ifndef START_MOVE;
 
124
Constant START_MOVE 0;              ! Traditionally 0 for Infocom, 1 for Inform
 
125
#Endif;
 
126
Global turns = START_MOVE;          ! Number of turns of play so far
 
127
Global the_time = NULL;             ! Current time (in minutes since midnight)
 
128
Global time_rate = 1;               ! How often time is updated
 
129
Global time_step;                   ! By how much
 
130
 
 
131
#Ifndef MAX_TIMERS;
 
132
Constant MAX_TIMERS  32;            ! Max number timers/daemons active at once
 
133
#Endif; ! MAX_TIMERS
 
134
Array  the_timers  --> MAX_TIMERS;
 
135
Global active_timers;               ! Number of timers/daemons actives
 
136
 
 
137
Global score;                       ! The current score
 
138
Global last_score;                  ! Score last turn (for testing for changes)
 
139
Global notify_mode = true;          ! Score notification
 
140
Global places_score;                ! Contribution to score made by visiting
 
141
Global things_score;                ! Contribution made by acquisition
 
142
 
 
143
! ------------------------------------------------------------------------------
 
144
!   The player
 
145
! ------------------------------------------------------------------------------
 
146
 
 
147
Global player;                      ! Which object the human is playing through
 
148
Global deadflag;                    ! Normally 0, or false; 1 for dead; 2 for victorious, and
 
149
                                    ! higher numbers represent exotic forms of death
 
150
 
 
151
! ------------------------------------------------------------------------------
 
152
!   Light and room descriptions
 
153
! ------------------------------------------------------------------------------
 
154
 
 
155
Global lightflag = true;            ! Is there currently light to see by?
 
156
Global real_location;               ! When in darkness, location = thedark
 
157
                                    ! and this holds the real location
 
158
Global visibility_ceiling;          ! Highest object in tree visible from the player's point of view
 
159
                                    ! (usually the room, sometimes darkness, sometimes a closed
 
160
                                    ! non-transparent container).
 
161
 
 
162
Global lookmode = 1;                ! 1=standard, 2=verbose, 3=brief room descs
 
163
Global print_player_flag;           ! If set, print something like "(as Fred)" in room descriptions,
 
164
                                    ! to reveal whom the human is playing through
 
165
Global lastdesc;                    ! Value of location at time of most recent room description
 
166
                                    ! printed out
 
167
 
 
168
! ------------------------------------------------------------------------------
 
169
!   List writing  (style bits are defined as Constants in "verblibm.h")
 
170
! ------------------------------------------------------------------------------
 
171
 
 
172
Global c_style;                     ! Current list-writer style
 
173
Global lt_value;                    ! Common value of list_together
 
174
Global listing_together;            ! Object number of one member of a group being listed together
 
175
Global listing_size;                ! Size of such a group
 
176
Global wlf_indent;                  ! Current level of indentation printed by WriteListFrom()
 
177
 
 
178
Global inventory_stage = 1;         ! 1 or 2 according to the context in which "invent" routines
 
179
                                    ! of objects are called
 
180
Global inventory_style;             ! List-writer style currently used while printing inventories
 
181
! ------------------------------------------------------------------------------
 
182
!   Menus and printing
 
183
! ------------------------------------------------------------------------------
 
184
 
 
185
Global pretty_flag = true;          ! Use character graphics, or plain text?
 
186
Global menu_nesting;                ! Level of nesting (0 = root menu)
 
187
Global menu_item;                   ! These are used in communicating
 
188
Global item_width = 8;              ! with the menu-creating routines
 
189
Global item_name = "---";
 
190
 
 
191
Global lm_n;                        ! Parameters used by LibraryMessages
 
192
Global lm_o;                        ! mechanism
 
193
 
 
194
#Ifdef DEBUG;
 
195
Global debug_flag;                  ! Bitmap of flags for tracing actions,
 
196
                                    ! calls to object routines, etc.
 
197
Global x_scope_count;               ! Used in printing a list of everything
 
198
#Endif; ! DEBUG                     ! in scope
 
199
 
 
200
! five for colour control
 
201
! see http://www.inform-fiction.org/patches/L61007.html
 
202
Global clr_fg = 1;                  ! foreground colour
 
203
Global clr_bg = 1;                  ! background colour
 
204
Global clr_fgstatus = 1;            ! foreground colour of statusline
 
205
Global clr_bgstatus = 1;            ! background colour of statusline
 
206
Global clr_on;                      ! has colour been enabled by the player?
 
207
Global statuswin_current;           ! if writing to top window
 
208
 
 
209
Constant CLR_DEFAULT 1;
 
210
Constant CLR_BLACK   2;
 
211
Constant CLR_RED     3;
 
212
Constant CLR_GREEN   4;
 
213
Constant CLR_YELLOW  5;
 
214
Constant CLR_BLUE    6;
 
215
Constant CLR_MAGENTA 7;
 
216
Constant CLR_CYAN    8;
 
217
Constant CLR_WHITE   9;
 
218
Constant CLR_PURPLE  7;
 
219
Constant CLR_AZURE   8;
 
220
 
 
221
Constant WIN_ALL     0;
 
222
Constant WIN_STATUS  1;
 
223
Constant WIN_MAIN    2;
 
224
 
 
225
! ------------------------------------------------------------------------------
 
226
!   Action processing
 
227
! ------------------------------------------------------------------------------
 
228
 
 
229
Global action;                      ! Action currently being asked to perform
 
230
Global inp1;                        ! 0 (nothing), 1 (number) or first noun
 
231
Global inp2;                        ! 0 (nothing), 1 (number) or second noun
 
232
Global noun;                        ! First noun or numerical value
 
233
Global second;                      ! Second noun or numerical value
 
234
 
 
235
Global keep_silent;                 ! If true, attempt to perform the action silently (e.g. for
 
236
                                    ! implicit takes, implicit opening of unlocked doors)
 
237
 
 
238
Global reason_code;                 ! Reason for calling a "life" rule
 
239
                                    ! (an action or fake such as ##Kiss)
 
240
 
 
241
Global receive_action;              ! Either ##PutOn or ##Insert, whichever is action being tried
 
242
                                    ! when an object's "before" rule is checking "Receive"
 
243
 
 
244
! ==============================================================================
 
245
!   Parser variables: first, for communication to the parser
 
246
! ------------------------------------------------------------------------------
 
247
 
 
248
Global parser_trace = 0;            ! Set this to 1 to make the parser trace tokens and lines
 
249
Global parser_action;               ! For the use of the parser when calling
 
250
Global parser_one;                  ! user-supplied routines
 
251
Global parser_two;                  !
 
252
Array  inputobjs       --> 16;      ! For parser to write its results in
 
253
Global parser_inflection;           ! A property (usually "name") to find object names in
 
254
 
 
255
! ------------------------------------------------------------------------------
 
256
!   Parser output
 
257
! ------------------------------------------------------------------------------
 
258
 
 
259
Global actor;                       ! Person asked to do something
 
260
Global actors_location;             ! Like location, but for the actor
 
261
Global meta;                        ! Verb is a meta-command (such as "save")
 
262
 
 
263
Array  multiple_object --> 64;      ! List of multiple parameters
 
264
Global multiflag;                   ! Multiple-object flag passed to actions
 
265
                                    ! Also used to prevent misleading MULTI_PE
 
266
Global toomany_flag;                ! Flag for "multiple match too large"
 
267
                                    ! (e.g. if "take all" took over 100 things)
 
268
 
 
269
Global special_word;                ! Dictionary address for "special" token
 
270
Global special_number;              ! Number typed for "special" token
 
271
Global parsed_number;               ! For user-supplied parsing routines
 
272
Global consult_from;                ! Word that a "consult" topic starts on
 
273
Global consult_words;               ! ...and number of words in topic
 
274
 
 
275
! ------------------------------------------------------------------------------
 
276
!   Implicit taking
 
277
! ------------------------------------------------------------------------------
 
278
 
 
279
Global notheld_mode;                ! To do with implicit taking
 
280
Global onotheld_mode;               !     "old copy of notheld_mode", ditto
 
281
Global not_holding;                 ! Object to be automatically taken as an
 
282
                                    ! implicit command
 
283
Array  kept_results --> 16;         ! Delayed command (while the take happens)
 
284
 
 
285
! ------------------------------------------------------------------------------
 
286
!   Error numbers when parsing a grammar line
 
287
! ------------------------------------------------------------------------------
 
288
 
 
289
Global etype;                       ! Error number on current line
 
290
Global best_etype;                  ! Preferred error number so far
 
291
Global nextbest_etype;              ! Preferred one, if ASKSCOPE_PE disallowed
 
292
 
 
293
Constant STUCK_PE     = 1;
 
294
Constant UPTO_PE      = 2;
 
295
Constant NUMBER_PE    = 3;
 
296
Constant CANTSEE_PE   = 4;
 
297
Constant TOOLIT_PE    = 5;
 
298
Constant NOTHELD_PE   = 6;
 
299
Constant MULTI_PE     = 7;
 
300
Constant MMULTI_PE    = 8;
 
301
Constant VAGUE_PE     = 9;
 
302
Constant EXCEPT_PE    = 10;
 
303
Constant ANIMA_PE     = 11;
 
304
Constant VERB_PE      = 12;
 
305
Constant SCENERY_PE   = 13;
 
306
Constant ITGONE_PE    = 14;
 
307
Constant JUNKAFTER_PE = 15;
 
308
Constant TOOFEW_PE    = 16;
 
309
Constant NOTHING_PE   = 17;
 
310
Constant ASKSCOPE_PE  = 18;
 
311
 
 
312
! ------------------------------------------------------------------------------
 
313
!   Pattern-matching against a single grammar line
 
314
! ------------------------------------------------------------------------------
 
315
 
 
316
Array pattern --> 32;               ! For the current pattern match
 
317
Global pcount;                      ! and a marker within it
 
318
Array pattern2 --> 32;              ! And another, which stores the best match
 
319
Global pcount2;                     ! so far
 
320
Constant PATTERN_NULL = $ffff;      ! Entry for a token producing no text
 
321
 
 
322
Array  line_ttype-->32;             ! For storing an analysed grammar line
 
323
Array  line_tdata-->32;
 
324
Array  line_token-->32;
 
325
 
 
326
Global parameters;                  ! Parameters (objects) entered so far
 
327
Global nsns;                        ! Number of special_numbers entered so far
 
328
Global special_number1;             ! First number, if one was typed
 
329
Global special_number2;             ! Second number, if two were typed
 
330
 
 
331
! ------------------------------------------------------------------------------
 
332
!   Inferences and looking ahead
 
333
! ------------------------------------------------------------------------------
 
334
 
 
335
Global params_wanted;               ! Number of parameters needed (which may change in parsing)
 
336
 
 
337
Global inferfrom;                   ! The point from which the rest of the command must be inferred
 
338
Global inferword;                   ! And the preposition inferred
 
339
Global dont_infer;                  ! Another dull flag
 
340
 
 
341
Global action_to_be;                ! (If the current line were accepted.)
 
342
Global action_reversed;             ! (Parameters would be reversed in order.)
 
343
Global advance_warning;             ! What a later-named thing will be
 
344
 
 
345
! ------------------------------------------------------------------------------
 
346
!   At the level of individual tokens now
 
347
! ------------------------------------------------------------------------------
 
348
 
 
349
Global found_ttype;                 ! Used to break up tokens into type
 
350
Global found_tdata;                 ! and data (by AnalyseToken)
 
351
Global token_filter;                ! For noun filtering by user routines
 
352
 
 
353
Global length_of_noun;              ! Set by NounDomain to no of words in noun
 
354
 
 
355
#Ifdef TARGET_ZCODE;
 
356
Constant REPARSE_CODE = 10000;      ! Signals "reparse the text" as a reply from NounDomain
 
357
#Ifnot; ! TARGET_GLULX
 
358
Constant REPARSE_CODE = $40000000;  ! The parser rather gunkily adds addresses to REPARSE_CODE for
 
359
                                    ! some purposes. And expects the result to be greater than
 
360
                                    ! REPARSE_CODE (signed comparison). So Glulx Inform is limited
 
361
                                    ! to a single gigabyte of storage, for the moment.
 
362
#Endif; ! TARGET_
 
363
 
 
364
Global lookahead;                   ! The token after the one now being matched
 
365
 
 
366
Global multi_mode;                  ! Multiple mode
 
367
Global multi_wanted;                ! Number of things needed in multitude
 
368
Global multi_had;                   ! Number of things actually found
 
369
Global multi_context;               ! What token the multi-obj was accepted for
 
370
 
 
371
Global indef_mode;                  ! "Indefinite" mode - ie, "take a brick"
 
372
                                    ! is in this mode
 
373
Global indef_type;                  ! Bit-map holding types of specification
 
374
Global indef_wanted;                ! Number of items wanted (100 for all)
 
375
Global indef_guess_p;               ! Plural-guessing flag
 
376
Global indef_owner;                 ! Object which must hold these items
 
377
Global indef_cases;                 ! Possible gender and numbers of them
 
378
Global indef_possambig;             ! Has a possibly dangerous assumption
 
379
                                    ! been made about meaning of a descriptor?
 
380
Global indef_nspec_at;              ! Word at which a number like "two" was parsed
 
381
                                    ! (for backtracking)
 
382
Global allow_plurals;               ! Whether plurals presently allowed or not
 
383
 
 
384
Global take_all_rule;               ! Slightly different rules apply to "take all" than other uses
 
385
                                    ! of multiple objects, to make adjudication produce more
 
386
                                    ! pragmatically useful results
 
387
                                    ! (Not a flag: possible values 0, 1, 2)
 
388
 
 
389
Global dict_flags_of_noun;          ! Of the noun currently being parsed
 
390
                                    ! (a bitmap in #dict_par1 format)
 
391
Global pronoun_word;                ! Records which pronoun ("it", "them", ...) caused an error
 
392
Global pronoun_obj;                 ! And what obj it was thought to refer to
 
393
Global pronoun__word;               ! Saved value
 
394
Global pronoun__obj;                ! Saved value
 
395
 
 
396
! ------------------------------------------------------------------------------
 
397
!   Searching through scope and parsing "scope=Routine" grammar tokens
 
398
! ------------------------------------------------------------------------------
 
399
 
 
400
Constant PARSING_REASON       = 0;  ! Possible reasons for searching scope
 
401
Constant TALKING_REASON       = 1;
 
402
Constant EACH_TURN_REASON     = 2;
 
403
Constant REACT_BEFORE_REASON  = 3;
 
404
Constant REACT_AFTER_REASON   = 4;
 
405
Constant LOOPOVERSCOPE_REASON = 5;
 
406
Constant TESTSCOPE_REASON     = 6;
 
407
 
 
408
Global scope_reason = PARSING_REASON; ! Current reason for searching scope
 
409
 
 
410
Global scope_token;                 ! For "scope=Routine" grammar tokens
 
411
Global scope_error;
 
412
Global scope_stage;                 ! 1, 2 then 3
 
413
 
 
414
Global ats_flag = 0;                ! For AddToScope routines
 
415
Global ats_hls;                     !
 
416
 
 
417
Global placed_in_flag;              ! To do with PlaceInScope
 
418
 
 
419
! ------------------------------------------------------------------------------
 
420
!   The match list of candidate objects for a given token
 
421
! ------------------------------------------------------------------------------
 
422
 
 
423
Constant MATCH_LIST_SIZE = 128;
 
424
Array  match_list    --> 64;        ! An array of matched objects so far
 
425
Array  match_classes --> 64;        ! An array of equivalence classes for them
 
426
Array  match_scores --> 64;         ! An array of match scores for them
 
427
Global number_matched;              ! How many items in it?  (0 means none)
 
428
Global number_of_classes;           ! How many equivalence classes?
 
429
Global match_length;                ! How many words long are these matches?
 
430
Global match_from;                  ! At what word of the input do they begin?
 
431
Global bestguess_score;             ! What did the best-guess object score?
 
432
 
 
433
! ------------------------------------------------------------------------------
 
434
!   Low level textual manipulation
 
435
! ------------------------------------------------------------------------------
 
436
 
 
437
#Ifdef TARGET_ZCODE;
 
438
 
 
439
Constant INPUT_BUFFER_LEN = 120;    ! Length of buffer array (although we leave an extra byte
 
440
                                    ! to allow for interpreter bugs)
 
441
 
 
442
Array  buffer    -> 123;            ! Buffer for parsing main line of input
 
443
#Ifdef VN_1630;
 
444
Array  parse     buffer 63;         ! Parse table mirroring it
 
445
Array  parse2    buffer 63;         !
 
446
#Ifnot;
 
447
Array  parse     -> 65;             ! Parse table mirroring it
 
448
Array  parse2    -> 65;             !
 
449
#Endif; ! VN_
 
450
Array  buffer2   -> 123;            ! Buffers for supplementary questions
 
451
Array  buffer3   -> 123;            ! Buffer retaining input for "again"
 
452
 
 
453
#Ifnot; ! TARGET_GLULX
 
454
 
 
455
Constant INPUT_BUFFER_LEN = 260;    ! No extra byte necessary
 
456
Constant MAX_BUFFER_WORDS = 20;
 
457
Constant PARSE_BUFFER_LEN = 244;    ! 4 + MAX_BUFFER_WORDS*4;
 
458
 
 
459
#Ifdef VN_1630;
 
460
Array  buffer    buffer INPUT_BUFFER_LEN;
 
461
Array  buffer2   buffer INPUT_BUFFER_LEN;
 
462
Array  buffer3   buffer INPUT_BUFFER_LEN;
 
463
#Ifnot;
 
464
Array  buffer    -> INPUT_BUFFER_LEN;
 
465
Array  buffer2   -> INPUT_BUFFER_LEN;
 
466
Array  buffer3   -> INPUT_BUFFER_LEN;
 
467
#Endif;
 
468
Array  parse     --> PARSE_BUFFER_LEN/WORDSIZE;
 
469
Array  parse2    --> PARSE_BUFFER_LEN/WORDSIZE;
 
470
 
 
471
#Endif; ! TARGET_
 
472
 
 
473
Constant comma_word = 'comma,';     ! An "untypeable word" used to substitute
 
474
                                    ! for commas in parse buffers
 
475
 
 
476
Global wn;                          ! Word number within "parse" (from 1)
 
477
Global num_words;                   ! Number of words typed
 
478
Global verb_word;                   ! Verb word (eg, take in "take all" or
 
479
                                    ! "dwarf, take all") - address in dict
 
480
Global verb_wordnum;                ! its number in typing order (eg, 1 or 3)
 
481
Global usual_grammar_after;         ! Point from which usual grammar is parsed (it may vary from the
 
482
                                    ! above if user's routines match multi-word verbs)
 
483
 
 
484
Global oops_from;                   ! The "first mistake" word number
 
485
Global saved_oops;                  ! Used in working this out
 
486
Array  oops_workspace -> 64;        ! Used temporarily by "oops" routine
 
487
 
 
488
Global held_back_mode;              ! Flag: is there some input from last time
 
489
Global hb_wn;                       ! left over?  (And a save value for wn.)
 
490
                                    ! (Used for full stops and "then".)
 
491
 
 
492
! ----------------------------------------------------------------------------
 
493
 
 
494
Array PowersOfTwo_TB                ! Used in converting case numbers to case bitmaps
 
495
  --> $$100000000000
 
496
      $$010000000000
 
497
      $$001000000000
 
498
      $$000100000000
 
499
      $$000010000000
 
500
      $$000001000000
 
501
      $$000000100000
 
502
      $$000000010000
 
503
      $$000000001000
 
504
      $$000000000100
 
505
      $$000000000010
 
506
      $$000000000001;
 
507
 
 
508
! ============================================================================
 
509
!  Constants, and one variable, needed for the language definition file
 
510
! ----------------------------------------------------------------------------
 
511
 
 
512
Constant POSSESS_PK  = $100;
 
513
Constant DEFART_PK   = $101;
 
514
Constant INDEFART_PK = $102;
 
515
Global short_name_case;
 
516
 
 
517
Global dict_start;
 
518
Global dict_entry_size;
 
519
Global dict_end;
 
520
 
 
521
! ----------------------------------------------------------------------------
 
522
 
 
523
Include "language__";               ! The natural language definition, whose filename is taken from
 
524
                                    ! the ICL language_name variable
 
525
 
 
526
! ----------------------------------------------------------------------------
 
527
 
 
528
#Ifndef LanguageCases;
 
529
Constant LanguageCases = 1;
 
530
#Endif; ! LanguageCases
 
531
 
 
532
! ------------------------------------------------------------------------------
 
533
!   Pronouns support for the cruder (library 6/2 and earlier) version:
 
534
!   only needed in English
 
535
! ------------------------------------------------------------------------------
 
536
 
 
537
#Ifdef EnglishNaturalLanguage;
 
538
Global itobj = NULL;                ! The object which is currently "it"
 
539
Global himobj = NULL;               ! The object which is currently "him"
 
540
Global herobj = NULL;               ! The object which is currently "her"
 
541
 
 
542
Global old_itobj = NULL;            ! The object which is currently "it"
 
543
Global old_himobj = NULL;           ! The object which is currently "him"
 
544
Global old_herobj = NULL;           ! The object which is currently "her"
 
545
#Endif; ! EnglishNaturalLanguage
 
546
 
 
547
! ============================================================================
 
548
! "Darkness" is not really a place: but it has to be an object so that the
 
549
!  location-name on the status line can be "Darkness".
 
550
! ----------------------------------------------------------------------------
 
551
 
 
552
Object  thedark "(darkness object)"
 
553
  with  initial 0,
 
554
        short_name DARKNESS__TX,
 
555
        description [;  return L__M(##Miscellany, 17); ];
 
556
 
 
557
Object  selfobj "(self object)"
 
558
  with  short_name  [;  return L__M(##Miscellany, 18); ],
 
559
        description [;  return L__M(##Miscellany, 19); ],
 
560
        before NULL,
 
561
        after NULL,
 
562
        life NULL,
 
563
        each_turn NULL,
 
564
        time_out NULL,
 
565
        describe NULL,
 
566
        add_to_scope 0,
 
567
        capacity 100,
 
568
        parse_name 0,
 
569
        orders 0,
 
570
        number 0,
 
571
        before_implicit NULL,
 
572
  has   concealed animate proper transparent;
 
573
 
 
574
! ============================================================================
 
575
!  The definition of the token-numbering system used by Inform.
 
576
! ----------------------------------------------------------------------------
 
577
 
 
578
Constant ILLEGAL_TT         = 0;    ! Types of grammar token: illegal
 
579
Constant ELEMENTARY_TT      = 1;    !     (one of those below)
 
580
Constant PREPOSITION_TT     = 2;    !     e.g. 'into'
 
581
Constant ROUTINE_FILTER_TT  = 3;    !     e.g. noun=CagedCreature
 
582
Constant ATTR_FILTER_TT     = 4;    !     e.g. edible
 
583
Constant SCOPE_TT           = 5;    !     e.g. scope=Spells
 
584
Constant GPR_TT             = 6;    !     a general parsing routine
 
585
 
 
586
Constant NOUN_TOKEN         = 0;    ! The elementary grammar tokens, and
 
587
Constant HELD_TOKEN         = 1;    ! the numbers compiled by Inform to
 
588
Constant MULTI_TOKEN        = 2;    ! encode them
 
589
Constant MULTIHELD_TOKEN    = 3;
 
590
Constant MULTIEXCEPT_TOKEN  = 4;
 
591
Constant MULTIINSIDE_TOKEN  = 5;
 
592
Constant CREATURE_TOKEN     = 6;
 
593
Constant SPECIAL_TOKEN      = 7;
 
594
Constant NUMBER_TOKEN       = 8;
 
595
Constant TOPIC_TOKEN        = 9;
 
596
 
 
597
 
 
598
Constant GPR_FAIL           = -1;   ! Return values from General Parsing
 
599
Constant GPR_PREPOSITION    = 0;    ! Routines
 
600
Constant GPR_NUMBER         = 1;
 
601
Constant GPR_MULTIPLE       = 2;
 
602
Constant GPR_REPARSE        = REPARSE_CODE;
 
603
Constant GPR_NOUN           = $ff00;
 
604
Constant GPR_HELD           = $ff01;
 
605
Constant GPR_MULTI          = $ff02;
 
606
Constant GPR_MULTIHELD      = $ff03;
 
607
Constant GPR_MULTIEXCEPT    = $ff04;
 
608
Constant GPR_MULTIINSIDE    = $ff05;
 
609
Constant GPR_CREATURE       = $ff06;
 
610
 
 
611
Constant ENDIT_TOKEN        = 15;   ! Value used to mean "end of grammar line"
 
612
 
 
613
#Iftrue (Grammar__Version == 1);
 
614
 
 
615
[ AnalyseToken token m;
 
616
    found_tdata = token;
 
617
    if (token < 0)   { found_ttype = ILLEGAL_TT; return; }
 
618
    if (token <= 8)  { found_ttype = ELEMENTARY_TT; return; }
 
619
    if (token < 15)  { found_ttype = ILLEGAL_TT; return; }
 
620
    if (token == 15) { found_ttype = ELEMENTARY_TT; return; }
 
621
    if (token < 48)  { found_ttype = ROUTINE_FILTER_TT;
 
622
                       found_tdata = token - 16;
 
623
                       return;
 
624
    }
 
625
    if (token < 80)  { found_ttype = GPR_TT;
 
626
                       found_tdata = #preactions_table-->(token-48);
 
627
                       return;
 
628
    }
 
629
    if (token < 128) { found_ttype = SCOPE_TT;
 
630
                       found_tdata = #preactions_table-->(token-80);
 
631
                       return;
 
632
    }
 
633
    if (token < 180) { found_ttype = ATTR_FILTER_TT;
 
634
                       found_tdata = token - 128;
 
635
                       return;
 
636
    }
 
637
 
 
638
    found_ttype = PREPOSITION_TT;
 
639
    m = #adjectives_table;
 
640
    for (::) {
 
641
        if (token == m-->1) { found_tdata = m-->0; return; }
 
642
        m = m+4;
 
643
    }
 
644
    m = #adjectives_table; RunTimeError(1);
 
645
    found_tdata = m;
 
646
];
 
647
 
 
648
[ UnpackGrammarLine line_address i m;
 
649
    for (i=0 : i<32 : i++) {
 
650
        line_token-->i = ENDIT_TOKEN;
 
651
        line_ttype-->i = ELEMENTARY_TT;
 
652
        line_tdata-->i = ENDIT_TOKEN;
 
653
    }
 
654
    for (i=0 : i<=5 : i++) {
 
655
        line_token-->i = line_address->(i+1);
 
656
        AnalyseToken(line_token-->i);
 
657
        if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN)
 
658
           && (m == line_address->0)) {
 
659
            line_token-->i = ENDIT_TOKEN;
 
660
            break;
 
661
        }
 
662
        line_ttype-->i = found_ttype;
 
663
        line_tdata-->i = found_tdata;
 
664
        if (found_ttype ~= PREPOSITION_TT) m++;
 
665
    }
 
666
    action_to_be = line_address->7;
 
667
    action_reversed = false;
 
668
    params_wanted = line_address->0;
 
669
    return line_address + 8;
 
670
];
 
671
 
 
672
#Ifnot; ! Grammar__Version == 2
 
673
 
 
674
[ AnalyseToken token;
 
675
    if (token == ENDIT_TOKEN) {
 
676
        found_ttype = ELEMENTARY_TT;
 
677
        found_tdata = ENDIT_TOKEN;
 
678
        return;
 
679
    }
 
680
    found_ttype = (token->0) & $$1111;
 
681
    found_tdata = (token+1)-->0;
 
682
];
 
683
 
 
684
#Ifdef TARGET_ZCODE;
 
685
 
 
686
[ UnpackGrammarLine line_address i;
 
687
    for (i=0 : i<32 : i++) {
 
688
        line_token-->i = ENDIT_TOKEN;
 
689
        line_ttype-->i = ELEMENTARY_TT;
 
690
        line_tdata-->i = ENDIT_TOKEN;
 
691
    }
 
692
    action_to_be = 256*(line_address->0) + line_address->1;
 
693
    action_reversed = ((action_to_be & $400) ~= 0);
 
694
    action_to_be = action_to_be & $3ff;
 
695
    line_address--;
 
696
    params_wanted = 0;
 
697
    for (i=0 : : i++) {
 
698
        line_address = line_address + 3;
 
699
        if (line_address->0 == ENDIT_TOKEN) break;
 
700
        line_token-->i = line_address;
 
701
        AnalyseToken(line_address);
 
702
        if (found_ttype ~= PREPOSITION_TT) params_wanted++;
 
703
        line_ttype-->i = found_ttype;
 
704
        line_tdata-->i = found_tdata;
 
705
    }
 
706
    return line_address + 1;
 
707
];
 
708
 
 
709
#Ifnot; ! TARGET_GLULX
 
710
 
 
711
[ UnpackGrammarLine line_address i;
 
712
    for (i=0 : i<32 : i++) {
 
713
        line_token-->i = ENDIT_TOKEN;
 
714
        line_ttype-->i = ELEMENTARY_TT;
 
715
        line_tdata-->i = ENDIT_TOKEN;
 
716
    }
 
717
    @aloads line_address 0 action_to_be;
 
718
    action_reversed = (((line_address->2) & 1) ~= 0);
 
719
    line_address = line_address - 2;
 
720
    params_wanted = 0;
 
721
    for (i=0 : : i++) {
 
722
        line_address = line_address + 5;
 
723
        if (line_address->0 == ENDIT_TOKEN) break;
 
724
        line_token-->i = line_address;
 
725
        AnalyseToken(line_address);
 
726
        if (found_ttype ~= PREPOSITION_TT) params_wanted++;
 
727
        line_ttype-->i = found_ttype;
 
728
        line_tdata-->i = found_tdata;
 
729
    }
 
730
    return line_address + 1;
 
731
];
 
732
 
 
733
#Endif; ! TARGET_
 
734
#Endif; ! Grammar__Version
 
735
 
 
736
! To protect against a bug in early versions of the "Zip" interpreter:
 
737
! Of course, in Glulx, this routine actually performs work.
 
738
 
 
739
#Ifdef TARGET_ZCODE;
 
740
 
 
741
[ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ];
 
742
 
 
743
#Ifnot; ! TARGET_GLULX
 
744
 
 
745
Array gg_tokenbuf -> DICT_WORD_SIZE;
 
746
 
 
747
[ GGWordCompare str1 str2 ix jx;
 
748
    for (ix=0 : ix<DICT_WORD_SIZE : ix++) {
 
749
        jx = (str1->ix) - (str2->ix);
 
750
        if (jx ~= 0) return jx;
 
751
    }
 
752
    return 0;
 
753
];
 
754
 
 
755
[ Tokenise__ buf tab
 
756
    cx numwords len bx ix wx wpos wlen val res dictlen entrylen;
 
757
    len = buf-->0;
 
758
    buf = buf+WORDSIZE;
 
759
 
 
760
    ! First, split the buffer up into words. We use the standard Infocom
 
761
    ! list of word separators (comma, period, double-quote).
 
762
 
 
763
    cx = 0;
 
764
    numwords = 0;
 
765
    while (cx < len) {
 
766
        while (cx < len && buf->cx == ' ') cx++;
 
767
        if (cx >= len) break;
 
768
        bx = cx;
 
769
        if (buf->cx == '.' or ',' or '"') cx++;
 
770
        else {
 
771
            while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++;
 
772
        }
 
773
        tab-->(numwords*3+2) = (cx-bx);
 
774
        tab-->(numwords*3+3) = WORDSIZE+bx;
 
775
        numwords++;
 
776
        if (numwords >= MAX_BUFFER_WORDS) break;
 
777
    }
 
778
    tab-->0 = numwords;
 
779
 
 
780
    ! Now we look each word up in the dictionary.
 
781
 
 
782
    dictlen = #dictionary_table-->0;
 
783
    entrylen = DICT_WORD_SIZE + 7;
 
784
 
 
785
    for (wx=0 : wx<numwords : wx++) {
 
786
        wlen = tab-->(wx*3+2);
 
787
        wpos = tab-->(wx*3+3);
 
788
 
 
789
        ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE
 
790
        ! characters and lower case.
 
791
        if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
 
792
        cx = wpos - WORDSIZE;
 
793
        for (ix=0 : ix<wlen : ix++) gg_tokenbuf->ix = glk($00A0, buf->(cx+ix));
 
794
        for (: ix<DICT_WORD_SIZE : ix++) gg_tokenbuf->ix = 0;
 
795
 
 
796
        val = #dictionary_table + WORDSIZE;
 
797
        @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
 
798
        tab-->(wx*3+1) = res;
 
799
    }
 
800
];
 
801
 
 
802
#Endif; ! TARGET_
 
803
 
 
804
! ============================================================================
 
805
!   The InformParser object abstracts the front end of the parser.
 
806
!
 
807
!   InformParser.parse_input(results)
 
808
!   returns only when a sensible request has been made, and puts into the
 
809
!   "results" buffer:
 
810
!
 
811
!   --> 0 = The action number
 
812
!   --> 1 = Number of parameters
 
813
!   --> 2, 3, ... = The parameters (object numbers), but
 
814
!                   0 means "put the multiple object list here"
 
815
!                   1 means "put one of the special numbers here"
 
816
!
 
817
! ----------------------------------------------------------------------------
 
818
 
 
819
Object  InformParser "(Inform Parser)"
 
820
  with  parse_input [ results; Parser__parse(results); ],
 
821
  has   proper;
 
822
 
 
823
! ----------------------------------------------------------------------------
 
824
!   The Keyboard routine actually receives the player's words,
 
825
!   putting the words in "a_buffer" and their dictionary addresses in
 
826
!   "a_table".  It is assumed that the table is the same one on each
 
827
!   (standard) call.
 
828
!
 
829
!   It can also be used by miscellaneous routines in the game to ask
 
830
!   yes-no questions and the like, without invoking the rest of the parser.
 
831
!
 
832
!   Return the number of words typed
 
833
! ----------------------------------------------------------------------------
 
834
 
 
835
#Ifdef TARGET_ZCODE;
 
836
 
 
837
[ KeyboardPrimitive  a_buffer a_table;
 
838
    read a_buffer a_table;
 
839
 
 
840
    #Iftrue (#version_number == 6);
 
841
    @output_stream -1;
 
842
    @loadb a_buffer 1 -> sp;
 
843
    @add a_buffer 2 -> sp;
 
844
    @print_table sp sp;
 
845
    new_line;
 
846
    @output_stream 1;
 
847
    #Endif;
 
848
];
 
849
 
 
850
[ KeyCharPrimitive win  key;
 
851
    if (win) @set_window win;
 
852
    @read_char 1 -> key;
 
853
    return key;
 
854
];
 
855
 
 
856
[ KeyTimerInterrupt;
 
857
    rtrue;
 
858
];
 
859
 
 
860
[ KeyDelay tenths  key;
 
861
    @read_char 1 tenths KeyTimerInterrupt -> key;
 
862
    return key;
 
863
];
 
864
 
 
865
#Ifnot; ! TARGET_GLULX
 
866
 
 
867
[ KeyCharPrimitive win nostat done res ix jx ch;
 
868
    jx = ch; ! squash compiler warnings
 
869
    if (win == 0) win = gg_mainwin;
 
870
    if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
 
871
        ! get_line_stream
 
872
        done = glk($0091, gg_commandstr, gg_arguments, 31);
 
873
        if (done == 0) {
 
874
            glk($0044, gg_commandstr, 0); ! stream_close
 
875
            gg_commandstr = 0;
 
876
            gg_command_reading = false;
 
877
            ! fall through to normal user input.
 
878
        }
 
879
        else {
 
880
            ! Trim the trailing newline
 
881
            if (gg_arguments->(done-1) == 10) done = done-1;
 
882
            res = gg_arguments->0;
 
883
            if (res == '\') {
 
884
                res = 0;
 
885
                for (ix=1 : ix<done : ix++) {
 
886
                    ch = gg_arguments->ix;
 
887
                    if (ch >= '0' && ch <= '9') {
 
888
                        @shiftl res 4 res;
 
889
                        res = res + (ch-'0');
 
890
                    }
 
891
                    else if (ch >= 'a' && ch <= 'f') {
 
892
                        @shiftl res 4 res;
 
893
                        res = res + (ch+10-'a');
 
894
                    }
 
895
                    else if (ch >= 'A' && ch <= 'F') {
 
896
                        @shiftl res 4 res;
 
897
                        res = res + (ch+10-'A');
 
898
                    }
 
899
                }
 
900
            }
 
901
        jump KCPContinue;
 
902
        }
 
903
    }
 
904
    done = false;
 
905
    glk($00D2, win); ! request_char_event
 
906
    while (~~done) {
 
907
        glk($00C0, gg_event); ! select
 
908
        switch (gg_event-->0) {
 
909
          5: ! evtype_Arrange
 
910
            if (nostat) {
 
911
                glk($00D3, win); ! cancel_char_event
 
912
                res = $80000000;
 
913
                done = true;
 
914
                break;
 
915
            }
 
916
            DrawStatusLine();
 
917
          2: ! evtype_CharInput
 
918
            if (gg_event-->1 == win) {
 
919
                res = gg_event-->2;
 
920
                done = true;
 
921
                }
 
922
        }
 
923
        ix = HandleGlkEvent(gg_event, 1, gg_arguments);
 
924
        if (ix == 2) {
 
925
            res = gg_arguments-->0;
 
926
            done = true;
 
927
        }
 
928
        else if (ix == -1) {
 
929
            done = false;
 
930
        }
 
931
    }
 
932
    if (gg_commandstr ~= 0 && gg_command_reading == false) {
 
933
        if (res < 32 || res >= 256 || (res == '\' or ' ')) {
 
934
            glk($0081, gg_commandstr, '\'); ! put_buffer_char
 
935
            done = 0;
 
936
            jx = res;
 
937
            for (ix=0 : ix<8 : ix++) {
 
938
                @ushiftr jx 28 ch;
 
939
                @shiftl jx 4 jx;
 
940
                ch = ch & $0F;
 
941
                if (ch ~= 0 || ix == 7) done = 1;
 
942
                if (done) {
 
943
                    if (ch >= 0 && ch <= 9) ch = ch + '0';
 
944
                    else                    ch = (ch - 10) + 'A';
 
945
                    glk($0081, gg_commandstr, ch); ! put_buffer_char
 
946
                }
 
947
            }
 
948
        }
 
949
        else {
 
950
            glk($0081, gg_commandstr, res); ! put_buffer_char
 
951
        }
 
952
        glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
 
953
    }
 
954
  .KCPContinue;
 
955
    return res;
 
956
];
 
957
 
 
958
[ KeyDelay tenths  key done ix;
 
959
    glk($00D2, gg_mainwin); ! request_char_event
 
960
    glk($00D6, tenths*100); ! request_timer_events
 
961
    while (~~done) {
 
962
        glk($00C0, gg_event); ! select
 
963
        ix = HandleGlkEvent(gg_event, 1, gg_arguments);
 
964
        if (ix == 2) {
 
965
            key = gg_arguments-->0;
 
966
            done = true;
 
967
        }
 
968
        else if (ix >= 0 && gg_event-->0 == 1 or 2) {
 
969
            key = gg_event-->2;
 
970
            done = true;
 
971
        }
 
972
    }
 
973
    glk($00D3, gg_mainwin); ! cancel_char_event
 
974
    glk($00D6, 0); ! request_timer_events
 
975
    return key;
 
976
];
 
977
 
 
978
[ KeyboardPrimitive  a_buffer a_table done ix;
 
979
    if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
 
980
        ! get_line_stream
 
981
        done = glk($0091, gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1);
 
982
        if (done == 0) {
 
983
            glk($0044, gg_commandstr, 0); ! stream_close
 
984
            gg_commandstr = 0;
 
985
            gg_command_reading = false;
 
986
            ! L__M(##CommandsRead, 5); would come after prompt
 
987
            ! fall through to normal user input.
 
988
        }
 
989
        else {
 
990
            ! Trim the trailing newline
 
991
            if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
 
992
            a_buffer-->0 = done;
 
993
            glk($0086, 8); ! set input style
 
994
            glk($0084, a_buffer+WORDSIZE, done); ! put_buffer
 
995
            glk($0086, 0); ! set normal style
 
996
            print "^";
 
997
            jump KPContinue;
 
998
        }
 
999
    }
 
1000
    done = false;
 
1001
    glk($00D0, gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); ! request_line_event
 
1002
    while (~~done) {
 
1003
        glk($00C0, gg_event); ! select
 
1004
        switch (gg_event-->0) {
 
1005
          5: ! evtype_Arrange
 
1006
            DrawStatusLine();
 
1007
          3: ! evtype_LineInput
 
1008
            if (gg_event-->1 == gg_mainwin) {
 
1009
                a_buffer-->0 = gg_event-->2;
 
1010
                done = true;
 
1011
            }
 
1012
        }
 
1013
        ix = HandleGlkEvent(gg_event, 0, a_buffer);
 
1014
        if (ix == 2) done = true;
 
1015
        else if (ix == -1) done = false;
 
1016
    }
 
1017
    if (gg_commandstr ~= 0 && gg_command_reading == false) {
 
1018
        ! put_buffer_stream
 
1019
        glk($0085, gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0);
 
1020
        glk($0081, gg_commandstr, 10); ! put_char_stream (newline)
 
1021
    }
 
1022
  .KPContinue;
 
1023
    Tokenise__(a_buffer,a_table);
 
1024
    ! It's time to close any quote window we've got going.
 
1025
    if (gg_quotewin) {
 
1026
        glk($0024, gg_quotewin, 0); ! close_window
 
1027
        gg_quotewin = 0;
 
1028
    }
 
1029
];
 
1030
 
 
1031
#Endif; ! TARGET_
 
1032
 
 
1033
[ Keyboard  a_buffer a_table  nw i w w2 x1 x2;
 
1034
    DisplayStatus();
 
1035
 
 
1036
  .FreshInput;
 
1037
 
 
1038
    ! Save the start of the buffer, in case "oops" needs to restore it
 
1039
    ! to the previous time's buffer
 
1040
 
 
1041
    for (i=0 : i<64 : i++) oops_workspace->i = a_buffer->i;
 
1042
 
 
1043
    ! In case of an array entry corruption that shouldn't happen, but would be
 
1044
    ! disastrous if it did:
 
1045
 
 
1046
    #Ifdef TARGET_ZCODE;
 
1047
    a_buffer->0 = INPUT_BUFFER_LEN;
 
1048
    a_table->0 = 15;  ! Allow to split input into this many words
 
1049
    #Endif; ! TARGET_
 
1050
 
 
1051
    ! Print the prompt, and read in the words and dictionary addresses
 
1052
 
 
1053
    L__M(##Prompt);
 
1054
    AfterPrompt();
 
1055
    #IfV5;
 
1056
    DrawStatusLine();
 
1057
    #Endif; ! V5
 
1058
    KeyboardPrimitive(a_buffer, a_table);
 
1059
    #Ifdef TARGET_ZCODE;
 
1060
    nw = a_table->1;
 
1061
    #Ifnot; ! TARGET_GLULX
 
1062
    nw = a_table-->0;
 
1063
    #Endif; ! TARGET_
 
1064
 
 
1065
    ! If the line was blank, get a fresh line
 
1066
    if (nw == 0) {
 
1067
        L__M(##Miscellany, 10);
 
1068
        jump FreshInput;
 
1069
    }
 
1070
 
 
1071
    ! Unless the opening word was "oops", return
 
1072
    ! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX.
 
1073
 
 
1074
    w = a_table-->1;
 
1075
    if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;
 
1076
 
 
1077
    if (a_buffer->WORDSIZE == COMMENT_CHARACTER) {
 
1078
        #Ifdef TARGET_ZCODE;
 
1079
        if ((HDR_GAMEFLAGS-->0) & 1 || xcommsdir)
 
1080
                                           L__M(##Miscellany, 54);
 
1081
        else                               L__M(##Miscellany, 55);
 
1082
        #Ifnot; ! TARGET_GLULX
 
1083
        if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54);
 
1084
        else                               L__M(##Miscellany, 55);
 
1085
        #Endif; ! TARGET_
 
1086
 
 
1087
        jump FreshInput;
 
1088
    }
 
1089
 
 
1090
    #IfV5;
 
1091
    ! Undo handling
 
1092
 
 
1093
    if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) {
 
1094
        if (turns == START_MOVE) {
 
1095
            L__M(##Miscellany, 11);
 
1096
            jump FreshInput;
 
1097
        }
 
1098
        if (undo_flag == 0) {
 
1099
            L__M(##Miscellany, 6);
 
1100
            jump FreshInput;
 
1101
        }
 
1102
        if (undo_flag == 1) jump UndoFailed;
 
1103
        #Ifdef TARGET_ZCODE;
 
1104
        @restore_undo i;
 
1105
        #Ifnot; ! TARGET_GLULX
 
1106
        @restoreundo i;
 
1107
        i = (~~i);
 
1108
        #Endif; ! TARGET_
 
1109
        if (i == 0) {
 
1110
          .UndoFailed;
 
1111
            L__M(##Miscellany, 7);
 
1112
        }
 
1113
        jump FreshInput;
 
1114
    }
 
1115
    #Ifdef TARGET_ZCODE;
 
1116
    @save_undo i;
 
1117
    #Ifnot; ! TARGET_GLULX
 
1118
    @saveundo i;
 
1119
    if (i == -1) {
 
1120
        GGRecoverObjects();
 
1121
        i = 2;
 
1122
    }
 
1123
    else  i = (~~i);
 
1124
    #Endif; ! TARGET_
 
1125
    just_undone = 0;
 
1126
    undo_flag = 2;
 
1127
    if (i == -1) undo_flag = 0;
 
1128
    if (i == 0) undo_flag = 1;
 
1129
    if (i == 2) {
 
1130
        RestoreColours();
 
1131
        #Ifdef TARGET_ZCODE;
 
1132
        style bold;
 
1133
        #Ifnot; ! TARGET_GLULX
 
1134
        glk($0086, 4); ! set subheader style
 
1135
        #Endif; ! TARGET_
 
1136
        print (name) location, "^";
 
1137
        #Ifdef TARGET_ZCODE;
 
1138
        style roman;
 
1139
        #Ifnot; ! TARGET_GLULX
 
1140
        glk($0086, 0); ! set normal style
 
1141
        #Endif; ! TARGET_
 
1142
        L__M(##Miscellany, 13);
 
1143
        just_undone = 1;
 
1144
        jump FreshInput;
 
1145
    }
 
1146
    #Endif; ! V5
 
1147
 
 
1148
    return nw;
 
1149
 
 
1150
  .DoOops;
 
1151
    if (oops_from == 0) {
 
1152
        L__M(##Miscellany, 14);
 
1153
        jump FreshInput;
 
1154
    }
 
1155
    if (nw == 1) {
 
1156
        L__M(##Miscellany, 15);
 
1157
        jump FreshInput;
 
1158
    }
 
1159
    if (nw > 2) {
 
1160
        L__M(##Miscellany, 16);
 
1161
        jump FreshInput;
 
1162
    }
 
1163
 
 
1164
    ! So now we know: there was a previous mistake, and the player has
 
1165
    ! attempted to correct a single word of it.
 
1166
 
 
1167
    for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer2->i = a_buffer->i;
 
1168
    #Ifdef TARGET_ZCODE;
 
1169
    x1 = a_table->9;  ! Start of word following "oops"
 
1170
    x2 = a_table->8;  ! Length of word following "oops"
 
1171
    #Ifnot; ! TARGET_GLULX
 
1172
    x1 = a_table-->6; ! Start of word following "oops"
 
1173
    x2 = a_table-->5; ! Length of word following "oops"
 
1174
    #Endif; ! TARGET_
 
1175
 
 
1176
    ! Repair the buffer to the text that was in it before the "oops"
 
1177
    ! was typed:
 
1178
 
 
1179
    for (i=0 : i<64 : i++) a_buffer->i = oops_workspace->i;
 
1180
    Tokenise__(a_buffer,a_table);
 
1181
 
 
1182
    ! Work out the position in the buffer of the word to be corrected:
 
1183
 
 
1184
    #Ifdef TARGET_ZCODE;
 
1185
    w = a_table->(4*oops_from + 1); ! Start of word to go
 
1186
    w2 = a_table->(4*oops_from);    ! Length of word to go
 
1187
    #Ifnot; ! TARGET_GLULX
 
1188
    w = a_table-->(3*oops_from);      ! Start of word to go
 
1189
    w2 = a_table-->(3*oops_from - 1); ! Length of word to go
 
1190
    #Endif; ! TARGET_
 
1191
 
 
1192
    ! Write spaces over the word to be corrected:
 
1193
 
 
1194
    for (i=0 : i<w2 : i++) a_buffer->(i+w) = ' ';
 
1195
 
 
1196
    if (w2 < x2) {
 
1197
        ! If the replacement is longer than the original, move up...
 
1198
        for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--)
 
1199
            a_buffer->i = a_buffer->(i-x2+w2);
 
1200
 
 
1201
        ! ...increasing buffer size accordingly.
 
1202
        #Ifdef TARGET_ZCODE;
 
1203
        a_buffer->1 = (a_buffer->1) + (x2-w2);
 
1204
        #Ifnot; ! TARGET_GLULX
 
1205
        a_buffer-->0 = (a_buffer-->0) + (x2-w2);
 
1206
        #Endif; ! TARGET_
 
1207
    }
 
1208
 
 
1209
    ! Write the correction in:
 
1210
 
 
1211
    for (i=0 : i<x2 : i++) a_buffer->(i+w) = buffer2->(i+x1);
 
1212
 
 
1213
    Tokenise__(a_buffer, a_table);
 
1214
    #Ifdef TARGET_ZCODE;
 
1215
    nw = a_table->1;
 
1216
    #Ifnot; ! TARGET_GLULX
 
1217
    nw = a_table-->0;
 
1218
    #Endif; ! TARGET_
 
1219
 
 
1220
    return nw;
 
1221
]; ! end of Keyboard
 
1222
 
 
1223
! ----------------------------------------------------------------------------
 
1224
!   To simplify the picture a little, a rough map of the main routine:
 
1225
!
 
1226
!   (A) Get the input, do "oops" and "again"
 
1227
!   (B) Is it a direction, and so an implicit "go"?  If so go to (K)
 
1228
!   (C) Is anyone being addressed?
 
1229
!   (D) Get the verb: try all the syntax lines for that verb
 
1230
!   (E) Break down a syntax line into analysed tokens
 
1231
!   (F) Look ahead for advance warning for multiexcept/multiinside
 
1232
!   (G) Parse each token in turn (calling ParseToken to do most of the work)
 
1233
!   (H) Cheaply parse otherwise unrecognised conversation and return
 
1234
!   (I) Print best possible error message
 
1235
!   (J) Retry the whole lot
 
1236
!   (K) Last thing: check for "then" and further instructions(s), return.
 
1237
!
 
1238
!   The strategic points (A) to (K) are marked in the commentary.
 
1239
!
 
1240
!   Note that there are three different places where a return can happen.
 
1241
! ----------------------------------------------------------------------------
 
1242
 
 
1243
[ Parser__parse  results   syntax line num_lines line_address i j k
 
1244
                           token l m;
 
1245
 
 
1246
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1247
    !
 
1248
    ! A: Get the input, do "oops" and "again"
 
1249
    !
 
1250
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1251
 
 
1252
    ! Firstly, in "not held" mode, we still have a command left over from last
 
1253
    ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
 
1254
    ! last time, with "eat biscuit" tucked away until now).  So we return that.
 
1255
 
 
1256
    if (notheld_mode == 1) {
 
1257
        for (i=0 : i<8 : i++) results-->i = kept_results-->i;
 
1258
        notheld_mode = 0;
 
1259
        rtrue;
 
1260
    }
 
1261
 
 
1262
    if (held_back_mode == 1) {
 
1263
        held_back_mode = 0;
 
1264
        Tokenise__(buffer, parse);
 
1265
        jump ReParse;
 
1266
    }
 
1267
 
 
1268
  .ReType;
 
1269
 
 
1270
    Keyboard(buffer,parse);
 
1271
 
 
1272
  .ReParse;
 
1273
 
 
1274
    parser_inflection = name;
 
1275
 
 
1276
    ! Initially assume the command is aimed at the player, and the verb
 
1277
    ! is the first word
 
1278
 
 
1279
    #Ifdef TARGET_ZCODE;
 
1280
    num_words = parse->1;
 
1281
    #Ifnot; ! TARGET_GLULX
 
1282
    num_words = parse-->0;
 
1283
    #Endif; ! TARGET_
 
1284
    wn = 1;
 
1285
 
 
1286
    #Ifdef LanguageToInformese;
 
1287
    LanguageToInformese();
 
1288
    #IfV5;
 
1289
    ! Re-tokenise:
 
1290
    Tokenise__(buffer,parse);
 
1291
    #Endif; ! V5
 
1292
    #Endif; ! LanguageToInformese
 
1293
 
 
1294
    BeforeParsing();
 
1295
    #Ifdef TARGET_ZCODE;
 
1296
    num_words = parse->1;
 
1297
    #Ifnot; ! TARGET_GLULX
 
1298
    num_words = parse-->0;
 
1299
    #Endif; ! TARGET_
 
1300
 
 
1301
    k=0;
 
1302
    #Ifdef DEBUG;
 
1303
    if (parser_trace >= 2) {
 
1304
        print "[ ";
 
1305
        for (i=0 : i<num_words : i++) {
 
1306
 
 
1307
            #Ifdef TARGET_ZCODE;
 
1308
            j = parse-->(i*2 + 1);
 
1309
            #Ifnot; ! TARGET_GLULX
 
1310
            j = parse-->(i*3 + 1);
 
1311
            #Endif; ! TARGET_
 
1312
            k = WordAddress(i+1);
 
1313
            l = WordLength(i+1);
 
1314
            print "~"; for (m=0 : m<l : m++) print (char) k->m; print "~ ";
 
1315
 
 
1316
            if (j == 0) print "?";
 
1317
            else {
 
1318
                #Ifdef TARGET_ZCODE;
 
1319
                if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 &&
 
1320
                    UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0)
 
1321
                     print (address) j;
 
1322
                else print j;
 
1323
                #Ifnot; ! TARGET_GLULX
 
1324
                if (j->0 == $60) print (address) j;
 
1325
                else print j;
 
1326
                #Endif; ! TARGET_
 
1327
            }
 
1328
            if (i ~= num_words-1) print " / ";
 
1329
        }
 
1330
        print " ]^";
 
1331
    }
 
1332
    #Endif; ! DEBUG
 
1333
    verb_wordnum = 1;
 
1334
    actor = player;
 
1335
    actors_location = ScopeCeiling(player);
 
1336
    usual_grammar_after = 0;
 
1337
 
 
1338
  .AlmostReParse;
 
1339
 
 
1340
    scope_token = 0;
 
1341
    action_to_be = NULL;
 
1342
 
 
1343
    ! Begin from what we currently think is the verb word
 
1344
 
 
1345
  .BeginCommand;
 
1346
 
 
1347
    wn = verb_wordnum;
 
1348
    verb_word = NextWordStopped();
 
1349
 
 
1350
    ! If there's no input here, we must have something like "person,".
 
1351
 
 
1352
    if (verb_word == -1) {
 
1353
        best_etype = STUCK_PE;
 
1354
        jump GiveError;
 
1355
    }
 
1356
 
 
1357
    ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing
 
1358
    ! has previously been typed; simply copy the previous text across
 
1359
 
 
1360
    if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD;
 
1361
    if (verb_word == AGAIN1__WD) {
 
1362
        if (actor ~= player) {
 
1363
            L__M(##Miscellany, 20);
 
1364
            jump ReType;
 
1365
        }
 
1366
        #Ifdef TARGET_ZCODE;
 
1367
        if (buffer3->1 == 0) {
 
1368
            L__M(##Miscellany, 21);
 
1369
            jump ReType;
 
1370
        }
 
1371
        #Ifnot; ! TARGET_GLULX
 
1372
        if (buffer3-->0 == 0) {
 
1373
            L__M(##Miscellany, 21);
 
1374
            jump ReType;
 
1375
        }
 
1376
        #Endif; ! TARGET_
 
1377
        for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer->i = buffer3->i;
 
1378
        jump ReParse;
 
1379
    }
 
1380
 
 
1381
    ! Save the present input in case of an "again" next time
 
1382
 
 
1383
    if (verb_word ~= AGAIN1__WD)
 
1384
        for (i=0 : i<INPUT_BUFFER_LEN : i++) buffer3->i = buffer->i;
 
1385
 
 
1386
    if (usual_grammar_after == 0) {
 
1387
        j = verb_wordnum;
 
1388
        i = RunRoutines(actor, grammar); 
 
1389
        #Ifdef DEBUG;
 
1390
        if (parser_trace >= 2 && actor.grammar ~= 0 or NULL)
 
1391
            print " [Grammar property returned ", i, "]^";
 
1392
        #Endif; ! DEBUG
 
1393
 
 
1394
        #Ifdef TARGET_ZCODE;
 
1395
        if ((i ~= 0 or 1) &&
 
1396
            (UnsignedCompare(i, dict_start) < 0 ||
 
1397
             UnsignedCompare(i, dict_end) >= 0 ||
 
1398
             (i - dict_start) % dict_entry_size ~= 0)) {
 
1399
            usual_grammar_after = j;
 
1400
            i=-i;
 
1401
        }
 
1402
 
 
1403
        #Ifnot; ! TARGET_GLULX
 
1404
        if (i < 0) { usual_grammar_after = verb_wordnum; i=-i; }
 
1405
        #Endif;
 
1406
 
 
1407
        if (i == 1) {
 
1408
            results-->0 = action;
 
1409
            results-->1 = noun;
 
1410
            results-->2 = second;
 
1411
            rtrue;
 
1412
        }
 
1413
        if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; }
 
1414
        else { wn = verb_wordnum; verb_word = NextWord(); }
 
1415
    }
 
1416
    else usual_grammar_after = 0;
 
1417
 
 
1418
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1419
    !
 
1420
    ! B: Is it a direction, and so an implicit "go"?  If so go to (K)
 
1421
    !
 
1422
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1423
 
 
1424
    #Ifdef LanguageIsVerb;
 
1425
    if (verb_word == 0) {
 
1426
        i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum);
 
1427
        wn = i;
 
1428
    }
 
1429
    #Endif; ! LanguageIsVerb
 
1430
 
 
1431
    ! If the first word is not listed as a verb, it must be a direction
 
1432
    ! or the name of someone to talk to
 
1433
 
 
1434
    if (verb_word == 0 || ((verb_word->#dict_par1) & 1) == 0) {
 
1435
 
 
1436
        ! So is the first word an object contained in the special object "compass"
 
1437
        ! (i.e., a direction)?  This needs use of NounDomain, a routine which
 
1438
        ! does the object matching, returning the object number, or 0 if none found,
 
1439
        ! or REPARSE_CODE if it has restructured the parse table so the whole parse
 
1440
        ! must be begun again...
 
1441
 
 
1442
        wn = verb_wordnum; indef_mode = false; token_filter = 0;
 
1443
        l = NounDomain(compass, 0, 0);
 
1444
        if (l == REPARSE_CODE) jump ReParse;
 
1445
 
 
1446
        ! If it is a direction, send back the results:
 
1447
        ! action=GoSub, no of arguments=1, argument 1=the direction.
 
1448
 
 
1449
        if (l ~= 0) {
 
1450
            results-->0 = ##Go;
 
1451
            action_to_be = ##Go;
 
1452
            results-->1 = 1;
 
1453
            results-->2 = l;
 
1454
            jump LookForMore;
 
1455
        }
 
1456
 
 
1457
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1458
    !
 
1459
    ! C: Is anyone being addressed?
 
1460
    !
 
1461
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1462
 
 
1463
        ! Only check for a comma (a "someone, do something" command) if we are
 
1464
        ! not already in the middle of one.  (This simplification stops us from
 
1465
        ! worrying about "robot, wizard, you are an idiot", telling the robot to
 
1466
        ! tell the wizard that she is an idiot.)
 
1467
 
 
1468
        if (actor == player) {
 
1469
            for (j=2 : j<=num_words : j++) {
 
1470
                i=NextWord();
 
1471
                if (i == comma_word) jump Conversation;
 
1472
            }
 
1473
            verb_word = UnknownVerb(verb_word);
 
1474
            if (verb_word ~= 0) jump VerbAccepted;
 
1475
        }
 
1476
        best_etype = VERB_PE;
 
1477
        jump GiveError;
 
1478
 
 
1479
        ! NextWord nudges the word number wn on by one each time, so we've now
 
1480
        ! advanced past a comma.  (A comma is a word all on its own in the table.)
 
1481
 
 
1482
      .Conversation;
 
1483
 
 
1484
        j = wn - 1;
 
1485
        if (j == 1) {
 
1486
            L__M(##Miscellany, 22);
 
1487
            jump ReType;
 
1488
        }
 
1489
 
 
1490
        ! Use NounDomain (in the context of "animate creature") to see if the
 
1491
        ! words make sense as the name of someone held or nearby
 
1492
 
 
1493
        wn = 1; lookahead = HELD_TOKEN;
 
1494
        scope_reason = TALKING_REASON;
 
1495
        l = NounDomain(player,actors_location,6);
 
1496
        scope_reason = PARSING_REASON;
 
1497
        if (l == REPARSE_CODE) jump ReParse;
 
1498
        if (l == 0) {
 
1499
            L__M(##Miscellany, 23);
 
1500
            jump ReType;
 
1501
        }
 
1502
 
 
1503
      .Conversation2;
 
1504
 
 
1505
        ! The object addressed must at least be "talkable" if not actually "animate"
 
1506
        ! (the distinction allows, for instance, a microphone to be spoken to,
 
1507
        ! without the parser thinking that the microphone is human).
 
1508
 
 
1509
        if (l hasnt animate && l hasnt talkable) {
 
1510
            L__M(##Miscellany, 24, l);
 
1511
            jump ReType;
 
1512
        }
 
1513
 
 
1514
        ! Check that there aren't any mystery words between the end of the person's
 
1515
        ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
 
1516
 
 
1517
        if (wn ~= j) {
 
1518
            L__M(##Miscellany, 25);
 
1519
            jump ReType;
 
1520
        }
 
1521
 
 
1522
        ! The player has now successfully named someone.  Adjust "him", "her", "it":
 
1523
 
 
1524
        PronounNotice(l);
 
1525
 
 
1526
        ! Set the global variable "actor", adjust the number of the first word,
 
1527
        ! and begin parsing again from there.
 
1528
 
 
1529
        verb_wordnum = j + 1;
 
1530
 
 
1531
        ! Stop things like "me, again":
 
1532
 
 
1533
        if (l == player) {
 
1534
            wn = verb_wordnum;
 
1535
            if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
 
1536
                L__M(##Miscellany, 20);
 
1537
                jump ReType;
 
1538
            }
 
1539
        }
 
1540
 
 
1541
        actor = l;
 
1542
        actors_location = ScopeCeiling(l);
 
1543
        #Ifdef DEBUG;
 
1544
        if (parser_trace >= 1)
 
1545
            print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^";
 
1546
        #Endif; ! DEBUG
 
1547
        jump BeginCommand;
 
1548
 
 
1549
    } ! end of first-word-not-a-verb
 
1550
 
 
1551
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1552
    !
 
1553
    ! D: Get the verb: try all the syntax lines for that verb
 
1554
    !
 
1555
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1556
 
 
1557
  .VerbAccepted;
 
1558
 
 
1559
    ! We now definitely have a verb, not a direction, whether we got here by the
 
1560
    ! "take ..." or "person, take ..." method.  Get the meta flag for this verb:
 
1561
 
 
1562
    meta = ((verb_word->#dict_par1) & 2)/2;
 
1563
 
 
1564
    ! You can't order other people to "full score" for you, and so on...
 
1565
 
 
1566
    if (meta == 1 && actor ~= player) {
 
1567
        best_etype = VERB_PE;
 
1568
        meta = 0;
 
1569
        jump GiveError;
 
1570
    }
 
1571
 
 
1572
    ! Now let i be the corresponding verb number, stored in the dictionary entry
 
1573
    ! (in a peculiar 255-n fashion for traditional Infocom reasons)...
 
1574
 
 
1575
    i = $ff-(verb_word->#dict_par2);
 
1576
 
 
1577
    ! ...then look up the i-th entry in the verb table, whose address is at word
 
1578
    ! 7 in the Z-machine (in the header), so as to get the address of the syntax
 
1579
    ! table for the given verb...
 
1580
 
 
1581
    #Ifdef TARGET_ZCODE;
 
1582
    syntax = (HDR_STATICMEMORY-->0)-->i;
 
1583
    #Ifnot; ! TARGET_GLULX
 
1584
    syntax = (#grammar_table)-->(i+1);
 
1585
    #Endif; ! TARGET_
 
1586
 
 
1587
    ! ...and then see how many lines (ie, different patterns corresponding to the
 
1588
    ! same verb) are stored in the parse table...
 
1589
 
 
1590
    num_lines = (syntax->0) - 1;
 
1591
 
 
1592
    ! ...and now go through them all, one by one.
 
1593
    ! To prevent pronoun_word 0 being misunderstood,
 
1594
 
 
1595
    pronoun_word = NULL; pronoun_obj = NULL;
 
1596
 
 
1597
    #Ifdef DEBUG;
 
1598
    if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^";
 
1599
    #Endif; ! DEBUG
 
1600
 
 
1601
    best_etype = STUCK_PE; nextbest_etype = STUCK_PE;
 
1602
    multiflag = false;
 
1603
 
 
1604
    ! "best_etype" is the current failure-to-match error - it is by default
 
1605
    ! the least informative one, "don't understand that sentence".
 
1606
    ! "nextbest_etype" remembers the best alternative to having to ask a
 
1607
    ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE).
 
1608
    ! multiflag is used here to prevent inappropriate MULTI_PE errors
 
1609
    ! in addition to its unrelated duties passing information to action routines
 
1610
 
 
1611
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1612
    !
 
1613
    ! E: Break down a syntax line into analysed tokens
 
1614
    !
 
1615
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1616
 
 
1617
    line_address = syntax + 1;
 
1618
 
 
1619
    for (line=0 : line<=num_lines : line++) {
 
1620
 
 
1621
        for (i=0 : i<32 : i++) {
 
1622
            line_token-->i = ENDIT_TOKEN;
 
1623
            line_ttype-->i = ELEMENTARY_TT;
 
1624
            line_tdata-->i = ENDIT_TOKEN;
 
1625
        }
 
1626
 
 
1627
        ! Unpack the syntax line from Inform format into three arrays; ensure that
 
1628
        ! the sequence of tokens ends in an ENDIT_TOKEN.
 
1629
 
 
1630
        line_address = UnpackGrammarLine(line_address);
 
1631
 
 
1632
        #Ifdef DEBUG;
 
1633
        if (parser_trace >= 1) {
 
1634
            if (parser_trace >= 2) new_line;
 
1635
            print "[line ", line; DebugGrammarLine();
 
1636
            print "]^";
 
1637
        }
 
1638
        #Endif; ! DEBUG
 
1639
 
 
1640
        ! We aren't in "not holding" or inferring modes, and haven't entered
 
1641
        ! any parameters on the line yet, or any special numbers; the multiple
 
1642
        ! object is still empty.
 
1643
 
 
1644
        not_holding = 0;
 
1645
        inferfrom = 0;
 
1646
        parameters = 0;
 
1647
        nsns = 0; special_word = 0; special_number = 0;
 
1648
        multiple_object-->0 = 0;
 
1649
        multi_context = 0;
 
1650
        etype = STUCK_PE;
 
1651
 
 
1652
        ! Put the word marker back to just after the verb
 
1653
 
 
1654
        wn = verb_wordnum+1;
 
1655
 
 
1656
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1657
    !
 
1658
    ! F: Look ahead for advance warning for multiexcept/multiinside
 
1659
    !
 
1660
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1661
 
 
1662
        ! There are two special cases where parsing a token now has to be
 
1663
        ! affected by the result of parsing another token later, and these
 
1664
        ! two cases (multiexcept and multiinside tokens) are helped by a quick
 
1665
        ! look ahead, to work out the future token now.  We can only carry this
 
1666
        ! out in the simple (but by far the most common) case:
 
1667
        !
 
1668
        !     multiexcept <one or more prepositions> noun
 
1669
        !
 
1670
        ! and similarly for multiinside.
 
1671
 
 
1672
        advance_warning = NULL; indef_mode = false;
 
1673
        for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
 
1674
            scope_token = 0;
 
1675
 
 
1676
            if (line_ttype-->pcount ~= PREPOSITION_TT) i++;
 
1677
 
 
1678
            if (line_ttype-->pcount == ELEMENTARY_TT) {
 
1679
                if (line_tdata-->pcount == MULTI_TOKEN) m = true;
 
1680
                if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN  && i == 1) {
 
1681
                    ! First non-preposition is "multiexcept" or
 
1682
                    ! "multiinside", so look ahead.
 
1683
 
 
1684
                    #Ifdef DEBUG;
 
1685
                    if (parser_trace >= 2) print " [Trying look-ahead]^";
 
1686
                    #Endif; ! DEBUG
 
1687
 
 
1688
                    ! We need this to be followed by 1 or more prepositions.
 
1689
 
 
1690
                    pcount++;
 
1691
                    if (line_ttype-->pcount == PREPOSITION_TT) {
 
1692
                        while (line_ttype-->pcount == PREPOSITION_TT) pcount++;
 
1693
 
 
1694
                        if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) {
 
1695
 
 
1696
                            ! Advance past the last preposition
 
1697
 
 
1698
                            while (wn < num_words) {
 
1699
                                l=NextWord();
 
1700
                                if ( l && (l->#dict_par1) &8 ) {   ! if preposition
 
1701
                                    l = Descriptors();  ! skip past THE etc
 
1702
                                    if (l~=0) etype=l;  ! don't allow multiple objects
 
1703
                                    l = NounDomain(actors_location, actor, NOUN_TOKEN);
 
1704
                                    #Ifdef DEBUG;
 
1705
                                    if (parser_trace >= 2) {
 
1706
                                        print " [Advanced to ~noun~ token: ";
 
1707
                                        if (l == REPARSE_CODE) print "re-parse request]^";
 
1708
                                        if (l == 1) print "but multiple found]^";
 
1709
                                        if (l == 0) print "error ", etype, "]^";
 
1710
                                        if (l >= 2) print (the) l, "]^";
 
1711
                                    }
 
1712
                                    #Endif; ! DEBUG
 
1713
                                    if (l == REPARSE_CODE) jump ReParse;
 
1714
                                    if (l >= 2) advance_warning = l;
 
1715
                                }
 
1716
                            }
 
1717
                        }
 
1718
                    }
 
1719
                    break;
 
1720
                }
 
1721
            }
 
1722
        }
 
1723
 
 
1724
        ! Slightly different line-parsing rules will apply to "take multi", to
 
1725
        ! prevent "take all" behaving correctly but misleadingly when there's
 
1726
        ! nothing to take.
 
1727
 
 
1728
        take_all_rule = 0;
 
1729
        if (m && params_wanted == 1 && action_to_be == ##Take)
 
1730
            take_all_rule = 1;
 
1731
 
 
1732
        ! And now start again, properly, forearmed or not as the case may be.
 
1733
        ! As a precaution, we clear all the variables again (they may have been
 
1734
        ! disturbed by the call to NounDomain, which may have called outside
 
1735
        ! code, which may have done anything!).
 
1736
 
 
1737
        not_holding = 0;
 
1738
        inferfrom = 0;
 
1739
        parameters = 0;
 
1740
        nsns = 0; special_word = 0; special_number = 0;
 
1741
        multiple_object-->0 = 0;
 
1742
        etype = STUCK_PE;
 
1743
        wn = verb_wordnum+1;
 
1744
 
 
1745
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1746
    !
 
1747
    ! G: Parse each token in turn (calling ParseToken to do most of the work)
 
1748
    !
 
1749
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1750
 
 
1751
        ! "Pattern" gradually accumulates what has been recognised so far,
 
1752
        ! so that it may be reprinted by the parser later on
 
1753
 
 
1754
        for (pcount=1 : : pcount++) {
 
1755
            pattern-->pcount = PATTERN_NULL; scope_token = 0;
 
1756
 
 
1757
            token = line_token-->(pcount-1);
 
1758
            lookahead = line_token-->pcount;
 
1759
 
 
1760
            #Ifdef DEBUG;
 
1761
            if (parser_trace >= 2)
 
1762
                print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token,
 
1763
                  "]^";
 
1764
            #Endif; ! DEBUG
 
1765
 
 
1766
            if (token ~= ENDIT_TOKEN) {
 
1767
                scope_reason = PARSING_REASON;
 
1768
                parser_inflection = name;
 
1769
                AnalyseToken(token);
 
1770
 
 
1771
                if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT &&
 
1772
                    found_tdata == TOPIC_TOKEN)
 
1773
                {
 
1774
                    l=inputobjs-->2;
 
1775
                    wn--;
 
1776
                    j = wn;
 
1777
                    jump Conversation2;
 
1778
                }
 
1779
 
 
1780
                l = ParseToken__(found_ttype, found_tdata, pcount-1, token);
 
1781
                while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256);
 
1782
                scope_reason = PARSING_REASON;
 
1783
 
 
1784
                if (l == GPR_PREPOSITION) {
 
1785
                    if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT ||
 
1786
                        found_tdata~=TOPIC_TOKEN)) params_wanted--;
 
1787
                    l = true;
 
1788
                }
 
1789
                else
 
1790
                    if (l < 0) l = false;
 
1791
                    else
 
1792
                        if (l ~= GPR_REPARSE) {
 
1793
                            if (l == GPR_NUMBER) {
 
1794
                                if (nsns == 0) special_number1 = parsed_number;
 
1795
                                else special_number2 = parsed_number;
 
1796
                                nsns++; l = 1;
 
1797
                            }
 
1798
                            if (l == GPR_MULTIPLE) l = 0;
 
1799
                            results-->(parameters+2) = l;
 
1800
                            parameters++;
 
1801
                            pattern-->pcount = l;
 
1802
                            l = true;
 
1803
                        }
 
1804
 
 
1805
                #Ifdef DEBUG;
 
1806
                if (parser_trace >= 3) {
 
1807
                    print "  [token resulted in ";
 
1808
                    if (l == REPARSE_CODE) print "re-parse request]^";
 
1809
                    if (l == 0) print "failure with error type ", etype, "]^";
 
1810
                    if (l == 1) print "success]^";
 
1811
                }
 
1812
                #Endif; ! DEBUG
 
1813
 
 
1814
                if (l == REPARSE_CODE) jump ReParse;
 
1815
                if (l == false) break;
 
1816
            }
 
1817
            else {
 
1818
 
 
1819
                ! If the player has entered enough already but there's still
 
1820
                ! text to wade through: store the pattern away so as to be able to produce
 
1821
                ! a decent error message if this turns out to be the best we ever manage,
 
1822
                ! and in the mean time give up on this line
 
1823
 
 
1824
                ! However, if the superfluous text begins with a comma or "then" then
 
1825
                ! take that to be the start of another instruction
 
1826
 
 
1827
                if (wn <= num_words) {
 
1828
                    l = NextWord();
 
1829
                    if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
 
1830
                        held_back_mode = 1; hb_wn = wn-1;
 
1831
                    }
 
1832
                    else {
 
1833
                        for (m=0 : m<32 : m++) pattern2-->m = pattern-->m;
 
1834
                        pcount2 = pcount;
 
1835
                        etype = UPTO_PE;
 
1836
                        break;
 
1837
                    }
 
1838
                }
 
1839
 
 
1840
                ! Now, we may need to revise the multiple object because of the single one
 
1841
                ! we now know (but didn't when the list was drawn up).
 
1842
 
 
1843
                if (parameters >= 1 && results-->2 == 0) {
 
1844
                    l = ReviseMulti(results-->3);
 
1845
                    if (l ~= 0) { etype = l; results-->0 = action_to_be; break; }
 
1846
                }
 
1847
                if (parameters >= 2 && results-->3 == 0) {
 
1848
                    l = ReviseMulti(results-->2);
 
1849
                    if (l ~= 0) { etype = l; break; }
 
1850
                }
 
1851
 
 
1852
                ! To trap the case of "take all" inferring only "yourself" when absolutely
 
1853
                ! nothing else is in the vicinity...
 
1854
 
 
1855
                if (take_all_rule == 2 && results-->2 == actor) {
 
1856
                    best_etype = NOTHING_PE;
 
1857
                    jump GiveError;
 
1858
                }
 
1859
 
 
1860
                #Ifdef DEBUG;
 
1861
                if (parser_trace >= 1) print "[Line successfully parsed]^";
 
1862
                #Endif; ! DEBUG
 
1863
 
 
1864
                ! The line has successfully matched the text.  Declare the input error-free...
 
1865
 
 
1866
                oops_from = 0;
 
1867
 
 
1868
                ! ...explain any inferences made (using the pattern)...
 
1869
 
 
1870
                if (inferfrom ~= 0) {
 
1871
                    print "("; PrintCommand(inferfrom); print ")^";
 
1872
                }
 
1873
 
 
1874
                ! ...copy the action number, and the number of parameters...
 
1875
 
 
1876
                results-->0 = action_to_be;
 
1877
                results-->1 = parameters;
 
1878
 
 
1879
                ! ...reverse first and second parameters if need be...
 
1880
 
 
1881
                if (action_reversed && parameters == 2) {
 
1882
                    i = results-->2; results-->2 = results-->3;
 
1883
                    results-->3 = i;
 
1884
                    if (nsns == 2) {
 
1885
                        i = special_number1; special_number1 = special_number2;
 
1886
                        special_number2 = i;
 
1887
                    }
 
1888
                }
 
1889
 
 
1890
                ! ...and to reset "it"-style objects to the first of these parameters, if
 
1891
                ! there is one (and it really is an object)...
 
1892
 
 
1893
                if (parameters > 0 && results-->2 >= 2)
 
1894
                    PronounNotice(results-->2);
 
1895
 
 
1896
                ! ...and worry about the case where an object was allowed as a parameter
 
1897
                ! even though the player wasn't holding it and should have been: in this
 
1898
                ! event, keep the results for next time round, go into "not holding" mode,
 
1899
                ! and for now tell the player what's happening and return a "take" request
 
1900
                ! instead...
 
1901
 
 
1902
                if (not_holding ~= 0 && actor == player) {
 
1903
                    action = ##Take;
 
1904
                    i = RunRoutines(not_holding, before_implicit);
 
1905
                    ! i = 0: Take the object, tell the player (default)
 
1906
                    ! i = 1: Take the object, don't tell the player
 
1907
                    ! i = 2: don't Take the object, continue
 
1908
                    ! i = 3: don't Take the object, don't continue
 
1909
                    if (i > 2) { best_etype = NOTHELD_PE; jump GiveError; }
 
1910
                    if (i < 2) {        ! perform the implicit Take
 
1911
                        if (i ~= 1)     ! and tell the player
 
1912
                            L__M(##Miscellany, 26, not_holding);
 
1913
                        notheld_mode = 1;
 
1914
                        for (i=0 : i<8 : i++) kept_results-->i = results-->i;
 
1915
                        results-->0 = ##Take;
 
1916
                        results-->1 = 1;
 
1917
                        results-->2 = not_holding;
 
1918
                    }
 
1919
                }
 
1920
 
 
1921
                ! (Notice that implicit takes are only generated for the player, and not
 
1922
                ! for other actors.  This avoids entirely logical, but misleading, text
 
1923
                ! being printed.)
 
1924
 
 
1925
                ! ...and return from the parser altogether, having successfully matched
 
1926
                ! a line.
 
1927
 
 
1928
                if (held_back_mode == 1) {
 
1929
                    wn=hb_wn;
 
1930
                    jump LookForMore;
 
1931
                }
 
1932
                rtrue;
 
1933
 
 
1934
            } ! end of if(token ~= ENDIT_TOKEN) else
 
1935
        } ! end of for(pcount++)
 
1936
 
 
1937
        ! The line has failed to match.
 
1938
        ! We continue the outer "for" loop, trying the next line in the grammar.
 
1939
 
 
1940
        if (etype > best_etype) best_etype = etype;
 
1941
        if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype;
 
1942
 
 
1943
        ! ...unless the line was something like "take all" which failed because
 
1944
        ! nothing matched the "all", in which case we stop and give an error now.
 
1945
 
 
1946
        if (take_all_rule == 2 && etype==NOTHING_PE) break;
 
1947
 
 
1948
    } ! end of for(line++)
 
1949
 
 
1950
    ! The grammar is exhausted: every line has failed to match.
 
1951
 
 
1952
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1953
    !
 
1954
    ! H: Cheaply parse otherwise unrecognised conversation and return
 
1955
    !
 
1956
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1957
 
 
1958
  .GiveError;
 
1959
 
 
1960
    etype = best_etype;
 
1961
 
 
1962
    ! Errors are handled differently depending on who was talking.
 
1963
    ! If the command was addressed to somebody else (eg, "dwarf, sfgh") then
 
1964
    ! it is taken as conversation which the parser has no business in disallowing.
 
1965
 
 
1966
    if (actor ~= player) {
 
1967
        if (usual_grammar_after ~= 0) {
 
1968
            verb_wordnum = usual_grammar_after;
 
1969
            jump AlmostReParse;
 
1970
        }
 
1971
        wn = verb_wordnum;
 
1972
        special_word = NextWord();
 
1973
        if (special_word == comma_word) {
 
1974
            special_word = NextWord();
 
1975
            verb_wordnum++;
 
1976
        }
 
1977
        special_number = TryNumber(verb_wordnum);
 
1978
        results-->0 = ##NotUnderstood;
 
1979
        results-->1 = 2;
 
1980
        results-->2 = 1; special_number1 = special_word;
 
1981
        results-->3 = actor;
 
1982
        consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
 
1983
        rtrue;
 
1984
    }
 
1985
 
 
1986
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1987
    !
 
1988
    ! I: Print best possible error message
 
1989
    !
 
1990
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
1991
 
 
1992
    ! If the player was the actor (eg, in "take dfghh") the error must be printed,
 
1993
    ! and fresh input called for.  In three cases the oops word must be jiggled.
 
1994
 
 
1995
    if (ParserError(etype) ~= 0) jump ReType;
 
1996
    pronoun_word = pronoun__word; pronoun_obj = pronoun__obj;
 
1997
 
 
1998
    if (etype == STUCK_PE) {    L__M(##Miscellany, 27); oops_from = 1; }
 
1999
    if (etype == UPTO_PE) {     L__M(##Miscellany, 28);
 
2000
        for (m=0 : m<32 : m++) pattern-->m = pattern2-->m;
 
2001
        pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56);
 
2002
    }
 
2003
    if (etype == NUMBER_PE)     L__M(##Miscellany, 29);
 
2004
    if (etype == CANTSEE_PE) {  L__M(##Miscellany, 30); oops_from=saved_oops; }
 
2005
    if (etype == TOOLIT_PE)     L__M(##Miscellany, 31);
 
2006
    if (etype == NOTHELD_PE) {  L__M(##Miscellany, 32); oops_from=saved_oops; }
 
2007
    if (etype == MULTI_PE)      L__M(##Miscellany, 33);
 
2008
    if (etype == MMULTI_PE)     L__M(##Miscellany, 34);
 
2009
    if (etype == VAGUE_PE)      L__M(##Miscellany, 35);
 
2010
    if (etype == EXCEPT_PE)     L__M(##Miscellany, 36);
 
2011
    if (etype == ANIMA_PE)      L__M(##Miscellany, 37);
 
2012
    if (etype == VERB_PE)       L__M(##Miscellany, 38);
 
2013
    if (etype == SCENERY_PE)    L__M(##Miscellany, 39);
 
2014
    if (etype == ITGONE_PE) {
 
2015
        if (pronoun_obj == NULL)
 
2016
                                L__M(##Miscellany, 35);
 
2017
        else                    L__M(##Miscellany, 40);
 
2018
    }
 
2019
    if (etype == JUNKAFTER_PE)  L__M(##Miscellany, 41);
 
2020
    if (etype == TOOFEW_PE)     L__M(##Miscellany, 42, multi_had);
 
2021
    if (etype == NOTHING_PE) {
 
2022
        if (results-->0 == ##Remove && results-->3 ofclass Object) {
 
2023
            noun = results-->3; ! ensure valid for messages
 
2024
            if (noun has animate) L__M(##Take, 6, noun);
 
2025
            else if (noun hasnt container or supporter) L__M(##Insert, 2, noun);
 
2026
            else if (noun has container && noun hasnt open) L__M(##Take, 9, noun);
 
2027
            else if (children(noun)==0) L__M(##Search, 6, noun);
 
2028
            else results-->0 = 0;
 
2029
            }
 
2030
        if (results-->0 ~= ##Remove) {
 
2031
            if (multi_wanted==100)  L__M(##Miscellany, 43);
 
2032
            else                    L__M(##Miscellany, 44);
 
2033
        }
 
2034
    }
 
2035
    if (etype == ASKSCOPE_PE) {
 
2036
        scope_stage = 3;
 
2037
        if (indirect(scope_error) == -1) {
 
2038
            best_etype = nextbest_etype;
 
2039
            jump GiveError;
 
2040
        }
 
2041
    }
 
2042
 
 
2043
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2044
    !
 
2045
    ! J: Retry the whole lot
 
2046
    !
 
2047
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2048
 
 
2049
    ! And go (almost) right back to square one...
 
2050
 
 
2051
    jump ReType;
 
2052
 
 
2053
    ! ...being careful not to go all the way back, to avoid infinite repetition
 
2054
    ! of a deferred command causing an error.
 
2055
 
 
2056
 
 
2057
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2058
    !
 
2059
    ! K: Last thing: check for "then" and further instructions(s), return.
 
2060
    !
 
2061
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2062
 
 
2063
    ! At this point, the return value is all prepared, and we are only looking
 
2064
    ! to see if there is a "then" followed by subsequent instruction(s).
 
2065
 
 
2066
  .LookForMore;
 
2067
 
 
2068
    if (wn > num_words) rtrue;
 
2069
 
 
2070
    i = NextWord();
 
2071
    if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word) {
 
2072
        if (wn > num_words) {
 
2073
           held_back_mode = false;
 
2074
           return;
 
2075
        }
 
2076
        i = WordAddress(verb_wordnum);
 
2077
        j = WordAddress(wn);
 
2078
        for (: i<j : i++) i->0 = ' ';
 
2079
        i = NextWord();
 
2080
        if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) {
 
2081
            ! Delete the words "then again" from the again buffer,
 
2082
            ! in which we have just realised that it must occur:
 
2083
            ! prevents an infinite loop on "i. again"
 
2084
 
 
2085
            i = WordAddress(wn-2)-buffer;
 
2086
            if (wn > num_words) j = INPUT_BUFFER_LEN-1;
 
2087
            else j = WordAddress(wn)-buffer;
 
2088
            for (: i<j : i++) buffer3->i = ' ';
 
2089
        }
 
2090
        Tokenise__(buffer,parse);
 
2091
        held_back_mode = true;
 
2092
        return;
 
2093
    }
 
2094
    best_etype = UPTO_PE;
 
2095
    jump GiveError;
 
2096
 
 
2097
]; ! end of Parser__parse
 
2098
 
 
2099
[ ScopeCeiling person act;
 
2100
  act = parent(person);
 
2101
  if (act == 0) return person;
 
2102
  if (person == player && location == thedark) return thedark;
 
2103
  while (parent(act)~=0 && (act has transparent || act has supporter ||
 
2104
                           (act has container && act has open)))
 
2105
      act = parent(act);
 
2106
  return act;
 
2107
];
 
2108
 
 
2109
! ----------------------------------------------------------------------------
 
2110
!  Descriptors()
 
2111
!
 
2112
!  Handles descriptive words like "my", "his", "another" and so on.
 
2113
!  Skips "the", and leaves wn pointing to the first misunderstood word.
 
2114
!
 
2115
!  Allowed to set up for a plural only if allow_p is set
 
2116
!
 
2117
!  Returns error number, or 0 if no error occurred
 
2118
! ----------------------------------------------------------------------------
 
2119
 
 
2120
Constant OTHER_BIT  =   1;     !  These will be used in Adjudicate()
 
2121
Constant MY_BIT     =   2;     !  to disambiguate choices
 
2122
Constant THAT_BIT   =   4;
 
2123
Constant PLURAL_BIT =   8;
 
2124
Constant LIT_BIT    =  16;
 
2125
Constant UNLIT_BIT  =  32;
 
2126
 
 
2127
[ ResetDescriptors;
 
2128
    indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0;
 
2129
    indef_possambig = false;
 
2130
    indef_owner = nothing;
 
2131
    indef_cases = $$111111111111;
 
2132
    indef_nspec_at = 0;
 
2133
];
 
2134
 
 
2135
[ Descriptors  o x flag cto type n;
 
2136
    ResetDescriptors();
 
2137
    if (wn > num_words) return 0;
 
2138
 
 
2139
    for (flag=true : flag :) {
 
2140
        o = NextWordStopped(); flag = false;
 
2141
 
 
2142
       for (x=1 : x<=LanguageDescriptors-->0 : x=x+4)
 
2143
            if (o == LanguageDescriptors-->x) {
 
2144
                flag = true;
 
2145
                type = LanguageDescriptors-->(x+2);
 
2146
                if (type ~= DEFART_PK) indef_mode = true;
 
2147
                indef_possambig = true;
 
2148
                indef_cases = indef_cases & (LanguageDescriptors-->(x+1));
 
2149
 
 
2150
                if (type == POSSESS_PK) {
 
2151
                    cto = LanguageDescriptors-->(x+3);
 
2152
                    switch (cto) {
 
2153
                      0: indef_type = indef_type | MY_BIT;
 
2154
                      1: indef_type = indef_type | THAT_BIT;
 
2155
                      default:
 
2156
                        indef_owner = PronounValue(cto);
 
2157
                        if (indef_owner == NULL) indef_owner = InformParser;
 
2158
                    }
 
2159
                }
 
2160
 
 
2161
                if (type == light)  indef_type = indef_type | LIT_BIT;
 
2162
                if (type == -light) indef_type = indef_type | UNLIT_BIT;
 
2163
            }
 
2164
 
 
2165
        if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) {
 
2166
            indef_mode = 1; flag = 1;
 
2167
            indef_type = indef_type | OTHER_BIT;
 
2168
        }
 
2169
        if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
 
2170
            indef_mode = 1; flag = 1; indef_wanted = 100;
 
2171
            if (take_all_rule == 1) take_all_rule = 2;
 
2172
            indef_type = indef_type | PLURAL_BIT;
 
2173
        }
 
2174
        if (allow_plurals) {
 
2175
            n = TryNumber(wn-1);
 
2176
            if (n == 1) { indef_mode = 1; flag = 1; }
 
2177
            if (n > 1) {
 
2178
                indef_guess_p = 1;
 
2179
                indef_mode = 1; flag = 1; indef_wanted = n;
 
2180
                indef_nspec_at = wn-1;
 
2181
                indef_type = indef_type | PLURAL_BIT;
 
2182
            }
 
2183
        }
 
2184
        if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD)
 
2185
            wn--;  ! Skip 'of' after these
 
2186
    }
 
2187
    wn--;
 
2188
    return 0;
 
2189
];
 
2190
 
 
2191
! ----------------------------------------------------------------------------
 
2192
!  CreatureTest: Will this person do for a "creature" token?
 
2193
! ----------------------------------------------------------------------------
 
2194
 
 
2195
[ CreatureTest obj;
 
2196
    if (obj has animate) rtrue;
 
2197
    if (obj hasnt talkable) rfalse;
 
2198
    if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue;
 
2199
    rfalse;
 
2200
];
 
2201
 
 
2202
[ PrepositionChain wd index;
 
2203
    if (line_tdata-->index == wd) return wd;
 
2204
    if ((line_token-->index)->0 & $20 == 0) return -1;
 
2205
    do {
 
2206
        if (line_tdata-->index == wd) return wd;
 
2207
        index++;
 
2208
    } until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0));
 
2209
    return -1;
 
2210
];
 
2211
 
 
2212
! ----------------------------------------------------------------------------
 
2213
!  ParseToken(type, data):
 
2214
!      Parses the given token, from the current word number wn, with exactly
 
2215
!      the specification of a general parsing routine.
 
2216
!      (Except that for "topic" tokens and prepositions, you need to supply
 
2217
!      a position in a valid grammar line as third argument.)
 
2218
!
 
2219
!  Returns:
 
2220
!    GPR_REPARSE  for "reconstructed input, please re-parse from scratch"
 
2221
!    GPR_PREPOSITION  for "token accepted with no result"
 
2222
!    $ff00 + x    for "please parse ParseToken(ELEMENTARY_TT, x) instead"
 
2223
!    0            for "token accepted, result is the multiple object list"
 
2224
!    1            for "token accepted, result is the number in parsed_number"
 
2225
!    object num   for "token accepted with this object as result"
 
2226
!    -1           for "token rejected"
 
2227
!
 
2228
!  (A)            Analyse the token; handle all tokens not involving
 
2229
!                 object lists and break down others into elementary tokens
 
2230
!  (B)            Begin parsing an object list
 
2231
!  (C)            Parse descriptors (articles, pronouns, etc.) in the list
 
2232
!  (D)            Parse an object name
 
2233
!  (E)            Parse connectives ("and", "but", etc.) and go back to (C)
 
2234
!  (F)            Return the conclusion of parsing an object list
 
2235
! ----------------------------------------------------------------------------
 
2236
 
 
2237
[ ParseToken given_ttype given_tdata token_n x y;
 
2238
    x = lookahead; lookahead = NOUN_TOKEN;
 
2239
    y = ParseToken__(given_ttype,given_tdata,token_n);
 
2240
    if (y == GPR_REPARSE) Tokenise__(buffer,parse);
 
2241
    lookahead = x; return y;
 
2242
];
 
2243
 
 
2244
[ ParseToken__ given_ttype given_tdata token_n
 
2245
             token l o i j k and_parity single_object desc_wn many_flag
 
2246
             token_allows_multiple prev_indef_wanted;
 
2247
 
 
2248
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2249
    !
 
2250
    ! A: Analyse token; handle all not involving object lists, break down others
 
2251
    !
 
2252
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2253
 
 
2254
    token_filter = 0;
 
2255
 
 
2256
    switch (given_ttype) {
 
2257
      ELEMENTARY_TT:
 
2258
        switch (given_tdata) {
 
2259
          SPECIAL_TOKEN:
 
2260
            l = TryNumber(wn);
 
2261
            special_word = NextWord();
 
2262
            #Ifdef DEBUG;
 
2263
            if (l ~= -1000)
 
2264
                if (parser_trace >= 3) print "  [Read special as the number ", l, "]^";
 
2265
            #Endif; ! DEBUG
 
2266
            if (l == -1000) {
 
2267
                #Ifdef DEBUG;
 
2268
                if (parser_trace >= 3) print "  [Read special word at word number ", wn, "]^";
 
2269
                #Endif; ! DEBUG
 
2270
                l = special_word;
 
2271
            }
 
2272
            parsed_number = l; return GPR_NUMBER;
 
2273
 
 
2274
          NUMBER_TOKEN:
 
2275
            l=TryNumber(wn++);
 
2276
            if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; }
 
2277
            #Ifdef DEBUG;
 
2278
            if (parser_trace>=3) print "  [Read number as ", l, "]^";
 
2279
            #Endif; ! DEBUG
 
2280
            parsed_number = l; return GPR_NUMBER;
 
2281
 
 
2282
          CREATURE_TOKEN:
 
2283
            if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell)
 
2284
                scope_reason = TALKING_REASON;
 
2285
 
 
2286
          TOPIC_TOKEN:
 
2287
            consult_from = wn;
 
2288
            if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) &&
 
2289
               (line_token-->(token_n+1) ~= ENDIT_TOKEN))
 
2290
                RunTimeError(13);
 
2291
            do o = NextWordStopped();
 
2292
            until (o == -1 || PrepositionChain(o, token_n+1) ~= -1);
 
2293
            wn--;
 
2294
            consult_words = wn-consult_from;
 
2295
            if (consult_words == 0) return GPR_FAIL;
 
2296
            if (action_to_be == ##Ask or ##Answer or ##Tell) {
 
2297
                o = wn; wn = consult_from; parsed_number = NextWord();
 
2298
                #Ifdef EnglishNaturalLanguage;
 
2299
                if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord();
 
2300
                #Endif; ! EnglishNaturalLanguage
 
2301
                wn = o; return 1;
 
2302
            }
 
2303
            if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT))
 
2304
                return GPR_FAIL;    ! don't infer if required preposition is absent
 
2305
            return GPR_PREPOSITION;
 
2306
        }
 
2307
 
 
2308
      PREPOSITION_TT:
 
2309
        #Iffalse (Grammar__Version == 1);
 
2310
        ! Is it an unnecessary alternative preposition, when a previous choice
 
2311
        ! has already been matched?
 
2312
        if ((token->0) & $10) return GPR_PREPOSITION;
 
2313
        #Endif; ! Grammar__Version
 
2314
 
 
2315
        ! If we've run out of the player's input, but still have parameters to
 
2316
        ! specify, we go into "infer" mode, remembering where we are and the
 
2317
        ! preposition we are inferring...
 
2318
 
 
2319
        if (wn > num_words) {
 
2320
            if (inferfrom==0 && parameters<params_wanted) {
 
2321
                inferfrom = pcount; inferword = token;
 
2322
                pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
 
2323
            }
 
2324
 
 
2325
            ! If we are not inferring, then the line is wrong...
 
2326
 
 
2327
            if (inferfrom == 0) return -1;
 
2328
 
 
2329
            ! If not, then the line is right but we mark in the preposition...
 
2330
 
 
2331
            pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata);
 
2332
            return GPR_PREPOSITION;
 
2333
        }
 
2334
 
 
2335
        o = NextWord();
 
2336
 
 
2337
        pattern-->pcount = REPARSE_CODE + Dword__No(o);
 
2338
 
 
2339
        ! Whereas, if the player has typed something here, see if it is the
 
2340
        ! required preposition... if it's wrong, the line must be wrong,
 
2341
        ! but if it's right, the token is passed (jump to finish this token).
 
2342
 
 
2343
        if (o == given_tdata) return GPR_PREPOSITION;
 
2344
        #Iffalse (Grammar__Version == 1);
 
2345
        if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION;
 
2346
        #Endif; ! Grammar__Version
 
2347
        return -1;
 
2348
 
 
2349
      GPR_TT:
 
2350
        l = indirect(given_tdata);
 
2351
        #Ifdef DEBUG;
 
2352
        if (parser_trace >= 3) print "  [Outside parsing routine returned ", l, "]^";
 
2353
        #Endif; ! DEBUG
 
2354
        return l;
 
2355
 
 
2356
      SCOPE_TT:
 
2357
        scope_token = given_tdata;
 
2358
        scope_stage = 1;
 
2359
        l = indirect(scope_token);
 
2360
        #Ifdef DEBUG;
 
2361
        if (parser_trace >= 3) print "  [Scope routine returned multiple-flag of ", l, "]^";
 
2362
        #Endif; ! DEBUG
 
2363
        if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN;
 
2364
 
 
2365
      ATTR_FILTER_TT:
 
2366
        token_filter = 1 + given_tdata;
 
2367
        given_tdata = NOUN_TOKEN;
 
2368
 
 
2369
      ROUTINE_FILTER_TT:
 
2370
        token_filter = given_tdata;
 
2371
        given_tdata = NOUN_TOKEN;
 
2372
 
 
2373
    } ! end of switch(given_ttype)
 
2374
 
 
2375
    token = given_tdata;
 
2376
 
 
2377
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2378
    !
 
2379
    ! B: Begin parsing an object list
 
2380
    !
 
2381
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2382
 
 
2383
    ! There are now three possible ways we can be here:
 
2384
    !     parsing an elementary token other than "special" or "number";
 
2385
    !     parsing a scope token;
 
2386
    !     parsing a noun-filter token (either by routine or attribute).
 
2387
    !
 
2388
    ! In each case, token holds the type of elementary parse to
 
2389
    ! perform in matching one or more objects, and
 
2390
    ! token_filter is 0 (default), an attribute + 1 for an attribute filter
 
2391
    ! or a routine address for a routine filter.
 
2392
 
 
2393
    token_allows_multiple = false;
 
2394
    if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
 
2395
        token_allows_multiple = true;
 
2396
 
 
2397
    many_flag = false; and_parity = true; dont_infer = false;
 
2398
 
 
2399
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2400
    !
 
2401
    ! C: Parse descriptors (articles, pronouns, etc.) in the list
 
2402
    !
 
2403
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2404
 
 
2405
    ! We expect to find a list of objects next in what the player's typed.
 
2406
 
 
2407
  .ObjectList;
 
2408
 
 
2409
    #Ifdef DEBUG;
 
2410
    if (parser_trace >= 3) print "  [Object list from word ", wn, "]^";
 
2411
    #Endif; ! DEBUG
 
2412
 
 
2413
    ! Take an advance look at the next word: if it's "it" or "them", and these
 
2414
    ! are unset, set the appropriate error number and give up on the line
 
2415
    ! (if not, these are still parsed in the usual way - it is not assumed
 
2416
    ! that they still refer to something in scope)
 
2417
 
 
2418
    o = NextWord(); wn--;
 
2419
 
 
2420
    pronoun_word = NULL; pronoun_obj = NULL;
 
2421
    l = PronounValue(o);
 
2422
    if (l ~= 0) {
 
2423
        pronoun_word = o; pronoun_obj = l;
 
2424
        if (l == NULL) {
 
2425
            ! Don't assume this is a use of an unset pronoun until the
 
2426
            ! descriptors have been checked, because it might be an
 
2427
            ! article (or some such) instead
 
2428
 
 
2429
            for (l=1 : l<=LanguageDescriptors-->0 : l=l+4)
 
2430
                if (o == LanguageDescriptors-->l) jump AssumeDescriptor;
 
2431
            pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
 
2432
            etype = VAGUE_PE; return GPR_FAIL;
 
2433
        }
 
2434
    }
 
2435
 
 
2436
  .AssumeDescriptor;
 
2437
 
 
2438
    if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; }
 
2439
 
 
2440
    allow_plurals = true; desc_wn = wn;
 
2441
 
 
2442
  .TryAgain;
 
2443
 
 
2444
    ! First, we parse any descriptive words (like "the", "five" or "every"):
 
2445
    l = Descriptors();
 
2446
    if (l ~= 0) { etype = l; return GPR_FAIL; }
 
2447
 
 
2448
  .TryAgain2;
 
2449
 
 
2450
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2451
    !
 
2452
    ! D: Parse an object name
 
2453
    !
 
2454
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2455
 
 
2456
    ! This is an actual specified object, and is therefore where a typing error
 
2457
    ! is most likely to occur, so we set:
 
2458
 
 
2459
    oops_from = wn;
 
2460
 
 
2461
    ! So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
 
2462
    ! but we may well be dealing with multiple objects
 
2463
 
 
2464
    ! In either case below we use NounDomain, giving it the token number as
 
2465
    ! context, and two places to look: among the actor's possessions, and in the
 
2466
    ! present location.  (Note that the order depends on which is likeliest.)
 
2467
 
 
2468
    if (token ~= HELD_TOKEN) {
 
2469
        i = multiple_object-->0;
 
2470
        #Ifdef DEBUG;
 
2471
        if (parser_trace >= 3) print "  [Calling NounDomain on location and actor]^";
 
2472
        #Endif; ! DEBUG
 
2473
        l = NounDomain(actors_location, actor, token);
 
2474
        if (l == REPARSE_CODE) return l;                  ! Reparse after Q&A
 
2475
        if (indef_wanted == 100 && l == 0 && number_matched == 0)
 
2476
            l = 1;  ! ReviseMulti if TAKE ALL FROM empty container
 
2477
 
 
2478
        if (token_allows_multiple && ~~multiflag) {
 
2479
            if (best_etype==MULTI_PE) best_etype=STUCK_PE;
 
2480
            multiflag = true;
 
2481
        }
 
2482
        if (l == 0) {
 
2483
            if (indef_possambig) {
 
2484
                ResetDescriptors();
 
2485
                wn = desc_wn;
 
2486
                jump TryAgain2;
 
2487
            }
 
2488
            if (etype == MULTI_PE or TOOFEW_PE && multiflag) etype = STUCK_PE;
 
2489
            etype=CantSee();
 
2490
            jump FailToken;
 
2491
        } ! Choose best error
 
2492
 
 
2493
        #Ifdef DEBUG;
 
2494
        if (parser_trace >= 3) {
 
2495
            if (l > 1) print "  [ND returned ", (the) l, "]^";
 
2496
            else {
 
2497
                print "  [ND appended to the multiple object list:^";
 
2498
                k = multiple_object-->0;
 
2499
                for (j=i+1 : j<=k : j++)
 
2500
                    print "  Entry ", j, ": ", (The) multiple_object-->j,
 
2501
                          " (", multiple_object-->j, ")^";
 
2502
                print "  List now has size ", k, "]^";
 
2503
            }
 
2504
        }
 
2505
        #Endif; ! DEBUG
 
2506
 
 
2507
        if (l == 1) {
 
2508
            if (~~many_flag) many_flag = true;
 
2509
            else {                                ! Merge with earlier ones
 
2510
                k = multiple_object-->0;            ! (with either parity)
 
2511
                multiple_object-->0 = i;
 
2512
                for (j=i+1 : j<=k : j++) {
 
2513
                    if (and_parity) MultiAdd(multiple_object-->j);
 
2514
                    else            MultiSub(multiple_object-->j);
 
2515
                }
 
2516
                #Ifdef DEBUG;
 
2517
                if (parser_trace >= 3) print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
 
2518
                #Endif; ! DEBUG
 
2519
            }
 
2520
        }
 
2521
        else {
 
2522
            ! A single object was indeed found
 
2523
 
 
2524
            if (match_length == 0 && indef_possambig) {
 
2525
                ! So the answer had to be inferred from no textual data,
 
2526
                ! and we know that there was an ambiguity in the descriptor
 
2527
                ! stage (such as a word which could be a pronoun being
 
2528
                ! parsed as an article or possessive).  It's worth having
 
2529
                ! another go.
 
2530
 
 
2531
                ResetDescriptors();
 
2532
                wn = desc_wn;
 
2533
                jump TryAgain2;
 
2534
            }
 
2535
 
 
2536
            if (token == CREATURE_TOKEN && CreatureTest(l) == 0) {
 
2537
                etype = ANIMA_PE;
 
2538
                jump FailToken;
 
2539
            } !  Animation is required
 
2540
 
 
2541
            if (~~many_flag) single_object = l;
 
2542
            else {
 
2543
                if (and_parity) MultiAdd(l); else MultiSub(l);
 
2544
                #Ifdef DEBUG;
 
2545
                if (parser_trace >= 3) print "  [Combining ", (the) l, " with list]^";
 
2546
                #Endif; ! DEBUG
 
2547
            }
 
2548
        }
 
2549
    }
 
2550
 
 
2551
    else {
 
2552
 
 
2553
    ! Case 2: token is "held" (which fortunately can't take multiple objects)
 
2554
    ! and may generate an implicit take
 
2555
 
 
2556
        l = NounDomain(actor,actors_location,token);       ! Same as above...
 
2557
        if (l == REPARSE_CODE) return GPR_REPARSE;
 
2558
        if (l == 0) {
 
2559
            if (indef_possambig) {
 
2560
                ResetDescriptors();
 
2561
                wn = desc_wn;
 
2562
                jump TryAgain2;
 
2563
            }
 
2564
            etype = CantSee(); jump FailToken;            ! Choose best error
 
2565
        }
 
2566
 
 
2567
        ! ...until it produces something not held by the actor.  Then an implicit
 
2568
        ! take must be tried.  If this is already happening anyway, things are too
 
2569
        ! confused and we have to give up (but saving the oops marker so as to get
 
2570
        ! it on the right word afterwards).
 
2571
        ! The point of this last rule is that a sequence like
 
2572
        !
 
2573
        !     > read newspaper
 
2574
        !     (taking the newspaper first)
 
2575
        !     The dwarf unexpectedly prevents you from taking the newspaper!
 
2576
        !
 
2577
        ! should not be allowed to go into an infinite repeat - read becomes
 
2578
        ! take then read, but take has no effect, so read becomes take then read...
 
2579
        ! Anyway for now all we do is record the number of the object to take.
 
2580
 
 
2581
        o = parent(l);
 
2582
        if (o ~= actor) {
 
2583
            if (notheld_mode == 1) {
 
2584
                saved_oops = oops_from;
 
2585
                etype = NOTHELD_PE;
 
2586
                jump FailToken;
 
2587
            }
 
2588
            not_holding = l;
 
2589
            #Ifdef DEBUG;
 
2590
            if (parser_trace >= 3) print "  [Allowing object ", (the) l, " for now]^";
 
2591
            #Endif; ! DEBUG
 
2592
        }
 
2593
        single_object = l;
 
2594
    } ! end of if (token ~= HELD_TOKEN) else
 
2595
 
 
2596
    ! The following moves the word marker to just past the named object...
 
2597
 
 
2598
    wn = oops_from + match_length;
 
2599
 
 
2600
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2601
    !
 
2602
    ! E: Parse connectives ("and", "but", etc.) and go back to (C)
 
2603
    !
 
2604
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2605
 
 
2606
    ! Object(s) specified now: is that the end of the list, or have we reached
 
2607
    ! "and", "but" and so on?  If so, create a multiple-object list if we
 
2608
    ! haven't already (and are allowed to).
 
2609
 
 
2610
  .NextInList;
 
2611
 
 
2612
    o = NextWord();
 
2613
 
 
2614
    if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) {
 
2615
 
 
2616
        #Ifdef DEBUG;
 
2617
        if (parser_trace >= 3) print "  [Read connective '", (address) o, "']^";
 
2618
        #Endif; ! DEBUG
 
2619
 
 
2620
        if (~~token_allows_multiple) {
 
2621
            if (multiflag) jump PassToken; ! give UPTO_PE error
 
2622
            etype=MULTI_PE;
 
2623
            jump FailToken;
 
2624
        }
 
2625
 
 
2626
        if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity;
 
2627
 
 
2628
        if (~~many_flag) {
 
2629
            multiple_object-->0 = 1;
 
2630
            multiple_object-->1 = single_object;
 
2631
            many_flag = true;
 
2632
            #Ifdef DEBUG;
 
2633
            if (parser_trace >= 3) print "  [Making new list from ", (the) single_object, "]^";
 
2634
            #Endif; ! DEBUG
 
2635
        }
 
2636
        dont_infer = true; inferfrom=0;           ! Don't print (inferences)
 
2637
        jump ObjectList;                          ! And back around
 
2638
    }
 
2639
 
 
2640
    wn--;   ! Word marker back to first not-understood word
 
2641
 
 
2642
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2643
    !
 
2644
    ! F: Return the conclusion of parsing an object list
 
2645
    !
 
2646
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
2647
 
 
2648
    ! Happy or unhappy endings:
 
2649
 
 
2650
  .PassToken;
 
2651
 
 
2652
    if (many_flag) {
 
2653
        single_object = GPR_MULTIPLE;
 
2654
        multi_context = token;
 
2655
    }
 
2656
    else {
 
2657
        if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
 
2658
            if (indef_wanted < 100 && indef_wanted > 1) {
 
2659
                multi_had = 1; multi_wanted = indef_wanted;
 
2660
                etype = TOOFEW_PE;
 
2661
                jump FailToken;
 
2662
            }
 
2663
        }
 
2664
    }
 
2665
    return single_object;
 
2666
 
 
2667
  .FailToken;
 
2668
 
 
2669
    ! If we were only guessing about it being a plural, try again but only
 
2670
    ! allowing singulars (so that words like "six" are not swallowed up as
 
2671
    ! Descriptors)
 
2672
 
 
2673
    if (allow_plurals && indef_guess_p == 1) {
 
2674
        #Ifdef DEBUG;
 
2675
        if (parser_trace >= 4) print "   [Retrying singulars after failure ", etype, "]^";
 
2676
        #Endif;
 
2677
        prev_indef_wanted = indef_wanted;
 
2678
        allow_plurals = false;
 
2679
        wn = desc_wn;
 
2680
        jump TryAgain;
 
2681
    }
 
2682
 
 
2683
    if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE;
 
2684
 
 
2685
    return GPR_FAIL;
 
2686
 
 
2687
]; ! end of ParseToken__
 
2688
 
 
2689
! ----------------------------------------------------------------------------
 
2690
!  NounDomain does the most substantial part of parsing an object name.
 
2691
!
 
2692
!  It is given two "domains" - usually a location and then the actor who is
 
2693
!  looking - and a context (i.e. token type), and returns:
 
2694
!
 
2695
!   0    if no match at all could be made,
 
2696
!   1    if a multiple object was made,
 
2697
!   k    if object k was the one decided upon,
 
2698
!   REPARSE_CODE if it asked a question of the player and consequently rewrote
 
2699
!        the player's input, so that the whole parser should start again
 
2700
!        on the rewritten input.
 
2701
!
 
2702
!   In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
 
2703
!   length_of_noun to the number of words in the input text matched to the
 
2704
!   noun.
 
2705
!   In the case k=1, the multiple objects are added to multiple_object by
 
2706
!   hand (not by MultiAdd, because we want to allow duplicates).
 
2707
! ----------------------------------------------------------------------------
 
2708
 
 
2709
[ NounDomain domain1 domain2 context    first_word i j k l
 
2710
                                        answer_words marker;
 
2711
    #Ifdef DEBUG;
 
2712
    if (parser_trace >= 4) {
 
2713
        print "   [NounDomain called at word ", wn, "^";
 
2714
        print "   ";
 
2715
        if (indef_mode) {
 
2716
            print "seeking indefinite object: ";
 
2717
            if (indef_type & OTHER_BIT)  print "other ";
 
2718
            if (indef_type & MY_BIT)     print "my ";
 
2719
            if (indef_type & THAT_BIT)   print "that ";
 
2720
            if (indef_type & PLURAL_BIT) print "plural ";
 
2721
            if (indef_type & LIT_BIT)    print "lit ";
 
2722
            if (indef_type & UNLIT_BIT)  print "unlit ";
 
2723
            if (indef_owner ~= 0) print "owner:", (name) indef_owner;
 
2724
            new_line;
 
2725
            print "   number wanted: ";
 
2726
            if (indef_wanted == 100) print "all"; else print indef_wanted;
 
2727
            new_line;
 
2728
            print "   most likely GNAs of names: ", indef_cases, "^";
 
2729
        }
 
2730
        else print "seeking definite object^";
 
2731
    }
 
2732
    #Endif; ! DEBUG
 
2733
 
 
2734
    match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0;
 
2735
 
 
2736
    SearchScope(domain1, domain2, context);
 
2737
 
 
2738
    #Ifdef DEBUG;
 
2739
    if (parser_trace >= 4) print "   [ND made ", number_matched, " matches]^";
 
2740
    #Endif; ! DEBUG
 
2741
 
 
2742
    wn = match_from+match_length;
 
2743
 
 
2744
    ! If nothing worked at all, leave with the word marker skipped past the
 
2745
    ! first unmatched word...
 
2746
 
 
2747
    if (number_matched == 0) { wn++; rfalse; }
 
2748
 
 
2749
    ! Suppose that there really were some words being parsed (i.e., we did
 
2750
    ! not just infer).  If so, and if there was only one match, it must be
 
2751
    ! right and we return it...
 
2752
 
 
2753
    if (match_from <= num_words) {
 
2754
        if (number_matched == 1) {
 
2755
            i=match_list-->0;
 
2756
            return i;
 
2757
        }
 
2758
 
 
2759
        ! ...now suppose that there was more typing to come, i.e. suppose that
 
2760
        ! the user entered something beyond this noun.  If nothing ought to follow,
 
2761
        ! then there must be a mistake, (unless what does follow is just a full
 
2762
        ! stop, and or comma)
 
2763
 
 
2764
        if (wn <= num_words) {
 
2765
            i = NextWord(); wn--;
 
2766
            if (i ~=  AND1__WD or AND2__WD or AND3__WD or comma_word
 
2767
                   or THEN1__WD or THEN2__WD or THEN3__WD
 
2768
                   or BUT1__WD or BUT2__WD or BUT3__WD) {
 
2769
                if (lookahead == ENDIT_TOKEN) rfalse;
 
2770
            }
 
2771
        }
 
2772
    }
 
2773
 
 
2774
    ! Now look for a good choice, if there's more than one choice...
 
2775
 
 
2776
    number_of_classes = 0;
 
2777
 
 
2778
    if (number_matched == 1) i = match_list-->0;
 
2779
    if (number_matched > 1) {
 
2780
        i = Adjudicate(context);
 
2781
        if (i == -1) rfalse;
 
2782
        if (i == 1) rtrue;       !  Adjudicate has made a multiple
 
2783
                             !  object, and we pass it on
 
2784
    }
 
2785
 
 
2786
    ! If i is non-zero here, one of two things is happening: either
 
2787
    ! (a) an inference has been successfully made that object i is
 
2788
    !     the intended one from the user's specification, or
 
2789
    ! (b) the user finished typing some time ago, but we've decided
 
2790
    !     on i because it's the only possible choice.
 
2791
    ! In either case we have to keep the pattern up to date,
 
2792
    ! note that an inference has been made and return.
 
2793
    ! (Except, we don't note which of a pile of identical objects.)
 
2794
 
 
2795
    if (i ~= 0) {
 
2796
        if (dont_infer) return i;
 
2797
        if (inferfrom == 0) inferfrom=pcount;
 
2798
        pattern-->pcount = i;
 
2799
        return i;
 
2800
    }
 
2801
 
 
2802
    ! If we get here, there was no obvious choice of object to make.  If in
 
2803
    ! fact we've already gone past the end of the player's typing (which
 
2804
    ! means the match list must contain every object in scope, regardless
 
2805
    ! of its name), then it's foolish to give an enormous list to choose
 
2806
    ! from - instead we go and ask a more suitable question...
 
2807
 
 
2808
    if (match_from > num_words) jump Incomplete;
 
2809
 
 
2810
    ! Now we print up the question, using the equivalence classes as worked
 
2811
    ! out by Adjudicate() so as not to repeat ourselves on plural objects...
 
2812
 
 
2813
    if (context==CREATURE_TOKEN) L__M(##Miscellany, 45);
 
2814
    else                         L__M(##Miscellany, 46);
 
2815
 
 
2816
    j = number_of_classes; marker = 0;
 
2817
    for (i=1 : i<=number_of_classes : i++) {
 
2818
        while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++;
 
2819
        k = match_list-->marker;
 
2820
 
 
2821
        if (match_classes-->marker > 0) print (the) k; else print (a) k;
 
2822
 
 
2823
        if (i < j-1)  print (string) COMMA__TX;
 
2824
        if (i == j-1) print (string) OR__TX;
 
2825
    }
 
2826
    L__M(##Miscellany, 57);
 
2827
 
 
2828
    ! ...and get an answer:
 
2829
 
 
2830
  .WhichOne;
 
2831
    #Ifdef TARGET_ZCODE;
 
2832
    for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i = ' ';
 
2833
    #Endif; ! TARGET_ZCODE
 
2834
    answer_words=Keyboard(buffer2, parse2);
 
2835
 
 
2836
    ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
 
2837
    first_word = (parse2-->1);
 
2838
 
 
2839
    ! Take care of "all", because that does something too clever here to do
 
2840
    ! later on:
 
2841
 
 
2842
    if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) {
 
2843
        if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
 
2844
            l = multiple_object-->0;
 
2845
            for (i=0 : i<number_matched && l+i<63 : i++) {
 
2846
                k = match_list-->i;
 
2847
                multiple_object-->(i+1+l) = k;
 
2848
            }
 
2849
            multiple_object-->0 = i+l;
 
2850
            rtrue;
 
2851
        }
 
2852
        L__M(##Miscellany, 47);
 
2853
        jump WhichOne;
 
2854
    }
 
2855
 
 
2856
    ! If the first word of the reply can be interpreted as a verb, then
 
2857
    ! assume that the player has ignored the question and given a new
 
2858
    ! command altogether.
 
2859
    ! (This is one time when it's convenient that the directions are
 
2860
    ! not themselves verbs - thus, "north" as a reply to "Which, the north
 
2861
    ! or south door" is not treated as a fresh command but as an answer.)
 
2862
 
 
2863
    #Ifdef LanguageIsVerb;
 
2864
    if (first_word == 0) {
 
2865
        j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j;
 
2866
    }
 
2867
    #Endif; ! LanguageIsVerb
 
2868
    if (first_word ~= 0) {
 
2869
        j = first_word->#dict_par1;
 
2870
        if ((0 ~= j&1) && ~~LanguageVerbMayBeName(first_word)) {
 
2871
            CopyBuffer(buffer, buffer2);
 
2872
            return REPARSE_CODE;
 
2873
        }
 
2874
    }
 
2875
 
 
2876
    ! Now we insert the answer into the original typed command, as
 
2877
    ! words additionally describing the same object
 
2878
    ! (eg, > take red button
 
2879
    !      Which one, ...
 
2880
    !      > music
 
2881
    ! becomes "take music red button".  The parser will thus have three
 
2882
    ! words to work from next time, not two.)
 
2883
 
 
2884
    #Ifdef TARGET_ZCODE;
 
2885
    k = WordAddress(match_from) - buffer; l=buffer2->1+1;
 
2886
    for (j=buffer + buffer->0 - 1 : j>=buffer+k+l : j--) j->0 = 0->(j-l);
 
2887
    for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(2+i);
 
2888
    buffer->(k+l-1) = ' ';
 
2889
    buffer->1 = buffer->1 + l;
 
2890
    if (buffer->1 >= (buffer->0 - 1)) buffer->1 = buffer->0;
 
2891
    #Ifnot; ! TARGET_GLULX
 
2892
    k = WordAddress(match_from) - buffer;
 
2893
    l = (buffer2-->0) + 1;
 
2894
    for (j=buffer+INPUT_BUFFER_LEN-1 : j>=buffer+k+l : j--) j->0 = j->(-l);
 
2895
    for (i=0 : i<l : i++) buffer->(k+i) = buffer2->(WORDSIZE+i);
 
2896
    buffer->(k+l-1) = ' ';
 
2897
    buffer-->0 = buffer-->0 + l;
 
2898
    if (buffer-->0 > (INPUT_BUFFER_LEN-WORDSIZE)) buffer-->0 = (INPUT_BUFFER_LEN-WORDSIZE);
 
2899
    #Endif; ! TARGET_
 
2900
 
 
2901
    ! Having reconstructed the input, we warn the parser accordingly
 
2902
    ! and get out.
 
2903
 
 
2904
    return REPARSE_CODE;
 
2905
 
 
2906
    ! Now we come to the question asked when the input has run out
 
2907
    ! and can't easily be guessed (eg, the player typed "take" and there
 
2908
    ! were plenty of things which might have been meant).
 
2909
 
 
2910
  .Incomplete;
 
2911
 
 
2912
    if (context == CREATURE_TOKEN) L__M(##Miscellany, 48);
 
2913
    else                           L__M(##Miscellany, 49);
 
2914
 
 
2915
    #Ifdef TARGET_ZCODE;
 
2916
    for (i=2 : i<INPUT_BUFFER_LEN : i++) buffer2->i=' ';
 
2917
    #Endif; ! TARGET_ZCODE
 
2918
    answer_words = Keyboard(buffer2, parse2);
 
2919
 
 
2920
    first_word=(parse2-->1);
 
2921
    #Ifdef LanguageIsVerb;
 
2922
    if (first_word==0) {
 
2923
        j = wn; first_word=LanguageIsVerb(buffer2, parse2, 1); wn = j;
 
2924
    }
 
2925
    #Endif; ! LanguageIsVerb
 
2926
 
 
2927
    ! Once again, if the reply looks like a command, give it to the
 
2928
    ! parser to get on with and forget about the question...
 
2929
 
 
2930
    if (first_word ~= 0) {
 
2931
        j = first_word->#dict_par1;
 
2932
        if (0 ~= j&1) {
 
2933
            CopyBuffer(buffer, buffer2);
 
2934
            return REPARSE_CODE;
 
2935
        }
 
2936
    }
 
2937
 
 
2938
    ! ...but if we have a genuine answer, then:
 
2939
    !
 
2940
    ! (1) we must glue in text suitable for anything that's been inferred.
 
2941
 
 
2942
    if (inferfrom ~= 0) {
 
2943
        for (j=inferfrom : j<pcount : j++) {
 
2944
            if (pattern-->j == PATTERN_NULL) continue;
 
2945
            #Ifdef TARGET_ZCODE;
 
2946
            i = 2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
 
2947
            #Ifnot; ! TARGET_GLULX
 
2948
            i = WORDSIZE + buffer-->0;
 
2949
            (buffer-->0)++; buffer->(i++) = ' ';
 
2950
            #Endif; ! TARGET_
 
2951
 
 
2952
            #Ifdef DEBUG;
 
2953
            if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^";
 
2954
            #Endif; ! DEBUG
 
2955
 
 
2956
            ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX.
 
2957
 
 
2958
            parse2-->1 = 0;
 
2959
 
 
2960
            ! An inferred object.  Best we can do is glue in a pronoun.
 
2961
            ! (This is imperfect, but it's very seldom needed anyway.)
 
2962
 
 
2963
            if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) {
 
2964
                PronounNotice(pattern-->j);
 
2965
                for (k=1 : k<=LanguagePronouns-->0 : k=k+3)
 
2966
                    if (pattern-->j == LanguagePronouns-->(k+2)) {
 
2967
                        parse2-->1 = LanguagePronouns-->k;
 
2968
                        #Ifdef DEBUG;
 
2969
                        if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^";
 
2970
                        #Endif; ! DEBUG
 
2971
                        break;
 
2972
                    }
 
2973
            }
 
2974
            else {
 
2975
                ! An inferred preposition.
 
2976
                parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE);
 
2977
                #Ifdef DEBUG;
 
2978
                if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^";
 
2979
                #Endif; ! DEBUG
 
2980
            }
 
2981
 
 
2982
            ! parse2-->1 now holds the dictionary address of the word to glue in.
 
2983
 
 
2984
            if (parse2-->1 ~= 0) {
 
2985
                k = buffer + i;
 
2986
                #Ifdef TARGET_ZCODE;
 
2987
                @output_stream 3 k;
 
2988
                 print (address) parse2-->1;
 
2989
                @output_stream -3;
 
2990
                k = k-->0;
 
2991
                for (l=i : l<i+k : l++) buffer->l = buffer->(l+2);
 
2992
                i = i + k; buffer->1 = i-2;
 
2993
                #Ifnot; ! TARGET_GLULX
 
2994
                k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1);
 
2995
                i = i + k; buffer-->0 = i - WORDSIZE;
 
2996
                #Endif; ! TARGET_
 
2997
            }
 
2998
        }
 
2999
    }
 
3000
 
 
3001
    ! (2) we must glue the newly-typed text onto the end.
 
3002
 
 
3003
    #Ifdef TARGET_ZCODE;
 
3004
    i = 2+buffer->1; (buffer->1)++; buffer->(i++) = ' ';
 
3005
    for (j=0 : j<buffer2->1 : i++,j++) {
 
3006
        buffer->i = buffer2->(j+2);
 
3007
        (buffer->1)++;
 
3008
        if (buffer->1 == INPUT_BUFFER_LEN) break;
 
3009
    }
 
3010
    #Ifnot; ! TARGET_GLULX
 
3011
    i = WORDSIZE + buffer-->0;
 
3012
    (buffer-->0)++; buffer->(i++) = ' ';
 
3013
    for (j=0 : j<buffer2-->0 : i++,j++) {
 
3014
        buffer->i = buffer2->(j+WORDSIZE);
 
3015
        (buffer-->0)++;
 
3016
        if (buffer-->0 == INPUT_BUFFER_LEN) break;
 
3017
    }
 
3018
    #Endif; ! TARGET_
 
3019
 
 
3020
    ! (3) we fill up the buffer with spaces, which is unnecessary, but may
 
3021
    !     help incorrectly-written interpreters to cope.
 
3022
 
 
3023
    #Ifdef TARGET_ZCODE;
 
3024
    for (: i<INPUT_BUFFER_LEN : i++) buffer->i = ' ';
 
3025
    #Endif; ! TARGET_ZCODE
 
3026
 
 
3027
    return REPARSE_CODE;
 
3028
 
 
3029
]; ! end of NounDomain
 
3030
 
 
3031
! ----------------------------------------------------------------------------
 
3032
!  The Adjudicate routine tries to see if there is an obvious choice, when
 
3033
!  faced with a list of objects (the match_list) each of which matches the
 
3034
!  player's specification equally well.
 
3035
!
 
3036
!  To do this it makes use of the context (the token type being worked on).
 
3037
!  It counts up the number of obvious choices for the given context
 
3038
!  (all to do with where a candidate is, except for 6 (animate) which is to
 
3039
!  do with whether it is animate or not);
 
3040
!
 
3041
!  if only one obvious choice is found, that is returned;
 
3042
!
 
3043
!  if we are in indefinite mode (don't care which) one of the obvious choices
 
3044
!    is returned, or if there is no obvious choice then an unobvious one is
 
3045
!    made;
 
3046
!
 
3047
!  at this stage, we work out whether the objects are distinguishable from
 
3048
!    each other or not: if they are all indistinguishable from each other,
 
3049
!    then choose one, it doesn't matter which;
 
3050
!
 
3051
!  otherwise, 0 (meaning, unable to decide) is returned (but remember that
 
3052
!    the equivalence classes we've just worked out will be needed by other
 
3053
!    routines to clear up this mess, so we can't economise on working them
 
3054
!    out).
 
3055
!
 
3056
!  Returns -1 if an error occurred
 
3057
! ----------------------------------------------------------------------------
 
3058
 
 
3059
Constant SCORE__CHOOSEOBJ = 1000;
 
3060
Constant SCORE__IFGOOD = 500;
 
3061
Constant SCORE__UNCONCEALED = 100;
 
3062
Constant SCORE__BESTLOC = 60;
 
3063
Constant SCORE__NEXTBESTLOC = 40;
 
3064
Constant SCORE__NOTCOMPASS = 20;
 
3065
Constant SCORE__NOTSCENERY = 10;
 
3066
Constant SCORE__NOTACTOR = 5;
 
3067
Constant SCORE__GNA = 1;
 
3068
Constant SCORE__DIVISOR = 20;
 
3069
 
 
3070
[ Adjudicate context i j k good_flag good_ones last n flag offset sovert;
 
3071
    #Ifdef DEBUG;
 
3072
    if (parser_trace >= 4) {
 
3073
        print "   [Adjudicating match list of size ", number_matched, " in context ", context, "^";
 
3074
        print "   ";
 
3075
        if (indef_mode) {
 
3076
            print "indefinite type: ";
 
3077
            if (indef_type & OTHER_BIT)  print "other ";
 
3078
            if (indef_type & MY_BIT)     print "my ";
 
3079
            if (indef_type & THAT_BIT)   print "that ";
 
3080
            if (indef_type & PLURAL_BIT) print "plural ";
 
3081
            if (indef_type & LIT_BIT)    print "lit ";
 
3082
            if (indef_type & UNLIT_BIT)  print "unlit ";
 
3083
            if (indef_owner ~= 0) print "owner:", (name) indef_owner;
 
3084
            new_line;
 
3085
            print "   number wanted: ";
 
3086
            if (indef_wanted == 100) print "all"; else print indef_wanted;
 
3087
            new_line;
 
3088
            print "   most likely GNAs of names: ", indef_cases, "^";
 
3089
        }
 
3090
        else print "definite object^";
 
3091
    }
 
3092
    #Endif; ! DEBUG
 
3093
 
 
3094
    j = number_matched-1; good_ones = 0; last = match_list-->0;
 
3095
    for (i=0 : i<=j : i++) {
 
3096
        n = match_list-->i;
 
3097
        match_scores-->i = 0;
 
3098
 
 
3099
        good_flag = false;
 
3100
 
 
3101
        switch (context) {
 
3102
          HELD_TOKEN, MULTIHELD_TOKEN:
 
3103
            if (parent(n) == actor) good_flag = true;
 
3104
          MULTIEXCEPT_TOKEN:
 
3105
            if (advance_warning == -1) {
 
3106
                good_flag = true;
 
3107
            }
 
3108
            else {
 
3109
                if (n ~= advance_warning) good_flag = true;
 
3110
            }
 
3111
          MULTIINSIDE_TOKEN:
 
3112
            if (advance_warning == -1) {
 
3113
                if (parent(n) ~= actor) good_flag = true;
 
3114
            }
 
3115
            else {
 
3116
                if (n in advance_warning) good_flag = true;
 
3117
            }
 
3118
          CREATURE_TOKEN:
 
3119
            if (CreatureTest(n) == 1) good_flag = true;
 
3120
          default:
 
3121
            good_flag = true;
 
3122
        }
 
3123
 
 
3124
        if (good_flag) {
 
3125
            match_scores-->i = SCORE__IFGOOD;
 
3126
            good_ones++; last = n;
 
3127
        }
 
3128
    }
 
3129
    if (good_ones == 1) return last;
 
3130
 
 
3131
    ! If there is ambiguity about what was typed, but it definitely wasn't
 
3132
    ! animate as required, then return anything; higher up in the parser
 
3133
    ! a suitable error will be given.  (This prevents a question being asked.)
 
3134
 
 
3135
    if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0;
 
3136
 
 
3137
    if (indef_mode == 0) indef_type=0;
 
3138
 
 
3139
    ScoreMatchL(context);
 
3140
    if (number_matched == 0) return -1;
 
3141
 
 
3142
    if (indef_mode == 0) {
 
3143
        !  Is there now a single highest-scoring object?
 
3144
        i = SingleBestGuess();
 
3145
        if (i >= 0) {
 
3146
 
 
3147
            #Ifdef DEBUG;
 
3148
            if (parser_trace >= 4) print "   Single best-scoring object returned.]^";
 
3149
            #Endif; ! DEBUG
 
3150
            return i;
 
3151
        }
 
3152
    }
 
3153
 
 
3154
    if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
 
3155
        if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
 
3156
                     or MULTIINSIDE_TOKEN) {
 
3157
            etype = MULTI_PE;
 
3158
            return -1;
 
3159
        }
 
3160
        i = 0; offset = multiple_object-->0; sovert = -1;
 
3161
        for (j=BestGuess() : j~=-1 && i<indef_wanted && i+offset<63 : j=BestGuess()) {
 
3162
            flag = 0;
 
3163
            if (j hasnt concealed && j hasnt worn) flag = 1;
 
3164
            if (sovert == -1) sovert = bestguess_score/SCORE__DIVISOR;
 
3165
            else {
 
3166
                if (indef_wanted == 100 && bestguess_score/SCORE__DIVISOR < sovert)
 
3167
                    flag = 0;
 
3168
            }
 
3169
            if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)
 
3170
                flag = 0;
 
3171
            if (action_to_be == ##Take or ##Remove && parent(j) == actor)
 
3172
                flag = 0;
 
3173
            k = ChooseObjects(j, flag);
 
3174
            if (k == 1)
 
3175
                flag = 1;
 
3176
            else {
 
3177
                if (k == 2) flag = 0;
 
3178
            }
 
3179
            if (flag == 1) {
 
3180
                i++; multiple_object-->(i+offset) = j;
 
3181
                #Ifdef DEBUG;
 
3182
                if (parser_trace >= 4) print "   Accepting it^";
 
3183
                #Endif; ! DEBUG
 
3184
            }
 
3185
            else {
 
3186
                i = i;
 
3187
                #Ifdef DEBUG;
 
3188
                if (parser_trace >= 4) print "   Rejecting it^";
 
3189
                #Endif; ! DEBUG
 
3190
            }
 
3191
        }
 
3192
        if (i < indef_wanted && indef_wanted < 100) {
 
3193
            etype = TOOFEW_PE; multi_wanted = indef_wanted;
 
3194
            multi_had=i;
 
3195
            return -1;
 
3196
        }
 
3197
        multiple_object-->0 = i+offset;
 
3198
        multi_context = context;
 
3199
        #Ifdef DEBUG;
 
3200
        if (parser_trace >= 4)
 
3201
            print "   Made multiple object of size ", i, "]^";
 
3202
        #Endif; ! DEBUG
 
3203
        return 1;
 
3204
    }
 
3205
 
 
3206
    for (i=0 : i<number_matched : i++) match_classes-->i = 0;
 
3207
 
 
3208
    n = 1;
 
3209
    for (i=0 : i<number_matched : i++)
 
3210
        if (match_classes-->i == 0) {
 
3211
            match_classes-->i = n++; flag = 0;
 
3212
            for (j=i+1 : j<number_matched : j++)
 
3213
                if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {
 
3214
                    flag=1;
 
3215
                    match_classes-->j = match_classes-->i;
 
3216
                }
 
3217
            if (flag == 1) match_classes-->i = 1-n;
 
3218
        }
 
3219
     n--; number_of_classes = n;
 
3220
 
 
3221
    #Ifdef DEBUG;
 
3222
    if (parser_trace >= 4) {
 
3223
        print "   Grouped into ", n, " possibilities by name:^";
 
3224
        for (i=0 : i<number_matched : i++)
 
3225
            if (match_classes-->i > 0)
 
3226
                print "   ", (The) match_list-->i, " (", match_list-->i, ")  ---  group ",
 
3227
                  match_classes-->i, "^";
 
3228
    }
 
3229
    #Endif; ! DEBUG
 
3230
 
 
3231
    if (indef_mode == 0) {
 
3232
        if (n > 1) {
 
3233
            k = -1;
 
3234
            for (i=0 : i<number_matched : i++) {
 
3235
                if (match_scores-->i > k) {
 
3236
                    k = match_scores-->i;
 
3237
                    j = match_classes-->i; j = j*j;
 
3238
                    flag = 0;
 
3239
                }
 
3240
                else
 
3241
                    if (match_scores-->i == k) {
 
3242
                        if ((match_classes-->i) * (match_classes-->i) ~= j)
 
3243
                            flag = 1;
 
3244
                    }
 
3245
            }
 
3246
 
 
3247
        if (flag) {
 
3248
            #Ifdef DEBUG;
 
3249
            if (parser_trace >= 4) print "   Unable to choose best group, so ask player.]^";
 
3250
            #Endif; ! DEBUG
 
3251
            return 0;
 
3252
        }
 
3253
        #Ifdef DEBUG;
 
3254
        if (parser_trace >= 4) print "   Best choices are all from the same group.^";
 
3255
        #Endif; ! DEBUG
 
3256
        }
 
3257
    }
 
3258
 
 
3259
    !  When the player is really vague, or there's a single collection of
 
3260
    !  indistinguishable objects to choose from, choose the one the player
 
3261
    !  most recently acquired, or if the player has none of them, then
 
3262
    !  the one most recently put where it is.
 
3263
 
 
3264
    if (n == 1) dont_infer = true;
 
3265
    return BestGuess();
 
3266
 
 
3267
]; ! Adjudicate
 
3268
 
 
3269
! ----------------------------------------------------------------------------
 
3270
!  ReviseMulti  revises the multiple object which already exists, in the
 
3271
!    light of information which has come along since then (i.e., the second
 
3272
!    parameter).  It returns a parser error number, or else 0 if all is well.
 
3273
!    This only ever throws things out, never adds new ones.
 
3274
! ----------------------------------------------------------------------------
 
3275
 
 
3276
[ ReviseMulti second_p  i low;
 
3277
    #Ifdef DEBUG;
 
3278
    if (parser_trace >= 4) print "   Revising multiple object list of size ", multiple_object-->0,
 
3279
      " with 2nd ", (name) second_p, "^";
 
3280
    #Endif; ! DEBUG
 
3281
 
 
3282
    if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
 
3283
        for (i=1,low=0 : i<=multiple_object-->0 : i++) {
 
3284
            if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) ||
 
3285
                 (multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) {
 
3286
                low++;
 
3287
                multiple_object-->low = multiple_object-->i;
 
3288
            }
 
3289
        }
 
3290
        multiple_object-->0 = low;
 
3291
    }
 
3292
 
 
3293
    if (multi_context == MULTI_TOKEN && action_to_be == ##Take) {
 
3294
        for (i=1,low=0 : i<=multiple_object-->0 : i++)
 
3295
            if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++;
 
3296
        #Ifdef DEBUG;
 
3297
        if (parser_trace >= 4) print "   Token 2 plural case: number with actor ", low, "^";
 
3298
        #Endif; ! DEBUG
 
3299
        if (take_all_rule == 2 || low > 0) {
 
3300
            for (i=1,low=0 : i<=multiple_object-->0 : i++) {
 
3301
                if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) {
 
3302
                    low++;
 
3303
                    multiple_object-->low = multiple_object-->i;
 
3304
                }
 
3305
            }
 
3306
            multiple_object-->0 = low;
 
3307
        }
 
3308
    }
 
3309
 
 
3310
    i = multiple_object-->0;
 
3311
    #Ifdef DEBUG;
 
3312
    if (parser_trace >= 4) print "   Done: new size ", i, "^";
 
3313
    #Endif; ! DEBUG
 
3314
    if (i == 0) return NOTHING_PE;
 
3315
    return 0;
 
3316
];
 
3317
 
 
3318
! ----------------------------------------------------------------------------
 
3319
!  ScoreMatchL  scores the match list for quality in terms of what the
 
3320
!  player has vaguely asked for.  Points are awarded for conforming with
 
3321
!  requirements like "my", and so on.  Remove from the match list any
 
3322
!  entries which fail the basic requirements of the descriptors.
 
3323
! ----------------------------------------------------------------------------
 
3324
 
 
3325
[ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s;
 
3326
!   if (indef_type & OTHER_BIT ~= 0) threshold++;
 
3327
    if (indef_type & MY_BIT ~= 0)    threshold++;
 
3328
    if (indef_type & THAT_BIT ~= 0)  threshold++;
 
3329
    if (indef_type & LIT_BIT ~= 0)   threshold++;
 
3330
    if (indef_type & UNLIT_BIT ~= 0) threshold++;
 
3331
    if (indef_owner ~= nothing)      threshold++;
 
3332
 
 
3333
    #Ifdef DEBUG;
 
3334
    if (parser_trace >= 4) print "   Scoring match list: indef mode ", indef_mode, " type ",
 
3335
      indef_type, ", satisfying ", threshold, " requirements:^";
 
3336
    #Endif; ! DEBUG
 
3337
 
 
3338
    a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC;
 
3339
    if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) {
 
3340
        a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC;
 
3341
    }
 
3342
 
 
3343
    for (i=0 : i<number_matched : i++) {
 
3344
        obj = match_list-->i; its_owner = parent(obj); its_score=0; met=0;
 
3345
 
 
3346
        !      if (indef_type & OTHER_BIT ~= 0
 
3347
        !          &&  obj ~= itobj or himobj or herobj) met++;
 
3348
        if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++;
 
3349
        if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++;
 
3350
        if (indef_type & LIT_BIT ~= 0 && obj has light) met++;
 
3351
        if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++;
 
3352
        if (indef_owner ~= 0 && its_owner == indef_owner) met++;
 
3353
 
 
3354
        if (met < threshold) {
 
3355
            #Ifdef DEBUG;
 
3356
            if (parser_trace >= 4) print "   ", (The) match_list-->i, " (", match_list-->i, ") in ",
 
3357
              (the) its_owner, " is rejected (doesn't match descriptors)^";
 
3358
            #Endif; ! DEBUG
 
3359
            match_list-->i = -1;
 
3360
        }
 
3361
        else {
 
3362
            its_score = 0;
 
3363
            if (obj hasnt concealed) its_score = SCORE__UNCONCEALED;
 
3364
 
 
3365
            if (its_owner == actor) its_score = its_score + a_s;
 
3366
            else
 
3367
                if (its_owner == actors_location) its_score = its_score + l_s;
 
3368
                else
 
3369
                    if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS;
 
3370
 
 
3371
            its_score = its_score + SCORE__CHOOSEOBJ * ChooseObjects(obj, 2);
 
3372
 
 
3373
            if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY;
 
3374
            if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR;
 
3375
 
 
3376
            !   A small bonus for having the correct GNA,
 
3377
            !   for sorting out ambiguous articles and the like.
 
3378
 
 
3379
            if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj))))
 
3380
                its_score = its_score + SCORE__GNA;
 
3381
 
 
3382
            match_scores-->i = match_scores-->i + its_score;
 
3383
            #Ifdef DEBUG;
 
3384
            if (parser_trace >= 4) print "     ", (The) match_list-->i, " (", match_list-->i,
 
3385
              ") in ", (the) its_owner, " : ", match_scores-->i, " points^";
 
3386
            #Endif; ! DEBUG
 
3387
        }
 
3388
     }
 
3389
 
 
3390
    for (i=0 : i<number_matched : i++) {
 
3391
        while (match_list-->i == -1) {
 
3392
            if (i == number_matched-1) { number_matched--; break; }
 
3393
            for (j=i : j<number_matched : j++) {
 
3394
                match_list-->j = match_list-->(j+1);
 
3395
                match_scores-->j = match_scores-->(j+1);
 
3396
            }
 
3397
            number_matched--;
 
3398
        }
 
3399
    }
 
3400
];
 
3401
 
 
3402
! ----------------------------------------------------------------------------
 
3403
!  BestGuess makes the best guess it can out of the match list, assuming that
 
3404
!  everything in the match list is textually as good as everything else;
 
3405
!  however it ignores items marked as -1, and so marks anything it chooses.
 
3406
!  It returns -1 if there are no possible choices.
 
3407
! ----------------------------------------------------------------------------
 
3408
 
 
3409
[ BestGuess  earliest its_score best i;
 
3410
    earliest = 0; best = -1;
 
3411
    for (i=0 : i<number_matched : i++) {
 
3412
        if (match_list-->i >= 0) {
 
3413
            its_score = match_scores-->i;
 
3414
            if (its_score > best) { best = its_score; earliest = i; }
 
3415
        }
 
3416
    }
 
3417
    #Ifdef DEBUG;
 
3418
    if (parser_trace >= 4)
 
3419
      if (best < 0) print "   Best guess ran out of choices^";
 
3420
      else print "   Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^";
 
3421
    #Endif; ! DEBUG
 
3422
    if (best < 0) return -1;
 
3423
    i = match_list-->earliest;
 
3424
    match_list-->earliest = -1;
 
3425
    bestguess_score = best;
 
3426
    return i;
 
3427
];
 
3428
 
 
3429
! ----------------------------------------------------------------------------
 
3430
!  SingleBestGuess returns the highest-scoring object in the match list
 
3431
!  if it is the clear winner, or returns -1 if there is no clear winner
 
3432
! ----------------------------------------------------------------------------
 
3433
 
 
3434
[ SingleBestGuess  earliest its_score best i;
 
3435
    earliest = -1; best = -1000;
 
3436
    for (i=0 : i<number_matched : i++) {
 
3437
        its_score = match_scores-->i;
 
3438
        if (its_score == best) earliest = -1;
 
3439
        if (its_score > best) { best = its_score; earliest = match_list-->i; }
 
3440
    }
 
3441
    bestguess_score = best;
 
3442
    return earliest;
 
3443
];
 
3444
 
 
3445
! ----------------------------------------------------------------------------
 
3446
!  Identical decides whether or not two objects can be distinguished from
 
3447
!  each other by anything the player can type.  If not, it returns true.
 
3448
! ----------------------------------------------------------------------------
 
3449
 
 
3450
[ Identical o1 o2 p1 p2 n1 n2 i j flag;
 
3451
    if (o1 == o2) rtrue;  ! This should never happen, but to be on the safe side
 
3452
    if (o1 == 0 || o2 == 0) rfalse;  ! Similarly
 
3453
    if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time
 
3454
 
 
3455
    !  What complicates things is that o1 or o2 might have a parsing routine,
 
3456
    !  so the parser can't know from here whether they are or aren't the same.
 
3457
    !  If they have different parsing routines, we simply assume they're
 
3458
    !  different.  If they have the same routine (which they probably got from
 
3459
    !  a class definition) then the decision process is as follows:
 
3460
    !
 
3461
    !     the routine is called (with self being o1, not that it matters)
 
3462
    !       with noun and second being set to o1 and o2, and action being set
 
3463
    !       to the fake action TheSame.  If it returns -1, they are found
 
3464
    !       identical; if -2, different; and if >=0, then the usual method
 
3465
    !       is used instead.
 
3466
 
 
3467
    if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
 
3468
      if (o1.parse_name ~= o2.parse_name) rfalse;
 
3469
      parser_action = ##TheSame; parser_one = o1; parser_two = o2;
 
3470
      j = wn; i = RunRoutines(o1,parse_name); wn = j;
 
3471
      if (i == -1) rtrue;
 
3472
      if (i == -2) rfalse;
 
3473
    }
 
3474
 
 
3475
    !  This is the default algorithm: do they have the same words in their
 
3476
    !  "name" (i.e. property no. 1) properties.  (Note that the following allows
 
3477
    !  for repeated words and words in different orders.)
 
3478
 
 
3479
    p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
 
3480
    p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;
 
3481
 
 
3482
    !  for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
 
3483
    !  for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;
 
3484
 
 
3485
    for (i=0 : i<n1 : i++) {
 
3486
        flag = 0;
 
3487
        for (j=0 : j<n2 : j++)
 
3488
            if (p1-->i == p2-->j) flag = 1;
 
3489
        if (flag == 0) rfalse;
 
3490
    }
 
3491
 
 
3492
    for (j=0 : j<n2 : j++) {
 
3493
        flag = 0;
 
3494
        for (i=0 : i<n1 : i++)
 
3495
            if (p1-->i == p2-->j) flag = 1;
 
3496
        if (flag == 0) rfalse;
 
3497
    }
 
3498
 
 
3499
    !  print "Which are identical!^";
 
3500
    rtrue;
 
3501
];
 
3502
 
 
3503
! ----------------------------------------------------------------------------
 
3504
!  PrintCommand reconstructs the command as it presently reads, from
 
3505
!  the pattern which has been built up
 
3506
!
 
3507
!  If from is 0, it starts with the verb: then it goes through the pattern.
 
3508
!  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
 
3509
!  if 1, it goes up to pcount-1.
 
3510
!
 
3511
!  Note that verbs and prepositions are printed out of the dictionary:
 
3512
!  and that since the dictionary may only preserve the first six characters
 
3513
!  of a word (in a V3 game), we have to hand-code the longer words needed.
 
3514
!
 
3515
!  (Recall that pattern entries are 0 for "multiple object", 1 for "special
 
3516
!  word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the
 
3517
!  preposition n)
 
3518
! ----------------------------------------------------------------------------
 
3519
 
 
3520
[ PrintCommand from i k spacing_flag;
 
3521
    if (from == 0) {
 
3522
        i = verb_word;
 
3523
        if (LanguageVerb(i) == 0)
 
3524
            if (PrintVerb(i) == 0) print (address) i;
 
3525
        from++; spacing_flag = true;
 
3526
    }
 
3527
 
 
3528
    for (k=from : k<pcount : k++) {
 
3529
        i = pattern-->k;
 
3530
        if (i == PATTERN_NULL) continue;
 
3531
        if (spacing_flag) print (char) ' ';
 
3532
        if (i ==0 ) { print (string) THOSET__TX; jump TokenPrinted; }
 
3533
        if (i == 1) { print (string) THAT__TX;   jump TokenPrinted; }
 
3534
        if (i >= REPARSE_CODE)
 
3535
            print (address) No__Dword(i-REPARSE_CODE);
 
3536
        else
 
3537
            if (i in compass && LanguageVerbLikesAdverb(verb_word))
 
3538
                LanguageDirection (i.door_dir); ! the direction name as adverb
 
3539
            else
 
3540
                print (the) i;
 
3541
      .TokenPrinted;
 
3542
        spacing_flag = true;
 
3543
    }
 
3544
];
 
3545
 
 
3546
! ----------------------------------------------------------------------------
 
3547
!  The CantSee routine returns a good error number for the situation where
 
3548
!  the last word looked at didn't seem to refer to any object in context.
 
3549
!
 
3550
!  The idea is that: if the actor is in a location (but not inside something
 
3551
!  like, for instance, a tank which is in that location) then an attempt to
 
3552
!  refer to one of the words listed as meaningful-but-irrelevant there
 
3553
!  will cause "you don't need to refer to that in this game" rather than
 
3554
!  "no such thing" or "what's 'it'?".
 
3555
!  (The advantage of not having looked at "irrelevant" local nouns until now
 
3556
!  is that it stops them from clogging up the ambiguity-resolving process.
 
3557
!  Thus game objects always triumph over scenery.)
 
3558
! ----------------------------------------------------------------------------
 
3559
 
 
3560
[ CantSee  i w e;
 
3561
    saved_oops=oops_from;
 
3562
 
 
3563
    if (scope_token ~= 0) {
 
3564
        scope_error = scope_token;
 
3565
        return ASKSCOPE_PE;
 
3566
    }
 
3567
 
 
3568
    wn--; w = NextWord();
 
3569
    e = CANTSEE_PE;
 
3570
    if (w == pronoun_word) {
 
3571
        pronoun__word = pronoun_word; pronoun__obj = pronoun_obj;
 
3572
        e = ITGONE_PE;
 
3573
    }
 
3574
    i = actor; while (parent(i) ~= 0) i = parent(i);
 
3575
 
 
3576
    wn--;
 
3577
    if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
 
3578
    else {
 
3579
        Descriptors();  ! skip past THE etc
 
3580
        if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE;
 
3581
    }
 
3582
    wn++;
 
3583
    if (etype > e) return etype;
 
3584
    return e;
 
3585
];
 
3586
 
 
3587
! ----------------------------------------------------------------------------
 
3588
!  The MultiAdd routine adds object "o" to the multiple-object-list.
 
3589
!
 
3590
!  This is only allowed to hold 63 objects at most, at which point it ignores
 
3591
!  any new entries (and sets a global flag so that a warning may later be
 
3592
!  printed if need be).
 
3593
! ----------------------------------------------------------------------------
 
3594
 
 
3595
[ MultiAdd o i j;
 
3596
    i = multiple_object-->0;
 
3597
    if (i == 63) { toomany_flag = 1; rtrue; }
 
3598
    for (j=1 : j<=i : j++)
 
3599
        if (o == multiple_object-->j) rtrue;
 
3600
    i++;
 
3601
    multiple_object-->i = o;
 
3602
    multiple_object-->0 = i;
 
3603
];
 
3604
 
 
3605
! ----------------------------------------------------------------------------
 
3606
!  The MultiSub routine deletes object "o" from the multiple-object-list.
 
3607
!
 
3608
!  It returns 0 if the object was there in the first place, and 9 (because
 
3609
!  this is the appropriate error number in Parser()) if it wasn't.
 
3610
! ----------------------------------------------------------------------------
 
3611
 
 
3612
[ MultiSub o i j k et;
 
3613
    i = multiple_object-->0; et = 0;
 
3614
    for (j=1 : j<=i : j++)
 
3615
        if (o == multiple_object-->j) {
 
3616
            for (k=j : k<=i : k++)
 
3617
                multiple_object-->k = multiple_object-->(k+1);
 
3618
            multiple_object-->0 = --i;
 
3619
            return et;
 
3620
        }
 
3621
    et = 9; return et;
 
3622
];
 
3623
 
 
3624
! ----------------------------------------------------------------------------
 
3625
!  The MultiFilter routine goes through the multiple-object-list and throws
 
3626
!  out anything without the given attribute "attr" set.
 
3627
! ----------------------------------------------------------------------------
 
3628
 
 
3629
[ MultiFilter attr  i j o;
 
3630
 
 
3631
  .MFiltl;
 
3632
 
 
3633
    i = multiple_object-->0;
 
3634
    for (j=1 : j<=i : j++) {
 
3635
        o = multiple_object-->j;
 
3636
        if (o hasnt attr) {
 
3637
            MultiSub(o);
 
3638
            jump Mfiltl;
 
3639
        }
 
3640
    }
 
3641
];
 
3642
 
 
3643
! ----------------------------------------------------------------------------
 
3644
!  The UserFilter routine consults the user's filter (or checks on attribute)
 
3645
!  to see what already-accepted nouns are acceptable
 
3646
! ----------------------------------------------------------------------------
 
3647
 
 
3648
[ UserFilter obj;
 
3649
    if (token_filter > 0 && token_filter < 49) {
 
3650
        if (obj has (token_filter-1)) rtrue;
 
3651
        rfalse;
 
3652
    }
 
3653
    noun = obj;
 
3654
    return indirect(token_filter);
 
3655
];
 
3656
 
 
3657
! ----------------------------------------------------------------------------
 
3658
!  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
 
3659
!  (the main parse buffer)
 
3660
! ----------------------------------------------------------------------------
 
3661
 
 
3662
#Ifdef TARGET_ZCODE;
 
3663
 
 
3664
[ MoveWord at1 b2 at2 x y;
 
3665
    x = at1*2-1; y = at2*2-1;
 
3666
    parse-->x++ = b2-->y++;
 
3667
    parse-->x = b2-->y;
 
3668
];
 
3669
 
 
3670
#Ifnot; ! TARGET_GLULX
 
3671
 
 
3672
[ MoveWord at1 b2 at2 x y;
 
3673
    x = at1*3-2; y = at2*3-2;
 
3674
    parse-->x++ = b2-->y++;
 
3675
    parse-->x++ = b2-->y++;
 
3676
    parse-->x = b2-->y;
 
3677
];
 
3678
 
 
3679
#Endif; ! TARGET_
 
3680
 
 
3681
! ----------------------------------------------------------------------------
 
3682
!  SearchScope  domain1 domain2 context
 
3683
!
 
3684
!  Works out what objects are in scope (possibly asking an outside routine),
 
3685
!  but does not look at anything the player has typed.
 
3686
! ----------------------------------------------------------------------------
 
3687
 
 
3688
[ SearchScope domain1 domain2 context i;
 
3689
    i = 0;
 
3690
    !  Everything is in scope to the debugging commands
 
3691
 
 
3692
    #Ifdef DEBUG;
 
3693
    if (scope_reason == PARSING_REASON
 
3694
        && LanguageVerbIsDebugging(verb_word)) {
 
3695
 
 
3696
        #Ifdef TARGET_ZCODE;
 
3697
        for (i=selfobj : i<=top_object : i++)
 
3698
            if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
 
3699
                PlaceInScope(i);
 
3700
        #Ifnot; ! TARGET_GLULX
 
3701
        objectloop (i)
 
3702
            if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object))
 
3703
                PlaceInScope(i);
 
3704
        #Endif; ! TARGET_
 
3705
        rtrue;
 
3706
    }
 
3707
    #Endif; ! DEBUG
 
3708
 
 
3709
    ! First, a scope token gets priority here:
 
3710
 
 
3711
    if (scope_token ~= 0) {
 
3712
        scope_stage = 2;
 
3713
        if (indirect(scope_token) ~= 0) rtrue;
 
3714
    }
 
3715
 
 
3716
    ! Next, call any user-supplied routine adding things to the scope,
 
3717
    ! which may circumvent the usual routines altogether if they return true:
 
3718
 
 
3719
    if (actor == domain1 or domain2 && InScope(actor) ~= 0) rtrue;
 
3720
 
 
3721
    ! Pick up everything in the location except the actor's possessions;
 
3722
    ! then go through those.  (This ensures the actor's possessions are in
 
3723
    ! scope even in Darkness.)
 
3724
 
 
3725
    if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) {
 
3726
        if (IsSeeThrough(advance_warning) == 1)
 
3727
           ScopeWithin(advance_warning, 0, context);
 
3728
    }
 
3729
    else {
 
3730
        if (domain1 ~= 0 && domain1 has supporter or container)
 
3731
            ScopeWithin_O(domain1, domain1, context);
 
3732
        ScopeWithin(domain1, domain2, context);
 
3733
        if (domain2 ~= 0 && domain2 has supporter or container)
 
3734
            ScopeWithin_O(domain2, domain2, context);
 
3735
        ScopeWithin(domain2, 0, context);
 
3736
    }
 
3737
 
 
3738
    ! A special rule applies:
 
3739
    ! in Darkness as in light, the actor is always in scope to himself.
 
3740
 
 
3741
    if (thedark == domain1 or domain2) {
 
3742
        ScopeWithin_O(actor, actor, context);
 
3743
        if (parent(actor) has supporter or container)
 
3744
            ScopeWithin_O(parent(actor), parent(actor), context);
 
3745
    }
 
3746
];
 
3747
 
 
3748
! ----------------------------------------------------------------------------
 
3749
!  IsSeeThrough is used at various places: roughly speaking, it determines
 
3750
!  whether o being in scope means that the contents of o are in scope.
 
3751
! ----------------------------------------------------------------------------
 
3752
 
 
3753
[ IsSeeThrough o;
 
3754
    if (o has supporter ||
 
3755
       (o has transparent) ||
 
3756
       (o has container && o has open))
 
3757
        rtrue;
 
3758
    rfalse;
 
3759
];
 
3760
 
 
3761
! ----------------------------------------------------------------------------
 
3762
!  PlaceInScope is provided for routines outside the library, and is not
 
3763
!  called within the parser (except for debugging purposes).
 
3764
! ----------------------------------------------------------------------------
 
3765
 
 
3766
[ PlaceInScope thing;
 
3767
    if (scope_reason~=PARSING_REASON or TALKING_REASON) {
 
3768
        DoScopeAction(thing); rtrue;
 
3769
    }
 
3770
    wn = match_from; TryGivenObject(thing); placed_in_flag = 1;
 
3771
];
 
3772
 
 
3773
! ----------------------------------------------------------------------------
 
3774
!  DoScopeAction
 
3775
! ----------------------------------------------------------------------------
 
3776
 
 
3777
[ DoScopeAction thing s p1;
 
3778
    s = scope_reason; p1 = parser_one;
 
3779
    #Ifdef DEBUG;
 
3780
    if (parser_trace >= 6)
 
3781
        print "[DSA on ", (the) thing, " with reason = ", scope_reason,
 
3782
            " p1 = ", parser_one, " p2 = ", parser_two, "]^";
 
3783
    #Endif; ! DEBUG
 
3784
    switch (scope_reason) {
 
3785
      REACT_BEFORE_REASON:
 
3786
        if (thing.react_before == 0 or NULL) return;
 
3787
        #Ifdef DEBUG;
 
3788
        if (parser_trace >= 2)
 
3789
              print "[Considering react_before for ", (the) thing, "]^";
 
3790
        #Endif; ! DEBUG
 
3791
        if (parser_one == 0) parser_one = RunRoutines(thing, react_before);
 
3792
      REACT_AFTER_REASON:
 
3793
        if (thing.react_after == 0 or NULL) return;
 
3794
        #Ifdef DEBUG;
 
3795
        if (parser_trace >= 2)
 
3796
            print "[Considering react_after for ", (the) thing, "]^";
 
3797
        #Endif; ! DEBUG
 
3798
        if (parser_one == 0) parser_one = RunRoutines(thing, react_after);
 
3799
      EACH_TURN_REASON:
 
3800
        if (thing.each_turn == 0 or NULL) return;
 
3801
        #Ifdef DEBUG;
 
3802
        if (parser_trace >= 2)
 
3803
              print "[Considering each_turn for ", (the) thing, "]^";
 
3804
        #Endif; ! DEBUG
 
3805
        PrintOrRun(thing, each_turn);
 
3806
      TESTSCOPE_REASON:
 
3807
        if (thing == parser_one) parser_two = 1;
 
3808
      LOOPOVERSCOPE_REASON:
 
3809
        indirect(parser_one, thing);
 
3810
        parser_one=p1;
 
3811
    }
 
3812
    scope_reason = s;
 
3813
];
 
3814
 
 
3815
! ----------------------------------------------------------------------------
 
3816
!  ScopeWithin looks for objects in the domain which make textual sense
 
3817
!  and puts them in the match list.  (However, it does not recurse through
 
3818
!  the second argument.)
 
3819
! ----------------------------------------------------------------------------
 
3820
 
 
3821
[ ScopeWithin domain nosearch context x y;
 
3822
    if (domain == 0) rtrue;
 
3823
 
 
3824
    ! Special rule: the directions (interpreted as the 12 walls of a room) are
 
3825
    ! always in context.  (So, e.g., "examine north wall" is always legal.)
 
3826
    ! (Unless we're parsing something like "all", because it would just slow
 
3827
    ! things down then, or unless the context is "creature".)
 
3828
 
 
3829
    if (indef_mode==0 && domain==actors_location
 
3830
        && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN)
 
3831
            ScopeWithin(compass);
 
3832
 
 
3833
    ! Look through the objects in the domain, avoiding "objectloop" in case
 
3834
    ! movements occur, e.g. when trying each_turn.
 
3835
 
 
3836
    x = child(domain);
 
3837
    while (x ~= 0) {
 
3838
        y = sibling(x);
 
3839
        ScopeWithin_O(x, nosearch, context);
 
3840
        x = y;
 
3841
    }
 
3842
];
 
3843
 
 
3844
[ ScopeWithin_O domain nosearch context i ad n;
 
3845
 
 
3846
    ! If the scope reason is unusual, don't parse.
 
3847
 
 
3848
    if (scope_reason ~= PARSING_REASON or TALKING_REASON) {
 
3849
        DoScopeAction(domain);
 
3850
        jump DontAccept;
 
3851
    }
 
3852
 
 
3853
    ! "it" or "them" matches to the it-object only.  (Note that (1) this means
 
3854
    ! that "it" will only be understood if the object in question is still
 
3855
    ! in context, and (2) only one match can ever be made in this case.)
 
3856
 
 
3857
    if (match_from <= num_words) {  ! If there's any text to match, that is
 
3858
        wn = match_from;
 
3859
        i = NounWord();
 
3860
        if (i == 1 && player == domain) MakeMatch(domain, 1);
 
3861
        if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1);
 
3862
    }
 
3863
 
 
3864
    ! Construing the current word as the start of a noun, can it refer to the
 
3865
    ! object?
 
3866
 
 
3867
    wn = match_from;
 
3868
    if (TryGivenObject(domain) > 0)
 
3869
        if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) {
 
3870
            ! This case arises if the player has typed a number in
 
3871
            ! which is hypothetically an indefinite descriptor:
 
3872
            ! e.g. "take two clubs".  We have just checked the object
 
3873
            ! against the word "clubs", in the hope of eventually finding
 
3874
            ! two such objects.  But we also backtrack and check it
 
3875
            ! against the words "two clubs", in case it turns out to
 
3876
            ! be the 2 of Clubs from a pack of cards, say.  If it does
 
3877
            ! match against "two clubs", we tear up our original
 
3878
            ! assumption about the meaning of "two" and lapse back into
 
3879
            ! definite mode.
 
3880
 
 
3881
            wn = indef_nspec_at;
 
3882
            if (TryGivenObject(domain) > 0) {
 
3883
                match_from = indef_nspec_at;
 
3884
                ResetDescriptors();
 
3885
            }
 
3886
            wn = match_from;
 
3887
        }
 
3888
 
 
3889
  .DontAccept;
 
3890
 
 
3891
    ! Shall we consider the possessions of the current object, as well?
 
3892
    ! Only if it's a container (so, for instance, if a dwarf carries a
 
3893
    ! sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
 
3894
    ! will).
 
3895
    ! Also, only if there are such possessions.
 
3896
    !
 
3897
    ! Notice that the parser can see "into" anything flagged as
 
3898
    ! transparent - such as a dwarf whose sword you can get at.
 
3899
 
 
3900
    if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1)
 
3901
        ScopeWithin(domain,nosearch,context);
 
3902
 
 
3903
    ! Drag any extras into context
 
3904
 
 
3905
    ad = domain.&add_to_scope;
 
3906
    if (ad ~= 0) {
 
3907
 
 
3908
        ! Test if the property value is not an object.
 
3909
        #Ifdef TARGET_ZCODE;
 
3910
        i = (UnsignedCompare(ad-->0, top_object) > 0);
 
3911
        #Ifnot; ! TARGET_GLULX
 
3912
        i = (((ad-->0)->0) ~= $70);
 
3913
        #Endif; ! TARGET_
 
3914
 
 
3915
        if (i) {
 
3916
            ats_flag = 2+context;
 
3917
            RunRoutines(domain, add_to_scope);
 
3918
            ats_flag = 0;
 
3919
        }
 
3920
        else {
 
3921
            n = domain.#add_to_scope;
 
3922
            for (i=0 : (WORDSIZE*i)<n : i++)
 
3923
                if (ad-->i)
 
3924
                    ScopeWithin_O(ad-->i, 0, context);
 
3925
        }
 
3926
    }
 
3927
];
 
3928
 
 
3929
[ AddToScope obj;
 
3930
    if (ats_flag >= 2)
 
3931
        ScopeWithin_O(obj, 0, ats_flag-2);
 
3932
    if (ats_flag == 1) {
 
3933
        if  (HasLightSource(obj)==1) ats_hls = 1;
 
3934
    }
 
3935
];
 
3936
 
 
3937
! ----------------------------------------------------------------------------
 
3938
!  MakeMatch looks at how good a match is.  If it's the best so far, then
 
3939
!  wipe out all the previous matches and start a new list with this one.
 
3940
!  If it's only as good as the best so far, add it to the list.
 
3941
!  If it's worse, ignore it altogether.
 
3942
!
 
3943
!  The idea is that "red panic button" is better than "red button" or "panic".
 
3944
!
 
3945
!  number_matched (the number of words matched) is set to the current level
 
3946
!  of quality.
 
3947
!
 
3948
!  We never match anything twice, and keep at most 64 equally good items.
 
3949
! ----------------------------------------------------------------------------
 
3950
 
 
3951
[ MakeMatch obj quality i;
 
3952
    #Ifdef DEBUG;
 
3953
    if (parser_trace >= 6) print "    Match with quality ",quality,"^";
 
3954
    #Endif; ! DEBUG
 
3955
    if (token_filter ~= 0 && UserFilter(obj) == 0) {
 
3956
        #Ifdef DEBUG;
 
3957
        if (parser_trace >= 6) print "    Match filtered out: token filter ", token_filter, "^";
 
3958
        #Endif; ! DEBUG
 
3959
        rtrue;
 
3960
    }
 
3961
    if (quality < match_length) rtrue;
 
3962
    if (quality > match_length) { match_length = quality; number_matched = 0; }
 
3963
    else {
 
3964
        if (2*number_matched >= MATCH_LIST_SIZE) rtrue;
 
3965
        for (i=0 : i<number_matched : i++)
 
3966
            if (match_list-->i == obj) rtrue;
 
3967
    }
 
3968
    match_list-->number_matched++ = obj;
 
3969
    #Ifdef DEBUG;
 
3970
    if (parser_trace >= 6) print "    Match added to list^";
 
3971
    #Endif; ! DEBUG
 
3972
];
 
3973
 
 
3974
! ----------------------------------------------------------------------------
 
3975
!  TryGivenObject tries to match as many words as possible in what has been
 
3976
!  typed to the given object, obj.  If it manages any words matched at all,
 
3977
!  it calls MakeMatch to say so, then returns the number of words (or 1
 
3978
!  if it was a match because of inadequate input).
 
3979
! ----------------------------------------------------------------------------
 
3980
 
 
3981
[ TryGivenObject obj threshold k w j;
 
3982
    #Ifdef DEBUG;
 
3983
    if (parser_trace >= 5) print "    Trying ", (the) obj, " (", obj, ") at word ", wn, "^";
 
3984
    #Endif; ! DEBUG
 
3985
 
 
3986
    dict_flags_of_noun = 0;
 
3987
 
 
3988
!  If input has run out then always match, with only quality 0 (this saves
 
3989
!  time).
 
3990
 
 
3991
    if (wn > num_words) {
 
3992
        if (indef_mode ~= 0)
 
3993
            dict_flags_of_noun = $$01110000;  ! Reject "plural" bit
 
3994
        MakeMatch(obj,0);
 
3995
        #Ifdef DEBUG;
 
3996
        if (parser_trace >= 5) print "    Matched (0)^";
 
3997
        #Endif; ! DEBUG
 
3998
        return 1;
 
3999
    }
 
4000
 
 
4001
!  Ask the object to parse itself if necessary, sitting up and taking notice
 
4002
!  if it says the plural was used:
 
4003
 
 
4004
    if (obj.parse_name~=0) {
 
4005
        parser_action = NULL; j=wn;
 
4006
        k = RunRoutines(obj,parse_name);
 
4007
        if (k > 0) {
 
4008
            wn=j+k;
 
4009
 
 
4010
          .MMbyPN;
 
4011
 
 
4012
            if (parser_action == ##PluralFound)
 
4013
                dict_flags_of_noun = dict_flags_of_noun | 4;
 
4014
 
 
4015
            if (dict_flags_of_noun & 4) {
 
4016
                if (~~allow_plurals) k = 0;
 
4017
                else {
 
4018
                    if (indef_mode == 0) {
 
4019
                        indef_mode = 1; indef_type = 0; indef_wanted = 0;
 
4020
                    }
 
4021
                    indef_type = indef_type | PLURAL_BIT;
 
4022
                    if (indef_wanted == 0) indef_wanted = 100;
 
4023
                }
 
4024
            }
 
4025
 
 
4026
            #Ifdef DEBUG;
 
4027
            if (parser_trace >= 5) print "    Matched (", k, ")^";
 
4028
            #Endif; ! DEBUG
 
4029
            MakeMatch(obj,k);
 
4030
            return k;
 
4031
        }
 
4032
        if (k == 0) jump NoWordsMatch;
 
4033
    }
 
4034
 
 
4035
    ! The default algorithm is simply to count up how many words pass the
 
4036
    ! Refers test:
 
4037
 
 
4038
    parser_action = NULL;
 
4039
 
 
4040
    w = NounWord();
 
4041
 
 
4042
    if (w == 1 && player == obj) { k=1; jump MMbyPN; }
 
4043
 
 
4044
    if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; }
 
4045
 
 
4046
    j=--wn;
 
4047
    threshold = ParseNoun(obj);
 
4048
    #Ifdef DEBUG;
 
4049
    if (threshold >= 0 && parser_trace >= 5) print "    ParseNoun returned ", threshold, "^";
 
4050
    #Endif; ! DEBUG
 
4051
    if (threshold < 0) wn++;
 
4052
    if (threshold > 0) { k = threshold; jump MMbyPN; }
 
4053
 
 
4054
    if (threshold == 0 || Refers(obj,wn-1) == 0) {
 
4055
      .NoWordsMatch;
 
4056
        if (indef_mode ~= 0) {
 
4057
            k = 0; parser_action = NULL;
 
4058
            jump MMbyPN;
 
4059
        }
 
4060
        rfalse;
 
4061
    }
 
4062
 
 
4063
    if (threshold < 0) {
 
4064
        threshold = 1;
 
4065
        dict_flags_of_noun = (w->#dict_par1) & $$01110100;
 
4066
        w = NextWord();
 
4067
        while (Refers(obj, wn-1)) {
 
4068
            threshold++;
 
4069
            if (w)
 
4070
               dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & $$01110100);
 
4071
            w = NextWord();
 
4072
        }
 
4073
    }
 
4074
 
 
4075
    k = threshold;
 
4076
    jump MMbyPN;
 
4077
];
 
4078
 
 
4079
! ----------------------------------------------------------------------------
 
4080
!  Refers works out whether the word at number wnum can refer to the object
 
4081
!  obj, returning true or false.  The standard method is to see if the
 
4082
!  word is listed under "name" for the object, but this is more complex
 
4083
!  in languages other than English.
 
4084
! ----------------------------------------------------------------------------
 
4085
 
 
4086
[ Refers obj wnum   wd k l m;
 
4087
    if (obj == 0) rfalse;
 
4088
 
 
4089
    #Ifdef LanguageRefers;
 
4090
    k = LanguageRefers(obj,wnum); if (k >= 0) return k;
 
4091
    #Endif; ! LanguageRefers
 
4092
 
 
4093
    k = wn; wn = wnum; wd = NextWordStopped(); wn = k;
 
4094
 
 
4095
    if (parser_inflection >= 256) {
 
4096
        k = indirect(parser_inflection, obj, wd);
 
4097
        if (k >= 0) return k;
 
4098
        m = -k;
 
4099
    }
 
4100
    else
 
4101
        m = parser_inflection;
 
4102
    k = obj.&m; l = (obj.#m)/WORDSIZE-1;
 
4103
    for (m=0 : m<=l : m++)
 
4104
        if (wd == k-->m) rtrue;
 
4105
    rfalse;
 
4106
];
 
4107
 
 
4108
[ WordInProperty wd obj prop k l m;
 
4109
    k = obj.&prop; l = (obj.#prop)/WORDSIZE-1;
 
4110
    for (m=0 : m<=l : m++)
 
4111
        if (wd == k-->m) rtrue;
 
4112
    rfalse;
 
4113
];
 
4114
 
 
4115
#Ifdef TARGET_ZCODE;
 
4116
 
 
4117
[ DictionaryLookup b l i;
 
4118
    for (i=0 : i<l : i++) buffer2->(2+i) = b->i;
 
4119
    buffer2->1 = l;
 
4120
    Tokenise__(buffer2,parse2);
 
4121
    return parse2-->1;
 
4122
];
 
4123
 
 
4124
#Ifnot; ! TARGET_GLULX
 
4125
 
 
4126
[ DictionaryLookup b l i;
 
4127
    for (i=0 : i<l : i++) buffer2->(WORDSIZE+i) = b->i;
 
4128
    buffer2-->0 = l;
 
4129
    Tokenise__(buffer2,parse2);
 
4130
    return parse2-->1;
 
4131
];
 
4132
 
 
4133
#Endif; ! TARGET_
 
4134
 
 
4135
! ----------------------------------------------------------------------------
 
4136
!  NounWord (which takes no arguments) returns:
 
4137
!
 
4138
!   0  if the next word is unrecognised or does not carry the "noun" bit in
 
4139
!      its dictionary entry,
 
4140
!   1  if a word meaning "me",
 
4141
!   the index in the pronoun table (plus 2) of the value field of a pronoun,
 
4142
!      if the word is a pronoun,
 
4143
!   the address in the dictionary if it is a recognised noun.
 
4144
!
 
4145
!  The "current word" marker moves on one.
 
4146
! ----------------------------------------------------------------------------
 
4147
 
 
4148
[ NounWord i j s;
 
4149
    i = NextWord();
 
4150
    if (i == 0) rfalse;
 
4151
    if (i == ME1__WD or ME2__WD or ME3__WD) return 1;
 
4152
    s = LanguagePronouns-->0;
 
4153
    for (j=1 : j<=s : j=j+3)
 
4154
        if (i == LanguagePronouns-->j)
 
4155
            return j+2;
 
4156
    if ((i->#dict_par1)&128 == 0) rfalse;
 
4157
    return i;
 
4158
];
 
4159
 
 
4160
! ----------------------------------------------------------------------------
 
4161
!  NextWord (which takes no arguments) returns:
 
4162
!
 
4163
!  0            if the next word is unrecognised,
 
4164
!  comma_word   if a comma
 
4165
!  THEN1__WD    if a full stop
 
4166
!  or the dictionary address if it is recognised.
 
4167
!  The "current word" marker is moved on.
 
4168
!
 
4169
!  NextWordStopped does the same, but returns -1 when input has run out
 
4170
! ----------------------------------------------------------------------------
 
4171
 
 
4172
#Ifdef TARGET_ZCODE;
 
4173
 
 
4174
[ NextWord i j;
 
4175
    if (wn > parse->1) { wn++; rfalse; }
 
4176
    i = wn*2-1; wn++;
 
4177
    j = parse-->i;
 
4178
    if (j == ',//') j = comma_word;
 
4179
    if (j == './/') j = THEN1__WD;
 
4180
    return j;
 
4181
];
 
4182
 
 
4183
[ NextWordStopped;
 
4184
    if (wn > parse->1) { wn++; return -1; }
 
4185
    return NextWord();
 
4186
];
 
4187
 
 
4188
[ WordAddress wordnum; return buffer + parse->(wordnum*4+1); ];
 
4189
 
 
4190
[ WordLength wordnum; return parse->(wordnum*4); ];
 
4191
 
 
4192
#Ifnot; ! TARGET_GLULX
 
4193
 
 
4194
[ NextWord i j;
 
4195
    if (wn > parse-->0) { wn++; rfalse; }
 
4196
    i = wn*3-2; wn++;
 
4197
    j = parse-->i;
 
4198
    if (j == ',//') j=comma_word;
 
4199
    if (j == './/') j=THEN1__WD;
 
4200
    return j;
 
4201
];
 
4202
 
 
4203
[ NextWordStopped;
 
4204
    if (wn > parse-->0) {
 
4205
        wn++;
 
4206
        return -1;
 
4207
    }
 
4208
    return NextWord();
 
4209
];
 
4210
 
 
4211
[ WordAddress wordnum; return buffer + parse-->(wordnum*3); ];
 
4212
 
 
4213
[ WordLength wordnum; return parse-->(wordnum*3-1); ];
 
4214
 
 
4215
#Endif; ! TARGET_
 
4216
 
 
4217
! ----------------------------------------------------------------------------
 
4218
!  TryNumber is the only routine which really does any character-level
 
4219
!  parsing, since that's normally left to the Z-machine.
 
4220
!  It takes word number "wordnum" and tries to parse it as an (unsigned)
 
4221
!  decimal number, returning
 
4222
!
 
4223
!  -1000                if it is not a number
 
4224
!  the number           if it has between 1 and 4 digits
 
4225
!  10000                if it has 5 or more digits.
 
4226
!
 
4227
!  (The danger of allowing 5 digits is that Z-machine integers are only
 
4228
!  16 bits long, and anyway this isn't meant to be perfect.)
 
4229
!
 
4230
!  Using NumberWord, it also catches "one" up to "twenty".
 
4231
!
 
4232
!  Note that a game can provide a ParseNumber routine which takes priority,
 
4233
!  to enable parsing of odder numbers ("x45y12", say).
 
4234
! ----------------------------------------------------------------------------
 
4235
 
 
4236
[ TryNumber wordnum   i j c num len mul tot d digit;
 
4237
    i = wn; wn = wordnum; j = NextWord(); wn = i;
 
4238
    j = NumberWord(j);
 
4239
    if (j >= 1) return j;
 
4240
 
 
4241
    #Ifdef TARGET_ZCODE;
 
4242
    i = wordnum*4+1; j = parse->i; num = j+buffer; len = parse->(i-1);
 
4243
    #Ifnot; ! TARGET_GLULX
 
4244
    i = wordnum*3; j = parse-->i; num = j+buffer; len = parse-->(i-1);
 
4245
    #Endif; ! TARGET_
 
4246
 
 
4247
    tot=ParseNumber(num, len);
 
4248
    if (tot ~= 0) return tot;
 
4249
 
 
4250
    if (len >= 4) mul=1000;
 
4251
    if (len == 3) mul=100;
 
4252
    if (len == 2) mul=10;
 
4253
    if (len == 1) mul=1;
 
4254
 
 
4255
    tot = 0; c = 0; len = len-1;
 
4256
 
 
4257
    for (c=0 : c<=len : c++) {
 
4258
        digit=num->c;
 
4259
        if (digit == '0') { d = 0; jump digok; }
 
4260
        if (digit == '1') { d = 1; jump digok; }
 
4261
        if (digit == '2') { d = 2; jump digok; }
 
4262
        if (digit == '3') { d = 3; jump digok; }
 
4263
        if (digit == '4') { d = 4; jump digok; }
 
4264
        if (digit == '5') { d = 5; jump digok; }
 
4265
        if (digit == '6') { d = 6; jump digok; }
 
4266
        if (digit == '7') { d = 7; jump digok; }
 
4267
        if (digit == '8') { d = 8; jump digok; }
 
4268
        if (digit == '9') { d = 9; jump digok; }
 
4269
        return -1000;
 
4270
     .digok;
 
4271
        tot = tot+mul*d; mul = mul/10;
 
4272
    }
 
4273
    if (len > 3) tot=10000;
 
4274
    return tot;
 
4275
];
 
4276
 
 
4277
! ----------------------------------------------------------------------------
 
4278
!  GetGender returns 0 if the given animate object is female, and 1 if male
 
4279
!  (not all games will want such a simple decision function!)
 
4280
! ----------------------------------------------------------------------------
 
4281
 
 
4282
[ GetGender person;
 
4283
    if (person hasnt female) rtrue;
 
4284
    rfalse;
 
4285
];
 
4286
 
 
4287
[ GetGNAOfObject obj case gender;
 
4288
    if (obj hasnt animate) case = 6;
 
4289
    if (obj has male) gender = male;
 
4290
    if (obj has female) gender = female;
 
4291
    if (obj has neuter) gender = neuter;
 
4292
    if (gender == 0) {
 
4293
        if (case == 0) gender = LanguageAnimateGender;
 
4294
        else gender = LanguageInanimateGender;
 
4295
    }
 
4296
    if (gender == female)   case = case + 1;
 
4297
    if (gender == neuter)   case = case + 2;
 
4298
    if (obj has pluralname) case = case + 3;
 
4299
    return case;
 
4300
];
 
4301
 
 
4302
! ----------------------------------------------------------------------------
 
4303
!  Converting between dictionary addresses and entry numbers
 
4304
! ----------------------------------------------------------------------------
 
4305
 
 
4306
#Ifdef TARGET_ZCODE;
 
4307
 
 
4308
[ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ];
 
4309
[ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ];
 
4310
 
 
4311
#Ifnot; ! TARGET_GLULX
 
4312
 
 
4313
! In Glulx, dictionary entries *are* addresses.
 
4314
[ Dword__No w; return w; ];
 
4315
[ No__Dword n; return n; ];
 
4316
 
 
4317
#Endif; ! TARGET_
 
4318
 
 
4319
! ----------------------------------------------------------------------------
 
4320
!  For copying buffers
 
4321
! ----------------------------------------------------------------------------
 
4322
 
 
4323
#Ifdef TARGET_ZCODE;
 
4324
 
 
4325
[ CopyBuffer bto bfrom i size;
 
4326
    size = bto->0;
 
4327
    for (i=1 : i<=size : i++) bto->i = bfrom->i;
 
4328
];
 
4329
 
 
4330
#Ifnot; ! TARGET_GLULX
 
4331
 
 
4332
[ CopyBuffer bto bfrom i;
 
4333
    for (i=0 : i<INPUT_BUFFER_LEN : i++) bto->i = bfrom->i;
 
4334
];
 
4335
 
 
4336
#Endif; ! TARGET_
 
4337
 
 
4338
! ----------------------------------------------------------------------------
 
4339
!  Provided for use by language definition files
 
4340
! ----------------------------------------------------------------------------
 
4341
 
 
4342
#Ifdef TARGET_ZCODE;
 
4343
 
 
4344
[ LTI_Insert i ch  b y;
 
4345
 
 
4346
    ! Protect us from strict mode, as this isn't an array in quite the
 
4347
    ! sense it expects
 
4348
    b = buffer;
 
4349
 
 
4350
    ! Insert character ch into buffer at point i.
 
4351
    ! Being careful not to let the buffer possibly overflow:
 
4352
    y = b->1;
 
4353
    if (y > b->0) y = b->0;
 
4354
 
 
4355
    ! Move the subsequent text along one character:
 
4356
    for (y=y+2 : y>i : y--) b->y = b->(y-1);
 
4357
    b->i = ch;
 
4358
 
 
4359
    ! And the text is now one character longer:
 
4360
    if (b->1 < b->0) (b->1)++;
 
4361
];
 
4362
 
 
4363
#Ifnot; ! TARGET_GLULX
 
4364
 
 
4365
[ LTI_Insert i ch  b y;
 
4366
 
 
4367
    ! Protect us from strict mode, as this isn't an array in quite the
 
4368
    ! sense it expects
 
4369
    b = buffer;
 
4370
 
 
4371
    ! Insert character ch into buffer at point i.
 
4372
    ! Being careful not to let the buffer possibly overflow:
 
4373
    y = b-->0;
 
4374
    if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN;
 
4375
 
 
4376
    ! Move the subsequent text along one character:
 
4377
    for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1);
 
4378
    b->i = ch;
 
4379
 
 
4380
    ! And the text is now one character longer:
 
4381
    if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++;
 
4382
];
 
4383
 
 
4384
#Endif; ! TARGET_
 
4385
 
 
4386
! ============================================================================
 
4387
 
 
4388
[ PronounsSub x y c d;
 
4389
    L__M(##Pronouns, 1);
 
4390
 
 
4391
    c = (LanguagePronouns-->0)/3;
 
4392
    if (player ~= selfobj) c++;
 
4393
 
 
4394
    if (c == 0) return L__M(##Pronouns, 4);
 
4395
 
 
4396
    for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) {
 
4397
        print "~", (address) LanguagePronouns-->x, "~ ";
 
4398
        y = LanguagePronouns-->(x+2);
 
4399
        if (y == NULL) L__M(##Pronouns, 3);
 
4400
        else {
 
4401
            L__M(##Pronouns, 2);
 
4402
            print (the) y;
 
4403
        }
 
4404
        d++;
 
4405
        if (d < c-1) print (string) COMMA__TX;
 
4406
        if (d == c-1) print (string) AND__TX;
 
4407
    }
 
4408
    if (player ~= selfobj) {
 
4409
        print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2);
 
4410
        c = player; player = selfobj;
 
4411
        print (the) c; player = c;
 
4412
    }
 
4413
    L__M(##Pronouns, 5);
 
4414
];
 
4415
 
 
4416
[ SetPronoun dword value x;
 
4417
    for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
 
4418
        if (LanguagePronouns-->x == dword) {
 
4419
            LanguagePronouns-->(x+2) = value; return;
 
4420
        }
 
4421
    RunTimeError(14);
 
4422
];
 
4423
 
 
4424
[ PronounValue dword x;
 
4425
    for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
 
4426
        if (LanguagePronouns-->x == dword)
 
4427
            return LanguagePronouns-->(x+2);
 
4428
    return 0;
 
4429
];
 
4430
 
 
4431
[ ResetVagueWords obj; PronounNotice(obj); ];
 
4432
 
 
4433
#Ifdef EnglishNaturalLanguage;
 
4434
 
 
4435
[ PronounOldEnglish;
 
4436
    if (itobj ~= old_itobj)   SetPronoun('it', itobj);
 
4437
    if (himobj ~= old_himobj) SetPronoun('him', himobj);
 
4438
    if (herobj ~= old_herobj) SetPronoun('her', herobj);
 
4439
    old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
 
4440
];
 
4441
 
 
4442
#Endif; !EnglishNaturalLanguage
 
4443
 
 
4444
[ PronounNotice obj x bm;
 
4445
    if (obj == player) return;
 
4446
 
 
4447
    #Ifdef EnglishNaturalLanguage;
 
4448
    PronounOldEnglish();
 
4449
    #Endif; ! EnglishNaturalLanguage
 
4450
 
 
4451
    bm = PowersOfTwo_TB-->(GetGNAOfObject(obj));
 
4452
 
 
4453
    for (x=1 : x<=LanguagePronouns-->0 : x=x+3)
 
4454
        if (bm & (LanguagePronouns-->(x+1)) ~= 0)
 
4455
            LanguagePronouns-->(x+2) = obj;
 
4456
 
 
4457
    #Ifdef EnglishNaturalLanguage;
 
4458
    itobj  = PronounValue('it');  old_itobj  = itobj;
 
4459
    himobj = PronounValue('him'); old_himobj = himobj;
 
4460
    herobj = PronounValue('her'); old_herobj = herobj;
 
4461
    #Endif; ! EnglishNaturalLanguage
 
4462
];
 
4463
 
 
4464
! ============================================================================
 
4465
!  End of the parser proper: the remaining routines are its front end.
 
4466
! ----------------------------------------------------------------------------
 
4467
 
 
4468
Object  InformLibrary "(Inform Library)"
 
4469
  with  play [ i j k l;
 
4470
 
 
4471
            #Ifdef TARGET_ZCODE;
 
4472
            standard_interpreter = HDR_TERPSTANDARD-->0;
 
4473
            transcript_mode = ((HDR_GAMEFLAGS-->0) & 1);
 
4474
            sys_statusline_flag = ( (HDR_TERPFLAGS->0) & 2 ) / 2;
 
4475
            #Ifnot; ! TARGET_GLULX
 
4476
            GGInitialise();
 
4477
            #Endif; ! TARGET_
 
4478
 
 
4479
            ChangeDefault(cant_go, CANTGO__TX);
 
4480
 
 
4481
            #Ifdef TARGET_ZCODE;
 
4482
            dict_start = HDR_DICTIONARY-->0;
 
4483
            dict_entry_size = dict_start->(dict_start->0 + 1);
 
4484
            dict_start = dict_start + dict_start->0 + 4;
 
4485
            dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size;
 
4486
            #Ifdef DEBUG;
 
4487
            if (dict_start > 0 && dict_end < 0 &&
 
4488
              ((-dict_start) - dict_end) % dict_entry_size == 0)
 
4489
                print "** Warning: grammar properties might not work correctly **^";
 
4490
            #Endif; ! DEBUG
 
4491
 
 
4492
            buffer->0  = INPUT_BUFFER_LEN;
 
4493
            buffer2->0 = INPUT_BUFFER_LEN;
 
4494
            buffer3->0 = INPUT_BUFFER_LEN;
 
4495
            parse->0   = 15;
 
4496
            parse2->0  = 15;
 
4497
            #Endif; ! TARGET_ZCODE
 
4498
 
 
4499
            real_location = thedark;
 
4500
            player = selfobj; actor = player;
 
4501
 
 
4502
            #Ifdef TARGET_ZCODE;
 
4503
            top_object = #largest_object-255;
 
4504
            #Endif; ! TARGET_ZCODE
 
4505
            selfobj.capacity = MAX_CARRIED; ! ### change?
 
4506
 
 
4507
            #Ifdef LanguageInitialise;
 
4508
            LanguageInitialise();
 
4509
            #Endif; ! LanguageInitialise
 
4510
 
 
4511
            new_line;
 
4512
            LibraryExtensions.RunAll(ext_initialise);
 
4513
            j = Initialise();
 
4514
            last_score = score;
 
4515
            move player to location;
 
4516
 
 
4517
            while (parent(location) ~= 0) location = parent(location);
 
4518
            real_location = location;
 
4519
 
 
4520
            actor = player; ! resync, because player may have been changed in initialise()
 
4521
            actors_location = location;
 
4522
 
 
4523
            objectloop (i in player) give i moved ~concealed;
 
4524
 
 
4525
            if (j ~= 2) Banner();
 
4526
 
 
4527
            MoveFloatingObjects();
 
4528
            lightflag = OffersLight(parent(player));
 
4529
            if (lightflag == 0) {
 
4530
                real_location = location;
 
4531
                location = thedark;
 
4532
            }
 
4533
            <Look>;
 
4534
 
 
4535
            for (i=1 : i<=100 : i++) j = random(i);
 
4536
 
 
4537
            #Ifdef EnglishNaturalLanguage;
 
4538
            old_itobj = itobj; old_himobj = himobj; old_herobj = herobj;
 
4539
            #Endif;! EnglishNaturalLanguage
 
4540
 
 
4541
            while (~~deadflag) {    ! everything happens in this loop
 
4542
 
 
4543
                #Ifdef EnglishNaturalLanguage;
 
4544
                PronounOldEnglish();
 
4545
                old_itobj = PronounValue('it');
 
4546
                old_himobj = PronounValue('him');
 
4547
                old_herobj = PronounValue('her');
 
4548
                #Endif; ! EnglishNaturalLanguage
 
4549
 
 
4550
              .very__late__error;
 
4551
 
 
4552
                if (score ~= last_score) {
 
4553
                    if (notify_mode == 1) NotifyTheScore();
 
4554
                    last_score = score;
 
4555
                }
 
4556
 
 
4557
              .late__error;
 
4558
 
 
4559
                inputobjs-->0 = 0; inputobjs-->1 = 0;
 
4560
                inputobjs-->2 = 0; inputobjs-->3 = 0; meta=false;
 
4561
 
 
4562
                ! The Parser writes its results into inputobjs and meta,
 
4563
                ! a flag indicating a "meta-verb".  This can only be set for
 
4564
                ! commands by the player, not for orders to others.
 
4565
 
 
4566
                InformParser.parse_input(inputobjs);
 
4567
 
 
4568
                action = inputobjs-->0;
 
4569
 
 
4570
                ! --------------------------------------------------------------
 
4571
 
 
4572
                ! Reverse "give fred biscuit" into "give biscuit to fred"
 
4573
 
 
4574
                if (action == ##GiveR or ##ShowR) {
 
4575
                    i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i;
 
4576
                    if (action == ##GiveR) action = ##Give; else action = ##Show;
 
4577
                }
 
4578
 
 
4579
                ! Convert "P, tell me about X" to "ask P about X"
 
4580
 
 
4581
                if (action == ##Tell && inputobjs-->2 == player && actor ~= player) {
 
4582
                    inputobjs-->2 = actor; actor = player; action = ##Ask;
 
4583
                }
 
4584
 
 
4585
                ! Convert "ask P for X" to "P, give X to me"
 
4586
 
 
4587
                if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) {
 
4588
                    actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3;
 
4589
                    inputobjs-->3 = player; action = ##Give;
 
4590
                }
 
4591
 
 
4592
                ! For old, obsolete code: special_word contains the topic word
 
4593
                ! in conversation
 
4594
 
 
4595
                if (action == ##Ask or ##Tell or ##Answer)
 
4596
                    special_word = special_number1;
 
4597
 
 
4598
                !  --------------------------------------------------------------
 
4599
 
 
4600
                multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false;
 
4601
                ! For implicit taking and multiple object detection
 
4602
 
 
4603
              .begin__action;
 
4604
                inp1 = 0; inp2 = 0; i = inputobjs-->1;
 
4605
                if (i >= 1) inp1 = inputobjs-->2;
 
4606
                if (i >= 2) inp2 = inputobjs-->3;
 
4607
 
 
4608
                ! inp1 and inp2 hold: object numbers, or 0 for "multiple object",
 
4609
                ! or 1 for "a number or dictionary address"
 
4610
 
 
4611
                if (inp1 == 1) noun = special_number1; else noun = inp1;
 
4612
                if (inp2 == 1) {
 
4613
                    if (inp1 == 1) second = special_number2;
 
4614
                    else           second = special_number1;
 
4615
                }
 
4616
                else second = inp2;
 
4617
 
 
4618
                !  --------------------------------------------------------------
 
4619
 
 
4620
                if (actor ~= player) {
 
4621
 
 
4622
                ! The player's "orders" property can refuse to allow conversation
 
4623
                ! here, by returning true.  If not, the order is sent to the
 
4624
                ! other person's "orders" property.  If that also returns false,
 
4625
                ! then: if it was a misunderstood command anyway, it is converted
 
4626
                ! to an Answer action (thus "floyd, grrr" ends up as
 
4627
                ! "say grrr to floyd").  If it was a good command, it is finally
 
4628
                ! offered to the Order: part of the other person's "life"
 
4629
                ! property, the old-fashioned way of dealing with conversation.
 
4630
 
 
4631
                    j = RunRoutines(player, orders);
 
4632
                    if (j == 0) {
 
4633
                        j = RunRoutines(actor, orders);
 
4634
                        if (j == 0) {
 
4635
                            if (action == ##NotUnderstood) {
 
4636
                                inputobjs-->3 = actor; actor = player; action = ##Answer;
 
4637
                                jump begin__action;
 
4638
                            }
 
4639
                            if (RunLife(actor, ##Order) == 0) L__M(##Order, 1, actor);
 
4640
                        }
 
4641
                    }
 
4642
                    jump turn__end;
 
4643
                }
 
4644
 
 
4645
                ! --------------------------------------------------------------
 
4646
                ! Generate the action...
 
4647
 
 
4648
                if ((i == 0) ||
 
4649
                    (i == 1 && inp1 ~= 0) ||
 
4650
                    (i == 2 && inp1 ~= 0 && inp2 ~= 0)) {
 
4651
                    self.begin_action(action, noun, second, 0);
 
4652
                    jump turn__end;
 
4653
                }
 
4654
 
 
4655
                ! ...unless a multiple object must be substituted.  First:
 
4656
                ! (a) check the multiple list isn't empty;
 
4657
                ! (b) warn the player if it has been cut short because too long;
 
4658
                ! (c) generate a sequence of actions from the list
 
4659
                !     (stopping in the event of death or movement away).
 
4660
 
 
4661
                multiflag = true;
 
4662
                j = multiple_object-->0;
 
4663
                if (j == 0) {
 
4664
                    L__M(##Miscellany, 2);
 
4665
                    jump late__error;
 
4666
                }
 
4667
                if (toomany_flag) {
 
4668
                    toomany_flag = false;
 
4669
                    L__M(##Miscellany, 1);
 
4670
                }
 
4671
                i = location;
 
4672
                for (k=1 : k<=j : k++) {
 
4673
                    if (deadflag) break;
 
4674
                    if (location ~= i) {
 
4675
                        L__M(##Miscellany, 51);
 
4676
                        break;
 
4677
                    }
 
4678
                    l = multiple_object-->k;
 
4679
                    PronounNotice(l);
 
4680
                    print (name) l, ": ";
 
4681
                    if (inp1 == 0) {
 
4682
                        inp1 = l; self.begin_action(action, l, second, 0); inp1 = 0;
 
4683
                    }
 
4684
                    else {
 
4685
                        inp2 = l; self.begin_action(action, noun, l, 0); inp2 = 0;
 
4686
                    }
 
4687
                }
 
4688
 
 
4689
                ! --------------------------------------------------------------
 
4690
 
 
4691
              .turn__end;
 
4692
 
 
4693
                ! No time passes if either (i) the verb was meta, or
 
4694
                ! (ii) we've only had the implicit take before the "real"
 
4695
                ! action to follow.
 
4696
 
 
4697
                if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; }
 
4698
                if (meta) continue;
 
4699
                if (~~deadflag) self.end_turn_sequence();
 
4700
                else if (START_MOVE ~= 1) turns++;
 
4701
 
 
4702
            } ! end of while()
 
4703
 
 
4704
            if (deadflag ~= 2) AfterLife();
 
4705
            if (deadflag == 0) jump very__late__error;
 
4706
 
 
4707
            print "^^    ";
 
4708
            #Ifdef TARGET_ZCODE;
 
4709
            #IfV5; style bold; #Endif; ! V5
 
4710
            #Ifnot; ! TARGET_GLULX
 
4711
            glk($0086, 5); ! set alert style
 
4712
            #Endif; ! TARGET_
 
4713
            print "***";
 
4714
            if (deadflag == 1) L__M(##Miscellany, 3);
 
4715
            if (deadflag == 2) L__M(##Miscellany, 4);
 
4716
            if (deadflag > 2)  {
 
4717
                print " ";
 
4718
                DeathMessage();
 
4719
                print " ";
 
4720
            }
 
4721
            print "***";
 
4722
            #Ifdef TARGET_ZCODE;
 
4723
            #IfV5; style roman; #Endif; ! V5
 
4724
            #Ifnot; ! TARGET_GLULX
 
4725
            glk($0086, 0); ! set normal style
 
4726
            #Endif; ! TARGET_
 
4727
            #Ifdef NO_SCORE;
 
4728
            print "^^";
 
4729
            #Ifnot;
 
4730
            print "^^^";
 
4731
            #Endif; ! NO_SCORE
 
4732
            ScoreSub();
 
4733
            DisplayStatus();
 
4734
            AfterGameOver();
 
4735
 
 
4736
        ], ! end of 'play' property
 
4737
 
 
4738
        end_turn_sequence [;
 
4739
            AdvanceWorldClock();
 
4740
            if (deadflag) return;
 
4741
            RunTimersAndDaemons();
 
4742
            if (deadflag) return;
 
4743
            RunEachTurnProperties();
 
4744
            if (deadflag) return;
 
4745
            TimePasses();
 
4746
            if (deadflag) return;
 
4747
            AdjustLight();
 
4748
            if (deadflag) return;
 
4749
            NoteObjectAcquisitions();
 
4750
        ],
 
4751
 
 
4752
        begin_action [ a n s source   sa sn ss;
 
4753
            sa = action; sn = noun; ss = second;
 
4754
            action = a; noun = n; second = s;
 
4755
            #Ifdef DEBUG;
 
4756
            if (debug_flag & 2 ~= 0) TraceAction(source);
 
4757
            #Ifnot;
 
4758
            source = 0;
 
4759
            #Endif; ! DEBUG
 
4760
            #Iftrue (Grammar__Version == 1);
 
4761
            if ((meta || BeforeRoutines() == false) && action < 256)
 
4762
                ActionPrimitive();
 
4763
            #Ifnot;
 
4764
            if ((meta || BeforeRoutines() == false) && action < 4096)
 
4765
                ActionPrimitive();
 
4766
            #Endif; ! Grammar__Version
 
4767
            action = sa; noun = sn; second = ss;
 
4768
        ],
 
4769
  has   proper;
 
4770
 
 
4771
[ AdvanceWorldClock;
 
4772
    turns++;
 
4773
    if (the_time ~= NULL) {
 
4774
        if (time_rate >= 0) the_time=the_time+time_rate;
 
4775
        else {
 
4776
            time_step--;
 
4777
            if (time_step == 0) {
 
4778
                the_time++;
 
4779
                time_step = -time_rate;
 
4780
            }
 
4781
        }
 
4782
        the_time = the_time % 1440;
 
4783
    }
 
4784
];
 
4785
 
 
4786
[ RunTimersAndDaemons i j;
 
4787
    #Ifdef DEBUG;
 
4788
    if (debug_flag & 4 ~= 0) {
 
4789
        for (i=0 : i<active_timers : i++) {
 
4790
            j = the_timers-->i;
 
4791
            if (j ~= 0) {
 
4792
                print (name) (j&~WORD_HIGHBIT), ": ";
 
4793
                if (j & WORD_HIGHBIT) print "daemon";
 
4794
                else
 
4795
                    print "timer with ", j.time_left, " turns to go";
 
4796
                new_line;
 
4797
            }
 
4798
        }
 
4799
    }
 
4800
    #Endif; ! DEBUG
 
4801
 
 
4802
    for (i=0 : i<active_timers : i++) {
 
4803
        if (deadflag) return;
 
4804
        j = the_timers-->i;
 
4805
        if (j ~= 0) {
 
4806
            if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon);
 
4807
            else {
 
4808
                if (j.time_left == 0) {
 
4809
                    StopTimer(j);
 
4810
                    RunRoutines(j, time_out);
 
4811
                }
 
4812
                else
 
4813
                    j.time_left = j.time_left-1;
 
4814
            }
 
4815
        }
 
4816
    }
 
4817
];
 
4818
 
 
4819
[ RunEachTurnProperties;
 
4820
    scope_reason = EACH_TURN_REASON; verb_word = 0;
 
4821
    DoScopeAction(location);
 
4822
    SearchScope(ScopeCeiling(player), player, 0);
 
4823
    scope_reason = PARSING_REASON;
 
4824
];
 
4825
 
 
4826
#Ifdef TARGET_ZCODE;
 
4827
 
 
4828
[ ActionPrimitive; indirect(#actions_table-->action); ];
 
4829
 
 
4830
#Ifnot; ! TARGET_GLULX
 
4831
 
 
4832
[ ActionPrimitive; indirect(#actions_table-->(action+1)); ];
 
4833
 
 
4834
#Endif; ! TARGET_
 
4835
 
 
4836
[ AfterGameOver i;
 
4837
 
 
4838
  .RRQPL;
 
4839
 
 
4840
    L__M(##Miscellany,5);
 
4841
 
 
4842
  .RRQL;
 
4843
 
 
4844
    print "> ";
 
4845
    #Ifdef TARGET_ZCODE;
 
4846
    #IfV3; read buffer parse; #Endif; ! V3
 
4847
    temp_global=0;
 
4848
    #IfV5; read buffer parse DrawStatusLine; #Endif; ! V5
 
4849
    #Ifnot; ! TARGET_GLULX
 
4850
    KeyboardPrimitive(buffer, parse);
 
4851
    #Endif; ! TARGET_
 
4852
    i = parse-->1;
 
4853
    if (i == QUIT1__WD or QUIT2__WD) {
 
4854
        #Ifdef TARGET_ZCODE;
 
4855
        quit;
 
4856
        #Ifnot; ! TARGET_GLULX
 
4857
        quit;
 
4858
        #Endif; ! TARGET_
 
4859
    }
 
4860
    if (i == RESTART__WD) {
 
4861
        #Ifdef TARGET_ZCODE;
 
4862
        @restart;
 
4863
        #Ifnot; ! TARGET_GLULX
 
4864
        @restart;
 
4865
        #Endif; ! TARGET_
 
4866
    }
 
4867
    if (i == RESTORE__WD) {
 
4868
        RestoreSub();
 
4869
        jump RRQPL;
 
4870
    }
 
4871
    if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) {
 
4872
        new_line; FullScoreSub();
 
4873
        jump RRQPL;
 
4874
    }
 
4875
    if (deadflag == 2 && i == AMUSING__WD && AMUSING_PROVIDED==0) {
 
4876
        new_line; Amusing();
 
4877
        jump RRQPL;
 
4878
    }
 
4879
    #IfV5;
 
4880
    if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) {
 
4881
        if (undo_flag == 0) {
 
4882
            L__M(##Miscellany, 6);
 
4883
            jump RRQPL;
 
4884
        }
 
4885
        if (undo_flag == 1) jump UndoFailed2;
 
4886
        #Ifdef TARGET_ZCODE;
 
4887
        @restore_undo i;
 
4888
        #Ifnot; ! TARGET_GLULX
 
4889
        @restoreundo i;
 
4890
        i = (~~i);
 
4891
        #Endif; ! TARGET_
 
4892
        if (i == 0) {
 
4893
          .UndoFailed2;
 
4894
            L__M(##Miscellany, 7);
 
4895
        }
 
4896
        jump RRQPL;
 
4897
    }
 
4898
    #Endif; ! V5
 
4899
    L__M(##Miscellany, 8);
 
4900
    jump RRQL;
 
4901
];
 
4902
 
 
4903
[ R_Process a i j s1 s2;
 
4904
    s1 = inp1; s2 = inp2;
 
4905
    inp1 = i; inp2 = j; InformLibrary.begin_action(a, i, j, 1);
 
4906
    inp1 = s1; inp2 = s2;
 
4907
];
 
4908
 
 
4909
[ NoteObjectAcquisitions i;
 
4910
    objectloop (i in player)
 
4911
        if (i hasnt moved) {
 
4912
            give i moved;
 
4913
            if (i has scored) {
 
4914
                score = score + OBJECT_SCORE;
 
4915
                things_score = things_score + OBJECT_SCORE;
 
4916
            }
 
4917
        }
 
4918
];
 
4919
 
 
4920
! ----------------------------------------------------------------------------
 
4921
 
 
4922
[ TestScope obj act a al sr x y;
 
4923
    x = parser_one; y = parser_two;
 
4924
    parser_one = obj; parser_two = 0; a = actor; al = actors_location;
 
4925
    sr = scope_reason; scope_reason = TESTSCOPE_REASON;
 
4926
    if (act == 0) actor = player; else actor = act;
 
4927
    actors_location = ScopeCeiling(actor);
 
4928
    SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a;
 
4929
    actors_location = al; parser_one = x; x = parser_two; parser_two = y;
 
4930
    return x;
 
4931
];
 
4932
 
 
4933
[ LoopOverScope routine act x y a al;
 
4934
    x = parser_one; y = scope_reason; a = actor; al = actors_location;
 
4935
    parser_one = routine;
 
4936
    if (act == 0) actor = player; else actor = act;
 
4937
    actors_location = ScopeCeiling(actor);
 
4938
    scope_reason = LOOPOVERSCOPE_REASON;
 
4939
    SearchScope(actors_location, actor, 0);
 
4940
    parser_one = x; scope_reason = y; actor = a; actors_location = al;
 
4941
];
 
4942
 
 
4943
[ BeforeRoutines;
 
4944
    if (GamePreRoutine() ~= 0) rtrue;
 
4945
    if (RunRoutines(player, orders) ~= 0) rtrue;
 
4946
    scope_reason = REACT_BEFORE_REASON; parser_one=0;
 
4947
    SearchScope(ScopeCeiling(player), player, 0);
 
4948
    scope_reason = PARSING_REASON;
 
4949
    if (parser_one ~= 0) rtrue;
 
4950
    if (location ~= 0 && RunRoutines(location, before) ~= 0) rtrue;
 
4951
    if (inp1 > 1 && RunRoutines(inp1, before) ~= 0) rtrue;
 
4952
    rfalse;
 
4953
];
 
4954
 
 
4955
[ AfterRoutines;
 
4956
    scope_reason = REACT_AFTER_REASON; parser_one = 0;
 
4957
    SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON;
 
4958
    if (parser_one ~= 0) rtrue;
 
4959
    if (location ~= 0 && RunRoutines(location, after) ~= 0) rtrue;
 
4960
    if (inp1 > 1 && RunRoutines(inp1, after) ~= 0) rtrue;
 
4961
    return GamePostRoutine();
 
4962
];
 
4963
 
 
4964
[ RunLife a j;
 
4965
    #Ifdef DEBUG;
 
4966
    if (debug_flag & 2 ~= 0) TraceAction(2, j);
 
4967
    #Endif; ! DEBUG
 
4968
    reason_code = j; return RunRoutines(a,life);
 
4969
];
 
4970
 
 
4971
[ ZRegion addr;
 
4972
    switch (metaclass(addr)) {      ! Left over from Inform 5
 
4973
      nothing:          return 0;
 
4974
      Object, Class:    return 1;
 
4975
      Routine:          return 2;
 
4976
      String:           return 3;
 
4977
    }
 
4978
];
 
4979
 
 
4980
[ PrintOrRun obj prop flag;
 
4981
    if (obj.#prop > WORDSIZE) return RunRoutines(obj,prop);
 
4982
    if (obj.prop == NULL) rfalse;
 
4983
    switch (metaclass(obj.prop)) {
 
4984
      Class, Object, nothing:
 
4985
        return RunTimeError(2,obj,prop);
 
4986
      String:
 
4987
        print (string) obj.prop;
 
4988
        if (flag == 0) new_line;
 
4989
        rtrue;
 
4990
      Routine:
 
4991
        return RunRoutines(obj,prop);
 
4992
    }
 
4993
];
 
4994
 
 
4995
[ PrintOrRunVar var flag;
 
4996
    switch (metaclass(var)) {
 
4997
      Object:
 
4998
        print (name) var;
 
4999
      String:
 
5000
        print (string) var;
 
5001
        if (flag == 0) new_line;
 
5002
      Routine:
 
5003
        return var();
 
5004
      default:
 
5005
        print (char) '(', var, (char) ')';
 
5006
    }
 
5007
    rtrue;
 
5008
];
 
5009
 
 
5010
[ ValueOrRun obj prop;
 
5011
  !### this is entirely unlikely to work. Does anyone care? (AP)
 
5012
  ! Well, it's certainly used three times in verblibm.h (RF)
 
5013
    if (obj.prop < 256) return obj.prop;
 
5014
    return RunRoutines(obj, prop);
 
5015
];
 
5016
 
 
5017
[ RunRoutines obj prop;
 
5018
    if (obj == thedark && prop ~= initial or short_name or description) obj = real_location;
 
5019
    if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse;
 
5020
    return obj.prop();
 
5021
];
 
5022
 
 
5023
#Ifdef TARGET_ZCODE;
 
5024
 
 
5025
[ ChangeDefault prop val a b;
 
5026
    ! Use assembly-language here because -S compilation won't allow this:
 
5027
    @loadw 0 5 -> a;
 
5028
    b = prop-1;
 
5029
    @storew a b val;
 
5030
];
 
5031
 
 
5032
#Ifnot; ! TARGET_GLULX
 
5033
 
 
5034
[ ChangeDefault prop val;
 
5035
    ! Use assembly-language here because -S compilation won't allow this:
 
5036
    ! #cpv__start-->prop = val;
 
5037
    @astore #cpv__start prop val;
 
5038
];
 
5039
 
 
5040
#Endif; ! TARGET_
 
5041
 
 
5042
! ----------------------------------------------------------------------------
 
5043
 
 
5044
[ StartTimer obj timer i;
 
5045
    for (i=0 : i<active_timers : i++)
 
5046
        if (the_timers-->i == obj) rfalse;
 
5047
    for (i=0 : i<active_timers : i++)
 
5048
        if (the_timers-->i == 0) jump FoundTSlot;
 
5049
    i = active_timers++;
 
5050
    if (i >= MAX_TIMERS) { RunTimeError(4); return; }
 
5051
  .FoundTSlot;
 
5052
    if (obj.&time_left == 0) { RunTimeError(5,obj); return; }
 
5053
    the_timers-->i = obj; obj.time_left = timer;
 
5054
];
 
5055
 
 
5056
[ StopTimer obj i;
 
5057
    for (i=0 : i<active_timers : i++)
 
5058
        if (the_timers-->i == obj) jump FoundTSlot2;
 
5059
    rfalse;
 
5060
  .FoundTSlot2;
 
5061
    if (obj.&time_left == 0) { RunTimeError(5,obj); return; }
 
5062
    the_timers-->i = 0; obj.time_left = 0;
 
5063
];
 
5064
 
 
5065
[ StartDaemon obj i;
 
5066
    for (i=0 : i<active_timers : i++)
 
5067
        if (the_timers-->i == WORD_HIGHBIT + obj) rfalse;
 
5068
    for (i=0 : i<active_timers : i++)
 
5069
        if (the_timers-->i == 0) jump FoundTSlot3;
 
5070
    i = active_timers++;
 
5071
    if (i >= MAX_TIMERS) RunTimeError(4);
 
5072
  .FoundTSlot3;
 
5073
    the_timers-->i = WORD_HIGHBIT + obj;
 
5074
];
 
5075
 
 
5076
[ StopDaemon obj i;
 
5077
    for (i=0 : i<active_timers : i++)
 
5078
        if (the_timers-->i == WORD_HIGHBIT + obj) jump FoundTSlot4;
 
5079
    rfalse;
 
5080
  .FoundTSlot4;
 
5081
    the_timers-->i = 0;
 
5082
];
 
5083
 
 
5084
! ----------------------------------------------------------------------------
 
5085
 
 
5086
[ DisplayStatus;
 
5087
    if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; }
 
5088
    else { sline1 = the_time/60; sline2 = the_time%60;}
 
5089
];
 
5090
 
 
5091
[ SetTime t s;
 
5092
    the_time = t; time_rate = s; time_step = 0;
 
5093
    if (s < 0) time_step = 0-s;
 
5094
];
 
5095
 
 
5096
[ NotifyTheScore;
 
5097
    #Ifdef TARGET_GLULX;
 
5098
    glk($0086, 6); ! set note style
 
5099
    #Endif; ! TARGET_GLULX
 
5100
    print "^[";  L__M(##Miscellany, 50, score-last_score);  print ".]^";
 
5101
    #Ifdef TARGET_GLULX;
 
5102
    glk($0086, 0); ! set normal style
 
5103
    #Endif; ! TARGET_GLULX
 
5104
];
 
5105
 
 
5106
! ----------------------------------------------------------------------------
 
5107
 
 
5108
[ AdjustLight flag i;
 
5109
    i = lightflag;
 
5110
    lightflag = OffersLight(parent(player));
 
5111
 
 
5112
    if (i == 0 && lightflag == 1) {
 
5113
        location = real_location;
 
5114
        if (flag == 0) <Look>;
 
5115
    }
 
5116
 
 
5117
    if (i == 1 && lightflag == 0) {
 
5118
        real_location = location; location = thedark;
 
5119
        if (flag == 0) {
 
5120
            NoteArrival();
 
5121
            return L__M(##Miscellany, 9);
 
5122
        }
 
5123
    }
 
5124
    if (i == 0 && lightflag == 0) location=thedark;
 
5125
];
 
5126
 
 
5127
[ OffersLight i j;
 
5128
    if (i == 0) rfalse;
 
5129
    if (i has light) rtrue;
 
5130
    objectloop (j in i)
 
5131
        if (HasLightSource(j) == 1) rtrue;
 
5132
    if (i has container) {
 
5133
        if (i has open || i has transparent)
 
5134
            return OffersLight(parent(i));
 
5135
    }
 
5136
    else {
 
5137
        if (i has enterable || i has transparent || i has supporter)
 
5138
            return OffersLight(parent(i));
 
5139
    }
 
5140
    rfalse;
 
5141
];
 
5142
 
 
5143
[ HidesLightSource obj;
 
5144
    if (obj == player) rfalse;
 
5145
    if (obj has transparent or supporter) rfalse;
 
5146
    if (obj has container) return (obj hasnt open);
 
5147
    return (obj hasnt enterable);
 
5148
];
 
5149
 
 
5150
[ HasLightSource i j ad;
 
5151
    if (i == 0) rfalse;
 
5152
    if (i has light) rtrue;
 
5153
    if (i has enterable || IsSeeThrough(i) == 1)
 
5154
        if (~~(HidesLightSource(i)))
 
5155
            objectloop (j in i)
 
5156
                if (HasLightSource(j) == 1) rtrue;
 
5157
    ad = i.&add_to_scope;
 
5158
    if (parent(i) ~= 0 && ad ~= 0) {
 
5159
        if (metaclass(ad-->0) == Routine) {
 
5160
            ats_hls = 0; ats_flag = 1;
 
5161
            RunRoutines(i, add_to_scope);
 
5162
            ats_flag = 0; if (ats_hls == 1) rtrue;
 
5163
        }
 
5164
        else {
 
5165
            for (j=0 : (WORDSIZE*j)<i.#add_to_scope : j++)
 
5166
                if (HasLightSource(ad-->j) == 1) rtrue;
 
5167
        }
 
5168
    }
 
5169
    rfalse;
 
5170
];
 
5171
 
 
5172
[ ChangePlayer obj flag i;
 
5173
!   if (obj.&number == 0) return RunTimeError(7,obj);
 
5174
    if (actor == player) actor=obj;
 
5175
    give player ~transparent ~concealed;
 
5176
    i = obj; while (parent(i) ~= 0) {
 
5177
        if (i has animate) give i transparent;
 
5178
        i = parent(i);
 
5179
    }
 
5180
    if (player == selfobj) player.short_name = FORMER__TX;
 
5181
 
 
5182
    player = obj;
 
5183
 
 
5184
    if (player == selfobj) player.short_name = NULL;
 
5185
    give player transparent concealed animate proper;
 
5186
    i = player; while (parent(i) ~= 0) i = parent(i);
 
5187
    location = i; real_location = location;
 
5188
    if (parent(player) == 0) return RunTimeError(10);
 
5189
    MoveFloatingObjects();
 
5190
    lightflag = OffersLight(parent(player));
 
5191
    if (lightflag == 0) location = thedark;
 
5192
    print_player_flag = flag;
 
5193
];
 
5194
 
 
5195
! ----------------------------------------------------------------------------
 
5196
 
 
5197
#Ifdef DEBUG;
 
5198
 
 
5199
#Ifdef TARGET_ZCODE;
 
5200
 
 
5201
[ DebugParameter w;
 
5202
    print w;
 
5203
    if (w >= 1 && w <= top_object) print " (", (name) w, ")";
 
5204
    if (UnsignedCompare(w, dict_start) >= 0 &&
 
5205
            UnsignedCompare(w, dict_end) < 0 &&
 
5206
            (w - dict_start) % dict_entry_size == 0)
 
5207
        print " ('", (address) w, "')";
 
5208
];
 
5209
 
 
5210
[ DebugAction a anames;
 
5211
    #Iftrue (Grammar__Version == 1);
 
5212
    if (a >= 256) { print "<fake action ", a-256, ">"; return; }
 
5213
    #Ifnot;
 
5214
    if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
 
5215
    #Endif; ! Grammar__Version
 
5216
    anames = #identifiers_table;
 
5217
    anames = anames + 2*(anames-->0) + 2*48;
 
5218
    print (string) anames-->a;
 
5219
];
 
5220
 
 
5221
[ DebugAttribute a anames;
 
5222
    if (a < 0 || a >= 48) print "<invalid attribute ", a, ">";
 
5223
    else {
 
5224
        anames = #identifiers_table; anames = anames + 2*(anames-->0);
 
5225
        print (string) anames-->a;
 
5226
    }
 
5227
];
 
5228
 
 
5229
#Ifnot; ! TARGET_GLULX
 
5230
 
 
5231
[ DebugParameter w endmem;
 
5232
    print w;
 
5233
    @getmemsize endmem;
 
5234
    if (w >= 1 && w < endmem) {
 
5235
        if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")";
 
5236
        if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')";
 
5237
    }
 
5238
];
 
5239
 
 
5240
[ DebugAction a str;
 
5241
    if (a >= 4096) { print "<fake action ", a-4096, ">"; return; }
 
5242
    if (a < 0 || a >= #identifiers_table-->7) print "<invalid action ", a, ">";
 
5243
    else {
 
5244
        str = #identifiers_table-->6;
 
5245
        str = str-->a;
 
5246
        if (str) print (string) str; else print "<unnamed action ", a, ">";
 
5247
    }
 
5248
];
 
5249
 
 
5250
[ DebugAttribute a str;
 
5251
    if (a < 0 || a >= NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">";
 
5252
    else {
 
5253
        str = #identifiers_table-->4;
 
5254
        str = str-->a;
 
5255
        if (str) print (string) str; else print "<unnamed attribute ", a, ">";
 
5256
    }
 
5257
];
 
5258
 
 
5259
#Endif; ! TARGET_
 
5260
 
 
5261
[ TraceAction source ar;
 
5262
    if (source < 2) print "[ Action ", (DebugAction) action;
 
5263
    else {
 
5264
        if (ar == ##Order)
 
5265
            print "[ Order to ", (name) actor, ": ", (DebugAction) action;
 
5266
        else
 
5267
            print "[ Life rule ", (DebugAction) ar;
 
5268
    }
 
5269
    if (noun ~= 0)   print " with noun ", (DebugParameter) noun;
 
5270
    if (second ~= 0) print " and second ", (DebugParameter) second;
 
5271
    if (source == 0) print " ";
 
5272
    if (source == 1) print " (from < > statement) ";
 
5273
    print "]^";
 
5274
];
 
5275
 
 
5276
[ DebugToken token;
 
5277
    AnalyseToken(token);
 
5278
    switch (found_ttype) {
 
5279
      ILLEGAL_TT:
 
5280
        print "<illegal token number ", token, ">";
 
5281
      ELEMENTARY_TT:
 
5282
        switch (found_tdata) {
 
5283
          NOUN_TOKEN:           print "noun";
 
5284
          HELD_TOKEN:           print "held";
 
5285
          MULTI_TOKEN:          print "multi";
 
5286
          MULTIHELD_TOKEN:      print "multiheld";
 
5287
          MULTIEXCEPT_TOKEN:    print "multiexcept";
 
5288
          MULTIINSIDE_TOKEN:    print "multiinside";
 
5289
          CREATURE_TOKEN:       print "creature";
 
5290
          SPECIAL_TOKEN:        print "special";
 
5291
          NUMBER_TOKEN:         print "number";
 
5292
          TOPIC_TOKEN:          print "topic";
 
5293
          ENDIT_TOKEN:          print "END";
 
5294
        }
 
5295
      PREPOSITION_TT:
 
5296
        print "'", (address) found_tdata, "'";
 
5297
      ROUTINE_FILTER_TT:
 
5298
        #Ifdef INFIX;
 
5299
        print "noun=", (InfixPrintPA) found_tdata;
 
5300
        #Ifnot;
 
5301
        print "noun=Routine(", found_tdata, ")";
 
5302
        #Endif; ! INFIX
 
5303
      ATTR_FILTER_TT:
 
5304
        print (DebugAttribute) found_tdata;
 
5305
      SCOPE_TT:
 
5306
        #Ifdef INFIX;
 
5307
        print "scope=", (InfixPrintPA) found_tdata;
 
5308
        #Ifnot;
 
5309
        print "scope=Routine(", found_tdata, ")";
 
5310
        #Endif; ! INFIX
 
5311
      GPR_TT:
 
5312
        #Ifdef INFIX;
 
5313
        print (InfixPrintPA) found_tdata;
 
5314
        #Ifnot;
 
5315
        print "Routine(", found_tdata, ")";
 
5316
        #Endif; ! INFIX
 
5317
    }
 
5318
];
 
5319
 
 
5320
[ DebugGrammarLine pcount;
 
5321
    print " * ";
 
5322
    for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) {
 
5323
        if ((line_token-->pcount)->0 & $10) print "/ ";
 
5324
        print (DebugToken) line_token-->pcount, " ";
 
5325
    }
 
5326
    print "-> ", (DebugAction) action_to_be;
 
5327
    if (action_reversed) print " reverse";
 
5328
];
 
5329
 
 
5330
#Ifdef TARGET_ZCODE;
 
5331
 
 
5332
[ ShowVerbSub address lines da meta i j;
 
5333
    if (noun == 0 || ((noun->#dict_par1) & 1) == 0)
 
5334
        "Try typing ~showverb~ and then the name of a verb.";
 
5335
    meta = ((noun->#dict_par1) & 2)/2;
 
5336
    i = $ff-(noun->#dict_par2);
 
5337
    address = (HDR_STATICMEMORY-->0)-->i;
 
5338
    lines = address->0;
 
5339
    address++;
 
5340
    print "Verb ";
 
5341
    if (meta) print "meta ";
 
5342
    da = HDR_DICTIONARY-->0;
 
5343
    for (j=0 : j<(da+5)-->0 : j++)
 
5344
        if (da->(j*9 + 14) == $ff-i) print "'", (address) (da + 9*j + 7), "' ";
 
5345
    new_line;
 
5346
    if (lines == 0) "has no grammar lines.";
 
5347
    for (: lines>0 : lines--) {
 
5348
        address = UnpackGrammarLine(address);
 
5349
        print "    "; DebugGrammarLine(); new_line;
 
5350
    }
 
5351
];
 
5352
 
 
5353
#Ifnot; ! TARGET_GLULX
 
5354
 
 
5355
[ ShowVerbSub address lines i j meta wd dictlen entrylen;
 
5356
    if (noun == 0 || ((noun->#dict_par1) & 1) == 0)
 
5357
        "Try typing ~showverb~ and then the name of a verb.";
 
5358
    meta = ((noun->#dict_par1) & 2)/2;
 
5359
    i = $ff-(noun->#dict_par2);
 
5360
    address = (#grammar_table)-->(i+1);
 
5361
    lines = address->0;
 
5362
    address++;
 
5363
    print "Verb ";
 
5364
    if (meta) print "meta ";
 
5365
    dictlen = #dictionary_table-->0;
 
5366
    entrylen = DICT_WORD_SIZE + 7;
 
5367
    for (j=0 : j<dictlen : j++) {
 
5368
        wd = #dictionary_table + WORDSIZE + entrylen*j;
 
5369
        if (wd->#dict_par2 == $ff-i)
 
5370
            print "'", (address) wd, "' ";
 
5371
    }
 
5372
    new_line;
 
5373
    if (lines == 0) "has no grammar lines.";
 
5374
    for (: lines>0 : lines--) {
 
5375
        address = UnpackGrammarLine(address);
 
5376
        print "    "; DebugGrammarLine(); new_line;
 
5377
    }
 
5378
];
 
5379
 
 
5380
#Endif; ! TARGET_
 
5381
 
 
5382
[ ShowobjSub c f l a n x numattr;
 
5383
    if (noun == 0) noun = location;
 
5384
    objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
 
5385
    if (f == 1) print (name) l, " ~"; else print "Object ~";
 
5386
    print (name) noun, "~ (", noun, ")";
 
5387
    if (parent(noun) ~= 0) print " in ~", (name) parent(noun), "~";
 
5388
    new_line;
 
5389
    if (f > 1) {
 
5390
        print "  class ";
 
5391
        objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
 
5392
        new_line;
 
5393
    }
 
5394
    #Ifdef TARGET_ZCODE;
 
5395
    numattr = 48;
 
5396
    #Ifnot; ! TARGET_GLULX
 
5397
    numattr = NUM_ATTR_BYTES * 8;
 
5398
    #Endif; ! TARGET_
 
5399
    for (a=0,f=0 : a<numattr : a++) if (noun has a) f=1;
 
5400
    if (f) {
 
5401
        print "  has ";
 
5402
        for (a=0 : a<numattr : a++) if (noun has a) print (DebugAttribute) a, " ";
 
5403
        new_line;
 
5404
    }
 
5405
    if (noun ofclass Class) return;
 
5406
 
 
5407
    f=0;
 
5408
    #Ifdef TARGET_ZCODE;
 
5409
    l = #identifiers_table-->0;
 
5410
    #Ifnot; ! TARGET_GLULX
 
5411
    l = INDIV_PROP_START + #identifiers_table-->3;
 
5412
    #Endif; ! TARGET_
 
5413
    for (a=1 : a<=l : a++) {
 
5414
        if ((a ~= 2 or 3) && noun.&a) {
 
5415
            if (f == 0) { print "  with "; f=1; }
 
5416
            print (property) a;
 
5417
            n = noun.#a;
 
5418
            for (c=0 : WORDSIZE*c<n : c++) {
 
5419
                print " ";
 
5420
                x = (noun.&a)-->c;
 
5421
                if (a == name) print "'", (address) x, "'";
 
5422
                else {
 
5423
                    if (a == number or capacity or time_left) print x;
 
5424
                    else {
 
5425
                        switch (x) {
 
5426
                          NULL: print "NULL";
 
5427
                          0:    print "0";
 
5428
                          1:    print "1";
 
5429
                          default:
 
5430
                            switch (metaclass(x)) {
 
5431
                              Class, Object:
 
5432
                                print (name) x;
 
5433
                              String:
 
5434
                                print "~", (string) x, "~";
 
5435
                              Routine:
 
5436
                                print "[...]";
 
5437
                           }
 
5438
                           print " (", x, ")";
 
5439
                        }
 
5440
                    }
 
5441
                }
 
5442
            }
 
5443
            print ",^       ";
 
5444
        }
 
5445
    }
 
5446
!   if (f==1) new_line;
 
5447
];
 
5448
 
 
5449
#Endif; ! DEBUG
 
5450
 
 
5451
! ----------------------------------------------------------------------------
 
5452
!  Miscellaneous display routines used by DrawStatusLine and available for
 
5453
!  user.  Most of these vary according to which machine is being compiled to
 
5454
! ----------------------------------------------------------------------------
 
5455
 
 
5456
#Ifdef TARGET_ZCODE;
 
5457
 
 
5458
[ ClearScreen window;
 
5459
    switch (window) {
 
5460
      WIN_ALL:    @erase_window -1;
 
5461
      WIN_STATUS: @erase_window 1;
 
5462
      WIN_MAIN:   @erase_window 0;
 
5463
    }
 
5464
];
 
5465
 
 
5466
#Iftrue (#version_number == 6);
 
5467
[ MoveCursorV6 line column  charw;  ! 1-based postion on text grid
 
5468
    @get_wind_prop 1 13 -> charw; ! font size
 
5469
    charw = charw & $FF;
 
5470
    line = 1 + charw*(line-1);
 
5471
    column = 1 + charw*(column-1);
 
5472
    @set_cursor line column;
 
5473
];
 
5474
#Endif;
 
5475
 
 
5476
#Ifndef MoveCursor;
 
5477
[ MoveCursor line column;  ! 1-based postion on text grid
 
5478
    if (~~statuswin_current) {
 
5479
         @set_window 1;
 
5480
         if (clr_on && clr_bgstatus > 1) @set_colour clr_fgstatus clr_bgstatus;
 
5481
         else                            style reverse;
 
5482
    }
 
5483
    if (line == 0) {
 
5484
        line = 1;
 
5485
        column = 1;
 
5486
    }
 
5487
    #Iftrue (#version_number == 6);
 
5488
    MoveCursorV6(line, column);
 
5489
    #Ifnot;
 
5490
    @set_cursor line column;
 
5491
    #Endif;
 
5492
statuswin_current = true;
 
5493
];
 
5494
#Endif;
 
5495
 
 
5496
[ MainWindow;
 
5497
    if (statuswin_current) {
 
5498
        if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg;
 
5499
        else                            style roman;
 
5500
        @set_window 0;
 
5501
        }
 
5502
    statuswin_current = false;
 
5503
];
 
5504
 
 
5505
#Iftrue (#version_number == 6);
 
5506
[ ScreenWidth  width charw;
 
5507
    @get_wind_prop 1 3 -> width;
 
5508
    @get_wind_prop 1 13 -> charw;
 
5509
    charw = charw & $FF;
 
5510
    return (width+charw-1) / charw;
 
5511
];
 
5512
#Ifnot;
 
5513
[ ScreenWidth;
 
5514
    return (HDR_SCREENWCHARS->0);
 
5515
];
 
5516
#Endif;
 
5517
 
 
5518
[ ScreenHeight;
 
5519
    return (HDR_SCREENHLINES->0);
 
5520
];
 
5521
 
 
5522
#Iftrue (#version_number == 6);
 
5523
[ StatusLineHeight height  wx wy x y charh;
 
5524
    ! Split the window. Standard 1.0 interpreters should keep the window 0
 
5525
    ! cursor in the same absolute position, but older interpreters,
 
5526
    ! including Infocom's don't - they keep the window 0 cursor in the
 
5527
    ! same position relative to its origin. We therefore compensate
 
5528
    ! manually.
 
5529
    @get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx;
 
5530
    @get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh;
 
5531
    @get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x;
 
5532
    height = height * charh;
 
5533
    @split_window height;
 
5534
    y = y - height + wy - 1;
 
5535
    if (y < 1) y = 1;
 
5536
    x = x + wx - 1;
 
5537
    @set_cursor y x 0;
 
5538
    gg_statuswin_cursize = height;
 
5539
];
 
5540
#Ifnot;
 
5541
[ StatusLineHeight height;
 
5542
    if (gg_statuswin_cursize ~= height)
 
5543
        @split_window height;
 
5544
    gg_statuswin_cursize = height;
 
5545
];
 
5546
#Endif;
 
5547
 
 
5548
[ SetColour f b window;
 
5549
    if (clr_on && f && b) {
 
5550
        if (window == 0) {  ! if setting both together, set reverse
 
5551
            clr_fgstatus = b;
 
5552
            clr_bgstatus = f;
 
5553
            }
 
5554
        if (window == 1) {
 
5555
            clr_fgstatus = f;
 
5556
            clr_bgstatus = b;
 
5557
        }
 
5558
        if (window == 0 or 2) {
 
5559
            clr_fg = f;
 
5560
            clr_bg = b;
 
5561
        }
 
5562
        if (statuswin_current)
 
5563
            @set_colour clr_fgstatus clr_bgstatus;
 
5564
        else
 
5565
            @set_colour clr_fg clr_bg;
 
5566
    }
 
5567
];
 
5568
 
 
5569
 
 
5570
#Ifnot; ! TARGET_GLULX
 
5571
 
 
5572
[ ClearScreen window;
 
5573
    if (window == WIN_ALL or WIN_MAIN) {
 
5574
        glk($002A, gg_mainwin);
 
5575
        if (gg_quotewin) {
 
5576
            glk($0024, gg_quotewin, 0); ! close_window
 
5577
            gg_quotewin = 0;
 
5578
        }
 
5579
    }
 
5580
    if (gg_statuswin && window == WIN_ALL or WIN_STATUS) glk($002A, gg_statuswin);
 
5581
];
 
5582
 
 
5583
[ MoveCursor line column;  ! 0-based postion on text grid
 
5584
    if (gg_statuswin) {
 
5585
        glk($002F, gg_statuswin); ! set_window
 
5586
    }
 
5587
    if (line == 0) {
 
5588
        line = 1;
 
5589
        column = 1;
 
5590
    }
 
5591
    glk($002B, gg_statuswin, column-1, line-1); ! window_move_cursor
 
5592
    statuswin_current=1;
 
5593
];
 
5594
 
 
5595
[ MainWindow;
 
5596
    glk($002F, gg_mainwin); ! set_window
 
5597
    statuswin_current=0;
 
5598
];
 
5599
 
 
5600
[ MakeColourWord c;
 
5601
    if (c > 9) return c;
 
5602
    c = c-2;
 
5603
    return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0);
 
5604
];
 
5605
 
 
5606
[ ScreenWidth  id;
 
5607
    id=gg_mainwin;
 
5608
    if (gg_statuswin && statuswin_current) id = gg_statuswin;
 
5609
    glk($0025, id, gg_arguments, 0); ! window_get_size
 
5610
    return gg_arguments-->0;
 
5611
];
 
5612
 
 
5613
[ ScreenHeight;
 
5614
    glk($0025, gg_mainwin, 0, gg_arguments); ! window_get_size
 
5615
    return gg_arguments-->0;
 
5616
];
 
5617
 
 
5618
[ SetColour f b window doclear  i fwd bwd swin;
 
5619
    if (clr_on && f && b) {
 
5620
        if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer
 
5621
 
 
5622
        fwd = MakeColourWord(f);
 
5623
        bwd = MakeColourWord(b);
 
5624
        for (i=0 : i<=10: i++) {
 
5625
            if (f == CLR_DEFAULT || b == CLR_DEFAULT) {  ! remove style hints
 
5626
                glk($00B1, swin, i, 7);
 
5627
                glk($00B1, swin, i, 8);
 
5628
            }
 
5629
            else {
 
5630
                glk($00B0, swin, i, 7, fwd);
 
5631
                glk($00B0, swin, i, 8, bwd);
 
5632
            }
 
5633
        }
 
5634
 
 
5635
        ! Now re-open the windows to apply the hints
 
5636
        if (gg_statuswin) glk($0024, gg_statuswin, 0); ! close_window
 
5637
 
 
5638
        if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) {
 
5639
            glk($0024, gg_mainwin, 0);
 
5640
            gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
 
5641
            if (gg_scriptstr ~= 0)
 
5642
                glk($002D, gg_mainwin, gg_scriptstr); ! window_set_echo_stream
 
5643
        }
 
5644
 
 
5645
        gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize,
 
5646
           4, GG_STATUSWIN_ROCK); ! window_open
 
5647
        if (statuswin_current && gg_statuswin) MoveCursor(); else MainWindow();
 
5648
 
 
5649
        if (window ~= 2) {
 
5650
            clr_fgstatus = f;
 
5651
            clr_bgstatus = b;
 
5652
        }
 
5653
        if (window ~= 1) {
 
5654
            clr_fg = f;
 
5655
            clr_bg = b;
 
5656
        }
 
5657
    }
 
5658
];
 
5659
#Endif;
 
5660
 
 
5661
[ SetClr f b w;
 
5662
    SetColour (f, b, w);
 
5663
];
 
5664
 
 
5665
[ RestoreColours;    ! L61007
 
5666
    if (clr_on) { ! check colour has been used
 
5667
        SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored
 
5668
        SetColour(clr_fgstatus, clr_bgstatus, 1, true);
 
5669
        ClearScreen();
 
5670
    }
 
5671
    #Ifdef TARGET_ZCODE;
 
5672
    #Iftrue (#version_number == 6); ! request screen update
 
5673
    (0-->8) = (0-->8) | $$00000100;
 
5674
    #Endif;
 
5675
    #Endif;
 
5676
];
 
5677
 
 
5678
! ----------------------------------------------------------------------------
 
5679
!  Except in Version 3, the DrawStatusLine routine does just that: this is
 
5680
!  provided explicitly so that it can be Replace'd to change the style, and
 
5681
!  as written it emulates the ordinary Standard game status line, which is
 
5682
!  drawn in hardware
 
5683
! ----------------------------------------------------------------------------
 
5684
 
 
5685
#Ifdef TARGET_ZCODE;
 
5686
 
 
5687
#IfV5;
 
5688
 
 
5689
#Iftrue (#version_number == 6);
 
5690
[ DrawStatusLine width x charw scw mvw;
 
5691
   (0-->8) = (0-->8) &~ $$00000100;
 
5692
 
 
5693
   StatusLineHeight(gg_statuswin_size);
 
5694
   ! Now clear the window. This isn't totally trivial. Our approach is to select the
 
5695
   ! fixed space font, measure its width, and print an appropriate
 
5696
   ! number of spaces. We round up if the screen isn't a whole number
 
5697
   ! of characters wide, and rely on window 1 being set to clip by default.
 
5698
   MoveCursor(1, 1);
 
5699
   @set_font 4 -> x;
 
5700
   width = ScreenWidth();
 
5701
   spaces width;
 
5702
   ! Back to standard font for the display. We use output_stream 3 to
 
5703
   ! measure the space required, the aim being to get 50 characters
 
5704
   ! worth of space for the location name.
 
5705
   MoveCursor(1, 2);
 
5706
   @set_font 1 -> x;
 
5707
   if (location == thedark)
 
5708
       print (name) location;
 
5709
   else {
 
5710
       FindVisibilityLevels();
 
5711
       if (visibility_ceiling == location) print (name) location;
 
5712
       else                                print (The) visibility_ceiling;
 
5713
   }
 
5714
   @get_wind_prop 1 3 -> width;
 
5715
   @get_wind_prop 1 13 -> charw;
 
5716
   charw = charw & $FF;
 
5717
   @output_stream 3 StorageForShortName;
 
5718
   print (string) SCORE__TX, "00000";
 
5719
   @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
 
5720
   @output_stream 3 StorageForShortName;
 
5721
   print (string) MOVES__TX, "00000";
 
5722
   @output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw;
 
5723
   if (width - scw - mvw >= 50*charw) {
 
5724
       x = 1+width-scw-mvw;
 
5725
       @set_cursor 1 x; print (string) SCORE__TX, sline1;
 
5726
       x = x+scw;
 
5727
       @set_cursor 1 x; print (string) MOVES__TX, sline2;
 
5728
   }
 
5729
   else {
 
5730
       @output_stream 3 StorageForShortName;
 
5731
       print "00000/00000";
 
5732
       @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw;
 
5733
       if (width - scw >= 50*charw) {
 
5734
           x = 1+width-scw;
 
5735
           @set_cursor 1 x; print sline1, "/", sline2;
 
5736
       }
 
5737
   }
 
5738
   ! Reselect roman, as Infocom's interpreters interpreters go funny
 
5739
   ! if reverse is selected twice.
 
5740
   MainWindow();
 
5741
];
 
5742
 
 
5743
#Endif; ! #version_number == 6
 
5744
 
 
5745
#Endif; ! V5
 
5746
 
 
5747
#Endif; ! TARGET_ZCODE
 
5748
 
 
5749
#Ifndef DrawStatusLine;
 
5750
[ DrawStatusLine width posa posb;
 
5751
    #Ifdef TARGET_GLULX;
 
5752
    ! If we have no status window, we must not try to redraw it.
 
5753
    if (gg_statuswin == 0)
 
5754
        return;
 
5755
    #Endif;
 
5756
 
 
5757
    ! If there is no player location, we shouldn't try to draw status window
 
5758
    if (location == nothing || parent(player) == nothing)
 
5759
        return;
 
5760
 
 
5761
    StatusLineHeight(gg_statuswin_size);
 
5762
    MoveCursor(1, 1);
 
5763
 
 
5764
    width = ScreenWidth();
 
5765
    posa = width-26; posb = width-13;
 
5766
    spaces width;
 
5767
 
 
5768
    MoveCursor(1, 2);
 
5769
    if (location == thedark) {
 
5770
        print (name) location;
 
5771
    }
 
5772
    else {
 
5773
        FindVisibilityLevels();
 
5774
        if (visibility_ceiling == location)
 
5775
            print (name) location;
 
5776
        else
 
5777
            print (The) visibility_ceiling;
 
5778
    }
 
5779
 
 
5780
    if (sys_statusline_flag && width > 53) {
 
5781
        MoveCursor(1, posa);
 
5782
        print (string) TIME__TX;
 
5783
        LanguageTimeOfDay(sline1, sline2);
 
5784
    }
 
5785
    else {
 
5786
        if (width > 66) {
 
5787
            #Ifndef NO_SCORE;
 
5788
            MoveCursor(1, posa);
 
5789
            print (string) SCORE__TX, sline1;
 
5790
            #Endif;
 
5791
            MoveCursor(1, posb);
 
5792
            print (string) MOVES__TX, sline2;
 
5793
        }
 
5794
        #Ifndef NO_SCORE;
 
5795
        if (width > 53 && width <= 66) {
 
5796
            MoveCursor(1, posb);
 
5797
            print sline1, "/", sline2;
 
5798
        }
 
5799
        #Endif;
 
5800
    }
 
5801
 
 
5802
    MainWindow(); ! set_window
 
5803
];
 
5804
#Endif;
 
5805
 
 
5806
#Ifdef TARGET_GLULX;
 
5807
 
 
5808
[ StatusLineHeight hgt parwin;
 
5809
    if (gg_statuswin == 0) return;
 
5810
    if (hgt == gg_statuswin_cursize) return;
 
5811
    parwin = glk($0029, gg_statuswin); ! window_get_parent
 
5812
    glk($0026, parwin, $12, hgt, 0); ! window_set_arrangement
 
5813
    gg_statuswin_cursize = hgt;
 
5814
];
 
5815
 
 
5816
[ Box__Routine maxwid arr ix lines lastnl parwin;
 
5817
    maxwid = 0; ! squash compiler warning
 
5818
    lines = arr-->0;
 
5819
 
 
5820
    if (gg_quotewin == 0) {
 
5821
        gg_arguments-->0 = lines;
 
5822
        ix = InitGlkWindow(GG_QUOTEWIN_ROCK);
 
5823
        if (ix == 0)
 
5824
            gg_quotewin = glk($0023, gg_mainwin, $12, lines, 3,
 
5825
                GG_QUOTEWIN_ROCK); ! window_open
 
5826
    }
 
5827
    else {
 
5828
        parwin = glk($0029, gg_quotewin); ! window_get_parent
 
5829
        glk($0026, parwin, $12, lines, 0); ! window_set_arrangement
 
5830
    }
 
5831
 
 
5832
    lastnl = true;
 
5833
    if (gg_quotewin) {
 
5834
        glk($002A, gg_quotewin); ! window_clear
 
5835
        glk($002F, gg_quotewin); ! set_window
 
5836
        lastnl = false;
 
5837
    }
 
5838
 
 
5839
    ! If gg_quotewin is zero here, the quote just appears in the story window.
 
5840
 
 
5841
    glk($0086, 7); ! set blockquote style
 
5842
    for (ix=0 : ix<lines : ix++) {
 
5843
        print (string) arr-->(ix+1);
 
5844
        if (ix < lines-1 || lastnl) new_line;
 
5845
    }
 
5846
    glk($0086, 0); ! set normal style
 
5847
 
 
5848
    if (gg_quotewin) {
 
5849
        glk($002F, gg_mainwin); ! set_window
 
5850
    }
 
5851
];
 
5852
 
 
5853
#Endif; ! TARGET_GLULX
 
5854
 
 
5855
#Ifdef TARGET_GLULX;
 
5856
 
 
5857
[ GGInitialise res;
 
5858
    @gestalt 4 2 res; ! Test if this interpreter has Glk.
 
5859
    if (res == 0) {
 
5860
      ! Without Glk, we're entirely screwed.
 
5861
      quit;
 
5862
    }
 
5863
    ! Set the VM's I/O system to be Glk.
 
5864
    @setiosys 2 0;
 
5865
 
 
5866
    ! First, we must go through all the Glk objects that exist, and see
 
5867
    ! if we created any of them. One might think this strange, since the
 
5868
    ! program has just started running, but remember that the player might
 
5869
    ! have just typed "restart".
 
5870
 
 
5871
    GGRecoverObjects();
 
5872
 
 
5873
    res = InitGlkWindow(0);
 
5874
    if (res ~= 0) return;
 
5875
 
 
5876
    ! Now, gg_mainwin and gg_storywin might already be set. If not, set them.
 
5877
 
 
5878
    if (gg_mainwin == 0) {
 
5879
        ! Open the story window.
 
5880
        res = InitGlkWindow(GG_MAINWIN_ROCK);
 
5881
        if (res == 0)
 
5882
            gg_mainwin = glk($0023, 0, 0, 0, 3, GG_MAINWIN_ROCK); ! window_open
 
5883
        if (gg_mainwin == 0) {
 
5884
            ! If we can't even open one window, there's no point in going on.
 
5885
            quit;
 
5886
        }
 
5887
    }
 
5888
    else {
 
5889
        ! There was already a story window. We should erase it.
 
5890
        glk($002A, gg_mainwin); ! window_clear
 
5891
    }
 
5892
 
 
5893
    if (gg_statuswin == 0) {
 
5894
        res = InitGlkWindow(GG_STATUSWIN_ROCK);
 
5895
        if (res == 0) {
 
5896
            gg_statuswin_cursize = gg_statuswin_size;
 
5897
            gg_statuswin = glk($0023, gg_mainwin, $12, gg_statuswin_cursize,
 
5898
                4, GG_STATUSWIN_ROCK); ! window_open
 
5899
        }
 
5900
    }
 
5901
    ! It's possible that the status window couldn't be opened, in which case
 
5902
    ! gg_statuswin is now zero. We must allow for that later on.
 
5903
 
 
5904
    glk($002F, gg_mainwin); ! set_window
 
5905
 
 
5906
    InitGlkWindow(1);
 
5907
];
 
5908
 
 
5909
[ GGRecoverObjects id;
 
5910
    ! If GGRecoverObjects() has been called, all these stored IDs are
 
5911
    ! invalid, so we start by clearing them all out.
 
5912
    ! (In fact, after a restoreundo, some of them may still be good.
 
5913
    ! For simplicity, though, we assume the general case.)
 
5914
    gg_mainwin = 0;
 
5915
    gg_statuswin = 0;
 
5916
    gg_quotewin = 0;
 
5917
    gg_scriptfref = 0;
 
5918
    gg_scriptstr = 0;
 
5919
    gg_savestr = 0;
 
5920
    gg_statuswin_cursize = 0;
 
5921
    #Ifdef DEBUG;
 
5922
    gg_commandstr = 0;
 
5923
    gg_command_reading = false;
 
5924
    #Endif; ! DEBUG
 
5925
    ! Also tell the game to clear its object references.
 
5926
    IdentifyGlkObject(0);
 
5927
 
 
5928
    id = glk($0040, 0, gg_arguments); ! stream_iterate
 
5929
    while (id) {
 
5930
        switch (gg_arguments-->0) {
 
5931
            GG_SAVESTR_ROCK: gg_savestr = id;
 
5932
            GG_SCRIPTSTR_ROCK: gg_scriptstr = id;
 
5933
            #Ifdef DEBUG;
 
5934
            GG_COMMANDWSTR_ROCK: gg_commandstr = id;
 
5935
                                 gg_command_reading = false;
 
5936
            GG_COMMANDRSTR_ROCK: gg_commandstr = id;
 
5937
                                 gg_command_reading = true;
 
5938
            #Endif; ! DEBUG
 
5939
            default: IdentifyGlkObject(1, 1, id, gg_arguments-->0);
 
5940
        }
 
5941
        id = glk($0040, id, gg_arguments); ! stream_iterate
 
5942
    }
 
5943
 
 
5944
    id = glk($0020, 0, gg_arguments); ! window_iterate
 
5945
    while (id) {
 
5946
        switch (gg_arguments-->0) {
 
5947
            GG_MAINWIN_ROCK: gg_mainwin = id;
 
5948
            GG_STATUSWIN_ROCK: gg_statuswin = id;
 
5949
            GG_QUOTEWIN_ROCK: gg_quotewin = id;
 
5950
            default: IdentifyGlkObject(1, 0, id, gg_arguments-->0);
 
5951
        }
 
5952
        id = glk($0020, id, gg_arguments); ! window_iterate
 
5953
    }
 
5954
 
 
5955
    id = glk($0064, 0, gg_arguments); ! fileref_iterate
 
5956
    while (id) {
 
5957
        switch (gg_arguments-->0) {
 
5958
            GG_SCRIPTFREF_ROCK: gg_scriptfref = id;
 
5959
            default: IdentifyGlkObject(1, 2, id, gg_arguments-->0);
 
5960
        }
 
5961
        id = glk($0064, id, gg_arguments); ! fileref_iterate
 
5962
    }
 
5963
 
 
5964
    ! Tell the game to tie up any loose ends.
 
5965
    IdentifyGlkObject(2);
 
5966
];
 
5967
 
 
5968
! This somewhat obfuscated function will print anything.
 
5969
! It handles strings, functions (with optional arguments), objects,
 
5970
! object properties (with optional arguments), and dictionary words.
 
5971
! It does *not* handle plain integers, but you can use
 
5972
! DecimalNumber or EnglishNumber to handle that case.
 
5973
!
 
5974
! Calling:                           Is equivalent to:
 
5975
! -------                            ----------------
 
5976
! PrintAnything()                    <nothing printed>
 
5977
! PrintAnything(0)                   <nothing printed>
 
5978
! PrintAnything("string");           print (string) "string";
 
5979
! PrintAnything('word')              print (address) 'word';
 
5980
! PrintAnything(obj)                 print (name) obj;
 
5981
! PrintAnything(obj, prop)           obj.prop();
 
5982
! PrintAnything(obj, prop, args...)  obj.prop(args...);
 
5983
! PrintAnything(func)                func();
 
5984
! PrintAnything(func, args...)       func(args...);
 
5985
 
 
5986
[ PrintAnything _vararg_count obj mclass;
 
5987
    if (_vararg_count == 0) return;
 
5988
    @copy sp obj;
 
5989
    _vararg_count--;
 
5990
    if (obj == 0) return;
 
5991
 
 
5992
    if (obj->0 == $60) {
 
5993
        ! Dictionary word. Metaclass() can't catch this case, so we do
 
5994
        ! it manually.
 
5995
        print (address) obj;
 
5996
        return;
 
5997
    }
 
5998
 
 
5999
    mclass = metaclass(obj);
 
6000
    switch (mclass) {
 
6001
      nothing:
 
6002
        return;
 
6003
      String:
 
6004
        print (string) obj;
 
6005
        return;
 
6006
      Routine:
 
6007
        ! Call the function with all the arguments which are already
 
6008
        ! on the stack.
 
6009
        @call obj _vararg_count 0;
 
6010
        return;
 
6011
      Object:
 
6012
        if (_vararg_count == 0) {
 
6013
            print (name) obj;
 
6014
        }
 
6015
        else {
 
6016
            ! Push the object back onto the stack, and call the
 
6017
            ! veneer routine that handles obj.prop() calls.
 
6018
            @copy obj sp;
 
6019
            _vararg_count++;
 
6020
            @call CA__Pr _vararg_count 0;
 
6021
        }
 
6022
        return;
 
6023
    }
 
6024
];
 
6025
 
 
6026
! This does the same as PrintAnything, but the output is sent to a
 
6027
! byte array in memory. The first two arguments must be the array
 
6028
! address and length; the following arguments are interpreted as
 
6029
! for PrintAnything. The return value is the number of characters
 
6030
! output.
 
6031
! If the output is longer than the array length given, the extra
 
6032
! characters are discarded, so the array does not overflow.
 
6033
! (However, the return value is the total length of the output,
 
6034
! including discarded characters.)
 
6035
 
 
6036
[ PrintAnyToArray _vararg_count arr arrlen str oldstr len;
 
6037
    @copy sp arr;
 
6038
    @copy sp arrlen;
 
6039
    _vararg_count = _vararg_count - 2;
 
6040
 
 
6041
    oldstr = glk($0048); ! stream_get_current
 
6042
    str = glk($0043, arr, arrlen, 1, 0); ! stream_open_memory
 
6043
    if (str == 0) return 0;
 
6044
 
 
6045
    glk($0047, str); ! stream_set_current
 
6046
 
 
6047
    @call PrintAnything _vararg_count 0;
 
6048
 
 
6049
    glk($0047, oldstr); ! stream_set_current
 
6050
    @copy $ffffffff sp;
 
6051
    @copy str sp;
 
6052
    @glk $0044 2 0; ! stream_close
 
6053
    @copy sp len;
 
6054
    @copy sp 0;
 
6055
 
 
6056
    return len;
 
6057
];
 
6058
 
 
6059
! And this calls PrintAnyToArray on a particular array, jiggering
 
6060
! the result to be a Glulx C-style ($E0) string.
 
6061
 
 
6062
Constant GG_ANYTOSTRING_LEN 66;
 
6063
Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1;
 
6064
 
 
6065
[ ChangeAnyToCString _vararg_count ix len;
 
6066
    ix = GG_ANYTOSTRING_LEN-2;
 
6067
    @copy ix sp;
 
6068
    ix = AnyToStrArr+1;
 
6069
    @copy ix sp;
 
6070
    ix = _vararg_count+2;
 
6071
    @call PrintAnyToArray ix len;
 
6072
    AnyToStrArr->0 = $E0;
 
6073
    if (len >= GG_ANYTOSTRING_LEN)
 
6074
        len = GG_ANYTOSTRING_LEN-1;
 
6075
    AnyToStrArr->(len+1) = 0;
 
6076
    return AnyToStrArr;
 
6077
];
 
6078
 
 
6079
#Endif; ! TARGET_GLULX
 
6080
 
 
6081
! This is a trivial function which just prints a number, in decimal
 
6082
! digits. It may be useful as a stub to pass to PrintAnything.
 
6083
 
 
6084
[ DecimalNumber num; print num; ];
 
6085
 
 
6086
#IfV5;
 
6087
 
 
6088
#Ifdef VN_1630;
 
6089
Array StorageForShortName buffer 160;
 
6090
#Ifnot;
 
6091
Array StorageForShortName -> 160 + WORDSIZE;
 
6092
#Endif; ! VN_1630
 
6093
 
 
6094
#Endif; ! V5
 
6095
 
 
6096
#Ifdef TARGET_ZCODE;
 
6097
 
 
6098
! Platform-independent way of printing strings and properties to a
 
6099
! buffer (defined as length word followed by byte characters).
 
6100
 
 
6101
[ PrintToBuffer buf len a b c;
 
6102
    @output_stream 3 buf;
 
6103
    switch (metaclass(a)) {
 
6104
      String:
 
6105
        print (string) a;
 
6106
      Routine:
 
6107
        a(b, c);
 
6108
      Object,Class:
 
6109
        if (b)
 
6110
            PrintOrRun(a, b, true);
 
6111
        else
 
6112
            print (name) a;
 
6113
    }
 
6114
    @output_stream -3;
 
6115
    if (buf-->0 > len) print "Error: Overflow in PrintToBuffer.^";
 
6116
    return buf-->0;
 
6117
];
 
6118
 
 
6119
#Ifnot; ! TARGET_GLULX
 
6120
 
 
6121
[ PrintToBuffer buf len a b;
 
6122
    if (b) {
 
6123
        if (metaclass(a) == Object && a.#b == WORDSIZE
 
6124
            && metaclass(a.b) == String)
 
6125
            buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b);
 
6126
        else
 
6127
            buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b);
 
6128
    }
 
6129
    else
 
6130
        buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a);
 
6131
    if (buf-->0 > len) buf-->0 = len;
 
6132
    return buf-->0;
 
6133
];
 
6134
 
 
6135
#Endif; ! TARGET_
 
6136
 
 
6137
! None of the following functions should be called for zcode if the
 
6138
! output exceeds the size of the buffer.
 
6139
 
 
6140
[ Length a b;
 
6141
    PrintToBuffer(StorageForShortName, 160, a, b);
 
6142
    return StorageForShortName-->0;
 
6143
];
 
6144
 
 
6145
#Ifdef TARGET_ZCODE;
 
6146
 
 
6147
[ LowerCase c;
 
6148
   switch (c) {
 
6149
     'A' to 'Z':
 
6150
       c = c + 32;
 
6151
     202, 204, 212, 214, 221:
 
6152
       c--;
 
6153
     217, 218:
 
6154
       c = c - 2;
 
6155
     158 to 160, 167 to 169, 208 to 210:
 
6156
       c = c - 3;
 
6157
     186 to 190, 196 to 200:
 
6158
       c = c - 5 ;
 
6159
     175 to 180:
 
6160
       c = c - 6;
 
6161
   }
 
6162
   return c;
 
6163
];
 
6164
 
 
6165
[ UpperCase c;
 
6166
   switch (c) {
 
6167
     'a' to 'z':
 
6168
       c = c - 32;
 
6169
     201, 203, 211, 213, 220:
 
6170
       c++;
 
6171
     215, 216:
 
6172
       c = c + 2;
 
6173
     155 to 157, 164 to 166, 205 to 207:
 
6174
       c = c + 3;
 
6175
     181 to 185, 191 to 195:
 
6176
       c = c + 5 ;
 
6177
     169 to 174:
 
6178
       c = c + 6;
 
6179
   }
 
6180
   return c;
 
6181
];
 
6182
 
 
6183
#Ifnot; ! TARGET_GLULX
 
6184
 
 
6185
[ LowerCase c; return glk($00A0, c); ];
 
6186
[ UpperCase c; return glk($00A1, c); ];
 
6187
 
 
6188
#Endif; ! TARGET_
 
6189
 
 
6190
[ PrintCapitalised obj prop flag nocaps centred  length i width;
 
6191
    ! a variation of PrintOrRun, capitalising the first letter and returning nothing
 
6192
 
 
6193
    if (obj ofclass String || prop == 0) {
 
6194
        PrintToBuffer (StorageForShortName, 160, obj);
 
6195
        flag = 1;
 
6196
    }
 
6197
    else
 
6198
        if (obj.#prop > WORDSIZE || metaclass(obj.prop) == Routine or String) {
 
6199
            PrintToBuffer(StorageForShortName, 160, obj, prop);
 
6200
        }
 
6201
        else
 
6202
            if (obj.prop == NULL) rfalse;
 
6203
            else                  return RunTimeError(2, obj, prop);
 
6204
 
 
6205
    length = StorageForShortName-->0;
 
6206
    width = ScreenWidth();
 
6207
    if (centred && length<width)
 
6208
        spaces ( (width-length)/2 );
 
6209
    if (~~nocaps)
 
6210
        StorageForShortName->WORDSIZE = UpperCase(StorageForShortName->WORDSIZE);
 
6211
 
 
6212
    for (i=WORDSIZE: i<length+WORDSIZE: i++) print (char) StorageForShortName->i;
 
6213
 
 
6214
    if (flag == 0 && obj.#prop == WORDSIZE && metaclass(obj.prop) == String)
 
6215
        new_line;
 
6216
    return;
 
6217
];
 
6218
 
 
6219
[ Centre a b;
 
6220
    PrintCapitalised(a, b, 0, 1, 1);
 
6221
];
 
6222
 
 
6223
[ Cap str nocaps;
 
6224
    if (nocaps) print (string) str;
 
6225
    else        PrintCapitalised(str);
 
6226
];
 
6227
 
 
6228
[ PrefaceByArticle o acode pluralise capitalise  i artform findout artval;
 
6229
    if (o provides articles) {
 
6230
        artval=(o.&articles)-->(acode+short_name_case*LanguageCases);
 
6231
        if (capitalise)
 
6232
            print (Cap) artval, " ";
 
6233
        else
 
6234
            print (string) artval, " ";
 
6235
        if (pluralise) return;
 
6236
        print (PSN__) o; return;
 
6237
    }
 
6238
 
 
6239
    i = GetGNAOfObject(o);
 
6240
    if (pluralise) {
 
6241
        if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
 
6242
    }
 
6243
    i = LanguageGNAsToArticles-->i;
 
6244
 
 
6245
    artform = LanguageArticles
 
6246
        + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);
 
6247
 
 
6248
    #Iftrue (LanguageContractionForms == 2);
 
6249
    if (artform-->acode ~= artform-->(acode+3)) findout = true;
 
6250
    #Endif; ! LanguageContractionForms
 
6251
    #Iftrue (LanguageContractionForms == 3);
 
6252
    if (artform-->acode ~= artform-->(acode+3)) findout = true;
 
6253
    if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
 
6254
    #Endif; ! LanguageContractionForms
 
6255
    #Iftrue (LanguageContractionForms == 4);
 
6256
    if (artform-->acode ~= artform-->(acode+3)) findout = true;
 
6257
    if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
 
6258
    if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
 
6259
    #Endif; ! LanguageContractionForms
 
6260
    #Iftrue (LanguageContractionForms > 4);
 
6261
    findout = true;
 
6262
    #Endif; ! LanguageContractionForms
 
6263
 
 
6264
    #Ifdef TARGET_ZCODE;
 
6265
    if (standard_interpreter ~= 0 && findout) {
 
6266
        StorageForShortName-->0 = 160;
 
6267
        @output_stream 3 StorageForShortName;
 
6268
        if (pluralise) print (number) pluralise; else print (PSN__) o;
 
6269
        @output_stream -3;
 
6270
        acode = acode + 3*LanguageContraction(StorageForShortName + 2);
 
6271
    }
 
6272
    #Ifnot; ! TARGET_GLULX
 
6273
    if (findout) {
 
6274
        if (pluralise)
 
6275
            PrintAnyToArray(StorageForShortName, 160, EnglishNumber, pluralise);
 
6276
        else
 
6277
            PrintAnyToArray(StorageForShortName, 160, PSN__, o);
 
6278
        acode = acode + 3*LanguageContraction(StorageForShortName);
 
6279
    }
 
6280
    #Endif; ! TARGET_
 
6281
 
 
6282
    Cap (artform-->acode, ~~capitalise); ! print article
 
6283
    if (pluralise) return;
 
6284
    print (PSN__) o;
 
6285
];
 
6286
 
 
6287
[ PSN__ o;
 
6288
    if (o == 0) { print (string) NOTHING__TX; rtrue; }
 
6289
    switch (metaclass(o)) {
 
6290
      Routine:  print "<routine ", o, ">"; rtrue;
 
6291
      String:   print "<string ~", (string) o, "~>"; rtrue;
 
6292
      nothing:  print "<illegal object number ", o, ">"; rtrue;
 
6293
    }
 
6294
    if (o == player) { print (string) YOURSELF__TX; rtrue; }
 
6295
    #Ifdef LanguagePrintShortName;
 
6296
    if (LanguagePrintShortName(o)) rtrue;
 
6297
    #Endif; ! LanguagePrintShortName
 
6298
    if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue;
 
6299
    if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue;
 
6300
    print (object) o;
 
6301
];
 
6302
 
 
6303
[ Indefart o i;
 
6304
    i = indef_mode; indef_mode = true;
 
6305
    if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
 
6306
    if (o provides article) {
 
6307
        PrintOrRun(o, article, 1); print " ", (PSN__) o; indef_mode = i;
 
6308
        return;
 
6309
    }
 
6310
    PrefaceByArticle(o, 2); indef_mode = i;
 
6311
];
 
6312
 
 
6313
[ CInDefArt o i;
 
6314
    i = indef_mode; indef_mode = true;
 
6315
    if (o has proper) { indef_mode = NULL; print (PSN__) o; return; }
 
6316
    if (o provides article) {
 
6317
        PrintCapitalised(o, article, 1); print " ", (PSN__) o; indef_mode = i;
 
6318
        return;
 
6319
    }
 
6320
    PrefaceByArticle(o, 2, 0, 1); indef_mode = i;
 
6321
];
 
6322
 
 
6323
[ Defart o i;
 
6324
    i = indef_mode; indef_mode = false;
 
6325
    if ((~~o ofclass Object) || o has proper) {
 
6326
        indef_mode = NULL; print (PSN__) o; indef_mode = i;
 
6327
        return;
 
6328
    }
 
6329
    PrefaceByArticle(o, 1); indef_mode = i;
 
6330
];
 
6331
 
 
6332
[ CDefart o i;
 
6333
    #Ifdef YOU__TX;
 
6334
    if (o == player) {
 
6335
        print (string) YOU__TX;
 
6336
        return;
 
6337
    }
 
6338
    #Endif;
 
6339
    i = indef_mode; indef_mode = false;
 
6340
    if ((~~o ofclass Object) || o has proper) {
 
6341
        indef_mode = NULL; print (PSN__) o; indef_mode = i;
 
6342
        return;
 
6343
    }
 
6344
    PrefaceByArticle(o, 0); indef_mode = i;
 
6345
];
 
6346
 
 
6347
[ PrintShortName o i;
 
6348
    i = indef_mode; indef_mode = NULL;
 
6349
    PSN__(o); indef_mode = i;
 
6350
];
 
6351
 
 
6352
[ EnglishNumber n; LanguageNumber(n); ];
 
6353
 
 
6354
[ NumberWord o i n;
 
6355
    n = LanguageNumbers-->0;
 
6356
    for (i=1 : i<=n : i=i+2)
 
6357
        if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1);
 
6358
    return 0;
 
6359
];
 
6360
 
 
6361
[ RandomEntry tab;
 
6362
    if (tab-->0 == 0) return RunTimeError(8);
 
6363
    return tab-->(random(tab-->0));
 
6364
];
 
6365
 
 
6366
! ----------------------------------------------------------------------------
 
6367
!  Useful routine: unsigned comparison (for addresses in Z-machine)
 
6368
!    Returns 1 if x>y, 0 if x=y, -1 if x<y
 
6369
! ----------------------------------------------------------------------------
 
6370
 
 
6371
[ UnsignedCompare x y u v;
 
6372
    if (x == y) return 0;
 
6373
    if (x < 0 && y >= 0) return 1;
 
6374
    if (x >= 0 && y < 0) return -1;
 
6375
    u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT;
 
6376
    if (u > v) return 1;
 
6377
    return -1;
 
6378
];
 
6379
 
 
6380
! ==============================================================================
 
6381
 
 
6382
#Ifdef NITFOL_HOOKS;          ! Code contributed by Evin Robertson
 
6383
#Ifdef TARGET_GLULX;          ! Might be nice for Z-machine games too,
 
6384
                              ! but I'm not going to try to make this work
 
6385
                              ! given #Ifdef funniness.
 
6386
 
 
6387
Array magic_array -->         ! This is so nitfol can do typo correction /
 
6388
                              ! automapping / debugging on Glulx games
 
6389
    $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc'  10 refers to length
 
6390
    Magic_Global_Dispatch__
 
6391
    DI__check_word            ! DI__check_word(buf, length)
 
6392
    PrintShortName
 
6393
    WV__Pr RV__Pr CA__Pr      ! obj.prop = x; x = obj.prop; obj.prop(x)
 
6394
    RA__Pr RL__Pr RA__Sc      ! obj.&prop; obj.#prop; class::prop
 
6395
    OP__Pr OC__Cl             ! obj provides prop; obj ofclass class
 
6396
    OB__Move OB__Remove
 
6397
    OB__Parent__ OB__Child__ OB__Sibling__  ! No explicit veneer for these
 
6398
    ;
 
6399
 
 
6400
[ OB__Parent__ obj; return parent(obj); ];
 
6401
 
 
6402
[ OB__Child__ obj; return child(obj); ];
 
6403
 
 
6404
[ OB__Sibling__ obj; return sibling(obj); ];
 
6405
 
 
6406
[ Magic_Global_Dispatch__ glbl;
 
6407
    switch (glbl) {
 
6408
      0:
 
6409
        if (location == TheDark) return real_location; return location;
 
6410
      1:
 
6411
        return Player;
 
6412
      -1:
 
6413
        return CompassDirection::number; ! Silliness to make exist RA__Sc
 
6414
                                         ! Should never be called.
 
6415
    }
 
6416
    return magic_array;       ! Silences a warning.
 
6417
];
 
6418
 
 
6419
[ DI__check_word buf wlen  ix val res dictlen entrylen;
 
6420
    ! Just like in Tokenise__.  In fact, Tokenise__ could call this if
 
6421
    ! it wanted, instead of doing this itself.
 
6422
    if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE;
 
6423
    for (ix=0 : ix<wlen : ix++) {
 
6424
        gg_tokenbuf->ix = glk($00A0, buf->ix);
 
6425
    }
 
6426
    for (: ix<DICT_WORD_SIZE : ix++) {
 
6427
        gg_tokenbuf->ix = 0;
 
6428
    }
 
6429
    val = #dictionary_table + WORDSIZE;
 
6430
    entrylen = DICT_WORD_SIZE + 7;
 
6431
    @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res;
 
6432
    return res;
 
6433
];
 
6434
 
 
6435
#Endif; ! TARGET_
 
6436
#Endif; ! NITFOL_HOOKS
 
6437
 
 
6438
! ==============================================================================
 
6439
 
 
6440
Object  LibraryExtensions "(Library Extensions)"
 
6441
  with  RunAll [ prop a1 a2 a3
 
6442
            obj rval max;
 
6443
            objectloop (obj in self)
 
6444
                if (obj provides prop && obj.prop ofclass Routine) {
 
6445
                    rval = obj.prop(a1, a2, a3);
 
6446
                    if (rval > max) max = rval;
 
6447
                }
 
6448
            return max;
 
6449
        ],
 
6450
        RunUntil [ prop exitval a1 a2 a3
 
6451
            obj rval;
 
6452
            objectloop (obj in self)
 
6453
                if (obj provides prop && obj.prop ofclass Routine) {
 
6454
                    rval = obj.prop(a1, a2, a3);
 
6455
                    if (rval == exitval) return rval;
 
6456
                }
 
6457
            return ~exitval;
 
6458
        ],
 
6459
        RunWhile [ prop exitval a1 a2 a3
 
6460
            obj rval;
 
6461
            objectloop (obj in self)
 
6462
                if (obj provides prop && obj.prop ofclass Routine) {
 
6463
                    rval = obj.prop(a1, a2, a3);
 
6464
                    if (rval ~= exitval) return rval;
 
6465
                }
 
6466
            return exitval;
 
6467
        ],
 
6468
        ext_initialise 0,
 
6469
        ext_messages 0,
 
6470
  has   proper;
 
6471
 
 
6472
! ==============================================================================