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