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