+{
+ const char* query = obituaries[game.numdie].query;
+ const char* yes_response = obituaries[game.numdie].yes_response;
+ ++game.numdie;
+ if (game.closng) {
+ /* He died during closing time. No resurrection. Tally up a
+ * death and exit. */
+ rspeak(DEATH_CLOSING);
+ terminate(endgame);
+ } else if (game.numdie == NDEATHS || !yes(query, yes_response, arbitrary_messages[OK_MAN]))
+ terminate(endgame);
+ else {
+ game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
+ if (TOTING(LAMP))
+ game.prop[LAMP] = LAMP_DARK;
+ for (int j = 1; j <= NOBJECTS; j++) {
+ int i = NOBJECTS + 1 - j;
+ if (TOTING(i)) {
+ /* Always leave lamp where it's accessible aboveground */
+ drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
+ }
+ }
+ game.loc = LOC_BUILDING;
+ game.oldloc = game.loc;
+ }
+}
+
+/* Given the current location in "game.loc", and a motion verb number in
+ * "motion", put the new location in "game.newloc". The current loc is saved
+ * in "game.oldloc" in case he wants to retreat. The current
+ * game.oldloc is saved in game.oldlc2, in case he dies. (if he
+ * does, game.newloc will be limbo, and game.oldloc will be what killed
+ * him, so we need game.oldlc2, which is the last place he was
+ * safe.) */
+
+static bool playermove(token_t verb, int motion)
+{
+ int scratchloc, k2, kk = tkey[game.loc];
+ game.newloc = game.loc;
+ if (kk == 0)
+ BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE
+ if (motion == NUL)
+ return true;
+ else if (motion == BACK) {
+ /* Handle "go back". Look for verb which goes from game.loc to
+ * game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion.
+ * k2 saves entry -> forced loc -> previous loc. */
+ motion = game.oldloc;
+ if (FORCED(motion))
+ motion = game.oldlc2;
+ game.oldlc2 = game.oldloc;
+ game.oldloc = game.loc;
+ k2 = 0;
+ if (motion == game.loc)k2 = FORGOT_PATH;
+ if (CNDBIT(game.loc, COND_NOBACK))k2 = TWIST_TURN;
+ if (k2 == 0) {
+ for (;;) {
+ scratchloc = T_DESTINATION(travel[kk]);
+ if (scratchloc != motion) {
+ if (!SPECIAL(scratchloc)) {
+ if (FORCED(scratchloc) && T_DESTINATION(travel[tkey[scratchloc]]) == motion)
+ k2 = kk;
+ }
+ if (!travel[kk].stop) {
+ ++kk; /* go to next travel entry for this location */
+ continue;
+ }
+ /* we've reached the end of travel entries for game.loc */
+ kk = k2;
+ if (kk == 0) {
+ rspeak(NOT_CONNECTED);
+ return true;
+ }
+ }
+
+ motion = travel[kk].motion;
+ kk = tkey[game.loc];
+ break; /* fall through to ordinary travel */
+ }
+ } else {
+ rspeak(k2);
+ return true;
+ }
+ } else if (motion == LOOK) {
+ /* Look. Can't give more detail. Pretend it wasn't dark
+ * (though it may now be dark) so he won't fall into a
+ * pit while staring into the gloom. */
+ if (game.detail < 3)
+ rspeak(NO_MORE_DETAIL);
+ ++game.detail;
+ game.wzdark = false;
+ game.abbrev[game.loc] = 0;
+ return true;
+ } else if (motion == CAVE) {
+ /* Cave. Different messages depending on whether above ground. */
+ rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
+ return true;
+ } else {
+ /* none of the specials */
+ game.oldlc2 = game.oldloc;
+ game.oldloc = game.loc;
+ }
+
+ /* Look for a way to fulfil the motion verb passed in - kk indexes
+ * the beginning of the motion entries for here (game.loc). */
+ for (;;) {
+ if (T_TERMINATE(travel[kk]) || travel[kk].motion == motion)
+ break;
+ if (travel[kk].stop) {
+ /* FIXME: Magic numbers! */
+ /* Couldn't find an entry matching the motion word passed
+ * in. Various messages depending on word given. */
+ int spk = CANT_APPLY;
+ if (motion >= 43 && motion <= 50)spk = BAD_DIRECTION;
+ if (motion == 29 || motion == 30)spk = BAD_DIRECTION;
+ if (motion == 7 || motion == 36 || motion == 37)spk = UNSURE_FACING;
+ if (motion == 11 || motion == 19)spk = NO_INOUT_HERE;
+ if (verb == FIND || verb == INVENTORY)spk = NEARBY;
+ if (motion == 62 || motion == 65)spk = NOTHING_HAPPENS;
+ if (motion == 17)spk = WHICH_WAY;
+ rspeak(spk);
+ return true;
+ }
+ ++kk;
+ }
+
+ /* (ESR) We've found a destination that goes with the motion verb.
+ * Next we need to check any conditional(s) on this destination, and
+ * possibly on following entries. */
+ scratchloc = T_HIGH(travel[kk]);
+
+ do {
+ /*
+ * (ESR) This conditional-skip loop may have to be repeated if
+ * it includes the plover passage. Same deal for any future
+ * cases where we need to block travel and then redo it once
+ * the blocking condition has been removed.
+ */
+ for (;;) { /* L12 loop */
+ for (;;) {
+ game.newloc = scratchloc / 1000;
+ motion = MOD(game.newloc, 100);
+ if (!SPECIAL(game.newloc)) {
+ if (game.newloc <= 100) {
+ if (game.newloc == 0 || PCT(game.newloc))
+ break;
+ /* else fall through */
+ }
+ /* handles the YAML "with" clause */
+ if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
+ break;
+ /* else fall through */
+ } else if (game.prop[motion] != game.newloc / 100 - 3)
+ break;
+ do {
+ if (travel[kk].stop)
+ BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
+ ++kk;
+ game.newloc = T_HIGH(travel[kk]);
+ } while
+ (game.newloc == scratchloc);
+ scratchloc = game.newloc;
+ }
+
+ game.newloc = MOD(scratchloc, 1000);
+ if (!SPECIAL(game.newloc))
+ return true;
+
+ if (game.newloc > 500) {
+ /* Execute a speak rule */
+ rspeak(L_SPEAK(game.newloc));
+ game.newloc = game.loc;
+ return true;
+ } else {
+ game.newloc -= SPECIALBASE;
+ switch (game.newloc) {
+ case 1:
+ /* Travel 301. Plover-alcove passage. Can carry only
+ * emerald. Note: travel table must include "useless"
+ * entries going through passage, which can never be used
+ * for actual motion, but can be spotted by "go back". */
+ /* FIXME: Arithmetic on location numbers */
+ game.newloc = 99 + 100 - game.loc;
+ if (game.holdng > 1 || (game.holdng == 1 && !TOTING(EMERALD))) {
+ game.newloc = game.loc;
+ rspeak(MUST_DROP);
+ }
+ return true;
+ case 2:
+ /* Travel 302. Plover transport. Drop the
+ * emerald (only use special travel if toting
+ * it), so he's forced to use the plover-passage
+ * to get it out. Having dropped it, go back and
+ * pretend he wasn't carrying it after all. */
+ drop(EMERALD, game.loc);
+ do {
+ if (travel[kk].stop)
+ BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
+ ++kk;
+ game.newloc = T_HIGH(travel[kk]);
+ } while
+ (game.newloc == scratchloc);
+ scratchloc = game.newloc;
+ continue; /* goto L12 */
+ case 3:
+ /* Travel 303. Troll bridge. Must be done only
+ * as special motion so that dwarves won't wander
+ * across and encounter the bear. (They won't
+ * follow the player there because that region is
+ * forbidden to the pirate.) If
+ * game.prop(TROLL)=1, he's crossed since paying,
+ * so step out and block him. (standard travel
+ * entries check for game.prop(TROLL)=0.) Special
+ * stuff for bear. */
+ if (game.prop[TROLL] == 1) {
+ pspeak(TROLL,look, 1);
+ game.prop[TROLL] = 0;
+ move(TROLL2, 0);
+ move(TROLL2 + NOBJECTS, 0);
+ move(TROLL, objects[TROLL].plac);
+ move(TROLL + NOBJECTS, objects[TROLL].fixd);
+ juggle(CHASM);
+ game.newloc = game.loc;
+ return true;
+ } else {
+ game.newloc = objects[TROLL].plac + objects[TROLL].fixd - game.loc;
+ if (game.prop[TROLL] == 0)game.prop[TROLL] = 1;
+ if (!TOTING(BEAR)) return true;
+ rspeak(BRIDGE_COLLAPSE);
+ game.prop[CHASM] = 1;
+ game.prop[TROLL] = 2;
+ drop(BEAR, game.newloc);
+ game.fixed[BEAR] = -1;
+ game.prop[BEAR] = 3;
+ game.oldlc2 = game.newloc;
+ croak();
+ return true;
+ }
+ default:
+ BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ }
+ break; /* Leave L12 loop */
+ }
+ } while
+ (false);
+}
+
+static bool closecheck(void)
+/* Handle the closing of the cave. The cave closes "clock1" turns
+ * after the last treasure has been located (including the pirate's
+ * chest, which may of course never show up). Note that the
+ * treasures need not have been taken yet, just located. Hence
+ * clock1 must be large enough to get out of the cave (it only ticks
+ * while inside the cave). When it hits zero, we branch to 10000 to
+ * start closing the cave, and then sit back and wait for him to try
+ * to get out. If he doesn't within clock2 turns, we close the cave;
+ * if he does try, we assume he panics, and give him a few additional
+ * turns to get frantic before we close. When clock2 hits zero, we
+ * transport him into the final puzzle. Note that the puzzle depends
+ * upon all sorts of random things. For instance, there must be no
+ * water or oil, since there are beanstalks which we don't want to be
+ * able to water, since the code can't handle it. Also, we can have
+ * no keys, since there is a grate (having moved the fixed object!)
+ * there separating him from all the treasures. Most of these
+ * problems arise from the use of negative prop numbers to suppress
+ * the object descriptions until he's actually moved the objects. */
+{
+ if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2)
+ --game.clock1;
+
+ /* When the first warning comes, we lock the grate, destroy
+ * the bridge, kill all the dwarves (and the pirate), remove
+ * the troll and bear (unless dead), and set "closng" to
+ * true. Leave the dragon; too much trouble to move it.
+ * from now until clock2 runs out, he cannot unlock the
+ * grate, move to any location outside the cave, or create
+ * the bridge. Nor can he be resurrected if he dies. Note
+ * that the snake is already gone, since he got to the
+ * treasure accessible only via the hall of the mountain
+ * king. Also, he's been in giant room (to get eggs), so we
+ * can refer to it. Also also, he's gotten the pearl, so we
+ * know the bivalve is an oyster. *And*, the dwarves must
+ * have been activated, since we've found chest. */
+ if (game.clock1 == 0) {
+ game.prop[GRATE] = GRATE_CLOSED;
+ game.prop[FISSURE] = 0;
+ for (int i = 1; i <= NDWARVES; i++) {
+ game.dseen[i] = false;
+ game.dloc[i] = 0;
+ }
+ move(TROLL, 0);
+ move(TROLL + NOBJECTS, 0);
+ move(TROLL2, objects[TROLL].plac);
+ move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
+ juggle(CHASM);
+ if (game.prop[BEAR] != 3)DESTROY(BEAR);
+ game.prop[CHAIN] = 0;
+ game.fixed[CHAIN] = 0;
+ game.prop[AXE] = 0;
+ game.fixed[AXE] = 0;
+ rspeak(CAVE_CLOSING);
+ game.clock1 = -1;
+ game.closng = true;
+ return true;
+ } else if (game.clock1 < 0)
+ --game.clock2;
+ if (game.clock2 == 0) {
+ /* Once he's panicked, and clock2 has run out, we come here
+ * to set up the storage room. The room has two locs,
+ * hardwired as LOC_NE and LOC_SW. At the ne end, we
+ * place empty bottles, a nursery of plants, a bed of
+ * oysters, a pile of lamps, rods with stars, sleeping
+ * dwarves, and him. At the sw end we place grate over
+ * treasures, snake pit, covey of caged birds, more rods, and
+ * pillows. A mirror stretches across one wall. Many of the
+ * objects come from known locations and/or states (e.g. the
+ * snake is known to have been destroyed and needn't be
+ * carried away from its old "place"), making the various
+ * objects be handled differently. We also drop all other
+ * objects he might be carrying (lest he have some which
+ * could cause trouble, such as the keys). We describe the
+ * flash of light and trundle back. */
+ game.prop[BOTTLE] = put(BOTTLE, LOC_NE, EMPTY_BOTTLE);
+ game.prop[PLANT] = put(PLANT, LOC_NE, 0);
+ game.prop[OYSTER] = put(OYSTER, LOC_NE, 0);
+ game.prop[LAMP] = put(LAMP, LOC_NE, 0);
+ game.prop[ROD] = put(ROD, LOC_NE, 0);
+ game.prop[DWARF] = put(DWARF, LOC_NE, 0);
+ game.loc = LOC_NE;
+ game.oldloc = LOC_NE;
+ game.newloc = LOC_NE;
+ /* Leave the grate with normal (non-negative) property.
+ * Reuse sign. */
+ put(GRATE, LOC_SW, 0);
+ put(SIGN, LOC_SW, 0);
+ game.prop[SIGN] = ENDGAME_SIGN;
+ game.prop[SNAKE] = put(SNAKE, LOC_SW, 1);
+ game.prop[BIRD] = put(BIRD, LOC_SW, 1);
+ game.prop[CAGE] = put(CAGE, LOC_SW, 0);
+ game.prop[ROD2] = put(ROD2, LOC_SW, 0);
+ game.prop[PILLOW] = put(PILLOW, LOC_SW, 0);
+
+ game.prop[MIRROR] = put(MIRROR, LOC_NE, 0);
+ game.fixed[MIRROR] = LOC_SW;
+
+ for (int i = 1; i <= NOBJECTS; i++) {
+ if (TOTING(i))
+ DESTROY(i);
+ }
+
+ rspeak(CAVE_CLOSED);
+ game.closed = true;
+ return true;
+ }
+
+ return false;
+}
+
+static void lampcheck(void)
+/* Check game limit and lamp timers */
+{
+ if (game.prop[LAMP] == LAMP_BRIGHT)
+ --game.limit;
+
+ /* Another way we can force an end to things is by having the
+ * lamp give out. When it gets close, we come here to warn him.
+ * First following arm checks if the lamp and fresh batteries are
+ * here, in which case we replace the batteries and continue.
+ * Second is for other cases of lamp dying. Eve after it goes
+ * out, he can explore outside for a while if desired. */
+ if (game.limit <= WARNTIME && HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) {
+ rspeak(REPLACE_BATTERIES);
+ game.prop[BATTERY] = DEAD_BATTERIES;
+ if (TOTING(BATTERY))
+ drop(BATTERY, game.loc);
+ game.limit += BATTERYLIFE;
+ game.lmwarn = false;
+ } else if (game.limit == 0) {
+ game.limit = -1;
+ game.prop[LAMP] = LAMP_DARK;
+ if (HERE(LAMP))
+ rspeak(LAMP_OUT);
+ } else if (game.limit <= WARNTIME) {
+ if (!game.lmwarn && HERE(LAMP)) {
+ game.lmwarn = true;
+ int spk = GET_BATTERIES;
+ if (game.place[BATTERY] == LOC_NOWHERE)spk = LAMP_DIM;
+ if (game.prop[BATTERY] == DEAD_BATTERIES)
+ spk = MISSING_BATTERIES;
+ rspeak(spk);
+ }
+ }
+}
+
+static void listobjects(void)
+/* Print out descriptions of objects at this location. If
+ * not closing and property value is negative, tally off
+ * another treasure. Rug is special case; once seen, its
+ * game.prop is 1 (dragon on it) till dragon is killed.
+ * Similarly for chain; game.prop is initially 1 (locked to
+ * bear). These hacks are because game.prop=0 is needed to
+ * get full score. */
+{
+ if (!DARK(game.loc)) {
+ ++game.abbrev[game.loc];
+ for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) {
+ long obj = i;
+ if (obj > NOBJECTS)obj = obj - NOBJECTS;
+ if (obj == STEPS && TOTING(NUGGET))
+ continue;
+ if (game.prop[obj] < 0) {
+ if (game.closed)
+ continue;
+ game.prop[obj] = 0;
+ if (obj == RUG || obj == CHAIN)
+ game.prop[obj] = 1;
+ --game.tally;
+ /* Note: There used to be a test here to see whether the
+ * player had blown it so badly that he could never ever see
+ * the remaining treasures, and if so the lamp was zapped to
+ * 35 turns. But the tests were too simple-minded; things
+ * like killing the bird before the snake was gone (can never
+ * see jewelry), and doing it "right" was hopeless. E.G.,
+ * could cross troll bridge several times, using up all
+ * available treasures, breaking vase, using coins to buy
+ * batteries, etc., and eventually never be able to get
+ * across again. If bottle were left on far side, could then
+ * never get eggs or trident, and the effects propagate. So
+ * the whole thing was flushed. anyone who makes such a
+ * gross blunder isn't likely to find everything else anyway
+ * (so goes the rationalisation). */
+ }
+ int kk = game.prop[obj];
+ if (obj == STEPS && game.loc == game.fixed[STEPS])
+ kk = 1;
+ pspeak(obj, look, kk);
+ }
+ }
+}
+
+static bool do_command(FILE *cmdin)
+/* Get and execute a command */
+{
+ long V1, V2;
+ long kmod, defn;
+ static long igo = 0;
+ static struct command_t command;
+ command.verb = 0;
+
+ /* Can't leave cave once it's closing (except by main office). */
+ if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
+ rspeak(EXIT_CLOSED);
+ game.newloc = game.loc;
+ if (!game.panic)game.clock2 = PANICTIME;
+ game.panic = true;
+ }