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