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