Save/resume fail test coverage -- test works in Docker now
[open-adventure.git] / main.c
diff --git a/main.c b/main.c
index c4892ffe04e8eb04f74b0cae5bebb62c2d5777e4..7a179212619ba34b306182894dde4205a0032cb9 100644 (file)
--- a/main.c
+++ b/main.c
@@ -77,7 +77,14 @@ int main(int argc, char *argv[])
 
     /*  Options. */
 
-    while ((ch = getopt(argc, argv, "l:or:s")) != EOF) {
+#ifndef ADVENT_NOSAVE
+    char* opts = "l:or:s";
+    char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [-s] \n";
+#else
+    char* opts = "l:os";
+    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");
@@ -91,6 +98,7 @@ int main(int argc, char *argv[])
             oldstyle = true;
             editline = prompt = false;
             break;
+#ifndef ADVENT_NOSAVE
         case 'r':
             rfp = fopen(optarg, "r");
             if (rfp == NULL)
@@ -99,18 +107,21 @@ int main(int argc, char *argv[])
                         optarg);
             signal(SIGINT, sig_handler);
             break;
+#endif
         case 's':
             editline = false;
             break;
         default:
             fprintf(stderr,
-                    "Usage: %s [-l logfilename] [-o] [-r restorefilename] [-s] \n", argv[0]);
+                    usage, argv[0]);
             fprintf(stderr,
                     "  where -l creates 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 indicates restoring from specified saved game file\n");
+#endif
             fprintf(stderr,
                     "        -s indicates playing with command editing suppressed\n");
             exit(-1);
@@ -148,7 +159,7 @@ int main(int argc, char *argv[])
     game.loc = LOC_START;
     game.limit = 330;
     if (!rfp) {
-        game.novice = YES(WELCOME_YOU, CAVE_NEARBY, NO_MESSAGE);
+        game.novice = YES(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
         if (game.novice)game.limit = 1000;
     } else {
         restore(rfp);
@@ -264,11 +275,11 @@ static void checkhints(void)
 
                 /* Fall through to hint display */
                 game.hintlc[hint] = 0;
-                if (!YES(HINTS[hint][3], NO_MESSAGE, OK_MAN))
+                if (!YES(arbitrary_messages[HINTS[hint][3]], arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
                     return;
                 SETPRM(1, HINTS[hint][2], HINTS[hint][2]);
                 RSPEAK(HINT_COST);
-                game.hinted[hint] = YES(WANT_HINT, HINTS[hint][4], OK_MAN);
+                game.hinted[hint] = YES(arbitrary_messages[WANT_HINT], arbitrary_messages[HINTS[hint][4]], arbitrary_messages[OK_MAN]);
                 if (game.hinted[hint] && game.limit > WARNTIME)
                     game.limit += WARNTIME * HINTS[hint][2];
             }
@@ -451,7 +462,7 @@ static bool dwarfmove(void)
     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);
     if (attack == 0)
         return true;
     if (game.dflag == 2)game.dflag = 3;
@@ -470,7 +481,7 @@ static bool dwarfmove(void)
 /*  "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.  maximum_deaths 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
@@ -490,15 +501,15 @@ static bool dwarfmove(void)
 static void croak(void)
 /*  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. */
         RSPEAK(DEATH_CLOSING);
         terminate(endgame);
-    }
-    /* FIXME: Arithmetic on message numbers */
-    else if (game.numdie == MAXDIE || !YES(WATCH_IT + game.numdie * 2, WHICH_WAY + game.numdie * 2, OK_MAN))
+    } else if (game.numdie == maximum_deaths || !YES(query, yes_response, arbitrary_messages[OK_MAN]))
         terminate(endgame);
     else {
         game.place[WATER] = game.place[OIL] = NOWHERE;
@@ -605,7 +616,7 @@ static bool playermove(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 (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;
             RSPEAK(spk);
@@ -618,93 +629,104 @@ static bool playermove(token_t verb, int motion)
     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.
          */
-        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 */
-                }
-                if (TOTING(motion) || (game.newloc > 200 && AT(motion)))
+                } else if (game.prop[motion] != game.newloc / 100 - 3)
                     break;
-                /* else fall through */
-            } else if (game.prop[motion] != game.newloc / 100 - 3)
-                break;
-L12:
-            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;
-        }
+                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;
+            }
 
-        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 > 1 || (game.holdng == 1 && !TOTING(EMERALD))) {
-                    game.newloc = game.loc;
-                    RSPEAK(MUST_DROP);
-                }
+            game.newloc = MOD(scratchloc, 1000);
+            if (!SPECIAL(game.newloc))
                 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);
-                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, 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;
+            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;
-                } 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();
+                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, 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(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST);
+            break; /* Leave L12 loop */
         }
     } while
     (false);
@@ -841,11 +863,11 @@ static void lampcheck(void)
      *  goes out.  Even then, he can explore outside for a while
      *  if desired. */
     if (game.limit <= WARNTIME && HERE(BATTERY) && game.prop[BATTERY] == 0 && HERE(LAMP)) {
-        RSPEAK(REPLACE_BATTERYIES);
+        RSPEAK(REPLACE_BATTERIES);
         game.prop[BATTERY] = 1;
         if (TOTING(BATTERY))
             DROP(BATTERY, game.loc);
-        game.limit = game.limit + 2500;
+        game.limit += BATTERYLIFE;
         game.lmwarn = false;
     } else if (game.limit == 0) {
         game.limit = -1;
@@ -855,7 +877,7 @@ static void lampcheck(void)
     } else if (game.limit <= WARNTIME) {
         if (!game.lmwarn && HERE(LAMP)) {
             game.lmwarn = true;
-            int spk = GET_BATTERYIES;
+            int spk = GET_BATTERIES;
             if (game.place[BATTERY] == NOWHERE)spk = LAMP_DIM;
             if (game.prop[BATTERY] == 1)spk = MISSING_BATTERYIES;
             RSPEAK(spk);
@@ -971,7 +993,8 @@ static bool do_command(FILE *cmdin)
             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();