Rationalize names of structure array sizes.
[open-adventure.git] / main.c
diff --git a/main.c b/main.c
index c2e73a0c5281bc9ae877d24749c07f442c39f435..6fa2ddcbc7c954f53fd111b5ce4bfbd386208586 100644 (file)
--- a/main.c
+++ b/main.c
 
 struct game_t game;
 
 
 struct game_t game;
 
-long LNLENG, LNPOSN, PARMS[MAXPARMS + 1];
+long LNLENG, LNPOSN;
 char rawbuf[LINESIZE], INLINE[LINESIZE + 1];
 
 char rawbuf[LINESIZE], INLINE[LINESIZE + 1];
 
-long AMBER, AXE, BACK, BATTER, BEAR, BIRD, BLOOD,
+long AMBER, AXE, BACK, BATTERY, BEAR, BIRD, BLOOD,
      BOTTLE, CAGE, CAVE, CAVITY, CHAIN, CHASM, CHEST,
      CLAM, COINS, DOOR, DPRSSN, DRAGON, DWARF, EGGS,
      BOTTLE, CAGE, CAVE, CAVITY, CHAIN, CHASM, CHEST,
      CLAM, COINS, DOOR, DPRSSN, DRAGON, DWARF, EGGS,
-     EMRALD, ENTER, ENTRNC, FIND, FISSUR, FOOD,
+     EMERALD, ENTER, ENTRNC, FIND, FISSURE, FOOD,
      GRATE, HINT, INVENT, JADE, KEYS,
      GRATE, HINT, INVENT, JADE, KEYS,
-     KNIFE, LAMP, LOCK, LOOK, MAGZIN,
+     KNIFE, LAMP, LOCK, LOOK, MAGAZINE,
      MESSAG, MIRROR, NUGGET, NUL, OGRE, OIL, OYSTER,
      MESSAG, MIRROR, NUGGET, NUL, OGRE, OIL, OYSTER,
-     PEARL, PILLOW, PLANT, PLANT2, PYRAM, RESER, ROD, ROD2,
+     PEARL, PILLOW, PLANT, PLANT2, PYRAMID, RESER, ROD, ROD2,
      RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
      RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
-     STEPS, STREAM, THROW, TRIDNT, TROLL, TROLL2,
-     URN, VASE, VEND, VOLCAN, WATER;
-long WD1, WD1X, WD2, WD2X;
+     STEPS, STREAM, THROW, TRIDENT, TROLL, TROLL2,
+     URN, VASE, VEND, VOLCANO, WATER;
 
 FILE  *logfp = NULL, *rfp = NULL;
 bool oldstyle = false;
 
 FILE  *logfp = NULL, *rfp = NULL;
 bool oldstyle = false;
@@ -66,7 +65,7 @@ static void sig_handler(int signo)
  *           15-treasure version (adventure) by Don Woods, April-June 1977
  *           20-treasure version (rev 2) by Don Woods, August 1978
  *             Errata fixed: 78/12/25
  *           15-treasure version (adventure) by Don Woods, April-June 1977
  *           20-treasure version (rev 2) by Don Woods, August 1978
  *             Errata fixed: 78/12/25
- *          Revived 2017 as Open Advebture.
+ *          Revived 2017 as Open Adventure.
  */
 
 static bool do_command(FILE *);
  */
 
 static bool do_command(FILE *);
@@ -77,7 +76,14 @@ int main(int argc, char *argv[])
 
     /*  Options. */
 
 
     /*  Options. */
 
-    while ((ch = getopt(argc, argv, "l:or:s")) != EOF) {
+#ifndef ADVENT_NOSAVE
+    const char* opts = "l:or:s";
+    const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [-s] \n";
+#else
+    const char* opts = "l:os";
+    const char* usage = "Usage: %s [-l logfilename] [-o] [-s] \n";
+#endif
+    while ((ch = getopt(argc, argv, opts)) != EOF) {
         switch (ch) {
         case 'l':
             logfp = fopen(optarg, "w");
         switch (ch) {
         case 'l':
             logfp = fopen(optarg, "w");
@@ -91,6 +97,7 @@ int main(int argc, char *argv[])
             oldstyle = true;
             editline = prompt = false;
             break;
             oldstyle = true;
             editline = prompt = false;
             break;
+#ifndef ADVENT_NOSAVE
         case 'r':
             rfp = fopen(optarg, "r");
             if (rfp == NULL)
         case 'r':
             rfp = fopen(optarg, "r");
             if (rfp == NULL)
@@ -99,24 +106,30 @@ int main(int argc, char *argv[])
                         optarg);
             signal(SIGINT, sig_handler);
             break;
                         optarg);
             signal(SIGINT, sig_handler);
             break;
+#endif
         case 's':
             editline = false;
             break;
         case 's':
             editline = false;
             break;
+        default:
+            fprintf(stderr,
+                    usage, argv[0]);
+            fprintf(stderr,
+                    "        -l create a log file of your game named as specified'\n");
+            fprintf(stderr,
+                    "        -o 'oldstyle' (no prompt, no command editing, displays 'Initialising...')\n");
+#ifndef ADVENT_NOSAVE
+            fprintf(stderr,
+                    "        -r restore from specified saved game file\n");
+#endif
+            fprintf(stderr,
+                    "        -s suppress command editing\n");
+            exit(-1);
+            break;
         }
     }
 
     linenoiseHistorySetMaxLen(350);
 
         }
     }
 
     linenoiseHistorySetMaxLen(350);
 
-    /* Logical variables:
-     *
-     *  game.closed says whether we're all the way closed
-     *  game.closng says whether it's closing time yet
-     *  game.clshnt says whether he's read the clue in the endgame
-     *  game.lmwarn says whether he's been warned about lamp going dim
-     *  game.novice says whether he asked for instructions at start-up
-     *  game.panic says whether he's found out he's trapped in the cave
-     *  game.wzdark says whether the loc he's leaving was dark */
-
     /* Initialize our LCG PRNG with parameters tested against
      * Knuth vol. 2. by the original authors */
     game.lcg_a = 1093;
     /* Initialize our LCG PRNG with parameters tested against
      * Knuth vol. 2. by the original authors */
     game.lcg_a = 1093;
@@ -133,10 +146,11 @@ int main(int argc, char *argv[])
     game.zzword = RNDVOC(3, 0);
     game.newloc = LOC_START;
     game.loc = LOC_START;
     game.zzword = RNDVOC(3, 0);
     game.newloc = LOC_START;
     game.loc = LOC_START;
-    game.limit = 330;
+    game.limit = GAMELIMIT;
     if (!rfp) {
     if (!rfp) {
-        game.novice = YES(stdin, WELCOME_YOU, CAVE_NEARBY, NO_MESSAGE);
-        if (game.novice)game.limit = 1000;
+        game.novice = YES(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
+        if (game.novice)
+            game.limit = NOVICELIMIT;
     } else {
         restore(rfp);
     }
     } else {
         restore(rfp);
     }
@@ -150,7 +164,7 @@ int main(int argc, char *argv[])
             break;
     }
     /* show score and exit */
             break;
     }
     /* show score and exit */
-    score(quitgame);
+    terminate(quitgame);
 }
 
 static bool fallback_handler(char *buf)
 }
 
 static bool fallback_handler(char *buf)
@@ -169,29 +183,27 @@ static bool fallback_handler(char *buf)
     return false;
 }
 
     return false;
 }
 
-/*  Check if this loc is eligible for any hints.  If been here
- *  long enough, branch to help section (on later page).  Hints
- *  all come back here eventually to finish the loop.  Ignore
- *  "HINTS" < 4 (special stuff, see database notes).
- */
-static void checkhints(FILE *cmdin)
+/*  Check if this loc is eligible for any hints.  If been here long
+ *  enough, display.  Ignore "HINTS" < 4 (special stuff, see database
+ *  notes). */
+static void checkhints(void)
 {
 {
-    if (COND[game.loc] >= game.conds) {
-        for (int hint = 1; hint <= HNTMAX; hint++) {
+    if (conditions[game.loc] >= game.conds) {
+        for (int hint = 0; hint < NHINTS; hint++) {
             if (game.hinted[hint])
                 continue;
             if (game.hinted[hint])
                 continue;
-            if (!CNDBIT(game.loc, hint + HBASE))
+            if (!CNDBIT(game.loc, hint + 1 + COND_HBASE))
                 game.hintlc[hint] = -1;
             ++game.hintlc[hint];
             /*  Come here if he's been long enough at required loc(s) for some
              *  unused hint. */
                 game.hintlc[hint] = -1;
             ++game.hintlc[hint];
             /*  Come here if he's been long enough at required loc(s) for some
              *  unused hint. */
-            if (game.hintlc[hint] >= HINTS[hint][1]) {
+            if (game.hintlc[hint] >= hints[hint].turns) {
                 int i;
 
                 int i;
 
-                switch (hint - 1) {
+                switch (hint) {
                 case 0:
                     /* cave */
                 case 0:
                     /* cave */
-                    if (game.prop[GRATE] == 0 && !HERE(KEYS))
+                    if (game.prop[GRATE] == GRATE_CLOSED && !HERE(KEYS))
                         break;
                     game.hintlc[hint] = 0;
                     return;
                         break;
                     game.hintlc[hint] = 0;
                     return;
@@ -213,7 +225,7 @@ static void checkhints(FILE *cmdin)
                     game.hintlc[hint] = 0;
                     return;
                 case 4:        /* dark */
                     game.hintlc[hint] = 0;
                     return;
                 case 4:        /* dark */
-                    if (game.prop[EMRALD] != -1 && game.prop[PYRAM] == -1)
+                    if (game.prop[EMERALD] != -1 && game.prop[PYRAMID] == -1)
                         break;
                     game.hintlc[hint] = 0;
                     return;
                         break;
                     game.hintlc[hint] = 0;
                     return;
@@ -245,19 +257,18 @@ static void checkhints(FILE *cmdin)
                     game.hintlc[hint] = 0;
                     return;
                 default:
                     game.hintlc[hint] = 0;
                     return;
                 default:
-                    BUG(27);
+                    BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST);
                     break;
                 }
 
                 /* Fall through to hint display */
                 game.hintlc[hint] = 0;
                     break;
                 }
 
                 /* Fall through to hint display */
                 game.hintlc[hint] = 0;
-                if (!YES(cmdin, HINTS[hint][3], NO_MESSAGE, OK_MAN))
+                if (!YES(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
                     return;
                     return;
-                SETPRM(1, HINTS[hint][2], HINTS[hint][2]);
-                RSPEAK(HINT_COST);
-                game.hinted[hint] = YES(cmdin, WANT_HINT, HINTS[hint][4], OK_MAN);
+                rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty);
+                game.hinted[hint] = YES(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
                 if (game.hinted[hint] && game.limit > WARNTIME)
                 if (game.hinted[hint] && game.limit > WARNTIME)
-                    game.limit += WARNTIME * HINTS[hint][2];
+                    game.limit += WARNTIME * hints[hint].penalty;
             }
         }
     }
             }
         }
     }
@@ -271,7 +282,7 @@ static bool spotted_by_pirate(int i)
     /*  The pirate's spotted him.  He 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
     /*  The pirate's spotted him.  He 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] = NOWHERE might mean that he's thrown
+     *  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). */
     if (game.loc == game.chloc || game.prop[CHEST] >= 0)
      *  it to the troll, but in that case he's seen the chest
      *  (game.prop=0). */
     if (game.loc == game.chloc || game.prop[CHEST] >= 0)
@@ -281,7 +292,7 @@ static bool spotted_by_pirate(int i)
     for (int treasure = MINTRS; treasure <= MAXTRS; treasure++) {
         /*  Pirate won't take pyramid from plover room or dark
          *  room (too easy!). */
     for (int treasure = MINTRS; treasure <= MAXTRS; treasure++) {
         /*  Pirate won't take pyramid from plover room or dark
          *  room (too easy!). */
-        if (treasure == PYRAM && (game.loc == PLAC[PYRAM] || game.loc == PLAC[EMRALD])) {
+        if (treasure == PYRAMID && (game.loc == PLAC[PYRAMID] || game.loc == PLAC[EMERALD])) {
             continue;
         }
         if (TOTING(treasure) || HERE(treasure))
             continue;
         }
         if (TOTING(treasure) || HERE(treasure))
@@ -292,8 +303,8 @@ static bool spotted_by_pirate(int i)
         }
     }
     /* Force chest placement before player finds last treasure */
         }
     }
     /* Force chest placement before player finds last treasure */
-    if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == NOWHERE && HERE(LAMP) && game.prop[LAMP] == 1) {
-        RSPEAK(PIRATE_SPOTTED);
+    if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == LOC_NOWHERE && HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT) {
+        rspeak(PIRATE_SPOTTED);
         movechest = true;
     }
     /* Do things in this order (chest move before robbery) so chest is listed
         movechest = true;
     }
     /* Do things in this order (chest move before robbery) so chest is listed
@@ -308,12 +319,12 @@ static bool spotted_by_pirate(int i)
         /* You might get a hint of the pirate's presence even if the
          * chest doesn't move... */
         if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20))
         /* You might get a hint of the pirate's presence even if the
          * chest doesn't move... */
         if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20))
-            RSPEAK(PIRATE_RUSTLES);
+            rspeak(PIRATE_RUSTLES);
     }
     if (robplayer) {
     }
     if (robplayer) {
-        RSPEAK(PIRATE_POUNCES);
+        rspeak(PIRATE_POUNCES);
         for (int treasure = MINTRS; treasure <= MAXTRS; treasure++) {
         for (int treasure = MINTRS; treasure <= MAXTRS; treasure++) {
-            if (!(treasure == PYRAM && (game.loc == PLAC[PYRAM] || game.loc == PLAC[EMRALD]))) {
+            if (!(treasure == PYRAMID && (game.loc == PLAC[PYRAMID] || game.loc == PLAC[EMERALD]))) {
                 if (AT(treasure) && game.fixed[treasure] == 0)
                     CARRY(treasure, game.loc);
                 if (TOTING(treasure))
                 if (AT(treasure) && game.fixed[treasure] == 0)
                     CARRY(treasure, game.loc);
                 if (TOTING(treasure))
@@ -344,7 +355,7 @@ static bool dwarfmove(void)
      *  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. */
      *  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 || FORCED(game.loc) || CNDBIT(game.newloc, NOARRR))
+    if (game.loc == 0 || FORCED(game.loc) || CNDBIT(game.newloc, COND_NOARRR))
         return true;
 
     /* Dwarf activity level ratchets up */
         return true;
 
     /* Dwarf activity level ratchets up */
@@ -358,7 +369,7 @@ static bool dwarfmove(void)
      *  the 5 dwarves.  If any of the survivors is at loc,
      *  replace him with the alternate. */
     if (game.dflag == 1) {
      *  the 5 dwarves.  If any of the survivors is at loc,
      *  replace him with the alternate. */
     if (game.dflag == 1) {
-        if (!INDEEP(game.loc) || (PCT(95) && (!CNDBIT(game.loc, NOBACK) || PCT(85))))
+        if (!INDEEP(game.loc) || (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85))))
             return true;
         game.dflag = 2;
         for (int i = 1; i <= 2; i++) {
             return true;
         game.dflag = 2;
         for (int i = 1; i <= 2; i++) {
@@ -371,7 +382,7 @@ static bool dwarfmove(void)
                 game.dloc[i] = DALTLC;
             game.odloc[i] = game.dloc[i];
         }
                 game.dloc[i] = DALTLC;
             game.odloc[i] = game.dloc[i];
         }
-        RSPEAK(DWARF_RAN);
+        rspeak(DWARF_RAN);
         DROP(AXE, game.loc);
         return true;
     }
         DROP(AXE, game.loc);
         return true;
     }
@@ -402,7 +413,7 @@ static bool dwarfmove(void)
                                 j >= 20 ||
                                 game.newloc == game.dloc[i] ||
                                 FORCED(game.newloc) ||
                                 j >= 20 ||
                                 game.newloc == game.dloc[i] ||
                                 FORCED(game.newloc) ||
-                                (i == PIRATE && CNDBIT(game.newloc, NOARRR)) ||
+                                (i == PIRATE && CNDBIT(game.newloc, COND_NOARRR)) ||
                                 labs(TRAVEL[kk]) / 1000000 == 100);
                 if (!avoided) {
                     tk[j++] = game.newloc;
                                 labs(TRAVEL[kk]) / 1000000 == 100);
                 if (!avoided) {
                     tk[j++] = game.newloc;
@@ -434,20 +445,20 @@ static bool dwarfmove(void)
 
     /*  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
 
     /*  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. */
+     *  positions in the rspeak database. */
     if (game.dtotal == 0)
         return true;
     if (game.dtotal == 0)
         return true;
-    SETPRM(1, game.dtotal, 0);
-    RSPEAK(DWARF_PACK + 1 / game.dtotal);      /* FIXME: Arithmetic on message number */
+    rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
     if (attack == 0)
         return true;
     if (game.dflag == 2)game.dflag = 3;
     if (attack == 0)
         return true;
     if (game.dflag == 2)game.dflag = 3;
-    SETPRM(1, attack, 0);
-    int k = 6;
-    if (attack > 1)k = THROWN_KNIVES;
-    RSPEAK(k);
-    SETPRM(1, stick, 0);
-    RSPEAK(k + 1 + 2 / (1 + stick));   /* FIXME: Arithmetic on message number */
+    if (attack > 1) {
+        rspeak(THROWN_KNIVES, attack);
+        rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick);
+    } else {
+        rspeak(KNIFE_THROWN);
+        rspeak(MISSES_YOU);
+    }
     if (stick == 0)
         return true;
     game.oldlc2 = game.loc;
     if (stick == 0)
         return true;
     game.oldlc2 = game.loc;
@@ -457,7 +468,7 @@ static bool dwarfmove(void)
 /*  "You're dead, Jim."
  *
  *  If the current loc is zero, it means the clown got himself killed.
 /*  "You're dead, Jim."
  *
  *  If the current loc is zero, it means the clown got himself killed.
- *  We'll allow this maxdie times.  MAXDIE is automatically set based
+ *  We'll allow this maxdie times.  NDEATHS is automatically set based
  *  on the number of snide messages available.  Each death results in
  *  a message (81, 83, etc.)  which offers reincarnation; if accepted,
  *  this results in message 82, 84, etc.  The last time, if he wants
  *  on the number of snide messages available.  Each death results in
  *  a message (81, 83, etc.)  which offers reincarnation; if accepted,
  *  this results in message 82, 84, etc.  The last time, if he wants
@@ -474,24 +485,23 @@ static bool dwarfmove(void)
  *  without the lamp!).  game.oldloc is zapped so he can't just
  *  "retreat". */
 
  *  without the lamp!).  game.oldloc is zapped so he can't just
  *  "retreat". */
 
-static void croak(FILE *cmdin)
+static void croak(void)
 /*  Okay, he's dead.  Let's get on with it. */
 {
 /*  Okay, he's dead.  Let's get on with it. */
 {
+    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. */
     ++game.numdie;
     if (game.closng) {
         /*  He died during closing time.  No resurrection.  Tally up a
          *  death and exit. */
-        RSPEAK(DEATH_CLOSING);
-        score(endgame);
-
-    }
-    /* FIXME: Arithmetic on message numbers */
-    else if (game.numdie == MAXDIE || !YES(cmdin, WATCH_IT + game.numdie * 2, WHICH_WAY + game.numdie * 2, OK_MAN))
-        score(endgame);
+        rspeak(DEATH_CLOSING);
+        terminate(endgame);
+    } else if (game.numdie == NDEATHS || !YES(query, yes_response, arbitrary_messages[OK_MAN]))
+        terminate(endgame);
     else {
     else {
-        game.place[WATER] = game.place[OIL] = NOWHERE;
+        game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
         if (TOTING(LAMP))
         if (TOTING(LAMP))
-            game.prop[LAMP] = 0;
+            game.prop[LAMP] = LAMP_DARK;
         for (int j = 1; j <= NOBJECTS; j++) {
             int i = NOBJECTS + 1 - j;
             if (TOTING(i)) {
         for (int j = 1; j <= NOBJECTS; j++) {
             int i = NOBJECTS + 1 - j;
             if (TOTING(i)) {
@@ -512,12 +522,12 @@ static void croak(FILE *cmdin)
  *  him, so we need game.oldlc2, which is the last place he was
  *  safe.) */
 
  *  him, so we need game.oldlc2, which is the last place he was
  *  safe.) */
 
-static bool playermove(FILE *cmdin, token_t verb, int motion)
+static bool playermove(token_t verb, int motion)
 {
     int scratchloc, k2, kk = KEY[game.loc];
     game.newloc = game.loc;
     if (kk == 0)
 {
     int scratchloc, k2, kk = KEY[game.loc];
     game.newloc = game.loc;
     if (kk == 0)
-        BUG(26);
+        BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES);
     if (motion == NUL)
         return true;
     else if (motion == BACK) {
     if (motion == NUL)
         return true;
     else if (motion == BACK) {
@@ -531,7 +541,7 @@ static bool playermove(FILE *cmdin, token_t verb, int motion)
         game.oldloc = game.loc;
         k2 = 0;
         if (motion == game.loc)k2 = FORGOT_PATH;
         game.oldloc = game.loc;
         k2 = 0;
         if (motion == game.loc)k2 = FORGOT_PATH;
-        if (CNDBIT(game.loc, NOBACK))k2 = TWIST_TURN;
+        if (CNDBIT(game.loc, COND_NOBACK))k2 = TWIST_TURN;
         if (k2 == 0) {
             for (;;) {
                 scratchloc = MOD((labs(TRAVEL[kk]) / 1000), 1000);
         if (k2 == 0) {
             for (;;) {
                 scratchloc = MOD((labs(TRAVEL[kk]) / 1000), 1000);
@@ -546,7 +556,7 @@ static bool playermove(FILE *cmdin, token_t verb, int motion)
                     }
                     kk = k2;
                     if (kk == 0) {
                     }
                     kk = k2;
                     if (kk == 0) {
-                        RSPEAK(NOT_CONNECTED);
+                        rspeak(NOT_CONNECTED);
                         return true;
                     }
                 }
                         return true;
                     }
                 }
@@ -556,7 +566,7 @@ static bool playermove(FILE *cmdin, token_t verb, int motion)
                 break; /* fall through to ordinary travel */
             }
         } else {
                 break; /* fall through to ordinary travel */
             }
         } else {
-            RSPEAK(k2);
+            rspeak(k2);
             return true;
         }
     } else if (motion == LOOK) {
             return true;
         }
     } else if (motion == LOOK) {
@@ -564,14 +574,14 @@ static bool playermove(FILE *cmdin, token_t verb, int motion)
          *  (though it may now be dark) so he won't fall into a
          *  pit while staring into the gloom. */
         if (game.detail < 3)
          *  (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);
+            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. */
         ++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);
+        rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
         return true;
     } else {
         /* none of the specials */
         return true;
     } else {
         /* none of the specials */
@@ -593,10 +603,10 @@ static bool playermove(FILE *cmdin, token_t verb, int motion)
             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 (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 == INVENT)spk = NEreplace;
+            if (verb == FIND || verb == INVENT)spk = NEARBY;
             if (motion == 62 || motion == 65)spk = NOTHING_HAPPENS;
             if (motion == 17)spk = WHICH_WAY;
             if (motion == 62 || motion == 65)spk = NOTHING_HAPPENS;
             if (motion == 17)spk = WHICH_WAY;
-            RSPEAK(spk);
+            rspeak(spk);
             return true;
         }
         ++kk;
             return true;
         }
         ++kk;
@@ -606,103 +616,109 @@ static bool playermove(FILE *cmdin, token_t verb, int motion)
     do {
         /*
          * (ESR) This special-travel loop may have to be repeated if it includes
     do {
         /*
          * (ESR) This special-travel loop may have to be repeated if it includes
-         * the plover passage.  Same deal for any future cases wgerw we beed to
+         * 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.
          */
          * block travel and then redo it once the blocking condition has been
          * removed.
          */
-        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))
+        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 */
+                    }
+                    if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
                         break;
                     /* else fall through */
                         break;
                     /* else fall through */
-                }
-                if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
+                } else if (game.prop[motion] != game.newloc / 100 - 3)
                     break;
                     break;
-                /* else fall through */
-            } else if (game.prop[motion] != game.newloc / 100 - 3)
-                break;
-            do {
-                if (TRAVEL[kk] < 0)BUG(25);
-                ++kk;
-                game.newloc = labs(TRAVEL[kk]) / 1000;
-            } while
-            (game.newloc == scratchloc);
-            scratchloc = game.newloc;
-        }
-
-        game.newloc = MOD(scratchloc, 1000);
-        if (!SPECIAL(game.newloc))
-            return true;
-        if (game.newloc <= 500) {
-            game.newloc = 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 == 0 || (game.holdng == 1 && TOTING(EMRALD)))
-                    return true;
-                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(EMRALD, game.loc);
                 do {
                 do {
-                    if (TRAVEL[kk] < 0)BUG(25);
+                    if (TRAVEL[kk] < 0)
+                        BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION);
                     ++kk;
                     game.newloc = labs(TRAVEL[kk]) / 1000;
                 } while
                 (game.newloc == scratchloc);
                     ++kk;
                     game.newloc = labs(TRAVEL[kk]) / 1000;
                 } while
                 (game.newloc == scratchloc);
-                continue;      /* back to top of do/while loop */
-            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, 1);
-                    game.prop[TROLL] = 0;
-                    MOVE(TROLL2, 0);
-                    MOVE(TROLL2 + NOBJECTS, 0);
-                    MOVE(TROLL, PLAC[TROLL]);
-                    MOVE(TROLL + NOBJECTS, FIXD[TROLL]);
-                    JUGGLE(CHASM);
-                    game.newloc = game.loc;
+                scratchloc = game.newloc;
+            }
+
+            game.newloc = MOD(scratchloc, 1000);
+            if (!SPECIAL(game.newloc))
+                return true;
+            if (game.newloc <= 500) {
+                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;
                     return true;
-                } else {
-                    game.newloc = PLAC[TROLL] + FIXD[TROLL] - 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(cmdin);
-                    return false;
+                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] < 0)
+                            BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION);
+                        ++kk;
+                        game.newloc = labs(TRAVEL[kk]) / 1000;
+                    } 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, PLAC[TROLL]);
+                        MOVE(TROLL + NOBJECTS, FIXD[TROLL]);
+                        JUGGLE(CHASM);
+                        game.newloc = game.loc;
+                        return true;
+                    } else {
+                        game.newloc = PLAC[TROLL] + FIXD[TROLL] - 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;
+                    }
                 }
                 }
+                BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST);
             }
             }
-            BUG(20);
+            break; /* Leave L12 loop */
         }
     } while
     (false);
     /* FIXME: Arithmetic on location number, becoming a message number */
         }
     } while
     (false);
     /* FIXME: Arithmetic on location number, becoming a message number */
-    RSPEAK(game.newloc - 500);
+    rspeak(game.newloc - 500);
     game.newloc = game.loc;
     return true;
 }
     game.newloc = game.loc;
     return true;
 }
@@ -745,8 +761,8 @@ static bool closecheck(void)
      *  know the bivalve is an oyster.  *And*, the dwarves must
      *  have been activated, since we've found chest. */
     if (game.clock1 == 0) {
      *  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] = 0;
-        game.prop[FISSUR] = 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;
         for (int i = 1; i <= NDWARVES; i++) {
             game.dseen[i] = false;
             game.dloc[i] = 0;
@@ -761,7 +777,7 @@ static bool closecheck(void)
         game.fixed[CHAIN] = 0;
         game.prop[AXE] = 0;
         game.fixed[AXE] = 0;
         game.fixed[CHAIN] = 0;
         game.prop[AXE] = 0;
         game.fixed[AXE] = 0;
-        RSPEAK(CAVE_CLOSING);
+        rspeak(CAVE_CLOSING);
         game.clock1 = -1;
         game.closng = true;
         return true;
         game.clock1 = -1;
         game.closng = true;
         return true;
@@ -783,10 +799,9 @@ static bool closecheck(void)
          *  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. */
          *  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, 1);
+        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[PLANT] = PUT(PLANT, LOC_NE, 0);
         game.prop[OYSTER] = PUT(OYSTER, LOC_NE, 0);
-        OBJTXT[OYSTER] = 3;
         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.prop[LAMP] = PUT(LAMP, LOC_NE, 0);
         game.prop[ROD] = PUT(ROD, LOC_NE, 0);
         game.prop[DWARF] = PUT(DWARF, LOC_NE, 0);
@@ -797,7 +812,7 @@ static bool closecheck(void)
          *  Reuse sign. */
         PUT(GRATE, LOC_SW, 0);
         PUT(SIGN, LOC_SW, 0);
          *  Reuse sign. */
         PUT(GRATE, LOC_SW, 0);
         PUT(SIGN, LOC_SW, 0);
-        ++OBJTXT[SIGN];
+        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[SNAKE] = PUT(SNAKE, LOC_SW, 1);
         game.prop[BIRD] = PUT(BIRD, LOC_SW, 1);
         game.prop[CAGE] = PUT(CAGE, LOC_SW, 0);
@@ -812,7 +827,7 @@ static bool closecheck(void)
                 DESTROY(i);
         }
 
                 DESTROY(i);
         }
 
-        RSPEAK(CAVE_CLOSED);
+        rspeak(CAVE_CLOSED);
         game.closed = true;
         return true;
     }
         game.closed = true;
         return true;
     }
@@ -823,35 +838,35 @@ static bool closecheck(void)
 static void lampcheck(void)
 /* Check game limit and lamp timers */
 {
 static void lampcheck(void)
 /* Check game limit and lamp timers */
 {
-    if (game.prop[LAMP] == 1)
+    if (game.prop[LAMP] == LAMP_BRIGHT)
         --game.limit;
 
     /*  Another way we can force an end to things is by having the
         --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 ar, if the lamp and fresh batteries are
+     *  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.
      *  here, in which case we replace the batteries and continue.
-     *  Second is for other cases of lamp dying.  12400 is when it
-     *  goes out.  Even then, he can explore outside for a while
-     *  if desired. */
-    if (game.limit <= WARNTIME && HERE(BATTER) && game.prop[BATTER] == 0 && HERE(LAMP)) {
-        RSPEAK(REPLACE_BATTERIES);
-        game.prop[BATTER] = 1;
-        if (TOTING(BATTER))
-            DROP(BATTER, game.loc);
-        game.limit = game.limit + 2500;
+     *  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.lmwarn = false;
     } else if (game.limit == 0) {
         game.limit = -1;
-        game.prop[LAMP] = 0;
+        game.prop[LAMP] = LAMP_DARK;
         if (HERE(LAMP))
         if (HERE(LAMP))
-            RSPEAK(LAMP_OUT);
+            rspeak(LAMP_OUT);
     } else if (game.limit <= WARNTIME) {
         if (!game.lmwarn && HERE(LAMP)) {
             game.lmwarn = true;
             int spk = GET_BATTERIES;
     } else if (game.limit <= WARNTIME) {
         if (!game.lmwarn && HERE(LAMP)) {
             game.lmwarn = true;
             int spk = GET_BATTERIES;
-            if (game.place[BATTER] == NOWHERE)spk = LAMP_DIM;
-            if (game.prop[BATTER] == 1)spk = MISSING_BATTERIES;
-            RSPEAK(spk);
+            if (game.place[BATTERY] == LOC_NOWHERE)spk = LAMP_DIM;
+            if (game.prop[BATTERY] == DEAD_BATTERIES)
+               spk = MISSING_BATTERIES;
+            rspeak(spk);
         }
     }
 }
         }
     }
 }
@@ -897,7 +912,7 @@ static void listobjects(void)
             int kk = game.prop[obj];
             if (obj == STEPS && game.loc == game.fixed[STEPS])
                 kk = 1;
             int kk = game.prop[obj];
             if (obj == STEPS && game.loc == game.fixed[STEPS])
                 kk = 1;
-            PSPEAK(obj, kk);
+            pspeak(obj, look, kk);
         }
     }
 }
         }
     }
 }
@@ -905,15 +920,15 @@ static void listobjects(void)
 static bool do_command(FILE *cmdin)
 /* Get and execute a command */
 {
 static bool do_command(FILE *cmdin)
 /* Get and execute a command */
 {
-    long verb = 0, V1, V2;
+    long V1, V2;
     long kmod, defn;
     static long igo = 0;
     long kmod, defn;
     static long igo = 0;
-    static long obj = 0;
-    enum speechpart part;
+    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) {
 
     /*  Can't leave cave once it's closing (except by main office). */
     if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
-        RSPEAK(EXIT_CLOSED);
+        rspeak(EXIT_CLOSED);
         game.newloc = game.loc;
         if (!game.panic)game.clock2 = PANICTIME;
         game.panic = true;
         game.newloc = game.loc;
         if (!game.panic)game.clock2 = PANICTIME;
         game.panic = true;
@@ -923,11 +938,11 @@ static bool do_command(FILE *cmdin)
      *  wants to go.  If so, the dwarf's blocking his way.  If
      *  coming from place forbidden to pirate (dwarves rooted in
      *  place) let him get out (and attacked). */
      *  wants to go.  If so, the dwarf's blocking his way.  If
      *  coming from place forbidden to pirate (dwarves rooted in
      *  place) let him get out (and attacked). */
-    if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, NOARRR)) {
+    if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, COND_NOARRR)) {
         for (size_t i = 1; i <= NDWARVES - 1; i++) {
             if (game.odloc[i] == game.newloc && game.dseen[i]) {
                 game.newloc = game.loc;
         for (size_t i = 1; i <= NDWARVES - 1; i++) {
             if (game.odloc[i] == game.newloc && game.dseen[i]) {
                 game.newloc = game.loc;
-                RSPEAK(DWARF_BLOCK);
+                rspeak(DWARF_BLOCK);
                 break;
             }
         }
                 break;
             }
         }
@@ -935,13 +950,13 @@ static bool do_command(FILE *cmdin)
     game.loc = game.newloc;
 
     if (!dwarfmove())
     game.loc = game.newloc;
 
     if (!dwarfmove())
-        croak(cmdin);
+        croak();
 
     /*  Describe the current location and (maybe) get next command. */
 
     for (;;) {
         if (game.loc == 0)
 
     /*  Describe the current location and (maybe) get next command. */
 
     for (;;) {
         if (game.loc == 0)
-            croak(cmdin);
+            croak();
         const char* msg = locations[game.loc].description.small;
         if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == 0)
             msg = locations[game.loc].description.big;
         const char* msg = locations[game.loc].description.small;
         if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == 0)
             msg = locations[game.loc].description.big;
@@ -949,32 +964,33 @@ static bool do_command(FILE *cmdin)
             /*  The easiest way to get killed is to fall into a pit in
              *  pitch darkness. */
             if (game.wzdark && PCT(35)) {
             /*  The easiest way to get killed is to fall into a pit in
              *  pitch darkness. */
             if (game.wzdark && PCT(35)) {
-                RSPEAK(PIT_FALL);
+                rspeak(PIT_FALL);
                 game.oldlc2 = game.loc;
                 game.oldlc2 = game.loc;
-                croak(cmdin);
+                croak();
                 continue;      /* back to top of main interpreter loop */
             }
             msg = arbitrary_messages[PITCH_DARK];
         }
                 continue;      /* back to top of main interpreter loop */
             }
             msg = arbitrary_messages[PITCH_DARK];
         }
-        if (TOTING(BEAR))RSPEAK(TAME_BEAR);
-        newspeak(msg);
+        if (TOTING(BEAR))rspeak(TAME_BEAR);
+        speak(msg);
         if (FORCED(game.loc)) {
         if (FORCED(game.loc)) {
-            if (playermove(cmdin, verb, 1))
+            if (playermove(command.verb, 1))
                 return true;
             else
                 continue;      /* back to top of main interpreter loop */
         }
                 return true;
             else
                 continue;      /* back to top of main interpreter loop */
         }
-        if (game.loc == 33 && PCT(25) && !game.closng)RSPEAK(SAYS_PLUGH);
+        if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
+            rspeak(SAYS_PLUGH);
 
         listobjects();
 
 L2012:
 
         listobjects();
 
 L2012:
-        verb = 0;
-        game.oldobj = obj;
-        obj = 0;
+        command.verb = 0;
+        game.oldobj = command.obj;
+        command.obj = 0;
 
 L2600:
 
 L2600:
-        checkhints(cmdin);
+        checkhints();
 
         /*  If closing time, check for any objects being toted with
          *  game.prop < 0 and set the prop to -1-game.prop.  This way
 
         /*  If closing time, check for any objects being toted with
          *  game.prop < 0 and set the prop to -1-game.prop.  This way
@@ -983,7 +999,7 @@ L2600:
          *  tick game.clock1 unless well into cave (and not at Y2). */
         if (game.closed) {
             if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
          *  tick game.clock1 unless well into cave (and not at Y2). */
         if (game.closed) {
             if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
-                PSPEAK(OYSTER, 1);
+                pspeak(OYSTER, look, 1);
             for (size_t i = 1; i <= NOBJECTS; i++) {
                 if (TOTING(i) && game.prop[i] < 0)
                     game.prop[i] = -1 - game.prop[i];
             for (size_t i = 1; i <= NOBJECTS; i++) {
                 if (TOTING(i) && game.prop[i] < 0)
                     game.prop[i] = -1 - game.prop[i];
@@ -994,7 +1010,7 @@ L2600:
             game.knfloc = 0;
 
         /* This is where we get a new command from the user */
             game.knfloc = 0;
 
         /* This is where we get a new command from the user */
-        if (!GETIN(cmdin, &WD1, &WD1X, &WD2, &WD2X))
+        if (!GETIN(cmdin, &command.wd1, &command.wd1x, &command.wd2, &command.wd2x))
             return false;
 
         /*  Every input, check "game.foobar" flag.  If zero, nothing's
             return false;
 
         /*  Every input, check "game.foobar" flag.  If zero, nothing's
@@ -1003,18 +1019,22 @@ L2600:
 L2607:
         game.foobar = (game.foobar > 0 ? -game.foobar : 0);
         ++game.turns;
 L2607:
         game.foobar = (game.foobar > 0 ? -game.foobar : 0);
         ++game.turns;
-        if (game.turns == game.thresh) {
-            newspeak(turn_threshold_messages[game.trndex]);
-            game.trnluz = game.trnluz + TRNVAL[game.trndex] / 100000;
-            ++game.trndex;
-            game.thresh = -1;
-            if (game.trndex <= TRNVLS)
-                game.thresh = MOD(TRNVAL[game.trndex], 100000) + 1;
-        }
-        if (verb == SAY && WD2 > 0)
-            verb = 0;
-        if (verb == SAY) {
-            part = transitive;
+
+       /* 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.wd2 > 0)
+            command.verb = 0;
+        if (command.verb == SAY) {
+            command.part = transitive;
             goto Laction;
         }
         if (closecheck()) {
             goto Laction;
         }
         if (closecheck()) {
@@ -1023,77 +1043,77 @@ L2607:
         } else
             lampcheck();
 
         } else
             lampcheck();
 
-        V1 = VOCAB(WD1, -1);
-        V2 = VOCAB(WD2, -1);
+        V1 = VOCAB(command.wd1, -1);
+        V2 = VOCAB(command.wd2, -1);
         if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
             if (LIQLOC(game.loc) == WATER) {
         if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
             if (LIQLOC(game.loc) == WATER) {
-                RSPEAK(FEET_WET);
+                rspeak(FEET_WET);
             } else {
             } else {
-                RSPEAK(WHERE_QUERY);
+                rspeak(WHERE_QUERY);
             }
             goto L2012;
         }
             }
             goto L2012;
         }
-        if (V1 == ENTER && WD2 > 0) {
-            WD1 = WD2;
-            WD1X = WD2X;
-            WD2 = 0;
+        if (V1 == ENTER && command.wd2 > 0) {
+            command.wd1 = command.wd2;
+            command.wd1x = command.wd2x;
+            wordclear(&command.wd2);
         } else {
         } else {
+            /* FIXME: Magic numbers */
             if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
                   (V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
                 if (AT(V2 - 1000))
             if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
                   (V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
                 if (AT(V2 - 1000))
-                    WD2 = MAKEWD(16152118);
+                    command.wd2 = MAKEWD(WORD_POUR);
             }
             if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
             }
             if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
-                WD1 = MAKEWD(301200308);
+                command.wd1 = MAKEWD(WORD_CATCH);
         }
 L2620:
         }
 L2620:
-        if (WD1 == MAKEWD(23051920)) {
+        if (wordeq(command.wd1, MAKEWD(WORD_WEST))) {
             ++game.iwest;
             if (game.iwest == 10)
             ++game.iwest;
             if (game.iwest == 10)
-                RSPEAK(W_IS_WEST);
+                rspeak(W_IS_WEST);
         }
         }
-        if (WD1 == MAKEWD( 715) && WD2 != 0) {
+        if (wordeq(command.wd1, MAKEWD(WORD_GO)) && !wordempty(command.wd2)) {
             if (++igo == 10)
             if (++igo == 10)
-                RSPEAK(GO_UNNEEDED);
+                rspeak(GO_UNNEEDED);
         }
 Lookup:
         }
 Lookup:
-        defn = VOCAB(WD1, -1);
+        defn = VOCAB(command.wd1, -1);
         if (defn == -1) {
             /* Gee, I don't understand. */
             if (fallback_handler(rawbuf))
                 continue;
         if (defn == -1) {
             /* Gee, I don't understand. */
             if (fallback_handler(rawbuf))
                 continue;
-            SETPRM(1, WD1, WD1X);
-            RSPEAK(DONT_KNOW);
+            rspeak(DONT_KNOW, command.wd1, command.wd1x);
             goto L2600;
         }
         kmod = MOD(defn, 1000);
         switch (defn / 1000) {
         case 0:
             goto L2600;
         }
         kmod = MOD(defn, 1000);
         switch (defn / 1000) {
         case 0:
-            if (playermove(cmdin, verb, kmod))
+            if (playermove(command.verb, kmod))
                 return true;
             else
                 continue;      /* back to top of main interpreter loop */
         case 1:
                 return true;
             else
                 continue;      /* back to top of main interpreter loop */
         case 1:
-            part = unknown;
-            obj = kmod;
+            command.part = unknown;
+            command.obj = kmod;
             break;
         case 2:
             break;
         case 2:
-            part = intransitive;
-            verb = kmod;
+            command.part = intransitive;
+            command.verb = kmod;
             break;
         case 3:
             break;
         case 3:
-            RSPEAK(kmod);
+            rspeak(kmod);
             goto L2012;
         default:
             goto L2012;
         default:
-            BUG(22);
+            BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3);
         }
 
 Laction:
         }
 
 Laction:
-        switch (action(cmdin, part, verb, obj)) {
+        switch (action(cmdin, &command)) {
         case GO_TERMINATE:
             return true;
         case GO_MOVE:
         case GO_TERMINATE:
             return true;
         case GO_MOVE:
-            playermove(cmdin, verb, NUL);
+            playermove(command.verb, NUL);
             return true;
         case GO_TOP:
             continue;  /* back to top of main interpreter loop */
             return true;
         case GO_TOP:
             continue;  /* back to top of main interpreter loop */
@@ -1107,24 +1127,22 @@ Laction:
             goto Lookup;
         case GO_WORD2:
             /* Get second word for analysis. */
             goto Lookup;
         case GO_WORD2:
             /* Get second word for analysis. */
-            WD1 = WD2;
-            WD1X = WD2X;
-            WD2 = 0;
+            command.wd1 = command.wd2;
+            command.wd1x = command.wd2x;
+            wordclear(&command.wd2);
             goto L2620;
         case GO_UNKNOWN:
             /*  Random intransitive verbs come here.  Clear obj just in case
              *  (see attack()). */
             goto L2620;
         case GO_UNKNOWN:
             /*  Random intransitive verbs come here.  Clear obj just in case
              *  (see attack()). */
-            SETPRM(1, WD1, WD1X);
-            RSPEAK(DO_WHAT);
-            obj = 0;
+            rspeak(DO_WHAT, command.wd1, command.wd1x);
+            command.obj = 0;
             goto L2600;
         case GO_DWARFWAKE:
             /*  Oh dear, he's disturbed the dwarves. */
             goto L2600;
         case GO_DWARFWAKE:
             /*  Oh dear, he's disturbed the dwarves. */
-            RSPEAK(DWARVES_AWAKEN);
-            score(endgame);
-            return true;
+            rspeak(DWARVES_AWAKEN);
+            terminate(endgame);
         default:
         default:
-            BUG(99);
+            BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH);
         }
     }
 }
         }
     }
 }