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