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