Factor out logic for listing objects at present location.
[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
716     /*  When the first warning comes, we lock the grate, destroy
717      *  the bridge, kill all the dwarves (and the pirate), remove
718      *  the troll and bear (unless dead), and set "closng" to
719      *  true.  Leave the dragon; too much trouble to move it.
720      *  from now until clock2 runs out, he cannot unlock the
721      *  grate, move to any location outside the cave, or create
722      *  the bridge.  Nor can he be resurrected if he dies.  Note
723      *  that the snake is already gone, since he got to the
724      *  treasure accessible only via the hall of the mountain
725      *  king. Also, he's been in giant room (to get eggs), so we
726      *  can refer to it.  Also also, he's gotten the pearl, so we
727      *  know the bivalve is an oyster.  *And*, the dwarves must
728      *  have been activated, since we've found chest. */
729     if (game.clock1 == 0)
730     {
731         game.prop[GRATE]=0;
732         game.prop[FISSUR]=0;
733         for (int i=1; i<=NDWARVES; i++) {
734             game.dseen[i]=false;
735             game.dloc[i]=0;
736         }
737         MOVE(TROLL,0);
738         MOVE(TROLL+NOBJECTS,0);
739         MOVE(TROLL2,PLAC[TROLL]);
740         MOVE(TROLL2+NOBJECTS,FIXD[TROLL]);
741         JUGGLE(CHASM);
742         if (game.prop[BEAR] != 3)DSTROY(BEAR);
743         game.prop[CHAIN]=0;
744         game.fixed[CHAIN]=0;
745         game.prop[AXE]=0;
746         game.fixed[AXE]=0;
747         RSPEAK(129);
748         game.clock1= -1;
749         game.closng=true;
750         return true;
751     } else if (game.clock1 < 0)
752         --game.clock2;
753     if (game.clock2 == 0) {
754         /*  Once he's panicked, and clock2 has run out, we come here
755          *  to set up the storage room.  The room has two locs,
756          *  hardwired as 115 (ne) and 116 (sw).  At the ne end, we
757          *  place empty bottles, a nursery of plants, a bed of
758          *  oysters, a pile of lamps, rods with stars, sleeping
759          *  dwarves, and him.  At the sw end we place grate over
760          *  treasures, snake pit, covey of caged birds, more rods, and
761          *  pillows.  A mirror stretches across one wall.  Many of the
762          *  objects come from known locations and/or states (e.g. the
763          *  snake is known to have been destroyed and needn't be
764          *  carried away from its old "place"), making the various
765          *  objects be handled differently.  We also drop all other
766          *  objects he might be carrying (lest he have some which
767          *  could cause trouble, such as the keys).  We describe the
768          *  flash of light and trundle back. */
769         game.prop[BOTTLE]=PUT(BOTTLE,115,1);
770         game.prop[PLANT]=PUT(PLANT,115,0);
771         game.prop[OYSTER]=PUT(OYSTER,115,0);
772         OBJTXT[OYSTER]=3;
773         game.prop[LAMP]=PUT(LAMP,115,0);
774         game.prop[ROD]=PUT(ROD,115,0);
775         game.prop[DWARF]=PUT(DWARF,115,0);
776         game.loc=115;
777         game.oldloc=115;
778         game.newloc=115;
779         /*  Leave the grate with normal (non-negative) property.
780          *  Reuse sign. */
781         PUT(GRATE,116,0);
782         PUT(SIGN,116,0);
783         ++OBJTXT[SIGN];
784         game.prop[SNAKE]=PUT(SNAKE,116,1);
785         game.prop[BIRD]=PUT(BIRD,116,1);
786         game.prop[CAGE]=PUT(CAGE,116,0);
787         game.prop[ROD2]=PUT(ROD2,116,0);
788         game.prop[PILLOW]=PUT(PILLOW,116,0);
789
790         game.prop[MIRROR]=PUT(MIRROR,115,0);
791         game.fixed[MIRROR]=116;
792
793         for (int i=1; i<=NOBJECTS; i++) {
794             if (TOTING(i))
795                 DSTROY(i);
796         }
797
798         RSPEAK(132);
799         game.closed=true;
800         return true;
801     }
802
803     return false;
804 }
805
806 static void lampcheck(void)
807 /* Check game limit and lamp timers */
808 {
809     if (game.prop[LAMP] == 1)
810         --game.limit;
811
812     /*  Another way we can force an end to things is by having the
813      *  lamp give out.  When it gets close, we come here to warn
814      *  him.  First following ar, if the lamp and fresh batteries are
815      *  here, in which case we replace the batteries and continue.
816      *  Second is for other cases of lamp dying.  12400 is when it
817      *  goes out.  Even then, he can explore outside for a while
818      *  if desired. */
819     if (game.limit<=30 && HERE(BATTER) && game.prop[BATTER]==0 && HERE(LAMP))
820     {
821         RSPEAK(188);
822         game.prop[BATTER]=1;
823         if (TOTING(BATTER))
824             DROP(BATTER,game.loc);
825         game.limit=game.limit+2500;
826         game.lmwarn=false;
827     } else if (game.limit == 0) {
828         game.limit= -1;
829         game.prop[LAMP]=0;
830         if (HERE(LAMP))
831             RSPEAK(184);
832     } else if (game.limit <= 30) {
833         if (!game.lmwarn && HERE(LAMP)) {
834             game.lmwarn=true;
835             int spk=187;
836             if (game.place[BATTER] == 0)spk=183;
837             if (game.prop[BATTER] == 1)spk=189;
838             RSPEAK(spk);
839         }
840     }
841 }
842
843 static void listobjects(void)
844 /*  Print out descriptions of objects at this location.  If
845  *  not closing and property value is negative, tally off
846  *  another treasure.  Rug is special case; once seen, its
847  *  game.prop is 1 (dragon on it) till dragon is killed.
848  *  Similarly for chain; game.prop is initially 1 (locked to
849  *  bear).  These hacks are because game.prop=0 is needed to
850  *  get full score. */
851 {
852     if (!DARK(game.loc)) {
853         long obj;
854         ++game.abbrev[game.loc];
855         for (int i=game.atloc[game.loc]; i != 0; i=game.link[i]) {
856             obj=i;
857             if (obj > NOBJECTS)obj=obj-NOBJECTS;
858             if (obj == STEPS && TOTING(NUGGET))
859                 continue;
860             if (game.prop[obj] < 0) {
861                 if (game.closed)
862                     continue;
863                 game.prop[obj]=0;
864                 if (obj == RUG || obj == CHAIN)
865                     game.prop[obj]=1;
866                 --game.tally;
867                 /*  Note: There used to be a test here to see whether the
868                  *  player had blown it so badly that he could never ever see
869                  *  the remaining treasures, and if so the lamp was zapped to
870                  *  35 turns.  But the tests were too simple-minded; things
871                  *  like killing the bird before the snake was gone (can never
872                  *  see jewelry), and doing it "right" was hopeless.  E.G.,
873                  *  could cross troll bridge several times, using up all
874                  *  available treasures, breaking vase, using coins to buy
875                  *  batteries, etc., and eventually never be able to get
876                  *  across again.  If bottle were left on far side, could then
877                  *  never get eggs or trident, and the effects propagate.  So
878                  *  the whole thing was flushed.  anyone who makes such a
879                  *  gross blunder isn't likely to find everything else anyway
880                  *  (so goes the rationalisation). */
881             }
882             int kk=game.prop[obj];
883             if (obj == STEPS && game.loc == game.fixed[STEPS])
884                 kk=1;
885             PSPEAK(obj,kk);
886         }
887     }
888 }
889
890 static bool do_command(FILE *cmdin)
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     /*  Print text for current loc. */
927
928 L2000:
929     if (game.loc == 0)
930         croak(cmdin);
931     char* msg = short_location_descriptions[game.loc];
932     if (MOD(game.abbrev[game.loc],game.abbnum) == 0 || msg == 0)
933         msg=long_location_descriptions[game.loc];
934     if (!FORCED(game.loc) && DARK(game.loc)) {
935         /*  The easiest way to get killed is to fall into a pit in
936          *  pitch darkness. */
937         if (game.wzdark && PCT(35)) {
938             RSPEAK(23);
939             game.oldlc2 = game.loc;
940             croak(cmdin);
941             goto L2000;
942         }
943         msg=arbitrary_messages[16];
944     }
945     if (TOTING(BEAR))RSPEAK(141);
946     newspeak(msg);
947     KMOD=1;
948     if (FORCED(game.loc)) {
949         goto L8;
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: goto L8;
1059     case 1: part=unknown; obj = KMOD; break;
1060     case 2: part=intransitive; VERB = KMOD; break;
1061     case 3: RSPEAK(KMOD); goto L2012;
1062     default: BUG(22);
1063     }
1064
1065 Laction:
1066     switch (action(cmdin, part, VERB, obj)) {
1067     case 2: return true;
1068     case 8: KMOD=NUL; goto L8;
1069     case 2000: goto L2000;
1070     case 2012: goto L2012;
1071     case 2600: goto L2600;
1072     case 2607: goto L2607;
1073     case 2630: goto L2630;
1074     case 2800:
1075         /* Get second word for analysis. */
1076         WD1=WD2;
1077         WD1X=WD2X;
1078         WD2=0;
1079         goto L2620;
1080     case 8000:
1081         /*  Random intransitive verbs come here.  Clear obj just in case
1082          *  (see attack()). */
1083         SETPRM(1,WD1,WD1X);
1084         RSPEAK(257);
1085         obj=0;
1086         goto L2600;
1087     case 19000:
1088         /*  Oh dear, he's disturbed the dwarves. */
1089         RSPEAK(136);
1090         score(0);
1091         return true;
1092     default:
1093         BUG(99);
1094     }
1095
1096     /* no fallthrough here */
1097          
1098     /*  Figure out the new location */
1099 L8:     if (playermove(cmdin, VERB, KMOD))
1100         return true;
1101     else
1102         goto L2000;
1103 }