Little cleanup of variable names
[open-adventure.git] / main.c
1 /*
2  * There used to be a note that said this:
3  *
4  * The author - Don Woods - apologises for the style of the code; it
5  * is a result of running the original Fortran IV source through a
6  * home-brew Fortran-to-C converter.
7  *
8  * Now that the code has been restructured into something much closer
9  * to idiomatic C, the following is more appropriate:
10  *
11  * ESR apologizes for the remaing gotos (now confined to one function
12  * in this file - there used to be over 350 of them, *everywhere*),
13  * and for the offensive globals.  Applying the Structured Program
14  * Theorem can be hard.
15  */
16 #define DEFINE_GLOBALS_FROM_INCLUDES
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <stdbool.h>
20 #include <getopt.h>
21 #include <signal.h>
22 #include <time.h>
23 #include "advent.h"
24 #include "database.h"
25 #include "linenoise/linenoise.h"
26 #include "newdb.h"
27
28 struct game_t game;
29
30 long LNLENG, LNPOSN, PARMS[MAXPARMS+1];
31 char rawbuf[LINESIZE], INLINE[LINESIZE+1];
32
33 long AMBER, AXE, BACK, BATTER, BEAR, BIRD, BLOOD,
34                 BOTTLE, CAGE, CAVE, CAVITY, CHAIN, CHASM, CHEST,
35                 CLAM, COINS, DOOR, DPRSSN, DRAGON, DWARF, EGGS,
36                 EMRALD, ENTER, ENTRNC, FIND, FISSUR, FOOD,
37                 GRATE, HINT, INVENT, JADE, KEYS,
38                 KNIFE, LAMP, LOCK, LOOK, MAGZIN,
39                 MESSAG, MIRROR, NUGGET, NUL, OGRE, OIL, OYSTER,
40                 PEARL, PILLOW, PLANT, PLANT2, PYRAM, RESER, ROD, ROD2,
41                 RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
42                 STEPS, STREAM, THROW, TRIDNT, TROLL, TROLL2,
43                 URN, VASE, VEND, VOLCAN, WATER;
44 long WD1, WD1X, WD2, WD2X;
45
46 FILE  *logfp = NULL, *rfp = NULL;
47 bool oldstyle = false;
48 bool editline = true;
49 bool prompt = true;
50
51 extern void initialise();
52 extern int action(FILE *, long, long, long);
53
54 void sig_handler(int signo)
55 {
56     if (signo == SIGINT){
57         if (logfp != NULL)
58             fflush(logfp);
59         }
60     exit(0);
61 }
62
63 /*
64  * MAIN PROGRAM
65  *
66  *  Adventure (rev 2: 20 treasures)
67  *
68  *  History: Original idea & 5-treasure version (adventures) by Willie Crowther
69  *           15-treasure version (adventure) by Don Woods, April-June 1977
70  *           20-treasure version (rev 2) by Don Woods, August 1978
71  *              Errata fixed: 78/12/25
72  *           Revived 2017 as Open Advebture.
73  */
74
75 static bool do_command(FILE *);
76
77 int main(int argc, char *argv[])
78 {
79     int ch;
80         
81 /*  Options. */
82
83     while ((ch = getopt(argc, argv, "l:or:s")) != EOF) {
84         switch (ch) {
85         case 'l':
86             logfp = fopen(optarg, "w");
87             if (logfp == NULL)
88                 fprintf(stderr,
89                         "advent: can't open logfile %s for write\n",
90                         optarg);
91             signal(SIGINT, sig_handler);
92             break;
93         case 'o':
94             oldstyle = true;
95             editline = prompt = false;
96             break;
97         case 'r':
98         rfp = fopen(optarg, "r");
99         if (rfp == NULL)
100         fprintf(stderr,
101             "advent: can't open save file %s for read\n",
102             optarg);
103         signal(SIGINT, sig_handler);
104         break;
105         case 's':
106             editline = false;
107             break;
108         }
109     }
110
111     linenoiseHistorySetMaxLen(350);
112
113     /* Logical variables:
114      *
115      *  game.closed says whether we're all the way closed
116      *  game.closng says whether it's closing time yet
117      *  game.clshnt says whether he's read the clue in the endgame
118      *  game.lmwarn says whether he's been warned about lamp going dim
119      *  game.novice says whether he asked for instructions at start-up
120      *  game.panic says whether he's found out he's trapped in the cave
121      *  game.wzdark says whether the loc he's leaving was dark */
122
123     /* Initialize our LCG PRNG with parameters tested against
124      * Knuth vol. 2. by the original authors */
125     game.lcg_a = 1093;
126     game.lcg_c = 221587;
127     game.lcg_m = 1048576;
128     srand(time(NULL));
129     long seedval = (long)rand();
130     set_seed(seedval);
131
132     /*  Initialize game variables */
133     initialise();
134
135     /*  Start-up, dwarf stuff */
136     game.zzword=RNDVOC(3,0);
137     game.newloc = LOC_START;
138     game.loc = LOC_START;
139     game.limit=330;
140     if (!rfp){
141         game.novice=YES(stdin, WELCOME_YOU,CAVE_NEARBY,NO_MESSAGE);
142         if (game.novice)game.limit=1000;
143     } else {
144         restore(rfp);
145     }
146
147     if (logfp)
148         fprintf(logfp, "seed %ld\n", seedval);
149
150     /* interpret commands until EOF or interrupt */
151     for (;;) {
152         if (!do_command(stdin))
153             break;
154     }
155     /* show score and exit */
156     score(quitgame);
157 }
158
159 static bool fallback_handler(char *buf)
160 /* fallback handler for commands not handled by FORTRANish parser */
161 {
162     long sv;
163     if (sscanf(buf, "seed %ld", &sv) == 1) {
164         set_seed(sv);
165         printf("Seed set to %ld\n", sv);
166         // autogenerated, so don't charge user time for it.
167         --game.turns;
168         // here we reconfigure any global game state that uses random numbers
169         game.zzword=RNDVOC(3,0);
170         return true;
171     }
172     return false;
173 }
174
175 /*  Check if this loc is eligible for any hints.  If been here
176  *  long enough, branch to help section (on later page).  Hints
177  *  all come back here eventually to finish the loop.  Ignore
178  *  "HINTS" < 4 (special stuff, see database notes).
179  */
180 static void checkhints(FILE *cmdin)
181 {
182     if (COND[game.loc] >= game.conds) {
183         for (int hint=1; hint<=HNTMAX; hint++) {
184             if (game.hinted[hint])
185                 continue;
186             if (!CNDBIT(game.loc,hint+HBASE))
187                 game.hintlc[hint]= -1;
188             ++game.hintlc[hint];
189             /*  Come here if he's been long enough at required loc(s) for some
190              *  unused hint. */
191             if (game.hintlc[hint] >= HINTS[hint][1])
192             {
193                 int i;
194
195                 switch (hint-1)
196                 {
197                 case 0:
198                     /* cave */
199                     if (game.prop[GRATE] == 0 && !HERE(KEYS))
200                         break;
201                     game.hintlc[hint]=0;
202                     return;
203                 case 1: /* bird */
204                     if (game.place[BIRD] == game.loc && TOTING(ROD) && game.oldobj == BIRD)
205                         break;
206                     return;
207                 case 2: /* snake */
208                     if (HERE(SNAKE) && !HERE(BIRD))
209                         break;
210                     game.hintlc[hint]=0;
211                     return;
212                 case 3: /* maze */
213                     if (game.atloc[game.loc] == 0 &&
214                         game.atloc[game.oldloc] == 0 &&
215                         game.atloc[game.oldlc2] == 0 &&
216                         game.holdng > 1)
217                         break;
218                     game.hintlc[hint]=0;
219                     return;
220                 case 4: /* dark */
221                     if (game.prop[EMRALD] != -1 && game.prop[PYRAM] == -1)
222                         break;
223                     game.hintlc[hint]=0;
224                     return;
225                 case 5: /* witt */
226                     break;
227                 case 6: /* urn */
228                     if (game.dflag == 0)
229                         break;
230                     game.hintlc[hint]=0;
231                     return;
232                 case 7: /* woods */
233                     if (game.atloc[game.loc] == 0 &&
234                         game.atloc[game.oldloc] == 0 &&
235                         game.atloc[game.oldlc2] == 0)
236                         break;
237                     return;
238                 case 8: /* ogre */
239                     i=ATDWRF(game.loc);
240                     if (i < 0) {
241                         game.hintlc[hint]=0;
242                         return;
243                     }
244                     if (HERE(OGRE) && i == 0)
245                         break;
246                     return;
247                 case 9: /* jade */
248                     if (game.tally == 1 && game.prop[JADE] < 0)
249                         break;
250                     game.hintlc[hint]=0;
251                     return;
252                 default:
253                     BUG(27);
254                     break;
255                 }
256     
257                 /* Fall through to hint display */
258                 game.hintlc[hint]=0;
259                 if (!YES(cmdin,HINTS[hint][3],NO_MESSAGE,OK_MAN))
260                     return;
261                 SETPRM(1,HINTS[hint][2],HINTS[hint][2]);
262                 RSPEAK(HINT_COST);
263                 game.hinted[hint]=YES(cmdin,WANT_HINT,HINTS[hint][4],OK_MAN);
264                 if (game.hinted[hint] && game.limit > WARNTIME)
265                     game.limit += WARNTIME*HINTS[hint][2];
266             }
267         }
268     }
269 }
270
271 bool spotted_by_pirate(int i)
272 {
273     if (i != PIRATE) 
274         return false;
275
276     /*  The pirate's spotted him.  He leaves him alone once we've
277      *  found chest.  K counts if a treasure is here.  If not, and
278      *  tally=1 for an unseen chest, let the pirate be spotted.  Note
279      *  that game.place[CHEST] = NOWHERE might mean that he's thrown
280      *  it to the troll, but in that case he's seen the chest
281      *  (game.prop=0). */
282     if (game.loc == game.chloc || game.prop[CHEST] >= 0)
283         return true;
284     int snarfed=0;
285     bool movechest = false, robplayer = false;
286     for (int treasure=MINTRS; treasure<=MAXTRS; treasure++) {
287         /*  Pirate won't take pyramid from plover room or dark
288          *  room (too easy!). */
289         if (treasure==PYRAM && (game.loc==PLAC[PYRAM] || game.loc==PLAC[EMRALD])) {
290             continue;
291         }
292         if (TOTING(treasure) || HERE(treasure))
293             ++snarfed;
294         if (TOTING(treasure)) {
295             movechest = true;
296             robplayer = true;
297         }
298     }
299     /* Force chest placement before player finds last treasure */
300     if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == NOWHERE && HERE(LAMP) && game.prop[LAMP] == 1) {
301         RSPEAK(PIRATE_SPOTTED);
302         movechest = true;
303     }
304     /* Do things in this order (chest move before robbery) so chest is listed
305      * last at the maze location. */
306     if (movechest) {
307         MOVE(CHEST,game.chloc);
308         MOVE(MESSAG,game.chloc2);
309         game.dloc[PIRATE]=game.chloc;
310         game.odloc[PIRATE]=game.chloc;
311         game.dseen[PIRATE]=false;
312     } else {
313         /* You might get a hint of the pirate's presence even if the 
314          * chest doesn't move... */
315         if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20))
316             RSPEAK(PIRATE_RUSTLES);
317     }
318     if (robplayer) {
319         RSPEAK(PIRATE_POUNCES);
320         for (int treasure=MINTRS; treasure<=MAXTRS; treasure++) {
321             if (!(treasure == PYRAM && (game.loc == PLAC[PYRAM] || game.loc == PLAC[EMRALD]))) {
322                 if (AT(treasure) && game.fixed[treasure] == 0)
323                     CARRY(treasure,game.loc);
324                 if (TOTING(treasure))
325                     DROP(treasure,game.chloc);
326             }
327         }
328     }
329
330     return true;
331 }
332
333 static bool dwarfmove(void)
334 /* Dwarves move.  Return true if player survives, false if he dies. */
335 {
336     int kk, stick, attack;
337     long tk[21];
338
339     /*  Dwarf stuff.  See earlier comments for description of
340      *  variables.  Remember sixth dwarf is pirate and is thus
341      *  very different except for motion rules. */
342
343     /*  First off, don't let the dwarves follow him into a pit or
344      *  a wall.  Activate the whole mess the first time he gets as
345      *  far as the hall of mists (loc 15).  If game.newloc is
346      *  forbidden to pirate (in particular, if it's beyond the
347      *  troll bridge), bypass dwarf stuff.  That way pirate can't
348      *  steal return toll, and dwarves can't meet the bear.  Also
349      *  means dwarves won't follow him into dead end in maze, but
350      *  c'est la vie.  They'll wait for him outside the dead
351      *  end. */
352     if (game.loc == 0 || FORCED(game.loc) || CNDBIT(game.newloc,NOARRR))
353         return true;
354
355     /* Dwarf activity level ratchets up */
356     if (game.dflag == 0) {
357         if (INDEEP(game.loc))
358             game.dflag=1;
359         return true;
360     }
361
362     /*  When we encounter the first dwarf, we kill 0, 1, or 2 of
363      *  the 5 dwarves.  If any of the survivors is at loc,
364      *  replace him with the alternate. */
365     if (game.dflag == 1) {
366         if (!INDEEP(game.loc) || (PCT(95) && (!CNDBIT(game.loc,NOBACK) || PCT(85))))
367             return true;
368         game.dflag=2;
369         for (int i=1; i<=2; i++) {
370             int j=1+randrange(NDWARVES-1);
371             if (PCT(50))
372                 game.dloc[j]=0;
373         }
374         for (int i=1; i<=NDWARVES-1; i++) {
375             if (game.dloc[i] == game.loc)
376                 game.dloc[i]=DALTLC;
377             game.odloc[i]=game.dloc[i];
378         }
379         RSPEAK(DWARF_RAN);
380         DROP(AXE,game.loc);
381         return true;
382     }
383
384     /*  Things are in full swing.  Move each dwarf at random,
385      *  except if he's seen us he sticks with us.  Dwarves stay
386      *  deep inside.  If wandering at random, they don't back up
387      *  unless there's no alternative.  If they don't have to
388      *  move, they attack.  And, of course, dead dwarves don't do
389      *  much of anything. */
390     game.dtotal=0;
391     attack=0;
392     stick=0;
393     for (int i=1; i<=NDWARVES; i++) {
394         if (game.dloc[i] == 0)
395             continue;
396         /*  Fill tk array with all the places this dwarf might go. */
397         int j=1;
398         kk=KEY[game.dloc[i]];
399         if (kk != 0)
400             do {
401                 game.newloc=MOD(labs(TRAVEL[kk])/1000,1000);
402                 /* Have we avoided a dwarf encounter? */
403                 bool avoided = (SPECIAL(game.newloc) ||
404                                 !INDEEP(game.newloc) ||
405                                 game.newloc == game.odloc[i] ||
406                                 (j > 1 && game.newloc == tk[j-1]) ||
407                                 j >= 20 ||
408                                 game.newloc == game.dloc[i] ||
409                                 FORCED(game.newloc) ||
410                                 (i == PIRATE && CNDBIT(game.newloc,NOARRR)) ||
411                                 labs(TRAVEL[kk])/1000000 == 100);
412                 if (!avoided) {
413                     tk[j++] = game.newloc;
414                 }
415                 ++kk;
416             } while
417                 (TRAVEL[kk-1] >= 0);
418         tk[j]=game.odloc[i];
419         if (j >= 2)
420             --j;
421         j=1+randrange(j);
422         game.odloc[i]=game.dloc[i];
423         game.dloc[i]=tk[j];
424         game.dseen[i]=(game.dseen[i] && INDEEP(game.loc)) || (game.dloc[i] == game.loc || game.odloc[i] == game.loc);
425         if (!game.dseen[i]) continue;
426         game.dloc[i]=game.loc;
427         if (spotted_by_pirate(i))
428             continue;
429         /* This threatening little dwarf is in the room with him! */
430         ++game.dtotal;
431         if (game.odloc[i] == game.dloc[i]) {
432             ++attack;
433             if (game.knfloc >= 0)
434                 game.knfloc=game.loc;
435             if (randrange(1000) < 95*(game.dflag-2))
436                 ++stick;
437         }
438     }
439
440     /*  Now we know what's happening.  Let's tell the poor sucker about it.
441      *  Note that various of the "knife" messages must have specific relative
442      *  positions in the RSPEAK database. */
443     if (game.dtotal == 0)
444         return true;
445     SETPRM(1,game.dtotal,0);
446     RSPEAK(DWARF_PACK+1/game.dtotal);   /* FIXME: Arithmetic on message number */
447     if (attack == 0)
448         return true;
449     if (game.dflag == 2)game.dflag=3;
450     SETPRM(1,attack,0);
451     int k=6;
452     if (attack > 1)k=THROWN_KNIVES;
453     RSPEAK(k);
454     SETPRM(1,stick,0);
455     RSPEAK(k+1+2/(1+stick));    /* FIXME: Arithmetic on message number */
456     if (stick == 0)
457         return true;
458     game.oldlc2=game.loc;
459     return false;
460 }
461
462 /*  "You're dead, Jim."
463  *
464  *  If the current loc is zero, it means the clown got himself killed.
465  *  We'll allow this maxdie times.  MAXDIE is automatically set based
466  *  on the number of snide messages available.  Each death results in
467  *  a message (81, 83, etc.)  which offers reincarnation; if accepted,
468  *  this results in message 82, 84, etc.  The last time, if he wants
469  *  another chance, he gets a snide remark as we exit.  When
470  *  reincarnated, all objects being carried get dropped at game.oldlc2
471  *  (presumably the last place prior to being killed) without change
472  *  of props.  the loop runs backwards to assure that the bird is
473  *  dropped before the cage.  (this kluge could be changed once we're
474  *  sure all references to bird and cage are done by keywords.)  The
475  *  lamp is a special case (it wouldn't do to leave it in the cave).
476  *  It is turned off and left outside the building (only if he was
477  *  carrying it, of course).  He himself is left inside the building
478  *  (and heaven help him if he tries to xyzzy back into the cave
479  *  without the lamp!).  game.oldloc is zapped so he can't just
480  *  "retreat". */
481
482 static void croak(FILE *cmdin)
483 /*  Okay, he's dead.  Let's get on with it. */
484 {
485     ++game.numdie;
486     if (game.closng) {
487         /*  He died during closing time.  No resurrection.  Tally up a
488          *  death and exit. */
489         RSPEAK(DEATH_CLOSING);
490         score(endgame);
491
492     }
493     /* FIXME: Arithmetic on message numbers */
494     else if (game.numdie == MAXDIE || !YES(cmdin,WATCH_IT+game.numdie*2,WHICH_WAY+game.numdie*2,OK_MAN))
495             score(endgame);
496     else {
497         game.place[WATER] = game.place[OIL] = NOWHERE;
498         if (TOTING(LAMP))
499             game.prop[LAMP]=0;
500         for (int j=1; j<=NOBJECTS; j++) {
501             int i=NOBJECTS + 1 - j;
502             if (TOTING(i)) {
503                 /* Always leave lamp where it's accessible aboveground */
504                 DROP(i, (i == LAMP) ? LOC_START : game.oldlc2);
505             }
506         }
507         game.loc = LOC_BUILDING;
508         game.oldloc=game.loc;
509     }
510 }
511
512 /*  Given the current location in "game.loc", and a motion verb number in
513  *  "motion", put the new location in "game.newloc".  The current loc is saved
514  *  in "game.oldloc" in case he wants to retreat.  The current
515  *  game.oldloc is saved in game.oldlc2, in case he dies.  (if he
516  *  does, game.newloc will be limbo, and game.oldloc will be what killed
517  *  him, so we need game.oldlc2, which is the last place he was
518  *  safe.) */
519
520 static bool playermove(FILE *cmdin, token_t verb, int motion)
521 {
522     int scratchloc, k2, kk=KEY[game.loc];
523     game.newloc=game.loc;
524     if (kk == 0)
525         BUG(26);
526     if (motion == NUL)
527         return true;
528     else if (motion == BACK) {
529         /*  Handle "go back".  Look for verb which goes from game.loc to
530          *  game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion.
531          *  k2 saves entry -> forced loc -> previous loc. */
532         motion=game.oldloc;
533         if (FORCED(motion))
534             motion=game.oldlc2;
535         game.oldlc2=game.oldloc;
536         game.oldloc=game.loc;
537         k2=0;
538         if (motion == game.loc)k2=FORGOT_PATH;
539         if (CNDBIT(game.loc,NOBACK))k2=TWIST_TURN;
540         if (k2 == 0) {
541             for (;;) {
542                 scratchloc=MOD((labs(TRAVEL[kk])/1000),1000);
543                 if (scratchloc != motion) {
544                     if (!SPECIAL(scratchloc)) {
545                         if (FORCED(scratchloc) && MOD((labs(TRAVEL[KEY[scratchloc]])/1000),1000) == motion)
546                             k2=kk;
547                     }
548                     if (TRAVEL[kk] >= 0) {
549                         ++kk;
550                         continue;
551                     }
552                     kk=k2;
553                     if (kk == 0) {
554                         RSPEAK(NOT_CONNECTED);
555                         return true;
556                     }
557                 }
558
559                 motion=MOD(labs(TRAVEL[kk]),1000);
560                 kk=KEY[game.loc];
561                 break; /* fall through to ordinary travel */
562             }
563         } else {
564             RSPEAK(k2);
565             return true;
566         }
567     }
568     else if (motion == LOOK) {
569         /*  Look.  Can't give more detail.  Pretend it wasn't dark
570          *  (though it may now be dark) so he won't fall into a
571          *  pit while staring into the gloom. */
572         if (game.detail < 3)
573             RSPEAK(NO_MORE_DETAIL);
574         ++game.detail;
575         game.wzdark=false;
576         game.abbrev[game.loc]=0;
577         return true;
578     }
579     else if (motion == CAVE) {
580         /*  Cave.  Different messages depending on whether above ground. */
581         RSPEAK((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
582         return true;
583     }
584     else {
585         /* none of the specials */
586         game.oldlc2=game.oldloc;
587         game.oldloc=game.loc;
588     }
589
590     /* ordinary travel */
591     for (;;) {
592         scratchloc=labs(TRAVEL[kk]);
593         if (MOD(scratchloc,1000) == 1 || MOD(scratchloc,1000) == motion)
594             break;
595         if (TRAVEL[kk] < 0) {
596             /* FIXME: Magic numbers! */
597             /*  Non-applicable motion.  Various messages depending on
598              *  word given. */
599             int spk=CANT_APPLY;
600             if (motion >= 43 && motion <= 50)spk=BAD_DIRECTION;
601             if (motion == 29 || motion == 30)spk=BAD_DIRECTION;
602             if (motion == 7 || motion == 36 || motion == 37)spk=UNSURE_FACING;
603             if (motion == 11 || motion == 19)spk=NO_INOUT_HERE;
604             if (verb == FIND || verb == INVENT)spk=NEreplace;
605             if (motion == 62 || motion == 65)spk=NOTHING_HAPPENS;
606             if (motion == 17)spk=WHICH_WAY;
607             RSPEAK(spk);
608             return true;
609         }
610         ++kk;
611     }
612     scratchloc=scratchloc/1000;
613
614     do {
615         /*
616          * (ESR) This special-travel loop may have to be repeated if it includes
617          * the plover passage.  Same deal for any future cases wgerw we beed to 
618          * block travel and then redo it once the blocking condition has been
619          * removed.
620          */
621         for (;;) {
622             game.newloc=scratchloc/1000;
623             motion=MOD(game.newloc,100);
624             if (!SPECIAL(game.newloc)) {
625                 if (game.newloc <= 100) {
626                     if (game.newloc == 0 || PCT(game.newloc))
627                         break;
628                     /* else fall through */
629                 } if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
630                       break;
631                 /* else fall through */
632             }
633             else if (game.prop[motion] != game.newloc/100-3)
634                 break;
635             do {
636                 if (TRAVEL[kk] < 0)BUG(25);
637                 ++kk;
638                 game.newloc=labs(TRAVEL[kk])/1000;
639             } while
640                 (game.newloc == scratchloc);
641             scratchloc=game.newloc;
642         }
643
644         game.newloc=MOD(scratchloc,1000);
645         if (!SPECIAL(game.newloc))
646             return true;
647         if (game.newloc <= 500) {
648             game.newloc=game.newloc-SPECIALBASE;
649             switch (game.newloc)
650             {
651             case 1:
652                 /*  Travel 301.  Plover-alcove passage.  Can carry only
653                  *  emerald.  Note: travel table must include "useless"
654                  *  entries going through passage, which can never be used for
655                  *  actual motion, but can be spotted by "go back". */
656                 /* FIXME: Arithmetic on location numbers */
657                 game.newloc=99+100-game.loc;
658                 if (game.holdng == 0 || (game.holdng == 1 && TOTING(EMRALD)))
659                     return true;
660                 game.newloc=game.loc;
661                 RSPEAK(MUST_DROP);
662                 return true;
663             case 2:
664                 /*  Travel 302.  Plover transport.  Drop the emerald (only use
665                  *  special travel if toting it), so he's forced to use the
666                  *  plover-passage to get it out.  Having dropped it, go back and
667                  *  pretend he wasn't carrying it after all. */
668                 DROP(EMRALD,game.loc);
669                 do {
670                     if (TRAVEL[kk] < 0)BUG(25);
671                     ++kk;
672                     game.newloc=labs(TRAVEL[kk])/1000;
673                 } while
674                     (game.newloc == scratchloc);
675                 continue;       /* back to top of do/while loop */
676             case 3:
677                 /*  Travel 303.  Troll bridge.  Must be done only as special
678                  *  motion so that dwarves won't wander across and encounter
679                  *  the bear.  (They won't follow the player there because
680                  *  that region is forbidden to the pirate.)  If
681                  *  game.prop(TROLL)=1, he's crossed since paying, so step out
682                  *  and block him.  (standard travel entries check for
683                  *  game.prop(TROLL)=0.)  Special stuff for bear. */
684                 if (game.prop[TROLL] == 1) {
685                     PSPEAK(TROLL,1);
686                     game.prop[TROLL]=0;
687                     MOVE(TROLL2,0);
688                     MOVE(TROLL2+NOBJECTS,0);
689                     MOVE(TROLL,PLAC[TROLL]);
690                     MOVE(TROLL+NOBJECTS,FIXD[TROLL]);
691                     JUGGLE(CHASM);
692                     game.newloc=game.loc;
693                     return true;
694                 } else {
695                     game.newloc=PLAC[TROLL]+FIXD[TROLL]-game.loc;
696                     if (game.prop[TROLL] == 0)game.prop[TROLL]=1;
697                     if (!TOTING(BEAR)) return true;
698                     RSPEAK(BRIDGE_COLLAPSE);
699                     game.prop[CHASM]=1;
700                     game.prop[TROLL]=2;
701                     DROP(BEAR,game.newloc);
702                     game.fixed[BEAR]= -1;
703                     game.prop[BEAR]=3;
704                     game.oldlc2=game.newloc;
705                     croak(cmdin);
706                     return false;
707                 }
708             }
709             BUG(20);
710         }
711     } while
712         (false);
713     /* FIXME: Arithmetic on location number, becoming a message number */
714     RSPEAK(game.newloc-500);
715     game.newloc=game.loc;
716     return true;
717 }
718
719 static bool closecheck(void)
720 /*  Handle the closing of the cave.  The cave closes "clock1" turns
721  *  after the last treasure has been located (including the pirate's
722  *  chest, which may of course never show up).  Note that the
723  *  treasures need not have been taken yet, just located.  Hence
724  *  clock1 must be large enough to get out of the cave (it only ticks
725  *  while inside the cave).  When it hits zero, we branch to 10000 to
726  *  start closing the cave, and then sit back and wait for him to try
727  *  to get out.  If he doesn't within clock2 turns, we close the cave;
728  *  if he does try, we assume he panics, and give him a few additional
729  *  turns to get frantic before we close.  When clock2 hits zero, we
730  *  branch to 11000 to transport him into the final puzzle.  Note that
731  *  the puzzle depends upon all sorts of random things.  For instance,
732  *  there must be no water or oil, since there are beanstalks which we
733  *  don't want to be able to water, since the code can't handle it.
734  *  Also, we can have no keys, since there is a grate (having moved
735  *  the fixed object!) there separating him from all the treasures.
736  *  Most of these problems arise from the use of negative prop numbers
737  *  to suppress the object descriptions until he's actually moved the
738  *  objects. */
739 {
740     if (game.tally == 0 && INDEEP(game.loc) && game.loc != 33)
741         --game.clock1;
742
743     /*  When the first warning comes, we lock the grate, destroy
744      *  the bridge, kill all the dwarves (and the pirate), remove
745      *  the troll and bear (unless dead), and set "closng" to
746      *  true.  Leave the dragon; too much trouble to move it.
747      *  from now until clock2 runs out, he cannot unlock the
748      *  grate, move to any location outside the cave, or create
749      *  the bridge.  Nor can he be resurrected if he dies.  Note
750      *  that the snake is already gone, since he got to the
751      *  treasure accessible only via the hall of the mountain
752      *  king. Also, he's been in giant room (to get eggs), so we
753      *  can refer to it.  Also also, he's gotten the pearl, so we
754      *  know the bivalve is an oyster.  *And*, the dwarves must
755      *  have been activated, since we've found chest. */
756     if (game.clock1 == 0)
757     {
758         game.prop[GRATE]=0;
759         game.prop[FISSUR]=0;
760         for (int i=1; i<=NDWARVES; i++) {
761             game.dseen[i]=false;
762             game.dloc[i]=0;
763         }
764         MOVE(TROLL,0);
765         MOVE(TROLL+NOBJECTS,0);
766         MOVE(TROLL2,PLAC[TROLL]);
767         MOVE(TROLL2+NOBJECTS,FIXD[TROLL]);
768         JUGGLE(CHASM);
769         if (game.prop[BEAR] != 3)DESTROY(BEAR);
770         game.prop[CHAIN]=0;
771         game.fixed[CHAIN]=0;
772         game.prop[AXE]=0;
773         game.fixed[AXE]=0;
774         RSPEAK(CAVE_CLOSING);
775         game.clock1= -1;
776         game.closng=true;
777         return true;
778     } else if (game.clock1 < 0)
779         --game.clock2;
780     if (game.clock2 == 0) {
781         /*  Once he's panicked, and clock2 has run out, we come here
782          *  to set up the storage room.  The room has two locs,
783          *  hardwired as 115 (ne) and 116 (sw).  At the ne end, we
784          *  place empty bottles, a nursery of plants, a bed of
785          *  oysters, a pile of lamps, rods with stars, sleeping
786          *  dwarves, and him.  At the sw end we place grate over
787          *  treasures, snake pit, covey of caged birds, more rods, and
788          *  pillows.  A mirror stretches across one wall.  Many of the
789          *  objects come from known locations and/or states (e.g. the
790          *  snake is known to have been destroyed and needn't be
791          *  carried away from its old "place"), making the various
792          *  objects be handled differently.  We also drop all other
793          *  objects he might be carrying (lest he have some which
794          *  could cause trouble, such as the keys).  We describe the
795          *  flash of light and trundle back. */
796         game.prop[BOTTLE]=PUT(BOTTLE,LOC_NE,1);
797         game.prop[PLANT]=PUT(PLANT,LOC_NE,0);
798         game.prop[OYSTER]=PUT(OYSTER,LOC_NE,0);
799         OBJTXT[OYSTER]=3;
800         game.prop[LAMP]=PUT(LAMP,LOC_NE,0);
801         game.prop[ROD]=PUT(ROD,LOC_NE,0);
802         game.prop[DWARF]=PUT(DWARF,LOC_NE,0);
803         game.loc = LOC_NE;
804         game.oldloc = LOC_NE;
805         game.newloc = LOC_NE;
806         /*  Leave the grate with normal (non-negative) property.
807          *  Reuse sign. */
808         PUT(GRATE,LOC_SW,0);
809         PUT(SIGN,LOC_SW,0);
810         ++OBJTXT[SIGN];
811         game.prop[SNAKE]=PUT(SNAKE,LOC_SW,1);
812         game.prop[BIRD]=PUT(BIRD,LOC_SW,1);
813         game.prop[CAGE]=PUT(CAGE,LOC_SW,0);
814         game.prop[ROD2]=PUT(ROD2,LOC_SW,0);
815         game.prop[PILLOW]=PUT(PILLOW,LOC_SW,0);
816
817         game.prop[MIRROR]=PUT(MIRROR,LOC_NE,0);
818         game.fixed[MIRROR]=LOC_SW;
819
820         for (int i=1; i<=NOBJECTS; i++) {
821             if (TOTING(i))
822                 DESTROY(i);
823         }
824
825         RSPEAK(CAVE_CLOSED);
826         game.closed=true;
827         return true;
828     }
829
830     return false;
831 }
832
833 static void lampcheck(void)
834 /* Check game limit and lamp timers */
835 {
836     if (game.prop[LAMP] == 1)
837         --game.limit;
838
839     /*  Another way we can force an end to things is by having the
840      *  lamp give out.  When it gets close, we come here to warn
841      *  him.  First following ar, if the lamp and fresh batteries are
842      *  here, in which case we replace the batteries and continue.
843      *  Second is for other cases of lamp dying.  12400 is when it
844      *  goes out.  Even then, he can explore outside for a while
845      *  if desired. */
846     if (game.limit <= WARNTIME && HERE(BATTER) && game.prop[BATTER]==0 && HERE(LAMP))
847     {
848         RSPEAK(REPLACE_BATTERIES);
849         game.prop[BATTER]=1;
850         if (TOTING(BATTER))
851             DROP(BATTER,game.loc);
852         game.limit=game.limit+2500;
853         game.lmwarn=false;
854     } else if (game.limit == 0) {
855         game.limit= -1;
856         game.prop[LAMP]=0;
857         if (HERE(LAMP))
858             RSPEAK(LAMP_OUT);
859     } else if (game.limit <= WARNTIME) {
860         if (!game.lmwarn && HERE(LAMP)) {
861             game.lmwarn=true;
862             int spk=GET_BATTERIES;
863             if (game.place[BATTER] == NOWHERE)spk=LAMP_DIM;
864             if (game.prop[BATTER] == 1)spk=MISSING_BATTERIES;
865             RSPEAK(spk);
866         }
867     }
868 }
869
870 static void listobjects(void)
871 /*  Print out descriptions of objects at this location.  If
872  *  not closing and property value is negative, tally off
873  *  another treasure.  Rug is special case; once seen, its
874  *  game.prop is 1 (dragon on it) till dragon is killed.
875  *  Similarly for chain; game.prop is initially 1 (locked to
876  *  bear).  These hacks are because game.prop=0 is needed to
877  *  get full score. */
878 {
879     if (!DARK(game.loc)) {
880         ++game.abbrev[game.loc];
881         for (int i=game.atloc[game.loc]; i != 0; i=game.link[i]) {
882             long obj=i;
883             if (obj > NOBJECTS)obj=obj-NOBJECTS;
884             if (obj == STEPS && TOTING(NUGGET))
885                 continue;
886             if (game.prop[obj] < 0) {
887                 if (game.closed)
888                     continue;
889                 game.prop[obj]=0;
890                 if (obj == RUG || obj == CHAIN)
891                     game.prop[obj]=1;
892                 --game.tally;
893                 /*  Note: There used to be a test here to see whether the
894                  *  player had blown it so badly that he could never ever see
895                  *  the remaining treasures, and if so the lamp was zapped to
896                  *  35 turns.  But the tests were too simple-minded; things
897                  *  like killing the bird before the snake was gone (can never
898                  *  see jewelry), and doing it "right" was hopeless.  E.G.,
899                  *  could cross troll bridge several times, using up all
900                  *  available treasures, breaking vase, using coins to buy
901                  *  batteries, etc., and eventually never be able to get
902                  *  across again.  If bottle were left on far side, could then
903                  *  never get eggs or trident, and the effects propagate.  So
904                  *  the whole thing was flushed.  anyone who makes such a
905                  *  gross blunder isn't likely to find everything else anyway
906                  *  (so goes the rationalisation). */
907             }
908             int kk=game.prop[obj];
909             if (obj == STEPS && game.loc == game.fixed[STEPS])
910                 kk=1;
911             PSPEAK(obj,kk);
912         }
913     }
914 }
915
916 static bool do_command(FILE *cmdin)
917 /* Get and execute a command */ 
918 {
919     long verb, V1, V2;
920     long kmod, defn;
921     static long igo = 0;
922     static long obj = 0;
923     enum speechpart part;
924
925     /*  Can't leave cave once it's closing (except by main office). */
926     if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
927         RSPEAK(EXIT_CLOSED);
928         game.newloc=game.loc;
929         if (!game.panic)game.clock2=15;
930         game.panic=true;
931     }
932
933     /*  See if a dwarf has seen him and has come from where he
934      *  wants to go.  If so, the dwarf's blocking his way.  If
935      *  coming from place forbidden to pirate (dwarves rooted in
936      *  place) let him get out (and attacked). */
937     if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc,NOARRR)) {
938         for (size_t i=1; i<=NDWARVES-1; i++) {
939             if (game.odloc[i] == game.newloc && game.dseen[i]) {
940                 game.newloc=game.loc;
941                 RSPEAK(DWARF_BLOCK);
942                 break;
943             }
944         }
945     }
946     game.loc=game.newloc;
947
948     if (!dwarfmove())
949         croak(cmdin);
950
951     /*  Describe the current location and (maybe) get next command. */
952
953     for (;;) {
954         if (game.loc == 0)
955             croak(cmdin);
956         char* msg = locations[game.loc].description.small;
957         if (MOD(game.abbrev[game.loc],game.abbnum) == 0 || msg == 0)
958             msg=locations[game.loc].description.big;
959         if (!FORCED(game.loc) && DARK(game.loc)) {
960             /*  The easiest way to get killed is to fall into a pit in
961              *  pitch darkness. */
962             if (game.wzdark && PCT(35)) {
963                 RSPEAK(PIT_FALL);
964                 game.oldlc2 = game.loc;
965                 croak(cmdin);
966                 continue;       /* back to top of main interpreter loop */
967             }
968             msg=arbitrary_messages[PITCH_DARK];
969         }
970         if (TOTING(BEAR))RSPEAK(TAME_BEAR);
971         newspeak(msg);
972         if (FORCED(game.loc)) {
973             if (playermove(cmdin, verb, 1))
974                 return true;
975             else
976                 continue;       /* back to top of main interpreter loop */
977         }
978         if (game.loc == 33 && PCT(25) && !game.closng)RSPEAK(SAYS_PLUGH);
979
980         listobjects();
981
982     L2012:
983         verb=0;
984         game.oldobj=obj;
985         obj=0;
986
987     L2600:
988         checkhints(cmdin);
989
990         /*  If closing time, check for any objects being toted with
991          *  game.prop < 0 and set the prop to -1-game.prop.  This way
992          *  objects won't be described until they've been picked up
993          *  and put down separate from their respective piles.  Don't
994          *  tick game.clock1 unless well into cave (and not at Y2). */
995         if (game.closed) {
996             if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
997                 PSPEAK(OYSTER,1);
998             for (size_t i=1; i<=NOBJECTS; i++) {
999                 if (TOTING(i) && game.prop[i] < 0)
1000                     game.prop[i] = -1-game.prop[i];
1001             }
1002         }
1003         game.wzdark=DARK(game.loc);
1004         if (game.knfloc > 0 && game.knfloc != game.loc)
1005             game.knfloc=0;
1006
1007         /* This is where we get a new command from the user */
1008         if (!GETIN(cmdin, &WD1,&WD1X,&WD2,&WD2X))
1009             return false;
1010
1011         /*  Every input, check "game.foobar" flag.  If zero, nothing's
1012          *  going on.  If pos, make neg.  If neg, he skipped a word,
1013          *  so make it zero. */
1014     L2607:
1015         game.foobar=(game.foobar>0 ? -game.foobar : 0);
1016         ++game.turns;
1017         if (game.turns == game.thresh) {
1018             newspeak(turn_threshold_messages[game.trndex]);
1019             game.trnluz=game.trnluz+TRNVAL[game.trndex]/100000;
1020             ++game.trndex;
1021             game.thresh = -1;
1022             if (game.trndex <= TRNVLS)
1023                 game.thresh=MOD(TRNVAL[game.trndex],100000)+1;
1024         }
1025         if (verb == SAY && WD2 > 0)
1026             verb=0;
1027         if (verb == SAY) {
1028             part=transitive;
1029             goto Laction;
1030         }
1031         if (closecheck()) {
1032             if (game.closed)
1033                 return true;
1034         } else
1035             lampcheck();
1036
1037         V1=VOCAB(WD1,-1);
1038         V2=VOCAB(WD2,-1);
1039         if (V1 == ENTER && (V2 == STREAM || V2 == 1000+WATER)) {
1040                 if(LIQLOC(game.loc) == WATER){
1041                         RSPEAK(FEET_WET);
1042                 } else {
1043                         RSPEAK(WHERE_QUERY);
1044                 }
1045             goto L2012;
1046         }
1047         if (V1 == ENTER && WD2 > 0) {
1048             WD1=WD2;
1049             WD1X=WD2X;
1050             WD2=0;
1051         } else {
1052             if (!((V1 != 1000+WATER && V1 != 1000+OIL) ||
1053                   (V2 != 1000+PLANT && V2 != 1000+DOOR))) {
1054                 if (AT(V2-1000))
1055                     WD2=MAKEWD(16152118);
1056             }
1057             if (V1 == 1000+CAGE && V2 == 1000+BIRD && HERE(CAGE) && HERE(BIRD))
1058                 WD1=MAKEWD(301200308);
1059         }
1060     L2620:
1061         if (WD1 == MAKEWD(23051920)) {
1062             ++game.iwest;
1063             if (game.iwest == 10)
1064                 RSPEAK(W_IS_WEST);
1065         }
1066         if (WD1 == MAKEWD( 715) && WD2 != 0) {
1067             if (++igo == 10)
1068                 RSPEAK(GO_UNNEEDED);
1069         }
1070     Lookup:
1071         defn = VOCAB(WD1,-1);
1072         if (defn == -1) {
1073             /* Gee, I don't understand. */
1074             if (fallback_handler(rawbuf))
1075                 continue;
1076             SETPRM(1,WD1,WD1X);
1077             RSPEAK(DONT_KNOW);
1078             goto L2600;
1079         }
1080         kmod=MOD(defn,1000);
1081         switch (defn/1000)
1082         {
1083         case 0:
1084             if (playermove(cmdin, verb, kmod))
1085                 return true;
1086             else
1087                 continue;       /* back to top of main interpreter loop */
1088         case 1: part=unknown; obj = kmod; break;
1089         case 2: part=intransitive; verb = kmod; break;
1090         case 3: RSPEAK(kmod); goto L2012;
1091         default: BUG(22);
1092         }
1093
1094     Laction:
1095         switch (action(cmdin, part, verb, obj)) {
1096         case GO_TERMINATE:
1097             return true;
1098         case GO_MOVE: 
1099             playermove(cmdin, verb, NUL);
1100             return true;
1101         case GO_TOP: continue;  /* back to top of main interpreter loop */
1102         case GO_CLEAROBJ: goto L2012;
1103         case GO_CHECKHINT: goto L2600;
1104         case GO_CHECKFOO: goto L2607;
1105         case GO_LOOKUP: goto Lookup;
1106         case GO_WORD2:
1107             /* Get second word for analysis. */
1108             WD1=WD2;
1109             WD1X=WD2X;
1110             WD2=0;
1111             goto L2620;
1112         case GO_UNKNOWN:
1113             /*  Random intransitive verbs come here.  Clear obj just in case
1114              *  (see attack()). */
1115             SETPRM(1,WD1,WD1X);
1116             RSPEAK(DO_WHAT);
1117             obj=0;
1118             goto L2600;
1119         case GO_DWARFWAKE:
1120             /*  Oh dear, he's disturbed the dwarves. */
1121             RSPEAK(DWARVES_AWAKEN);
1122             score(endgame);
1123             return true;
1124         default:
1125             BUG(99);
1126         }
1127     }
1128 }
1129
1130 /* end */