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