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