No spk remains in the code base
[open-adventure.git] / actions.c
index 588b4d979aa51d3bf2e9e418035d61429fdabaf5..40a1928f62d99299970e9a07fef2775cd59196f1 100644 (file)
--- a/actions.c
+++ b/actions.c
@@ -4,7 +4,7 @@
 #include "advent.h"
 #include "dungeon.h"
 
-static int fill(token_t, token_t);
+static int fill(verb_t, obj_t);
 
 static int attack(struct command_t *command)
 /*  Attack.  Assume target if unambiguous.  "Throw" also links here.
@@ -12,11 +12,10 @@ static int attack(struct command_t *command)
  *  dwarf, etc.)  and others (bird, clam, machine).  Ambiguous if 2
  *  enemies, or no enemies but 2 others. */
 {
-    vocab_t verb = command->verb;
+    verb_t verb = command->verb;
     obj_t obj = command->obj;
 
-    if (obj == NO_OBJECT ||
-        obj == INTRANSITIVE) {
+    if (obj == INTRANSITIVE) {
         int changes = 0;
         if (atdwrf(game.loc) > 0) {
             obj = DWARF;
@@ -43,7 +42,7 @@ static int attack(struct command_t *command)
             ++changes;
         }
         /* check for low-priority targets */
-        if (obj == NO_OBJECT) {
+        if (obj == INTRANSITIVE) {
             /* Can't attack bird or machine by throwing axe. */
             if (HERE(BIRD) && verb != THROW) {
                 obj = BIRD;
@@ -113,19 +112,19 @@ static int attack(struct command_t *command)
         state_change(DRAGON, DRAGON_DEAD);
         game.prop[RUG] = RUG_FLOOR;
         /* Hardcoding LOC_SECRET5 as the dragon's death location is ugly.
-         * The way it was computed before was wirse; it depended on the
+         * The way it was computed before was worse; it depended on the
          * two dragon locations being LOC_SECRET4 and LOC_SECRET6 and
          * LOC_SECRET5 being right between them.
          */
-        move(DRAGON + NOBJECTS, -1);
-        move(RUG + NOBJECTS, LOC_NOWHERE);
+        move(DRAGON + NOBJECTS, IS_FIXED);
+        move(RUG + NOBJECTS, IS_FREE);
         move(DRAGON, LOC_SECRET5);
         move(RUG, LOC_SECRET5);
         drop(BLOOD, LOC_SECRET5);
-        for (obj = 1; obj <= NOBJECTS; obj++) {
-            if (game.place[obj] == objects[DRAGON].plac ||
-                game.place[obj] == objects[DRAGON].fixd)
-                move(obj, LOC_SECRET5);
+        for (obj_t i = 1; i <= NOBJECTS; i++) {
+            if (game.place[i] == objects[DRAGON].plac ||
+                game.place[i] == objects[DRAGON].fixd)
+                move(i, LOC_SECRET5);
         }
         game.loc = LOC_SECRET5;
         return GO_MOVE;
@@ -153,7 +152,7 @@ static int attack(struct command_t *command)
     }
 
     switch (obj) {
-    case NO_OBJECT:
+    case INTRANSITIVE:
         rspeak(NO_TARGET);
         break;
     case CLAM:
@@ -186,13 +185,13 @@ static int bigwords(long id)
  *  Look up foo in special section of vocab to determine which word we've got.
  *  Last word zips the eggs back to the giant room (unless already there). */
 {
-    if ((game.foobar == WORD_EMPTY && id == ACTION_WORD(FEE)) ||
-        (game.foobar == ACTION_WORD(FEE) && id == ACTION_WORD(FIE)) ||
-        (game.foobar == ACTION_WORD(FIE) && id == ACTION_WORD(FOE)) ||
-        (game.foobar == ACTION_WORD(FOE) && id == ACTION_WORD(FOO)) ||
-        (game.foobar == ACTION_WORD(FOE) && id == ACTION_WORD(FUM))) {
+    if ((game.foobar == WORD_EMPTY && id == FEE) ||
+        (game.foobar == FEE && id == FIE) ||
+        (game.foobar == FIE && id == FOE) ||
+        (game.foobar == FOE && id == FOO) ||
+        (game.foobar == FOE && id == FUM)) {
         game.foobar = id;
-        if ((id != ACTION_WORD(FOO)) && (id != ACTION_WORD(FUM))) {
+        if ((id != FOO) && (id != FUM)) {
             rspeak(OK_MAN);
             return GO_CLEAROBJ;
         }
@@ -206,11 +205,13 @@ static int bigwords(long id)
              *  crossing. */
             if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID)
                 game.prop[TROLL] = TROLL_PAIDONCE;
-            int k = EGGS_DONE;
+            int k;
             if (HERE(EGGS))
                 k = EGGS_VANISHED;
-            if (game.loc == objects[EGGS].plac)
+            else if (game.loc == objects[EGGS].plac)
                 k = EGGS_HERE;
+            else
+                k = EGGS_DONE;
             move(EGGS, objects[EGGS].plac);
             pspeak(EGGS, look, k, true);
             return GO_CLEAROBJ;
@@ -231,40 +232,44 @@ static int bigwords(long id)
 static void blast(void)
 /*  Blast.  No effect unless you've got dynamite, which is a neat trick! */
 {
-    if (game.prop[ROD2] < 0 ||
+    if (game.prop[ROD2] == STATE_NOTFOUND ||
         !game.closed)
         rspeak(REQUIRES_DYNAMITE);
     else {
-        game.bonus = VICTORY_MESSAGE;
-        if (game.loc == LOC_NE)
-            game.bonus = DEFEAT_MESSAGE;
         if (HERE(ROD2))
             game.bonus = SPLATTER_MESSAGE;
+        else if (game.loc == LOC_NE)
+            game.bonus = DEFEAT_MESSAGE;
+        else
+            game.bonus = VICTORY_MESSAGE;
         rspeak(game.bonus);
         terminate(endgame);
     }
 }
 
-static int vbreak(vocab_t verb, obj_t obj)
+static int vbreak(verb_t verb, obj_t obj)
 /*  Break.  Only works for mirror in repository and, of course, the vase. */
 {
-    if (obj == MIRROR) {
+    switch (obj) {
+    case MIRROR:
         if (game.closed) {
             rspeak(BREAK_MIRROR);
             return GO_DWARFWAKE;
         } else {
             rspeak(TOO_FAR);
-            return GO_CLEAROBJ;
+            break;
         }
+    case VASE:
+        if (game.prop[VASE] == VASE_WHOLE) {
+            if (TOTING(VASE))
+                drop(VASE, game.loc);
+            state_change(VASE, VASE_BROKEN);
+            game.fixed[VASE] = IS_FIXED;
+            break;
+        }
+    default:
+        speak(actions[verb].message);
     }
-    if (obj == VASE && game.prop[VASE] == VASE_WHOLE) {
-        if (TOTING(VASE))
-            drop(VASE, game.loc);
-        state_change(VASE, VASE_BROKEN);
-        game.fixed[VASE] = IS_FIXED;
-        return GO_CLEAROBJ;
-    }
-    speak(actions[verb].message);
     return (GO_CLEAROBJ);
 }
 
@@ -277,14 +282,14 @@ static int brief(void)
     return GO_CLEAROBJ;
 }
 
-static int vcarry(token_t verb, obj_t obj)
+static int vcarry(verb_t verb, obj_t obj)
 /*  Carry an object.  Special cases for bird and cage (if bird in cage, can't
  *  take one without the other).  Liquids also special, since they depend on
  *  status of bottle.  Also various side effects, etc. */
 {
     if (obj == INTRANSITIVE) {
         /*  Carry, no object given yet.  OK if only one object present. */
-        if (game.atloc[game.loc] == 0 ||
+        if (game.atloc[game.loc] == NO_OBJECT ||
             game.link[game.atloc[game.loc]] != 0 ||
             atdwrf(game.loc) > 0)
             return GO_UNKNOWN;
@@ -292,7 +297,7 @@ static int vcarry(token_t verb, obj_t obj)
     }
 
     if (TOTING(obj)) {
-        rspeak(ALREADY_CARRYING);
+        speak(actions[verb].message);
         return GO_CLEAROBJ;
     }
 
@@ -395,7 +400,7 @@ static int vcarry(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int chain(token_t verb)
+static int chain(verb_t verb)
 /* Do something to the bear's chain */
 {
     if (verb != LOCK) {
@@ -414,8 +419,13 @@ static int chain(token_t verb)
 
         switch (game.prop[BEAR]) {
         case BEAR_DEAD:
+            // LCOV_EXCL_START
+            /* Can't be reached as long as the only way for the bear to die
+             * is from a bridge collapse. Leave in in case this changes, but
+             * exclude from coverage testing. */
             game.fixed[BEAR] = IS_FIXED;
             break;
+        // LCOV_EXCL_STOP
         default:
             game.fixed[BEAR] = IS_FREE;
         }
@@ -442,7 +452,7 @@ static int chain(token_t verb)
     return GO_CLEAROBJ;
 }
 
-static int discard(token_t verb, obj_t obj)
+static int discard(verb_t verb, obj_t obj)
 /*  Discard object.  "Throw" also comes here for most objects.  Special cases for
  *  bird (might attack snake or dragon) and cage (might contain bird) and vase.
  *  Drop coins at vending machine for extra batteries. */
@@ -467,13 +477,13 @@ static int discard(token_t verb, obj_t obj)
         game.prop[CAVITY] = CAVITY_FULL;
         if (HERE(RUG) && ((obj == EMERALD && game.prop[RUG] != RUG_HOVER) ||
                           (obj == RUBY && game.prop[RUG] == RUG_HOVER))) {
-            int spk = RUG_RISES;
-            if (TOTING(RUG))
-                spk = RUG_WIGGLES;
             if (obj == RUBY)
-                spk = RUG_SETTLES;
-            rspeak(spk);
-            if (spk != RUG_WIGGLES) {
+                rspeak(RUG_SETTLES);
+            else if (TOTING(RUG))
+                rspeak(RUG_WIGGLES);
+            else
+                rspeak(RUG_RISES);
+            if (!TOTING(RUG) || obj == RUBY) {
                 int k = (game.prop[RUG] == RUG_HOVER) ? RUG_FLOOR : RUG_HOVER;
                 game.prop[RUG] = k;
                 if (k == RUG_HOVER)
@@ -493,7 +503,7 @@ static int discard(token_t verb, obj_t obj)
     } else if (obj == BEAR && AT(TROLL)) {
         state_change(TROLL, TROLL_GONE);
         move(TROLL, LOC_NOWHERE);
-        move(TROLL + NOBJECTS, LOC_NOWHERE);
+        move(TROLL + NOBJECTS, IS_FREE);
         move(TROLL2, objects[TROLL].plac);
         move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
         juggle(CHASM);
@@ -523,11 +533,11 @@ static int discard(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int drink(token_t verb, obj_t obj)
+static int drink(verb_t verb, obj_t obj)
 /*  Drink.  If no object, assume water and look for it here.  If water is in
  *  the bottle, drink that, else must be at a water loc, so drink stream. */
 {
-    if (obj == NO_OBJECT && LIQLOC(game.loc) != WATER &&
+    if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
         (LIQUID() != WATER || !HERE(BOTTLE))) {
         return GO_UNKNOWN;
     }
@@ -539,7 +549,7 @@ static int drink(token_t verb, obj_t obj)
         return GO_CLEAROBJ;
     }
 
-    if (obj != NO_OBJECT && obj != WATER) {
+    if (obj != INTRANSITIVE && obj != WATER) {
         rspeak(RIDICULOUS_ATTEMPT);
         return GO_CLEAROBJ;
     }
@@ -553,7 +563,7 @@ static int drink(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int eat(token_t verb, obj_t obj)
+static int eat(verb_t verb, obj_t obj)
 /*  Eat.  Intransitive: assume food if present, else ask what.  Transitive: food
  *  ok, some things lose appetite, rest are ridiculous. */
 {
@@ -582,7 +592,7 @@ static int eat(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int extinguish(token_t verb, obj_t obj)
+static int extinguish(verb_t verb, obj_t obj)
 /* Extinguish.  Lamp, urn, dragon/volcano (nice try). */
 {
     if (obj == INTRANSITIVE) {
@@ -618,7 +628,7 @@ static int extinguish(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int feed(token_t verb, obj_t obj)
+static int feed(verb_t verb, obj_t obj)
 /*  Feed.  If bird, no seed.  Snake, dragon, troll: quip.  If dwarf, make him
  *  mad.  Bear, special. */
 {
@@ -678,7 +688,7 @@ static int feed(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-int fill(token_t verb, obj_t obj)
+int fill(verb_t verb, obj_t obj)
 /*  Fill.  Bottle or urn must be empty, and liquid available.  (Vase
  *  is nasty.) */
 {
@@ -726,11 +736,11 @@ int fill(token_t verb, obj_t obj)
         game.place[k] = LOC_NOWHERE;
         return GO_CLEAROBJ;
     }
-    if (obj != NO_OBJECT && obj != BOTTLE) {
+    if (obj != INTRANSITIVE && obj != BOTTLE) {
         speak(actions[verb].message);
         return GO_CLEAROBJ;
     }
-    if (obj == NO_OBJECT && !HERE(BOTTLE))
+    if (obj == INTRANSITIVE && !HERE(BOTTLE))
         return GO_UNKNOWN;
 
     if (HERE(URN) && game.prop[URN] != URN_EMPTY) {
@@ -754,7 +764,7 @@ int fill(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int find(token_t verb, obj_t obj)
+static int find(verb_t verb, obj_t obj)
 /* Find.  Might be carrying it, or it might be here.  Else give caveat. */
 {
     if (TOTING(obj)) {
@@ -780,7 +790,7 @@ static int find(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int fly(token_t verb, obj_t obj)
+static int fly(verb_t verb, obj_t obj)
 /* Fly.  Snide remarks unless hovering rug is here. */
 {
     if (obj == INTRANSITIVE) {
@@ -837,7 +847,7 @@ static int inven(void)
     return GO_CLEAROBJ;
 }
 
-static int light(token_t verb, obj_t obj)
+static int light(verb_t verb, obj_t obj)
 /*  Light.  Applicable only to lamp and urn. */
 {
     if (obj == INTRANSITIVE) {
@@ -891,7 +901,11 @@ static int listen(void)
             game.prop[i] < 0)
             continue;
         int mi =  game.prop[i];
-        /* FIXME: Weird magic on object states */
+        /* (ESR) Some unpleasant magic on object states here. Ideally
+         * we'd have liked the bird to be a normal object that we can
+         * use state_change() on; can't do it, because there are
+         * actually two different series of per-state birdsounds
+         * depending on whether player has drunk dragon's blood. */
         if (i == BIRD)
             mi += 3 * game.blooded;
         long packed_zzword = token_to_packed(game.zzword);
@@ -905,7 +919,7 @@ static int listen(void)
     return GO_CLEAROBJ;
 }
 
-static int lock(token_t verb, obj_t obj)
+static int lock(verb_t verb, obj_t obj)
 /* Lock, unlock, no object given.  Assume various things if present. */
 {
     if (obj == INTRANSITIVE) {
@@ -984,12 +998,12 @@ static int lock(token_t verb, obj_t obj)
     return GO_CLEAROBJ;
 }
 
-static int pour(token_t verb, obj_t obj)
+static int pour(verb_t verb, obj_t obj)
 /*  Pour.  If no object, or object is bottle, assume contents of bottle.
  *  special tests for pouring water or oil on plant or rusty door. */
 {
     if (obj == BOTTLE ||
-        obj == NO_OBJECT)
+        obj == INTRANSITIVE)
         obj = LIQUID();
     if (obj == NO_OBJECT)
         return GO_UNKNOWN;
@@ -1084,7 +1098,7 @@ static int reservoir(void)
     }
 }
 
-static int rub(token_t verb, obj_t obj)
+static int rub(verb_t verb, obj_t obj)
 /* Rub.  Yields various snide remarks except for lit urn. */
 {
     if (obj == URN && game.prop[URN] == URN_LIT) {
@@ -1107,20 +1121,22 @@ static int say(struct command_t *command)
 {
     if (command->wd2 > 0) {
         command->wd1 = command->wd2;
-        strcpy(command->raw1, command->raw2);
+        strncpy(command->raw1, command->raw2, LINESIZE - 1);
     }
     char word1[TOKLEN + 1];
     packed_to_token(command->wd1, word1);
-    int wd = (int) get_vocab_id(word1);
-    if (wd == MOTION_WORD(XYZZY) ||
-        wd == MOTION_WORD(PLUGH) ||
-        wd == MOTION_WORD(PLOVER) ||
-        wd == ACTION_WORD(FEE) ||
-        wd == ACTION_WORD(FIE) ||
-        wd == ACTION_WORD(FOE) ||
-        wd == ACTION_WORD(FOO) ||
-        wd == ACTION_WORD(FUM) ||
-        wd == ACTION_WORD(PART)) {
+    long wd;
+    enum wordtype type;
+    get_vocab_metadata(word1, &wd, &type);
+    if (wd == XYZZY ||
+        wd == PLUGH ||
+        wd == PLOVER ||
+        wd == FEE ||
+        wd == FIE ||
+        wd == FOE ||
+        wd == FOO ||
+        wd == FUM ||
+        wd == PART) {
         /* FIXME: scribbles on the interpreter's command block */
         wordclear(&command->wd2);
         return GO_LOOKUP;
@@ -1150,7 +1166,7 @@ static int throw (struct command_t *command)
         /*  Snarf a treasure for the troll. */
         drop(command->obj, LOC_NOWHERE);
         move(TROLL, LOC_NOWHERE);
-        move(TROLL + NOBJECTS, LOC_NOWHERE);
+        move(TROLL + NOBJECTS, IS_FREE);
         drop(TROLL2, objects[TROLL].plac);
         drop(TROLL2 + NOBJECTS, objects[TROLL].fixd);
         juggle(CHASM);
@@ -1180,7 +1196,7 @@ static int throw (struct command_t *command)
                 state_change(AXE, AXE_LOST);
                 return GO_CLEAROBJ;
             }
-            command->obj = NO_OBJECT;
+            command->obj = INTRANSITIVE;
             return (attack(command));
         }
 
@@ -1197,7 +1213,7 @@ static int throw (struct command_t *command)
     }
 }
 
-static int wake(token_t verb, obj_t obj)
+static int wake(verb_t verb, obj_t obj)
 /* Wake.  Only use is to disturb the dwarves. */
 {
     if (obj != DWARF ||
@@ -1210,7 +1226,7 @@ static int wake(token_t verb, obj_t obj)
     }
 }
 
-static int wave(token_t verb, obj_t obj)
+static int wave(verb_t verb, obj_t obj)
 /* Wave.  No effect unless waving rod at fissure or at bird. */
 {
     if (obj != ROD ||
@@ -1289,10 +1305,10 @@ int action(struct command_t *command)
         else if ((LIQUID() == command->obj && HERE(BOTTLE)) ||
                  command->obj == LIQLOC(game.loc))
             /* FALL THROUGH */;
-        else if (command->obj == OIL && HERE(URN) && game.prop[URN] != 0) {
+        else if (command->obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) {
             command->obj = URN;
             /* FALL THROUGH */;
-        } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) {
+        } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) {
             command->obj = PLANT2;
             /* FALL THROUGH */;
         } else if (command->obj == KNIFE && game.knfloc == game.loc) {
@@ -1353,13 +1369,14 @@ int action(struct command_t *command)
                 return GO_CLEAROBJ;
             }
             case ATTACK:
+                command->obj = INTRANSITIVE;
                 return attack(command);
             case POUR:
-                return pour(command->verb, command->obj);
+                return pour(command->verb, INTRANSITIVE);
             case EAT:
                 return eat(command->verb, INTRANSITIVE);
             case DRINK:
-                return drink(command->verb, command->obj);
+                return drink(command->verb, INTRANSITIVE);
             case RUB:
                 return GO_UNKNOWN;
             case THROW:
@@ -1373,7 +1390,7 @@ int action(struct command_t *command)
             case FEED:
                 return GO_UNKNOWN;
             case FILL:
-                return fill(command->verb, command->obj);
+                return fill(command->verb, INTRANSITIVE);
             case BLAST:
                 blast();
                 return GO_CLEAROBJ;