* to idiomatic C, the following is more appropriate:
*
* ESR apologizes for the remaing gotos (now confined to one function
- * in this file - there used to be over 350 of them, *everywhere*),
- * and for the offensive globals. Applying the Structured Program
- * Theorem can be hard.
+ * in this file - there used to be over 350 of them, *everywhere*).
+ * Applying the Structured Program Theorem can be hard.
+ *
+ * Copyright (c) 1977, 2005 by Will Crowther and Don Woods
+ * Copyright (c) 2017 by Eric S. Raymond
+ * SPDX-License-Identifier: BSD-2-clause
*/
#include <stdlib.h>
fprintf(stderr,
"advent: can't open save file %s for read\n",
optarg);
- signal(SIGINT, sig_handler);
break;
#endif
default:
} else {
restore(rfp);
}
+#else
+ game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
+ if (game.novice)
+ game.limit = NOVICELIMIT;
#endif
if (settings.logfp)
terminate(quitgame);
}
-static bool fallback_handler(char *buf)
-/* fallback handler for commands not handled by FORTRANish parser */
-{
- long sv;
- if (sscanf(buf, "seed %ld", &sv) == 1) {
- set_seed(sv);
- printf("Seed set to %ld\n", sv);
- // autogenerated, so don't charge user time for it.
- --game.turns;
- return true;
- }
- return false;
-}
-
/* Check if this loc is eligible for any hints. If been here long
* enough, display. Ignore "HINTS" < 4 (special stuff, see database
* notes). */
game.hintlc[hint] = 0;
return;
case 3: /* maze */
- if (game.atloc[game.loc] == 0 &&
- game.atloc[game.oldloc] == 0 &&
- game.atloc[game.oldlc2] == 0 &&
+ if (game.atloc[game.loc] == NO_OBJECT &&
+ game.atloc[game.oldloc] == NO_OBJECT &&
+ game.atloc[game.oldlc2] == NO_OBJECT &&
game.holdng > 1)
break;
game.hintlc[hint] = 0;
game.hintlc[hint] = 0;
return;
case 7: /* woods */
- if (game.atloc[game.loc] == 0 &&
- game.atloc[game.oldloc] == 0 &&
- game.atloc[game.oldlc2] == 0)
+ if (game.atloc[game.loc] == NO_OBJECT &&
+ game.atloc[game.oldloc] == NO_OBJECT &&
+ game.atloc[game.oldlc2] == NO_OBJECT)
break;
return;
case 8: /* ogre */
break;
game.hintlc[hint] = 0;
return;
- default:
+ default: // LCOV_EXCL_LINE
BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- break;
}
/* Fall through to hint display */
if (i != PIRATE)
return false;
- /* The pirate's spotted him. He leaves him alone once we've
+ /* The pirate's spotted him. Pirate leaves him alone once we've
* found chest. K counts if a treasure is here. If not, and
* tally=1 for an unseen chest, let the pirate be spotted. Note
* that game.place[CHEST] = LOC_NOWHERE might mean that he's thrown
* it to the troll, but in that case he's seen the chest
- * (game.prop=0). */
+ * (game.prop[CHEST] == STATE_FOUND). */
if (game.loc == game.chloc ||
- game.prop[CHEST] >= 0)
+ game.prop[CHEST] != STATE_NOTFOUND)
return true;
int snarfed = 0;
bool movechest = false, robplayer = false;
/* Dwarves move. Return true if player survives, false if he dies. */
{
int kk, stick, attack;
- long tk[21];
+ loc_t tk[21];
/* Dwarf stuff. See earlier comments for description of
* variables. Remember sixth dwarf is pirate and is thus
* very different except for motion rules. */
- /* First off, don't let the dwarves follow him into a pit or
- * a wall. Activate the whole mess the first time he gets as
- * far as the hall of mists (loc 15). If game.newloc is
- * forbidden to pirate (in particular, if it's beyond the
+ /* First off, don't let the dwarves follow him into a pit or a
+ * wall. Activate the whole mess the first time he gets as far
+ * as the Hall of Mists (what INDEEP() tests). If game.newloc
+ * is forbidden to pirate (in particular, if it's beyond the
* troll bridge), bypass dwarf stuff. That way pirate can't
* steal return toll, and dwarves can't meet the bear. Also
* means dwarves won't follow him into dead end in maze, but
- * c'est la vie. They'll wait for him outside the dead
- * end. */
- if (game.loc == 0 ||
+ * c'est la vie. They'll wait for him outside the dead end. */
+ if (game.loc == LOC_NOWHERE ||
FORCED(game.loc) ||
CNDBIT(game.newloc, COND_NOARRR))
return true;
}
/* When we encounter the first dwarf, we kill 0, 1, or 2 of
- * the 5 dwarves. If any of the survivors is at loc,
+ * the 5 dwarves. If any of the survivors is at game.loc,
* replace him with the alternate. */
if (game.dflag == 1) {
if (!INDEEP(game.loc) ||
kk = tkey[game.dloc[i]];
if (kk != 0)
do {
- game.newloc = travel[kk].dest;
+ enum desttype_t desttype = travel[kk].desttype;
+ game.newloc = travel[kk].destval;
/* Have we avoided a dwarf encounter? */
- if (SPECIAL(game.newloc))
+ if (desttype != dest_goto)
continue;
else if (!INDEEP(game.newloc))
continue;
else if (j > 1 && game.newloc == tk[j - 1])
continue;
else if (j >= DIM(tk) - 1)
- continue;
+ /* This can't actually happen. */
+ continue; // LCOV_EXCL_LINE
else if (game.newloc == game.dloc[i])
continue;
else if (FORCED(game.newloc))
}
}
- /* Now we know what's happening. Let's tell the poor sucker about it.
- * Note that various of the "knife" messages must have specific relative
- * positions in the rspeak database. */
+ /* Now we know what's happening. Let's tell the poor sucker about it. */
if (game.dtotal == 0)
return true;
rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick);
} else {
rspeak(KNIFE_THROWN);
- rspeak(MISSES_YOU);
+ rspeak(stick ? GETS_YOU : MISSES_YOU);
}
if (stick == 0)
return true;
static void croak(void)
/* Okay, he's dead. Let's get on with it. */
{
+ if (game.numdie < 0)
+ game.numdie = 0;
const char* query = obituaries[game.numdie].query;
const char* yes_response = obituaries[game.numdie].yes_response;
++game.numdie;
* death and exit. */
rspeak(DEATH_CLOSING);
terminate(endgame);
- } else if (game.numdie == NDEATHS ||
- !yes(query, yes_response, arbitrary_messages[OK_MAN]))
+ } else if ( !yes(query, yes_response, arbitrary_messages[OK_MAN])
+ || game.numdie == NDEATHS)
terminate(endgame);
else {
game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
}
}
- game.loc = LOC_BUILDING;
- game.oldloc = game.loc;
+ game.oldloc = game.loc = game.newloc = LOC_BUILDING;
}
}
-static bool traveleq(long a, long b)
+static bool traveleq(int a, int b)
/* Are two travel entries equal for purposes of skip after failed condition? */
{
- return (travel[a].cond == travel[b].cond)
- && (travel[a].dest == travel[b].dest);
+ return (travel[a].condtype == travel[b].condtype)
+ && (travel[a].condarg1 == travel[b].condarg1)
+ && (travel[a].condarg2 == travel[b].condarg2)
+ && (travel[a].desttype == travel[b].desttype)
+ && (travel[a].destval == travel[b].destval);
}
/* Given the current location in "game.loc", and a motion verb number in
motion = game.oldlc2;
game.oldlc2 = game.oldloc;
game.oldloc = game.loc;
- int spk = 0;
- if (motion == game.loc)
- spk = FORGOT_PATH;
- if (CNDBIT(game.loc, COND_NOBACK))
- spk = TWIST_TURN;
- if (spk == 0) {
- int te_tmp = 0;
- for (;;) {
- scratchloc = travel[travel_entry].dest;
- if (scratchloc != motion) {
- if (!SPECIAL(scratchloc)) {
- if (FORCED(scratchloc) && travel[tkey[scratchloc]].dest == motion)
- te_tmp = travel_entry;
- }
- if (!travel[travel_entry].stop) {
- ++travel_entry; /* go to next travel entry for this location */
- continue;
- }
- /* we've reached the end of travel entries for game.loc */
- travel_entry = te_tmp;
- if (travel_entry == 0) {
- rspeak(NOT_CONNECTED);
- return;
- }
- }
+ if (CNDBIT(game.loc, COND_NOBACK)) {
+ rspeak(TWIST_TURN);
+ return;
+ }
+ if (motion == game.loc) {
+ rspeak(FORGOT_PATH);
+ return;
+ }
- motion = travel[travel_entry].motion;
- travel_entry = tkey[game.loc];
- break; /* fall through to ordinary travel */
+ int te_tmp = 0;
+ for (;;) {
+ enum desttype_t desttype = travel[travel_entry].desttype;
+ scratchloc = travel[travel_entry].destval;
+ if (desttype != dest_goto || scratchloc != motion) {
+ if (desttype == dest_goto) {
+ if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == motion)
+ te_tmp = travel_entry;
+ }
+ if (!travel[travel_entry].stop) {
+ ++travel_entry; /* go to next travel entry for this location */
+ continue;
+ }
+ /* we've reached the end of travel entries for game.loc */
+ travel_entry = te_tmp;
+ if (travel_entry == 0) {
+ rspeak(NOT_CONNECTED);
+ return;
+ }
}
- } else {
- rspeak(spk);
- return;
+
+ motion = travel[travel_entry].motion;
+ travel_entry = tkey[game.loc];
+ break; /* fall through to ordinary travel */
}
} else if (motion == LOOK) {
/* Look. Can't give more detail. Pretend it wasn't dark
if (travel[travel_entry].stop) {
/* Couldn't find an entry matching the motion word passed
* in. Various messages depending on word given. */
- int spk = CANT_APPLY;
- if (motion >= EAST && motion <= NW)
- spk = BAD_DIRECTION;
- if (motion == UP ||
- motion == DOWN)
- spk = BAD_DIRECTION;
- if (motion == FORWARD ||
- motion == LEFT ||
- motion == RIGHT)
- spk = UNSURE_FACING;
- if (motion == OUTSIDE ||
- motion == INSIDE)
- spk = NO_INOUT_HERE;
- if (motion == XYZZY ||
- motion == PLUGH)
- spk = NOTHING_HAPPENS;
- if (motion == CRAWL)
- spk = WHICH_WAY;
- rspeak(spk);
+ switch (motion) {
+ case EAST:
+ case WEST:
+ case SOUTH:
+ case NORTH:
+ case NE:
+ case NW:
+ case SW:
+ case SE:
+ case UP:
+ case DOWN:
+ rspeak(BAD_DIRECTION);
+ break;
+ case FORWARD:
+ case LEFT:
+ case RIGHT:
+ rspeak(UNSURE_FACING);
+ break;
+ case OUTSIDE:
+ case INSIDE:
+ rspeak(NO_INOUT_HERE);
+ break;
+ case XYZZY:
+ case PLUGH:
+ rspeak(NOTHING_HAPPENS);
+ break;
+ case CRAWL:
+ rspeak(WHICH_WAY);
+ break;
+ default:
+ rspeak(CANT_APPLY);
+ }
return;
}
++travel_entry;
/* (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. */
- /* FIXME: Magic numbers related to move opcodes */
do {
for (;;) { /* L12 loop */
for (;;) {
- long cond = travel[travel_entry].cond;
- long arg = MOD(cond, 100);
- if (!SPECIAL(cond)) {
+ enum condtype_t condtype = travel[travel_entry].condtype;
+ long condarg1 = travel[travel_entry].condarg1;
+ long condarg2 = travel[travel_entry].condarg2;
+ if (condtype < cond_not) {
/* YAML N and [pct N] conditionals */
- if (cond <= 100) {
- if (cond == 0 ||
- PCT(cond))
+ if (condtype == cond_goto || condtype == cond_pct) {
+ if (condarg1 == 0 ||
+ PCT(condarg1))
break;
/* else fall through */
}
/* YAML [with OBJ] clause */
- if (TOTING(arg) ||
- (cond > 200 && AT(arg)))
+ else if (TOTING(condarg1) ||
+ (condtype == cond_with && AT(condarg1)))
break;
/* else fall through to check [not OBJ STATE] */
- } else if (game.prop[arg] != cond / 100 - 3)
+ } else if (game.prop[condarg1] != condarg2)
break;
/* We arrive here on conditional failure.
* Skip to next non-matching destination */
- long te_tmp = travel_entry;
+ int te_tmp = travel_entry;
do {
if (travel[te_tmp].stop)
BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
}
/* Found an eligible rule, now execute it */
- game.newloc = travel[travel_entry].dest;
- if (!SPECIAL(game.newloc))
+ enum desttype_t desttype = travel[travel_entry].desttype;
+ game.newloc = travel[travel_entry].destval;
+ if (desttype == dest_goto)
return;
- if (game.newloc > 500) {
+ if (desttype == dest_speak) {
/* Execute a speak rule */
- rspeak(L_SPEAK(game.newloc));
+ rspeak(game.newloc);
game.newloc = game.loc;
return;
} else {
- game.newloc -= SPECIALBASE;
switch (game.newloc) {
case 1:
/* Special travel 1. Plover-alcove passage. Can carry only
* 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;
+ {
+ 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
* 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. */
+ * 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.prop[TROLL] == TROLL_PAIDONCE) {
pspeak(TROLL, look, TROLL_PAIDONCE, true);
game.prop[TROLL] = TROLL_UNPAID;
- move(TROLL2, 0);
- move(TROLL2 + NOBJECTS, 0);
+ move(TROLL2, LOC_NOWHERE);
+ move(TROLL2 + NOBJECTS, IS_FREE);
move(TROLL, objects[TROLL].plac);
move(TROLL + NOBJECTS, objects[TROLL].fixd);
juggle(CHASM);
croak();
return;
}
- default:
+ default: // LCOV_EXCL_LINE
BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
}
}
* problems arise from the use of negative prop numbers to suppress
* the object descriptions until he's actually moved the objects. */
{
+ /* 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);
+ }
+ }
+
/* Don't tick game.clock1 unless well into cave (and not at Y2). */
if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2)
--game.clock1;
game.prop[FISSURE] = UNBRIDGED;
for (int i = 1; i <= NDWARVES; i++) {
game.dseen[i] = false;
- game.dloc[i] = 0;
+ game.dloc[i] = LOC_NOWHERE;
}
- move(TROLL, 0);
- move(TROLL + NOBJECTS, 0);
+ move(TROLL, LOC_NOWHERE);
+ move(TROLL + NOBJECTS, IS_FREE);
move(TROLL2, objects[TROLL].plac);
move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
juggle(CHASM);
* 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[PLANT] = put(PLANT, LOC_NE, PLANT_THIRSTY);
+ game.prop[OYSTER] = put(OYSTER, LOC_NE, STATE_FOUND);
+ game.prop[LAMP] = put(LAMP, LOC_NE, LAMP_DARK);
+ game.prop[ROD] = put(ROD, LOC_NE, STATE_FOUND);
game.prop[DWARF] = put(DWARF, LOC_NE, 0);
game.loc = LOC_NE;
game.oldloc = LOC_NE;
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[SNAKE] = put(SNAKE, LOC_SW, SNAKE_CHASED);
+ game.prop[BIRD] = put(BIRD, LOC_SW, BIRD_CAGED);
+ game.prop[CAGE] = put(CAGE, LOC_SW, STATE_FOUND);
+ game.prop[ROD2] = put(ROD2, LOC_SW, STATE_FOUND);
+ game.prop[PILLOW] = put(PILLOW, LOC_SW, STATE_FOUND);
- game.prop[MIRROR] = put(MIRROR, LOC_NE, 0);
+ game.prop[MIRROR] = put(MIRROR, LOC_NE, STATE_FOUND);
game.fixed[MIRROR] = LOC_SW;
for (int i = 1; i <= NOBJECTS; i++) {
* 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
+ * 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 && 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) {
+ if (game.limit <= WARNTIME) {
+ if (HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) {
+ rspeak(REPLACE_BATTERIES);
+ game.prop[BATTERY] = 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.prop[BATTERY] == DEAD_BATTERIES)
+ rspeak(MISSING_BATTERIES);
+ else if (game.place[BATTERY] == LOC_NOWHERE)
+ rspeak(LAMP_DIM);
+ else
+ rspeak(GET_BATTERIES);
+ }
+ }
+ 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);
- }
}
}
if (!DARK(game.loc)) {
++game.abbrev[game.loc];
for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) {
- long obj = i;
+ obj_t obj = i;
if (obj > NOBJECTS)
obj = obj - NOBJECTS;
if (obj == STEPS && TOTING(NUGGET))
static bool do_command()
/* Get and execute a command */
{
- long kmod, defn;
- static long igo = 0;
- static struct command_t command;
- command.verb = 0;
+ static command_t command;
/* Can't leave cave once it's closing (except by main office). */
if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
listobjects();
-L2012:
- command.verb = 0;
+Lclearobj:
game.oldobj = command.obj;
- command.obj = 0;
-L2600:
checkhints();
/* If closing time, check for any objects being toted with
if (game.knfloc > 0 && game.knfloc != game.loc)
game.knfloc = 0;
- /* This is where we get a new command from the user */
- char* input;
- char inputbuf[LINESIZE];
-
- for (;;) {
- input = get_input();
- if (input == NULL)
- return (false);
- if (word_count(input) > 2) {
- rspeak(TWO_WORDS);
- continue;
- }
- if (strcmp(input, "") != 0)
- break;
- }
-
- strncpy(inputbuf, input, LINESIZE - 1);
- free(input);
-
- tokenize(inputbuf, &command);
+ /* Preserve state from last command for reuse when required */
+ command_t preserve = command;
+
+ // Get command input from user
+ if (!get_command_input(&command))
+ return false;
+
+#ifdef GDEBUG
+ /* Needs to stay synced with enum word_type_t */
+ const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION", "NUMERIC"};
+ /* needs to stay synced with enum speechpart */
+ const char *roles[] = {"unknown", "intransitive", "transitive"};
+ printf("Preserve: role = %s type1 = %s, id1 = %ld, type2 = %s, id2 = %ld\n",
+ roles[preserve.part],
+ types[preserve.word[0].type],
+ preserve.word[0].id,
+ types[preserve.word[1].type],
+ preserve.word[1].id);
+ printf("Command: role = %s type1 = %s, id1 = %ld, type2 = %s, id2 = %ld\n",
+ roles[command.part],
+ types[command.word[0].type],
+ command.word[0].id,
+ types[command.word[1].type],
+ command.word[1].id);
+#endif
- char word1[TOKLEN + 1];
- char word2[TOKLEN + 1];
- packed_to_token(command.wd1, word1);
- packed_to_token(command.wd2, word2);
- command.id1 = get_vocab_id(word1);
- command.id2 = get_vocab_id(word2);
+ /* Handle of objectless action followed by actionless object */
+ if (preserve.word[0].type == ACTION && preserve.word[1].type == NO_WORD_TYPE && command.word[1].id == 0)
+ command.verb = preserve.verb;
-L2607:
+#ifdef BROKEN
+ /* Handling of actionless object followed by objectless action */
+ if (preserve.word[0].type == OBJECT && preserve.word[1].type == NO_WORD_TYPE && command.word[1].id == 0 && command.word[0].id == CARRY)
+ command.obj = preserve.obj;
+#endif /* BROKEN */
+
++game.turns;
- /* 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);
- }
- }
-
- if (command.verb == SAY && command.id2 != WORD_NOT_FOUND && command.id2 != WORD_EMPTY)
- command.verb = 0;
- if (command.verb == SAY) {
- command.part = transitive;
- goto Laction;
- }
if (closecheck()) {
if (game.closed)
return true;
} else
lampcheck();
- if (command.id1 == ENTER && (command.id2 == STREAM ||
- command.id2 == PROMOTE_WORD(WATER))) {
- if (LIQLOC(game.loc) == WATER) {
+ if (command.word[0].type == MOTION && command.word[0].id == ENTER
+ && (command.word[1].id == STREAM || command.word[1].id == WATER)) {
+ if (LIQLOC(game.loc) == WATER)
rspeak(FEET_WET);
- } else {
+ else
rspeak(WHERE_QUERY);
- }
- goto L2012;
+
+ goto Lclearobj;
}
- if (command.id1 == ENTER && command.id2 != WORD_NOT_FOUND && command.id2 != WORD_EMPTY) {
- /* command.wd1 = command.wd2; */
- /* wordclear(&command.wd2); */
- command.id1 = command.id2;
- command.id2 = WORD_EMPTY;
- } else {
- /* FIXME: Magic numbers related to vocabulary */
- if (!((command.id1 != PROMOTE_WORD(WATER) && command.id1 != PROMOTE_WORD(OIL)) ||
- (command.id2 != PROMOTE_WORD(PLANT) && command.id2 != PROMOTE_WORD(DOOR)))) {
- if (AT(DEMOTE_WORD(command.id2)))
- command.wd2 = token_to_packed("POUR");
+
+ if (command.word[0].type == OBJECT) {
+ if (command.word[0].id == GRATE) {
+ command.word[0].type = MOTION;
+ if (game.loc == LOC_START ||
+ game.loc == LOC_VALLEY ||
+ game.loc == LOC_SLIT) {
+ command.word[0].id = DEPRESSION;
+ }
+ if (game.loc == LOC_COBBLE ||
+ game.loc == LOC_DEBRIS ||
+ game.loc == LOC_AWKWARD ||
+ game.loc == LOC_BIRD ||
+ game.loc == LOC_PITTOP) {
+ command.word[0].id = ENTRANCE;
+ }
+ }
+ if ((command.word[0].id == WATER || command.word[0].id == OIL) && (command.word[1].id == PLANT || command.word[1].id == DOOR)) {
+ if (AT(command.word[1].id)) {
+ command.word[1] = command.word[0];
+ command.word[0].id = POUR;
+ command.word[0].type = ACTION;
+ strncpy(command.word[0].raw, "pour", LINESIZE - 1);
+ }
+ }
+ if (command.word[0].id == CAGE && command.word[1].id == BIRD && HERE(CAGE) && HERE(BIRD)) {
+ command.word[0].id = CARRY;
+ command.word[0].type = ACTION;
+ }
+
+ /* From OV to VO form */
+ if (command.word[0].type == OBJECT && command.word[1].type == ACTION) {
+ command_word_t stage = command.word[0];
+ command.word[0] = command.word[1];
+ command.word[1] = stage;
}
- if (command.id1 == PROMOTE_WORD(CAGE) && command.id2 == PROMOTE_WORD(BIRD) && HERE(CAGE) && HERE(BIRD))
- command.wd1 = token_to_packed("CATCH");
}
-L2620:
- if (wordeq(command.wd1, token_to_packed("WEST"))) {
- ++game.iwest;
- if (game.iwest == 10)
+
+Lookup:
+ if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) {
+ if (++game.iwest == 10)
rspeak(W_IS_WEST);
}
- if (wordeq(command.wd1, token_to_packed("GO")) && !wordempty(command.wd2)) {
- if (++igo == 10)
+ if (strncasecmp(command.word[0].raw, "go", sizeof("go")) == 0 && command.word[1].id != WORD_EMPTY) {
+ if (++game.igo == 10)
rspeak(GO_UNNEEDED);
}
-Lookup:
- packed_to_token(command.wd1, word1);
- defn = get_vocab_id(word1);
- if (defn == WORD_NOT_FOUND) {
+ if (command.word[0].id == WORD_NOT_FOUND) {
/* Gee, I don't understand. */
- if (fallback_handler(inputbuf))
- continue;
- sspeak(DONT_KNOW, command.raw1);
- goto L2600;
+ sspeak(DONT_KNOW, command.word[0].raw);
+ goto Lclearobj;
}
- /* FIXME: magic numbers related to vocabulary */
- kmod = MOD(defn, 1000);
- switch (defn / 1000) {
- case 0:
- playermove(kmod);
+ switch (command.word[0].type) {
+ case NO_WORD_TYPE: // FIXME: treating NO_WORD_TYPE as a motion word is confusing
+ case MOTION:
+ playermove(command.word[0].id);
return true;
- case 1:
+ case OBJECT:
command.part = unknown;
- command.obj = kmod;
+ command.obj = command.word[0].id;
break;
- case 2:
- command.part = intransitive;
- command.verb = kmod;
+ case ACTION:
+ if (command.word[1].type == NUMERIC)
+ command.part = transitive;
+ else
+ command.part = intransitive;
+ command.verb = command.word[0].id;
break;
- case 3:
- speak(specials[kmod].message);
- goto L2012;
- default:
+ case NUMERIC: // LCOV_EXCL_LINE
+ if (!settings.oldstyle) {
+ sspeak(DONT_KNOW, command.word[0].raw);
+ goto Lclearobj;
+ }
+ default: // LCOV_EXCL_LINE
BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
}
-
-Laction:
- switch (action(&command)) {
+ switch (action(command)) {
case GO_TERMINATE:
return true;
case GO_MOVE:
return true;
case GO_TOP:
continue; /* back to top of main interpreter loop */
- case GO_CLEAROBJ:
- goto L2012;
- case GO_CHECKHINT:
- goto L2600;
- case GO_CHECKFOO:
- goto L2607;
- case GO_LOOKUP:
- goto Lookup;
case GO_WORD2:
+#ifdef GDEBUG
+ printf("Word shift\n");
+#endif /* GDEBUG */
/* Get second word for analysis. */
- command.wd1 = command.wd2;
- strcpy(command.raw1, command.raw2);
- wordclear(&command.wd2);
- command.raw2[0] = '\0';
- goto L2620;
+ command.word[0] = command.word[1];
+ command.word[1] = empty_command_word;
+ goto Lookup;
case GO_UNKNOWN:
/* Random intransitive verbs come here. Clear obj just in case
* (see attack()). */
- command.raw1[0] = toupper(command.raw1[0]);
- sspeak(DO_WHAT, command.raw1);
+ command.word[0].raw[0] = toupper(command.word[0].raw[0]);
+ sspeak(DO_WHAT, command.word[0].raw);
command.obj = 0;
- goto L2600;
+ // Fallthrough
+ case GO_CLEAROBJ:
+ goto Lclearobj;
case GO_DWARFWAKE:
/* Oh dear, he's disturbed the dwarves. */
rspeak(DWARVES_AWAKEN);
terminate(endgame);
- default:
+ default: // LCOV_EXCL_LINE
BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
}
}