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