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