+ /* (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. */
+ do {
+ for (;;) { /* L12 loop */
+ for (;;) {
+ enum condtype_t condtype =
+ travel[travel_entry].condtype;
+ int condarg1 = travel[travel_entry].condarg1;
+ int condarg2 = travel[travel_entry].condarg2;
+ if (condtype < cond_not) {
+ /* YAML N and [pct N] conditionals */
+ if (condtype == cond_goto ||
+ condtype == cond_pct) {
+ if (condarg1 == 0 ||
+ PCT(condarg1)) {
+ break;
+ }
+ /* else fall through */
+ }
+ /* YAML [with OBJ] clause */
+ else if (TOTING(condarg1) ||
+ (condtype == cond_with &&
+ AT(condarg1))) {
+ break;
+ }
+ /* else fall through to check [not OBJ
+ * STATE] */
+ } else if (game.objects[condarg1].prop !=
+ condarg2) {
+ break;
+ }
+
+ /* We arrive here on conditional failure.
+ * Skip to next non-matching destination */
+ int te_tmp = travel_entry;
+ do {
+ if (travel[te_tmp].stop) {
+ BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
+ }
+ ++te_tmp;
+ } while (traveleq(travel_entry, te_tmp));
+ travel_entry = te_tmp;
+ }
+
+ /* Found an eligible rule, now execute it */
+ enum desttype_t desttype =
+ travel[travel_entry].desttype;
+ game.newloc = travel[travel_entry].destval;
+ if (desttype == dest_goto) {
+ return;
+ }
+
+ if (desttype == dest_speak) {
+ /* Execute a speak rule */
+ rspeak(game.newloc);
+ game.newloc = game.loc;
+ return;
+ } else {
+ switch (game.newloc) {
+ case 1:
+ /* Special travel 1. 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". */
+ game.newloc = (game.loc == LOC_PLOVER)
+ ? LOC_ALCOVE
+ : LOC_PLOVER;
+ if (game.holdng > 1 ||
+ (game.holdng == 1 &&
+ !TOTING(EMERALD))) {
+ game.newloc = game.loc;
+ rspeak(MUST_DROP);
+ }
+ return;
+ case 2:
+ /* Special travel 2. 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);
+ {
+ int te_tmp = travel_entry;
+ do {
+ if (travel[te_tmp]
+ .stop) {
+ BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
+ }
+ ++te_tmp;
+ } while (traveleq(travel_entry,
+ te_tmp));
+ travel_entry = te_tmp;
+ }
+ continue; /* goto L12 */
+ case 3:
+ /* Special travel 3. 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]=TROLL_PAIDONCE,
+ * he's crossed since paying, so step
+ * out and block him. (standard travel
+ * entries check for
+ * game.prop[TROLL]=TROLL_UNPAID.)
+ * Special stuff for bear. */
+ if (game.objects[TROLL].prop ==
+ TROLL_PAIDONCE) {
+ pspeak(TROLL, look, true,
+ TROLL_PAIDONCE);
+ game.objects[TROLL].prop =
+ TROLL_UNPAID;
+ DESTROY(TROLL2);
+ move(TROLL2 + NOBJECTS,
+ IS_FREE);
+ move(TROLL,
+ objects[TROLL].plac);
+ move(TROLL + NOBJECTS,
+ objects[TROLL].fixd);
+ juggle(CHASM);
+ game.newloc = game.loc;
+ return;
+ } else {
+ game.newloc =
+ objects[TROLL].plac +
+ objects[TROLL].fixd -
+ game.loc;
+ if (game.objects[TROLL].prop ==
+ TROLL_UNPAID) {
+ game.objects[TROLL]
+ .prop =
+ TROLL_PAIDONCE;
+ }
+ if (!TOTING(BEAR)) {
+ return;
+ }
+ state_change(CHASM,
+ BRIDGE_WRECKED);
+ game.objects[TROLL].prop =
+ TROLL_GONE;
+ drop(BEAR, game.newloc);
+ game.objects[BEAR].fixed =
+ IS_FIXED;
+ game.objects[BEAR].prop =
+ BEAR_DEAD;
+ game.oldlc2 = game.newloc;
+ croak();
+ return;
+ }
+ default: // LCOV_EXCL_LINE
+ BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ }
+ break; /* Leave L12 loop */
+ }
+ } while (false);
+}
+
+static void lampcheck(void) {
+ /* Check game limit and lamp timers */
+ if (game.objects[LAMP].prop == 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. Even after it goes
+ * out, he can explore outside for a while if desired. */
+ if (game.limit <= WARNTIME) {
+ if (HERE(BATTERY) &&
+ game.objects[BATTERY].prop == FRESH_BATTERIES &&
+ HERE(LAMP)) {
+ rspeak(REPLACE_BATTERIES);
+ game.objects[BATTERY].prop = DEAD_BATTERIES;
+#ifdef __unused__
+ /* This code from the original game seems to have been
+ * faulty. No tests ever passed the guard, and with the
+ * guard removed the game hangs when the lamp limit is
+ * reached.
+ */
+ if (TOTING(BATTERY)) {
+ drop(BATTERY, game.loc);
+ }
+#endif
+ game.limit += BATTERYLIFE;
+ game.lmwarn = false;
+ } else if (!game.lmwarn && HERE(LAMP)) {
+ game.lmwarn = true;
+ if (game.objects[BATTERY].prop == DEAD_BATTERIES) {
+ rspeak(MISSING_BATTERIES);
+ } else if (game.objects[BATTERY].place == LOC_NOWHERE) {
+ rspeak(LAMP_DIM);
+ } else {
+ rspeak(GET_BATTERIES);
+ }
+ }
+ }
+ if (game.limit == 0) {
+ game.limit = -1;
+ game.objects[LAMP].prop = LAMP_DARK;
+ if (HERE(LAMP)) {
+ rspeak(LAMP_OUT);
+ }
+ }
+}
+
+/* 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 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. */
+static bool closecheck(void) {
+ /* If a turn threshold has been met, apply penalties and tell
+ * the player about it. */
+ for (int i = 0; i < NTHRESHOLDS; ++i) {
+ if (game.turns == turn_thresholds[i].threshold + 1) {
+ game.trnluz += turn_thresholds[i].point_loss;
+ speak(turn_thresholds[i].message);
+ }