YAMLify section 11 (hints).
[open-adventure.git] / main.c
diff --git a/main.c b/main.c
index 7a179212619ba34b306182894dde4205a0032cb9..00a4d890e17ea25d7e450a88dcc536b8a750f237 100644 (file)
--- a/main.c
+++ b/main.c
@@ -41,7 +41,6 @@ long AMBER, AXE, BACK, BATTERY, BEAR, BIRD, BLOOD,
      RUBY, RUG, SAPPH, SAY, SIGN, SNAKE,
      STEPS, STREAM, THROW, TRIDENT, TROLL, TROLL2,
      URN, VASE, VEND, VOLCANO, WATER;
-long WD1, WD1X, WD2, WD2X;
 
 FILE  *logfp = NULL, *rfp = NULL;
 bool oldstyle = false;
@@ -78,11 +77,11 @@ int main(int argc, char *argv[])
     /*  Options. */
 
 #ifndef ADVENT_NOSAVE
-    char* opts = "l:or:s";
-    char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [-s] \n";
+    const char* opts = "l:or:s";
+    const 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";
+    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) {
@@ -115,15 +114,15 @@ int main(int argc, char *argv[])
             fprintf(stderr,
                     usage, argv[0]);
             fprintf(stderr,
-                    "  where -l creates a log file of your game named as specified'\n");
+                    "        -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 indicates restoring from specified saved game file\n");
+                    "        -r restore from specified saved game file\n");
 #endif
             fprintf(stderr,
-                    "        -s indicates playing with command editing suppressed\n");
+                    "        -s suppress command editing\n");
             exit(-1);
             break;
         }
@@ -131,16 +130,6 @@ int main(int argc, char *argv[])
 
     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;
@@ -157,10 +146,11 @@ int main(int argc, char *argv[])
     game.zzword = RNDVOC(3, 0);
     game.newloc = LOC_START;
     game.loc = LOC_START;
-    game.limit = 330;
+    game.limit = GAMELIMIT;
     if (!rfp) {
         game.novice = YES(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
-        if (game.novice)game.limit = 1000;
+        if (game.novice)
+            game.limit = NOVICELIMIT;
     } else {
         restore(rfp);
     }
@@ -193,11 +183,9 @@ static bool fallback_handler(char *buf)
     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).
- */
+/*  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) {
@@ -209,7 +197,7 @@ static void checkhints(void)
             ++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-1].turns) {
                 int i;
 
                 switch (hint - 1) {
@@ -275,13 +263,13 @@ static void checkhints(void)
 
                 /* Fall through to hint display */
                 game.hintlc[hint] = 0;
-                if (!YES(arbitrary_messages[HINTS[hint][3]], arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
+                if (!YES(hints[hint-1].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
                     return;
-                SETPRM(1, HINTS[hint][2], HINTS[hint][2]);
+                SETPRM(1, hints[hint-1].penalty, hints[hint-1].penalty);
                 RSPEAK(HINT_COST);
-                game.hinted[hint] = YES(arbitrary_messages[WANT_HINT], arbitrary_messages[HINTS[hint][4]], arbitrary_messages[OK_MAN]);
+                game.hinted[hint] = YES(arbitrary_messages[WANT_HINT], hints[hint-1].hint, arbitrary_messages[OK_MAN]);
                 if (game.hinted[hint] && game.limit > WARNTIME)
-                    game.limit += WARNTIME * HINTS[hint][2];
+                    game.limit += WARNTIME * hints[hint-1].penalty;
             }
         }
     }
@@ -295,7 +283,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
-     *  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)
@@ -316,7 +304,7 @@ static bool spotted_by_pirate(int i)
         }
     }
     /* Force chest placement before player finds last treasure */
-    if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == NOWHERE && HERE(LAMP) && game.prop[LAMP] == 1) {
+    if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == LOC_NOWHERE && HERE(LAMP) && game.prop[LAMP] == 1) {
         RSPEAK(PIRATE_SPOTTED);
         movechest = true;
     }
@@ -466,12 +454,15 @@ static bool dwarfmove(void)
     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){
+       SETPRM(1, attack, 0);
+       RSPEAK(THROWN_KNIVES);
+       SETPRM(1, stick, 0);
+       RSPEAK(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT));
+    } else {
+       RSPEAK(KNIFE_THROWN);
+       RSPEAK(MISSES_YOU);
+    }
     if (stick == 0)
         return true;
     game.oldlc2 = game.loc;
@@ -512,7 +503,7 @@ static void croak(void)
     } else if (game.numdie == maximum_deaths || !YES(query, yes_response, arbitrary_messages[OK_MAN]))
         terminate(endgame);
     else {
-        game.place[WATER] = game.place[OIL] = NOWHERE;
+        game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
         if (TOTING(LAMP))
             game.prop[LAMP] = 0;
         for (int j = 1; j <= NOBJECTS; j++) {
@@ -856,12 +847,11 @@ static void lampcheck(void)
         --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.
-     *  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. */
+     *  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] == 0 && HERE(LAMP)) {
         RSPEAK(REPLACE_BATTERIES);
         game.prop[BATTERY] = 1;
@@ -878,7 +868,7 @@ static void lampcheck(void)
         if (!game.lmwarn && HERE(LAMP)) {
             game.lmwarn = true;
             int spk = GET_BATTERIES;
-            if (game.place[BATTERY] == NOWHERE)spk = LAMP_DIM;
+            if (game.place[BATTERY] == LOC_NOWHERE)spk = LAMP_DIM;
             if (game.prop[BATTERY] == 1)spk = MISSING_BATTERYIES;
             RSPEAK(spk);
         }
@@ -934,11 +924,11 @@ static void listobjects(void)
 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;
-    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) {
@@ -988,7 +978,7 @@ static bool do_command(FILE *cmdin)
         if (TOTING(BEAR))RSPEAK(TAME_BEAR);
         speak(msg);
         if (FORCED(game.loc)) {
-            if (playermove(verb, 1))
+            if (playermove(command.verb, 1))
                 return true;
             else
                 continue;      /* back to top of main interpreter loop */
@@ -999,9 +989,9 @@ static bool do_command(FILE *cmdin)
         listobjects();
 
 L2012:
-        verb = 0;
-        game.oldobj = obj;
-        obj = 0;
+        command.verb = 0;
+        game.oldobj = command.obj;
+        command.obj = 0;
 
 L2600:
         checkhints();
@@ -1024,7 +1014,7 @@ L2600:
             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
@@ -1033,18 +1023,22 @@ L2600:
 L2607:
         game.foobar = (game.foobar > 0 ? -game.foobar : 0);
         ++game.turns;
-        if (game.turns == game.thresh) {
-            speak(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 < turn_threshold_count; ++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()) {
@@ -1053,8 +1047,8 @@ L2607:
         } 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) {
                 RSPEAK(FEET_WET);
@@ -1063,53 +1057,54 @@ L2607:
             }
             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 {
+           /* FIXME: Magic numbers */
             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))
-                WD1 = MAKEWD(301200308);
+                command.wd1 = MAKEWD(WORD_CATCH);
         }
 L2620:
-        if (WD1 == MAKEWD(23051920)) {
+        if (wordeq(command.wd1, MAKEWD(WORD_WEST))) {
             ++game.iwest;
             if (game.iwest == 10)
                 RSPEAK(W_IS_WEST);
         }
-        if (WD1 == MAKEWD( 715) && WD2 != 0) {
+        if (wordeq(command.wd1, MAKEWD(WORD_GO)) && !wordempty(command.wd2)) {
             if (++igo == 10)
                 RSPEAK(GO_UNNEEDED);
         }
 Lookup:
-        defn = VOCAB(WD1, -1);
+        defn = VOCAB(command.wd1, -1);
         if (defn == -1) {
             /* Gee, I don't understand. */
             if (fallback_handler(rawbuf))
                 continue;
-            SETPRM(1, WD1, WD1X);
+            SETPRM(1, command.wd1, command.wd1x);
             RSPEAK(DONT_KNOW);
             goto L2600;
         }
         kmod = MOD(defn, 1000);
         switch (defn / 1000) {
         case 0:
-            if (playermove(verb, kmod))
+            if (playermove(command.verb, kmod))
                 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:
-            part = intransitive;
-            verb = kmod;
+            command.part = intransitive;
+            command.verb = kmod;
             break;
         case 3:
             RSPEAK(kmod);
@@ -1119,11 +1114,11 @@ Lookup:
         }
 
 Laction:
-        switch (action(cmdin, part, verb, obj)) {
+        switch (action(cmdin, &command)) {
         case GO_TERMINATE:
             return true;
         case GO_MOVE:
-            playermove(verb, NUL);
+            playermove(command.verb, NUL);
             return true;
         case GO_TOP:
             continue;  /* back to top of main interpreter loop */
@@ -1137,16 +1132,16 @@ Laction:
             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()). */
-            SETPRM(1, WD1, WD1X);
+            SETPRM(1, command.wd1, command.wd1x);
             RSPEAK(DO_WHAT);
-            obj = 0;
+            command.obj = 0;
             goto L2600;
         case GO_DWARFWAKE:
             /*  Oh dear, he's disturbed the dwarves. */