2 * There used to be a note that said this:
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.
8 * Now that the code has been restructured into something much closer
9 * to idiomatic C, the following is more appropriate:
11 * ESR apologizes for the remaing gotos (now confined to one function
12 * in this file - there used to be over 350 of them, *everywhere*),
13 * and for the offensive globals. Applying the Structured Program
14 * Theorem can be hard.
16 #define DEFINE_GLOBALS_FROM_INCLUDES
25 #include "linenoise/linenoise.h"
28 #define DIM(a) (sizeof(a)/sizeof(a[0]))
30 /* Abstract out the encoding of words in the travel array. Gives us
31 * some hope of getting to a less cryptic representation than we
32 * inherited from FORTRAN, someday. To understand these, read the
33 * encoding description for TRAVEL.
35 #define T_DESTINATION(entry) MOD(labs(entry) / 1000, 1000)
36 #define T_NODWARVES(entry) labs(entry) / 1000000 == 100
37 #define T_MOTION(entry) MOD(labs(entry), 1000)
38 #define L_SPEAK(loc) ((loc) - 500)
39 #define T_TERMINATE(entry) (T_MOTION(entry) == 1)
44 char rawbuf[LINESIZE], INLINE[LINESIZE + 1];
46 long AMBER, AXE, BACK, BATTERY, BEAR, BIRD, BLOOD,
47 BOTTLE, CAGE, CAVE, CAVITY, CHAIN, CHASM, CHEST,
48 CLAM, COINS, DOOR, DPRSSN, DRAGON, DWARF, EGGS,
49 EMERALD, ENTER, ENTRNC, FIND, FISSURE, FOOD,
50 GRATE, HINT, INVENT, JADE, KEYS,
51 KNIFE, LAMP, LOCK, LOOK, MAGAZINE,
52 MESSAG, MIRROR, NUGGET, NUL, OGRE, OIL, OYSTER,
53 PEARL, PILLOW, PLANT, PLANT2, PYRAMID, RESER, ROD, ROD2,
54 RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
55 STEPS, STREAM, THROW, TRIDENT, TROLL, TROLL2,
56 URN, VASE, VEND, VOLCANO, WATER;
58 FILE *logfp = NULL, *rfp = NULL;
59 bool oldstyle = false;
63 static void sig_handler(int signo)
65 if (signo == SIGINT) {
75 * Adventure (rev 2: 20 treasures)
77 * History: Original idea & 5-treasure version (adventures) by Willie Crowther
78 * 15-treasure version (adventure) by Don Woods, April-June 1977
79 * 20-treasure version (rev 2) by Don Woods, August 1978
80 * Errata fixed: 78/12/25
81 * Revived 2017 as Open Adventure.
84 static bool do_command(FILE *);
86 int main(int argc, char *argv[])
93 const char* opts = "l:or:s";
94 const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [-s] \n";
96 const char* opts = "l:os";
97 const char* usage = "Usage: %s [-l logfilename] [-o] [-s] \n";
99 while ((ch = getopt(argc, argv, opts)) != EOF) {
102 logfp = fopen(optarg, "w");
105 "advent: can't open logfile %s for write\n",
107 signal(SIGINT, sig_handler);
111 editline = prompt = false;
113 #ifndef ADVENT_NOSAVE
115 rfp = fopen(optarg, "r");
118 "advent: can't open save file %s for read\n",
120 signal(SIGINT, sig_handler);
130 " -l create a log file of your game named as specified'\n");
132 " -o 'oldstyle' (no prompt, no command editing, displays 'Initialising...')\n");
133 #ifndef ADVENT_NOSAVE
135 " -r restore from specified saved game file\n");
138 " -s suppress command editing\n");
144 linenoiseHistorySetMaxLen(350);
146 /* Initialize our LCG PRNG with parameters tested against
147 * Knuth vol. 2. by the original authors */
150 game.lcg_m = 1048576;
152 long seedval = (long)rand();
155 /* Initialize game variables */
158 /* Start-up, dwarf stuff */
159 game.zzword = rndvoc(3, 0);
160 game.newloc = LOC_START;
161 game.loc = LOC_START;
162 game.limit = GAMELIMIT;
164 game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
166 game.limit = NOVICELIMIT;
172 fprintf(logfp, "seed %ld\n", seedval);
174 /* interpret commands until EOF or interrupt */
176 if (!do_command(stdin))
179 /* show score and exit */
183 static bool fallback_handler(char *buf)
184 /* fallback handler for commands not handled by FORTRANish parser */
187 if (sscanf(buf, "seed %ld", &sv) == 1) {
189 printf("Seed set to %ld\n", sv);
190 // autogenerated, so don't charge user time for it.
192 // here we reconfigure any global game state that uses random numbers
193 game.zzword = rndvoc(3, 0);
199 /* Check if this loc is eligible for any hints. If been here long
200 * enough, display. Ignore "HINTS" < 4 (special stuff, see database
202 static void checkhints(void)
204 if (conditions[game.loc] >= game.conds) {
205 for (int hint = 0; hint < NHINTS; hint++) {
206 if (game.hinted[hint])
208 if (!CNDBIT(game.loc, hint + 1 + COND_HBASE))
209 game.hintlc[hint] = -1;
211 /* Come here if he's been long enough at required loc(s) for some
213 if (game.hintlc[hint] >= hints[hint].turns) {
219 if (game.prop[GRATE] == GRATE_CLOSED && !HERE(KEYS))
221 game.hintlc[hint] = 0;
224 if (game.place[BIRD] == game.loc && TOTING(ROD) && game.oldobj == BIRD)
228 if (HERE(SNAKE) && !HERE(BIRD))
230 game.hintlc[hint] = 0;
233 if (game.atloc[game.loc] == 0 &&
234 game.atloc[game.oldloc] == 0 &&
235 game.atloc[game.oldlc2] == 0 &&
238 game.hintlc[hint] = 0;
241 if (game.prop[EMERALD] != -1 && game.prop[PYRAMID] == -1)
243 game.hintlc[hint] = 0;
250 game.hintlc[hint] = 0;
253 if (game.atloc[game.loc] == 0 &&
254 game.atloc[game.oldloc] == 0 &&
255 game.atloc[game.oldlc2] == 0)
259 i = atdwrf(game.loc);
261 game.hintlc[hint] = 0;
264 if (HERE(OGRE) && i == 0)
268 if (game.tally == 1 && game.prop[JADE] < 0)
270 game.hintlc[hint] = 0;
273 BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST);
277 /* Fall through to hint display */
278 game.hintlc[hint] = 0;
279 if (!yes(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
281 rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty);
282 game.hinted[hint] = yes(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
283 if (game.hinted[hint] && game.limit > WARNTIME)
284 game.limit += WARNTIME * hints[hint].penalty;
290 static bool spotted_by_pirate(int i)
295 /* The pirate's spotted him. He leaves him alone once we've
296 * found chest. K counts if a treasure is here. If not, and
297 * tally=1 for an unseen chest, let the pirate be spotted. Note
298 * that game.place[CHEST] = LOC_NOWHERE might mean that he's thrown
299 * it to the troll, but in that case he's seen the chest
301 if (game.loc == game.chloc || game.prop[CHEST] >= 0)
304 bool movechest = false, robplayer = false;
305 for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
306 if (!object_descriptions[treasure].is_treasure)
308 /* Pirate won't take pyramid from plover room or dark
309 * room (too easy!). */
310 if (treasure == PYRAMID && (game.loc == object_descriptions[PYRAMID].plac || game.loc == object_descriptions[EMERALD].plac)) {
313 if (TOTING(treasure) || HERE(treasure))
315 if (TOTING(treasure)) {
320 /* Force chest placement before player finds last treasure */
321 if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == LOC_NOWHERE && HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT) {
322 rspeak(PIRATE_SPOTTED);
325 /* Do things in this order (chest move before robbery) so chest is listed
326 * last at the maze location. */
328 move(CHEST, game.chloc);
329 move(MESSAG, game.chloc2);
330 game.dloc[PIRATE] = game.chloc;
331 game.odloc[PIRATE] = game.chloc;
332 game.dseen[PIRATE] = false;
334 /* You might get a hint of the pirate's presence even if the
335 * chest doesn't move... */
336 if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20))
337 rspeak(PIRATE_RUSTLES);
340 rspeak(PIRATE_POUNCES);
341 for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
342 if (!object_descriptions[treasure].is_treasure)
344 if (!(treasure == PYRAMID && (game.loc == object_descriptions[PYRAMID].plac || game.loc == object_descriptions[EMERALD].plac))) {
345 if (AT(treasure) && game.fixed[treasure] == 0)
346 carry(treasure, game.loc);
347 if (TOTING(treasure))
348 drop(treasure, game.chloc);
356 static bool dwarfmove(void)
357 /* Dwarves move. Return true if player survives, false if he dies. */
359 int kk, stick, attack;
362 /* Dwarf stuff. See earlier comments for description of
363 * variables. Remember sixth dwarf is pirate and is thus
364 * very different except for motion rules. */
366 /* First off, don't let the dwarves follow him into a pit or
367 * a wall. Activate the whole mess the first time he gets as
368 * far as the hall of mists (loc 15). If game.newloc is
369 * forbidden to pirate (in particular, if it's beyond the
370 * troll bridge), bypass dwarf stuff. That way pirate can't
371 * steal return toll, and dwarves can't meet the bear. Also
372 * means dwarves won't follow him into dead end in maze, but
373 * c'est la vie. They'll wait for him outside the dead
375 if (game.loc == 0 || FORCED(game.loc) || CNDBIT(game.newloc, COND_NOARRR))
378 /* Dwarf activity level ratchets up */
379 if (game.dflag == 0) {
380 if (INDEEP(game.loc))
385 /* When we encounter the first dwarf, we kill 0, 1, or 2 of
386 * the 5 dwarves. If any of the survivors is at loc,
387 * replace him with the alternate. */
388 if (game.dflag == 1) {
389 if (!INDEEP(game.loc) || (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85))))
392 for (int i = 1; i <= 2; i++) {
393 int j = 1 + randrange(NDWARVES - 1);
397 for (int i = 1; i <= NDWARVES - 1; i++) {
398 if (game.dloc[i] == game.loc)
399 game.dloc[i] = DALTLC;
400 game.odloc[i] = game.dloc[i];
407 /* Things are in full swing. Move each dwarf at random,
408 * except if he's seen us he sticks with us. Dwarves stay
409 * deep inside. If wandering at random, they don't back up
410 * unless there's no alternative. If they don't have to
411 * move, they attack. And, of course, dead dwarves don't do
412 * much of anything. */
416 for (int i = 1; i <= NDWARVES; i++) {
417 if (game.dloc[i] == 0)
419 /* Fill tk array with all the places this dwarf might go. */
421 kk = KEY[game.dloc[i]];
424 game.newloc = T_DESTINATION(TRAVEL[kk]);
425 /* Have we avoided a dwarf encounter? */
426 bool avoided = (SPECIAL(game.newloc) ||
427 !INDEEP(game.newloc) ||
428 game.newloc == game.odloc[i] ||
429 (j > 1 && game.newloc == tk[j - 1]) ||
431 game.newloc == game.dloc[i] ||
432 FORCED(game.newloc) ||
433 (i == PIRATE && CNDBIT(game.newloc, COND_NOARRR)) ||
434 T_NODWARVES(TRAVEL[kk]));
436 tk[j++] = game.newloc;
440 (TRAVEL[kk - 1] >= 0);
441 tk[j] = game.odloc[i];
444 j = 1 + randrange(j);
445 game.odloc[i] = game.dloc[i];
446 game.dloc[i] = tk[j];
447 game.dseen[i] = (game.dseen[i] && INDEEP(game.loc)) || (game.dloc[i] == game.loc || game.odloc[i] == game.loc);
448 if (!game.dseen[i]) continue;
449 game.dloc[i] = game.loc;
450 if (spotted_by_pirate(i))
452 /* This threatening little dwarf is in the room with him! */
454 if (game.odloc[i] == game.dloc[i]) {
456 if (game.knfloc >= 0)
457 game.knfloc = game.loc;
458 if (randrange(1000) < 95 * (game.dflag - 2))
463 /* Now we know what's happening. Let's tell the poor sucker about it.
464 * Note that various of the "knife" messages must have specific relative
465 * positions in the rspeak database. */
466 if (game.dtotal == 0)
468 rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
471 if (game.dflag == 2)game.dflag = 3;
473 rspeak(THROWN_KNIVES, attack);
474 rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick);
476 rspeak(KNIFE_THROWN);
481 game.oldlc2 = game.loc;
485 /* "You're dead, Jim."
487 * If the current loc is zero, it means the clown got himself killed.
488 * We'll allow this maxdie times. NDEATHS is automatically set based
489 * on the number of snide messages available. Each death results in
490 * a message (81, 83, etc.) which offers reincarnation; if accepted,
491 * this results in message 82, 84, etc. The last time, if he wants
492 * another chance, he gets a snide remark as we exit. When
493 * reincarnated, all objects being carried get dropped at game.oldlc2
494 * (presumably the last place prior to being killed) without change
495 * of props. the loop runs backwards to assure that the bird is
496 * dropped before the cage. (this kluge could be changed once we're
497 * sure all references to bird and cage are done by keywords.) The
498 * lamp is a special case (it wouldn't do to leave it in the cave).
499 * It is turned off and left outside the building (only if he was
500 * carrying it, of course). He himself is left inside the building
501 * (and heaven help him if he tries to xyzzy back into the cave
502 * without the lamp!). game.oldloc is zapped so he can't just
505 static void croak(void)
506 /* Okay, he's dead. Let's get on with it. */
508 const char* query = obituaries[game.numdie].query;
509 const char* yes_response = obituaries[game.numdie].yes_response;
512 /* He died during closing time. No resurrection. Tally up a
514 rspeak(DEATH_CLOSING);
516 } else if (game.numdie == NDEATHS || !yes(query, yes_response, arbitrary_messages[OK_MAN]))
519 game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
521 game.prop[LAMP] = LAMP_DARK;
522 for (int j = 1; j <= NOBJECTS; j++) {
523 int i = NOBJECTS + 1 - j;
525 /* Always leave lamp where it's accessible aboveground */
526 drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
529 game.loc = LOC_BUILDING;
530 game.oldloc = game.loc;
534 /* Given the current location in "game.loc", and a motion verb number in
535 * "motion", put the new location in "game.newloc". The current loc is saved
536 * in "game.oldloc" in case he wants to retreat. The current
537 * game.oldloc is saved in game.oldlc2, in case he dies. (if he
538 * does, game.newloc will be limbo, and game.oldloc will be what killed
539 * him, so we need game.oldlc2, which is the last place he was
542 static bool playermove(token_t verb, int motion)
544 int scratchloc, k2, kk = KEY[game.loc];
545 game.newloc = game.loc;
547 BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES);
550 else if (motion == BACK) {
551 /* Handle "go back". Look for verb which goes from game.loc to
552 * game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion.
553 * k2 saves entry -> forced loc -> previous loc. */
554 motion = game.oldloc;
556 motion = game.oldlc2;
557 game.oldlc2 = game.oldloc;
558 game.oldloc = game.loc;
560 if (motion == game.loc)k2 = FORGOT_PATH;
561 if (CNDBIT(game.loc, COND_NOBACK))k2 = TWIST_TURN;
564 scratchloc = T_DESTINATION(TRAVEL[kk]);
565 if (scratchloc != motion) {
566 if (!SPECIAL(scratchloc)) {
567 if (FORCED(scratchloc) && T_DESTINATION(TRAVEL[KEY[scratchloc]]) == motion)
570 if (TRAVEL[kk] >= 0) {
571 ++kk; /* go to next travel entry for this location */
574 /* we've reached the end of travel entries for game.loc */
577 rspeak(NOT_CONNECTED);
582 motion = T_MOTION(TRAVEL[kk]);
584 break; /* fall through to ordinary travel */
590 } else if (motion == LOOK) {
591 /* Look. Can't give more detail. Pretend it wasn't dark
592 * (though it may now be dark) so he won't fall into a
593 * pit while staring into the gloom. */
595 rspeak(NO_MORE_DETAIL);
598 game.abbrev[game.loc] = 0;
600 } else if (motion == CAVE) {
601 /* Cave. Different messages depending on whether above ground. */
602 rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
605 /* none of the specials */
606 game.oldlc2 = game.oldloc;
607 game.oldloc = game.loc;
610 /* Look for a way to fulfil the motion - kk indexes the beginning
611 * of the motion entries for here (game.loc). */
613 if (T_TERMINATE(TRAVEL[kk]) || T_MOTION(TRAVEL[kk]) == motion)
615 if (TRAVEL[kk] < 0) {
616 /* FIXME: Magic numbers! */
617 /* Couldn't find an entry matching the motion word passed
618 * in. Various messages depending on word given. */
619 int spk = CANT_APPLY;
620 if (motion >= 43 && motion <= 50)spk = BAD_DIRECTION;
621 if (motion == 29 || motion == 30)spk = BAD_DIRECTION;
622 if (motion == 7 || motion == 36 || motion == 37)spk = UNSURE_FACING;
623 if (motion == 11 || motion == 19)spk = NO_INOUT_HERE;
624 if (verb == FIND || verb == INVENT)spk = NEARBY;
625 if (motion == 62 || motion == 65)spk = NOTHING_HAPPENS;
626 if (motion == 17)spk = WHICH_WAY;
632 scratchloc = labs(TRAVEL[kk]) / 1000;
636 * (ESR) This special-travel loop may have to be repeated if it includes
637 * the plover passage. Same deal for any future cases where we need to
638 * block travel and then redo it once the blocking condition has been
641 for (;;) { /* L12 loop */
643 game.newloc = scratchloc / 1000;
644 motion = MOD(game.newloc, 100);
645 if (!SPECIAL(game.newloc)) {
646 if (game.newloc <= 100) {
647 if (game.newloc == 0 || PCT(game.newloc))
649 /* else fall through */
651 if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
653 /* else fall through */
654 } else if (game.prop[motion] != game.newloc / 100 - 3)
658 BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION);
660 game.newloc = labs(TRAVEL[kk]) / 1000;
662 (game.newloc == scratchloc);
663 scratchloc = game.newloc;
666 game.newloc = MOD(scratchloc, 1000);
667 if (!SPECIAL(game.newloc))
669 if (game.newloc <= 500) {
670 game.newloc -= SPECIALBASE;
671 switch (game.newloc) {
673 /* Travel 301. Plover-alcove passage. Can carry only
674 * emerald. Note: travel table must include "useless"
675 * entries going through passage, which can never be used for
676 * actual motion, but can be spotted by "go back". */
677 /* FIXME: Arithmetic on location numbers */
678 game.newloc = 99 + 100 - game.loc;
679 if (game.holdng > 1 || (game.holdng == 1 && !TOTING(EMERALD))) {
680 game.newloc = game.loc;
685 /* Travel 302. Plover transport. Drop the emerald (only use
686 * special travel if toting it), so he's forced to use the
687 * plover-passage to get it out. Having dropped it, go back and
688 * pretend he wasn't carrying it after all. */
689 drop(EMERALD, game.loc);
692 BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION);
694 game.newloc = labs(TRAVEL[kk]) / 1000;
696 (game.newloc == scratchloc);
697 scratchloc = game.newloc;
698 continue; /* goto L12 */
700 /* Travel 303. Troll bridge. Must be done only as special
701 * motion so that dwarves won't wander across and encounter
702 * the bear. (They won't follow the player there because
703 * that region is forbidden to the pirate.) If
704 * game.prop(TROLL)=1, he's crossed since paying, so step out
705 * and block him. (standard travel entries check for
706 * game.prop(TROLL)=0.) Special stuff for bear. */
707 if (game.prop[TROLL] == 1) {
708 pspeak(TROLL,look, 1);
709 game.prop[TROLL] = 0;
711 move(TROLL2 + NOBJECTS, 0);
712 move(TROLL, object_descriptions[TROLL].plac);
713 move(TROLL + NOBJECTS, object_descriptions[TROLL].fixd);
715 game.newloc = game.loc;
718 game.newloc = object_descriptions[TROLL].plac + object_descriptions[TROLL].fixd - game.loc;
719 if (game.prop[TROLL] == 0)game.prop[TROLL] = 1;
720 if (!TOTING(BEAR)) return true;
721 rspeak(BRIDGE_COLLAPSE);
722 game.prop[CHASM] = 1;
723 game.prop[TROLL] = 2;
724 drop(BEAR, game.newloc);
725 game.fixed[BEAR] = -1;
727 game.oldlc2 = game.newloc;
732 BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST);
734 break; /* Leave L12 loop */
739 /* Execute a speak rule */
740 rspeak(L_SPEAK(game.newloc));
741 game.newloc = game.loc;
745 static bool closecheck(void)
746 /* Handle the closing of the cave. The cave closes "clock1" turns
747 * after the last treasure has been located (including the pirate's
748 * chest, which may of course never show up). Note that the
749 * treasures need not have been taken yet, just located. Hence
750 * clock1 must be large enough to get out of the cave (it only ticks
751 * while inside the cave). When it hits zero, we branch to 10000 to
752 * start closing the cave, and then sit back and wait for him to try
753 * to get out. If he doesn't within clock2 turns, we close the cave;
754 * if he does try, we assume he panics, and give him a few additional
755 * turns to get frantic before we close. When clock2 hits zero, we
756 * branch to 11000 to transport him into the final puzzle. Note that
757 * the puzzle depends upon all sorts of random things. For instance,
758 * there must be no water or oil, since there are beanstalks which we
759 * don't want to be able to water, since the code can't handle it.
760 * Also, we can have no keys, since there is a grate (having moved
761 * the fixed object!) there separating him from all the treasures.
762 * Most of these problems arise from the use of negative prop numbers
763 * to suppress the object descriptions until he's actually moved the
766 if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2)
769 /* When the first warning comes, we lock the grate, destroy
770 * the bridge, kill all the dwarves (and the pirate), remove
771 * the troll and bear (unless dead), and set "closng" to
772 * true. Leave the dragon; too much trouble to move it.
773 * from now until clock2 runs out, he cannot unlock the
774 * grate, move to any location outside the cave, or create
775 * the bridge. Nor can he be resurrected if he dies. Note
776 * that the snake is already gone, since he got to the
777 * treasure accessible only via the hall of the mountain
778 * king. Also, he's been in giant room (to get eggs), so we
779 * can refer to it. Also also, he's gotten the pearl, so we
780 * know the bivalve is an oyster. *And*, the dwarves must
781 * have been activated, since we've found chest. */
782 if (game.clock1 == 0) {
783 game.prop[GRATE] = GRATE_CLOSED;
784 game.prop[FISSURE] = 0;
785 for (int i = 1; i <= NDWARVES; i++) {
786 game.dseen[i] = false;
790 move(TROLL + NOBJECTS, 0);
791 move(TROLL2, object_descriptions[TROLL].plac);
792 move(TROLL2 + NOBJECTS, object_descriptions[TROLL].fixd);
794 if (game.prop[BEAR] != 3)DESTROY(BEAR);
795 game.prop[CHAIN] = 0;
796 game.fixed[CHAIN] = 0;
799 rspeak(CAVE_CLOSING);
803 } else if (game.clock1 < 0)
805 if (game.clock2 == 0) {
806 /* Once he's panicked, and clock2 has run out, we come here
807 * to set up the storage room. The room has two locs,
808 * hardwired as LOC_NE and LOC_SW. At the ne end, we
809 * place empty bottles, a nursery of plants, a bed of
810 * oysters, a pile of lamps, rods with stars, sleeping
811 * dwarves, and him. At the sw end we place grate over
812 * treasures, snake pit, covey of caged birds, more rods, and
813 * pillows. A mirror stretches across one wall. Many of the
814 * objects come from known locations and/or states (e.g. the
815 * snake is known to have been destroyed and needn't be
816 * carried away from its old "place"), making the various
817 * objects be handled differently. We also drop all other
818 * objects he might be carrying (lest he have some which
819 * could cause trouble, such as the keys). We describe the
820 * flash of light and trundle back. */
821 game.prop[BOTTLE] = put(BOTTLE, LOC_NE, EMPTY_BOTTLE);
822 game.prop[PLANT] = put(PLANT, LOC_NE, 0);
823 game.prop[OYSTER] = put(OYSTER, LOC_NE, 0);
824 game.prop[LAMP] = put(LAMP, LOC_NE, 0);
825 game.prop[ROD] = put(ROD, LOC_NE, 0);
826 game.prop[DWARF] = put(DWARF, LOC_NE, 0);
828 game.oldloc = LOC_NE;
829 game.newloc = LOC_NE;
830 /* Leave the grate with normal (non-negative) property.
832 put(GRATE, LOC_SW, 0);
833 put(SIGN, LOC_SW, 0);
834 game.prop[SIGN] = ENDGAME_SIGN;
835 game.prop[SNAKE] = put(SNAKE, LOC_SW, 1);
836 game.prop[BIRD] = put(BIRD, LOC_SW, 1);
837 game.prop[CAGE] = put(CAGE, LOC_SW, 0);
838 game.prop[ROD2] = put(ROD2, LOC_SW, 0);
839 game.prop[PILLOW] = put(PILLOW, LOC_SW, 0);
841 game.prop[MIRROR] = put(MIRROR, LOC_NE, 0);
842 game.fixed[MIRROR] = LOC_SW;
844 for (int i = 1; i <= NOBJECTS; i++) {
857 static void lampcheck(void)
858 /* Check game limit and lamp timers */
860 if (game.prop[LAMP] == LAMP_BRIGHT)
863 /* Another way we can force an end to things is by having the
864 * lamp give out. When it gets close, we come here to warn him.
865 * First following arm checks if the lamp and fresh batteries are
866 * here, in which case we replace the batteries and continue.
867 * Second is for other cases of lamp dying. Eve after it goes
868 * out, he can explore outside for a while if desired. */
869 if (game.limit <= WARNTIME && HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) {
870 rspeak(REPLACE_BATTERIES);
871 game.prop[BATTERY] = DEAD_BATTERIES;
873 drop(BATTERY, game.loc);
874 game.limit += BATTERYLIFE;
876 } else if (game.limit == 0) {
878 game.prop[LAMP] = LAMP_DARK;
881 } else if (game.limit <= WARNTIME) {
882 if (!game.lmwarn && HERE(LAMP)) {
884 int spk = GET_BATTERIES;
885 if (game.place[BATTERY] == LOC_NOWHERE)spk = LAMP_DIM;
886 if (game.prop[BATTERY] == DEAD_BATTERIES)
887 spk = MISSING_BATTERIES;
893 static void listobjects(void)
894 /* Print out descriptions of objects at this location. If
895 * not closing and property value is negative, tally off
896 * another treasure. Rug is special case; once seen, its
897 * game.prop is 1 (dragon on it) till dragon is killed.
898 * Similarly for chain; game.prop is initially 1 (locked to
899 * bear). These hacks are because game.prop=0 is needed to
902 if (!DARK(game.loc)) {
903 ++game.abbrev[game.loc];
904 for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) {
906 if (obj > NOBJECTS)obj = obj - NOBJECTS;
907 if (obj == STEPS && TOTING(NUGGET))
909 if (game.prop[obj] < 0) {
913 if (obj == RUG || obj == CHAIN)
916 /* Note: There used to be a test here to see whether the
917 * player had blown it so badly that he could never ever see
918 * the remaining treasures, and if so the lamp was zapped to
919 * 35 turns. But the tests were too simple-minded; things
920 * like killing the bird before the snake was gone (can never
921 * see jewelry), and doing it "right" was hopeless. E.G.,
922 * could cross troll bridge several times, using up all
923 * available treasures, breaking vase, using coins to buy
924 * batteries, etc., and eventually never be able to get
925 * across again. If bottle were left on far side, could then
926 * never get eggs or trident, and the effects propagate. So
927 * the whole thing was flushed. anyone who makes such a
928 * gross blunder isn't likely to find everything else anyway
929 * (so goes the rationalisation). */
931 int kk = game.prop[obj];
932 if (obj == STEPS && game.loc == game.fixed[STEPS])
934 pspeak(obj, look, kk);
939 static bool do_command(FILE *cmdin)
940 /* Get and execute a command */
945 static struct command_t command;
948 /* Can't leave cave once it's closing (except by main office). */
949 if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
951 game.newloc = game.loc;
952 if (!game.panic)game.clock2 = PANICTIME;
956 /* See if a dwarf has seen him and has come from where he
957 * wants to go. If so, the dwarf's blocking his way. If
958 * coming from place forbidden to pirate (dwarves rooted in
959 * place) let him get out (and attacked). */
960 if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, COND_NOARRR)) {
961 for (size_t i = 1; i <= NDWARVES - 1; i++) {
962 if (game.odloc[i] == game.newloc && game.dseen[i]) {
963 game.newloc = game.loc;
969 game.loc = game.newloc;
974 /* Describe the current location and (maybe) get next command. */
979 const char* msg = locations[game.loc].description.small;
980 if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == 0)
981 msg = locations[game.loc].description.big;
982 if (!FORCED(game.loc) && DARK(game.loc)) {
983 /* The easiest way to get killed is to fall into a pit in
985 if (game.wzdark && PCT(35)) {
987 game.oldlc2 = game.loc;
989 continue; /* back to top of main interpreter loop */
991 msg = arbitrary_messages[PITCH_DARK];
993 if (TOTING(BEAR))rspeak(TAME_BEAR);
995 if (FORCED(game.loc)) {
996 if (playermove(command.verb, 1))
999 continue; /* back to top of main interpreter loop */
1001 if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
1008 game.oldobj = command.obj;
1014 /* If closing time, check for any objects being toted with
1015 * game.prop < 0 and set the prop to -1-game.prop. This way
1016 * objects won't be described until they've been picked up
1017 * and put down separate from their respective piles. Don't
1018 * tick game.clock1 unless well into cave (and not at Y2). */
1020 if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
1021 pspeak(OYSTER, look, 1);
1022 for (size_t i = 1; i <= NOBJECTS; i++) {
1023 if (TOTING(i) && game.prop[i] < 0)
1024 game.prop[i] = -1 - game.prop[i];
1027 game.wzdark = DARK(game.loc);
1028 if (game.knfloc > 0 && game.knfloc != game.loc)
1031 /* This is where we get a new command from the user */
1032 if (!GETIN(cmdin, &command.wd1, &command.wd1x, &command.wd2, &command.wd2x))
1035 /* Every input, check "game.foobar" flag. If zero, nothing's
1036 * going on. If pos, make neg. If neg, he skipped a word,
1037 * so make it zero. */
1039 game.foobar = (game.foobar > 0 ? -game.foobar : 0);
1042 /* If a turn threshold has been met, apply penalties and tell
1043 * the player about it. */
1044 for (int i = 0; i < NTHRESHOLDS; ++i)
1046 if (game.turns == turn_thresholds[i].threshold + 1)
1048 game.trnluz += turn_thresholds[i].point_loss;
1049 speak(turn_thresholds[i].message);
1053 if (command.verb == SAY && command.wd2 > 0)
1055 if (command.verb == SAY) {
1056 command.part = transitive;
1065 V1 = vocab(command.wd1, -1);
1066 V2 = vocab(command.wd2, -1);
1067 if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
1068 if (LIQLOC(game.loc) == WATER) {
1071 rspeak(WHERE_QUERY);
1075 if (V1 == ENTER && command.wd2 > 0) {
1076 command.wd1 = command.wd2;
1077 command.wd1x = command.wd2x;
1078 wordclear(&command.wd2);
1080 /* FIXME: Magic numbers */
1081 if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
1082 (V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
1084 command.wd2 = MAKEWD(WORD_POUR);
1086 if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
1087 command.wd1 = MAKEWD(WORD_CATCH);
1090 if (wordeq(command.wd1, MAKEWD(WORD_WEST))) {
1092 if (game.iwest == 10)
1095 if (wordeq(command.wd1, MAKEWD(WORD_GO)) && !wordempty(command.wd2)) {
1097 rspeak(GO_UNNEEDED);
1100 defn = vocab(command.wd1, -1);
1102 /* Gee, I don't understand. */
1103 if (fallback_handler(rawbuf))
1105 rspeak(DONT_KNOW, command.wd1, command.wd1x);
1108 kmod = MOD(defn, 1000);
1109 switch (defn / 1000) {
1111 if (playermove(command.verb, kmod))
1114 continue; /* back to top of main interpreter loop */
1116 command.part = unknown;
1120 command.part = intransitive;
1121 command.verb = kmod;
1127 BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3);
1131 switch (action(cmdin, &command)) {
1135 playermove(command.verb, NUL);
1138 continue; /* back to top of main interpreter loop */
1148 /* Get second word for analysis. */
1149 command.wd1 = command.wd2;
1150 command.wd1x = command.wd2x;
1151 wordclear(&command.wd2);
1154 /* Random intransitive verbs come here. Clear obj just in case
1155 * (see attack()). */
1156 rspeak(DO_WHAT, command.wd1, command.wd1x);
1160 /* Oh dear, he's disturbed the dwarves. */
1161 rspeak(DWARVES_AWAKEN);
1164 BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH);