LCOV - code coverage report
Current view: top level - open-adventure - actions.c (source / functions) Hit Total Coverage
Test: advent.info Lines: 1056 1057 99.9 %
Date: 2017-09-17 15:40:10 Functions: 32 32 100.0 %

          Line data    Source code
       1             : #include <stdlib.h>
       2             : #include <stdbool.h>
       3             : #include <string.h>
       4             : #include "advent.h"
       5             : #include "dungeon.h"
       6             : #include <inttypes.h>
       7             : 
       8             : static int fill(verb_t, obj_t);
       9             : 
      10         165 : static int attack(command_t command)
      11             : /*  Attack.  Assume target if unambiguous.  "Throw" also links here.
      12             :  *  Attackable objects fall into two categories: enemies (snake,
      13             :  *  dwarf, etc.)  and others (bird, clam, machine).  Ambiguous if 2
      14             :  *  enemies, or no enemies but 2 others. */
      15             : {
      16         165 :     verb_t verb = command.verb;
      17         165 :     obj_t obj = command.obj;
      18             : 
      19         165 :     if (obj == INTRANSITIVE) {
      20          23 :         int changes = 0;
      21          23 :         if (atdwrf(game.loc) > 0) {
      22           3 :             obj = DWARF;
      23           3 :             ++changes;
      24             :         }
      25          23 :         if (HERE(SNAKE)) {
      26           1 :             obj = SNAKE;
      27           1 :             ++changes;
      28             :         }
      29          23 :         if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) {
      30           6 :             obj = DRAGON;
      31           6 :             ++changes;
      32             :         }
      33          23 :         if (AT(TROLL)) {
      34           1 :             obj = TROLL;
      35           1 :             ++changes;
      36             :         }
      37          23 :         if (AT(OGRE)) {
      38           3 :             obj = OGRE;
      39           3 :             ++changes;
      40             :         }
      41          23 :         if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) {
      42           1 :             obj = BEAR;
      43           1 :             ++changes;
      44             :         }
      45             :         /* check for low-priority targets */
      46          23 :         if (obj == INTRANSITIVE) {
      47             :             /* Can't attack bird or machine by throwing axe. */
      48          10 :             if (HERE(BIRD) && verb != THROW) {
      49           2 :                 obj = BIRD;
      50           2 :                 ++changes;
      51             :             }
      52          10 :             if (HERE(VEND) && verb != THROW) {
      53           1 :                 obj = VEND;
      54           1 :                 ++changes;
      55             :             }
      56             :             /* Clam and oyster both treated as clam for intransitive case;
      57             :              * no harm done. */
      58          10 :             if (HERE(CLAM) || HERE(OYSTER)) {
      59           1 :                 obj = CLAM;
      60           1 :                 ++changes;
      61             :             }
      62             :         }
      63          23 :         if (changes >= 2)
      64           3 :             return GO_UNKNOWN;
      65             :     }
      66             : 
      67         162 :     if (obj == BIRD) {
      68           3 :         if (game.closed) {
      69           1 :             rspeak(UNHAPPY_BIRD);
      70             :         } else {
      71           2 :             DESTROY(BIRD);
      72           2 :             rspeak(BIRD_DEAD);
      73             :         }
      74           3 :         return GO_CLEAROBJ;
      75             :     }
      76         159 :     if (obj == VEND) {
      77          47 :         state_change(VEND,
      78          47 :                      game.prop[VEND] == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS);
      79          47 :         return GO_CLEAROBJ;
      80             :     }
      81             : 
      82         112 :     if (obj == BEAR) {
      83           7 :         switch (game.prop[BEAR]) {
      84           2 :         case UNTAMED_BEAR:
      85           2 :             rspeak(BEAR_HANDS);
      86           2 :             break;
      87           1 :         case SITTING_BEAR:
      88           1 :             rspeak(BEAR_CONFUSED);
      89           1 :             break;
      90           3 :         case CONTENTED_BEAR:
      91           3 :             rspeak(BEAR_CONFUSED);
      92           3 :             break;
      93           1 :         case BEAR_DEAD:
      94           1 :             rspeak(ALREADY_DEAD);
      95           1 :             break;
      96             :         }
      97           7 :         return GO_CLEAROBJ;
      98             :     }
      99         105 :     if (obj == DRAGON && game.prop[DRAGON] == DRAGON_BARS) {
     100             :         /*  Fun stuff for dragon.  If he insists on attacking it, win!
     101             :          *  Set game.prop to dead, move dragon to central loc (still
     102             :          *  fixed), move rug there (not fixed), and move him there,
     103             :          *  too.  Then do a null motion to get new description. */
     104          52 :         rspeak(BARE_HANDS_QUERY);
     105          52 :         if (!silent_yes()) {
     106           1 :             speak(arbitrary_messages[NASTY_DRAGON]);
     107           1 :             return GO_MOVE;
     108             :         }
     109          51 :         state_change(DRAGON, DRAGON_DEAD);
     110          51 :         game.prop[RUG] = RUG_FLOOR;
     111             :         /* Hardcoding LOC_SECRET5 as the dragon's death location is ugly.
     112             :          * The way it was computed before was worse; it depended on the
     113             :          * two dragon locations being LOC_SECRET4 and LOC_SECRET6 and
     114             :          * LOC_SECRET5 being right between them.
     115             :          */
     116          51 :         move(DRAGON + NOBJECTS, IS_FIXED);
     117          51 :         move(RUG + NOBJECTS, IS_FREE);
     118          51 :         move(DRAGON, LOC_SECRET5);
     119          51 :         move(RUG, LOC_SECRET5);
     120          51 :         drop(BLOOD, LOC_SECRET5);
     121        3570 :         for (obj_t i = 1; i <= NOBJECTS; i++) {
     122        7038 :             if (game.place[i] == objects[DRAGON].plac ||
     123        3519 :                 game.place[i] == objects[DRAGON].fixd)
     124           1 :                 move(i, LOC_SECRET5);
     125             :         }
     126          51 :         game.loc = LOC_SECRET5;
     127          51 :         return GO_MOVE;
     128             :     }
     129             : 
     130          53 :     if (obj == OGRE) {
     131          38 :         rspeak(OGRE_DODGE);
     132          38 :         if (atdwrf(game.loc) == 0)
     133           1 :             return GO_CLEAROBJ;
     134             : 
     135          37 :         rspeak(KNIFE_THROWN);
     136          37 :         DESTROY(OGRE);
     137          37 :         int dwarves = 0;
     138         222 :         for (int i = 1; i < PIRATE; i++) {
     139         185 :             if (game.dloc[i] == game.loc) {
     140          38 :                 ++dwarves;
     141          38 :                 game.dloc[i] = LOC_LONGWEST;
     142          38 :                 game.dseen[i] = false;
     143             :             }
     144             :         }
     145          37 :         rspeak((dwarves > 1) ?
     146             :                OGRE_PANIC1 :
     147             :                OGRE_PANIC2);
     148          37 :         return GO_CLEAROBJ;
     149             :     }
     150             : 
     151          15 :     switch (obj) {
     152           7 :     case INTRANSITIVE:
     153           7 :         rspeak(NO_TARGET);
     154           7 :         break;
     155           1 :     case CLAM:
     156             :     case OYSTER:
     157           1 :         rspeak(SHELL_IMPERVIOUS);
     158           1 :         break;
     159           1 :     case SNAKE:
     160           1 :         rspeak(SNAKE_WARNING);
     161           1 :         break;
     162           3 :     case DWARF:
     163           3 :         if (game.closed) {
     164           1 :             return GO_DWARFWAKE;
     165             :         }
     166           2 :         rspeak(BARE_HANDS_QUERY);
     167           2 :         break;
     168           1 :     case DRAGON:
     169           1 :         rspeak(ALREADY_DEAD);
     170           1 :         break;
     171           1 :     case TROLL:
     172           1 :         rspeak(ROCKY_TROLL);
     173           1 :         break;
     174           1 :     default:
     175           1 :         speak(actions[verb].message);
     176             :     }
     177          14 :     return GO_CLEAROBJ;
     178             : }
     179             : 
     180         113 : static int bigwords(vocab_t id)
     181             : /*  FEE FIE FOE FOO (AND FUM).  Advance to next state if given in proper order.
     182             :  *  Look up foo in special section of vocab to determine which word we've got.
     183             :  *  Last word zips the eggs back to the giant room (unless already there). */
     184             : {
     185         198 :     if ((game.foobar == WORD_EMPTY && id == FEE) ||
     186         170 :         (game.foobar == FEE && id == FIE) ||
     187         114 :         (game.foobar == FIE && id == FOE) ||
     188          61 :         (game.foobar == FOE && id == FOO) ||
     189           7 :         (game.foobar == FOE && id == FUM)) {
     190         110 :         game.foobar = id;
     191         110 :         if ((id != FOO) && (id != FUM)) {
     192          84 :             rspeak(OK_MAN);
     193          84 :             return GO_CLEAROBJ;
     194             :         }
     195          26 :         game.foobar = WORD_EMPTY;
     196          51 :         if (game.place[EGGS] == objects[EGGS].plac ||
     197          27 :             (TOTING(EGGS) && game.loc == objects[EGGS].plac)) {
     198           2 :             rspeak(NOTHING_HAPPENS);
     199           2 :             return GO_CLEAROBJ;
     200             :         } else {
     201             :             /*  Bring back troll if we steal the eggs back from him before
     202             :              *  crossing. */
     203          24 :             if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID)
     204           1 :                 game.prop[TROLL] = TROLL_PAIDONCE;
     205          24 :             if (HERE(EGGS))
     206           1 :                 pspeak(EGGS, look, EGGS_VANISHED, true);
     207          23 :             else if (game.loc == objects[EGGS].plac)
     208          21 :                 pspeak(EGGS, look, EGGS_HERE, true);
     209             :             else
     210           2 :                 pspeak(EGGS, look, EGGS_DONE, true);
     211          24 :             move(EGGS, objects[EGGS].plac);
     212             : 
     213          24 :             return GO_CLEAROBJ;
     214             :         }
     215             :     } else {
     216           3 :         if (game.loc == LOC_GIANTROOM) {
     217           2 :             rspeak(START_OVER);
     218             :         } else {
     219             :             /* This is new begavior in Open Adventure - sounds better when
     220             :              * player isn't in the Giant Room. */
     221           1 :             rspeak(WELL_POINTLESS);
     222             :         }
     223           3 :         game.foobar = WORD_EMPTY;
     224           3 :         return GO_CLEAROBJ;
     225             :     }
     226             : }
     227             : 
     228           7 : static void blast(void)
     229             : /*  Blast.  No effect unless you've got dynamite, which is a neat trick! */
     230             : {
     231          14 :     if (game.prop[ROD2] == STATE_NOTFOUND ||
     232           7 :         !game.closed)
     233           3 :         rspeak(REQUIRES_DYNAMITE);
     234             :     else {
     235           4 :         if (HERE(ROD2)) {
     236           1 :             game.bonus = splatter;
     237           1 :             rspeak(SPLATTER_MESSAGE);
     238           3 :         } else if (game.loc == LOC_NE) {
     239           1 :             game.bonus = defeat;
     240           1 :             rspeak(DEFEAT_MESSAGE);
     241             :         } else {
     242           2 :             game.bonus = victory;
     243           2 :             rspeak(VICTORY_MESSAGE);
     244             :         }
     245           4 :         terminate(endgame);
     246             :     }
     247           3 : }
     248             : 
     249           5 : static int vbreak(verb_t verb, obj_t obj)
     250             : /*  Break.  Only works for mirror in repository and, of course, the vase. */
     251             : {
     252           5 :     switch (obj) {
     253           2 :     case MIRROR:
     254           2 :         if (game.closed) {
     255           1 :             state_change(MIRROR, MIRROR_BROKEN);
     256           1 :             return GO_DWARFWAKE;
     257             :         } else {
     258           1 :             rspeak(TOO_FAR);
     259           1 :             break;
     260             :         }
     261           2 :     case VASE:
     262           2 :         if (game.prop[VASE] == VASE_WHOLE) {
     263           1 :             if (TOTING(VASE))
     264           1 :                 drop(VASE, game.loc);
     265           1 :             state_change(VASE, VASE_BROKEN);
     266           1 :             game.fixed[VASE] = IS_FIXED;
     267           1 :             break;
     268             :         }
     269             :     /* FALLTHRU */
     270             :     default:
     271           2 :         speak(actions[verb].message);
     272             :     }
     273           4 :     return (GO_CLEAROBJ);
     274             : }
     275             : 
     276           1 : static int brief(void)
     277             : /*  Brief.  Intransitive only.  Suppress full descriptions after first time. */
     278             : {
     279           1 :     game.abbnum = 10000;
     280           1 :     game.detail = 3;
     281           1 :     rspeak(BRIEF_CONFIRM);
     282           1 :     return GO_CLEAROBJ;
     283             : }
     284             : 
     285        1969 : static int vcarry(verb_t verb, obj_t obj)
     286             : /*  Carry an object.  Special cases for bird and cage (if bird in cage, can't
     287             :  *  take one without the other).  Liquids also special, since they depend on
     288             :  *  status of bottle.  Also various side effects, etc. */
     289             : {
     290        1969 :     if (obj == INTRANSITIVE) {
     291             :         /*  Carry, no object given yet.  OK if only one object present. */
     292          70 :         if (game.atloc[game.loc] == NO_OBJECT ||
     293          66 :             game.link[game.atloc[game.loc]] != 0 ||
     294          32 :             atdwrf(game.loc) > 0)
     295           4 :             return GO_UNKNOWN;
     296          32 :         obj = game.atloc[game.loc];
     297             :     }
     298             : 
     299        1965 :     if (TOTING(obj)) {
     300           7 :         speak(actions[verb].message);
     301           7 :         return GO_CLEAROBJ;
     302             :     }
     303             : 
     304        1958 :     if (obj == MESSAG) {
     305           1 :         rspeak(REMOVE_MESSAGE);
     306           1 :         DESTROY(MESSAG);
     307           1 :         return GO_CLEAROBJ;
     308             :     }
     309             : 
     310        1957 :     if (game.fixed[obj] != IS_FREE) {
     311          15 :         switch (obj) {
     312           2 :         case PLANT:
     313             :             /* Next guard tests whether plant is tiny or stashed */
     314           2 :             rspeak(game.prop[PLANT] <= PLANT_THIRSTY ? DEEP_ROOTS : YOU_JOKING);
     315           2 :             break;
     316           1 :         case BEAR:
     317           1 :             rspeak( game.prop[BEAR] == SITTING_BEAR ? BEAR_CHAINED : YOU_JOKING);
     318           1 :             break;
     319           4 :         case CHAIN:
     320           4 :             rspeak( game.prop[BEAR] != UNTAMED_BEAR ? STILL_LOCKED : YOU_JOKING);
     321           4 :             break;
     322           1 :         case RUG:
     323           1 :             rspeak(game.prop[RUG] == RUG_HOVER ? RUG_HOVERS : YOU_JOKING);
     324           1 :             break;
     325           1 :         case URN:
     326           1 :             rspeak(URN_NOBUDGE);
     327           1 :             break;
     328           1 :         case CAVITY:
     329           1 :             rspeak(DOUGHNUT_HOLES);
     330           1 :             break;
     331           1 :         case BLOOD:
     332           1 :             rspeak(FEW_DROPS);
     333           1 :             break;
     334           1 :         case SIGN:
     335           1 :             rspeak(HAND_PASSTHROUGH);
     336           1 :             break;
     337           3 :         default:
     338           3 :             rspeak(YOU_JOKING);
     339             :         }
     340          15 :         return GO_CLEAROBJ;
     341             :     }
     342             : 
     343        1942 :     if (obj == WATER ||
     344             :         obj == OIL) {
     345         307 :         if (!HERE(BOTTLE) ||
     346         153 :             LIQUID() != obj) {
     347         104 :             if (!TOTING(BOTTLE)) {
     348           1 :                 rspeak(NO_CONTAINER);
     349           1 :                 return GO_CLEAROBJ;
     350             :             }
     351         103 :             if (game.prop[BOTTLE] == EMPTY_BOTTLE) {
     352         102 :                 return (fill(verb, BOTTLE));
     353             :             } else
     354           1 :                 rspeak(BOTTLE_FULL);
     355           1 :             return GO_CLEAROBJ;
     356             :         }
     357          50 :         obj = BOTTLE;
     358             :     }
     359             : 
     360        1838 :     if (game.holdng >= INVLIMIT) {
     361           8 :         rspeak(CARRY_LIMIT);
     362           8 :         return GO_CLEAROBJ;
     363             : 
     364             :     }
     365             : 
     366        1830 :     if (obj == BIRD && game.prop[BIRD] != BIRD_CAGED && STASHED(BIRD) != BIRD_CAGED) {
     367         146 :         if (game.prop[BIRD] == BIRD_FOREST_UNCAGED) {
     368           1 :             DESTROY(BIRD);
     369           1 :             rspeak(BIRD_CRAP);
     370           1 :             return GO_CLEAROBJ;
     371             :         }
     372         145 :         if (!TOTING(CAGE)) {
     373           1 :             rspeak(CANNOT_CARRY);
     374           1 :             return GO_CLEAROBJ;
     375             :         }
     376         144 :         if (TOTING(ROD)) {
     377           1 :             rspeak(BIRD_EVADES);
     378           1 :             return GO_CLEAROBJ;
     379             :         }
     380         143 :         game.prop[BIRD] = BIRD_CAGED;
     381             :     }
     382        1827 :     if ((obj == BIRD ||
     383         232 :          obj == CAGE) &&
     384         320 :         (game.prop[BIRD] == BIRD_CAGED || STASHED(BIRD) == BIRD_CAGED)) {
     385             :         /* expression maps BIRD to CAGE and CAGE to BIRD */
     386         144 :         carry(BIRD + CAGE - obj, game.loc);
     387             :     }
     388             : 
     389        1827 :     carry(obj, game.loc);
     390             : 
     391        1827 :     if (obj == BOTTLE && LIQUID() != NO_OBJECT)
     392          54 :         game.place[LIQUID()] = CARRIED;
     393             : 
     394        1827 :     if (GSTONE(obj) && game.prop[obj] != STATE_FOUND) {
     395          58 :         game.prop[obj] = STATE_FOUND;
     396          58 :         game.prop[CAVITY] = CAVITY_EMPTY;
     397             :     }
     398        1827 :     rspeak(OK_MAN);
     399        1827 :     return GO_CLEAROBJ;
     400             : }
     401             : 
     402          34 : static int chain(verb_t verb)
     403             : /* Do something to the bear's chain */
     404             : {
     405          34 :     if (verb != LOCK) {
     406          30 :         if (game.prop[BEAR] == UNTAMED_BEAR) {
     407           1 :             rspeak(BEAR_BLOCKS);
     408           1 :             return GO_CLEAROBJ;
     409             :         }
     410          29 :         if (game.prop[CHAIN] == CHAIN_HEAP) {
     411           2 :             rspeak(ALREADY_UNLOCKED);
     412           2 :             return GO_CLEAROBJ;
     413             :         }
     414          27 :         game.prop[CHAIN] = CHAIN_HEAP;
     415          27 :         game.fixed[CHAIN] = IS_FREE;
     416          27 :         if (game.prop[BEAR] != BEAR_DEAD)
     417          27 :             game.prop[BEAR] = CONTENTED_BEAR;
     418             : 
     419          27 :         switch (game.prop[BEAR]) {
     420             :         // LCOV_EXCL_START
     421             :         case BEAR_DEAD:
     422             :             /* Can't be reached until the bear can die in some way other
     423             :              * than a bridge collapse. Leave in in case this changes, but
     424             :              * exclude from coverage testing. */
     425             :             game.fixed[BEAR] = IS_FIXED;
     426             :             break;
     427             :         // LCOV_EXCL_STOP
     428          27 :         default:
     429          27 :             game.fixed[BEAR] = IS_FREE;
     430             :         }
     431          27 :         rspeak(CHAIN_UNLOCKED);
     432          27 :         return GO_CLEAROBJ;
     433             :     }
     434             : 
     435           4 :     if (game.prop[CHAIN] != CHAIN_HEAP) {
     436           1 :         rspeak(ALREADY_LOCKED);
     437           1 :         return GO_CLEAROBJ;
     438             :     }
     439           3 :     if (game.loc != objects[CHAIN].plac) {
     440           1 :         rspeak(NO_LOCKSITE);
     441           1 :         return GO_CLEAROBJ;
     442             :     }
     443             : 
     444           2 :     game.prop[CHAIN] = CHAIN_FIXED;
     445             : 
     446           2 :     if (TOTING(CHAIN))
     447           1 :         drop(CHAIN, game.loc);
     448           2 :     game.fixed[CHAIN] = IS_FIXED;
     449             : 
     450           2 :     rspeak(CHAIN_LOCKED);
     451           2 :     return GO_CLEAROBJ;
     452             : }
     453             : 
     454        1359 : static int discard(verb_t verb, obj_t obj)
     455             : /*  Discard object.  "Throw" also comes here for most objects.  Special cases for
     456             :  *  bird (might attack snake or dragon) and cage (might contain bird) and vase.
     457             :  *  Drop coins at vending machine for extra batteries. */
     458             : {
     459        1359 :     if (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) {
     460           2 :         obj = ROD2;
     461             :     }
     462             : 
     463        1359 :     if (!TOTING(obj)) {
     464           1 :         speak(actions[verb].message);
     465           1 :         return GO_CLEAROBJ;
     466             :     }
     467             : 
     468        1358 :     if (GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != CAVITY_FULL) {
     469          41 :         rspeak(GEM_FITS);
     470          41 :         game.prop[obj] = STATE_IN_CAVITY;
     471          41 :         game.prop[CAVITY] = CAVITY_FULL;
     472          41 :         if (HERE(RUG) && ((obj == EMERALD && game.prop[RUG] != RUG_HOVER) ||
     473          18 :                           (obj == RUBY && game.prop[RUG] == RUG_HOVER))) {
     474          40 :             if (obj == RUBY)
     475          18 :                 rspeak(RUG_SETTLES);
     476          22 :             else if (TOTING(RUG))
     477           1 :                 rspeak(RUG_WIGGLES);
     478             :             else
     479          21 :                 rspeak(RUG_RISES);
     480          40 :             if (!TOTING(RUG) || obj == RUBY) {
     481          39 :                 int k = (game.prop[RUG] == RUG_HOVER) ? RUG_FLOOR : RUG_HOVER;
     482          39 :                 game.prop[RUG] = k;
     483          39 :                 if (k == RUG_HOVER)
     484          21 :                     k = objects[SAPPH].plac;
     485          39 :                 move(RUG + NOBJECTS, k);
     486             :             }
     487             :         }
     488          41 :         drop(obj, game.loc);
     489          41 :         return GO_CLEAROBJ;
     490             :     }
     491             : 
     492        1317 :     if (obj == COINS && HERE(VEND)) {
     493           3 :         DESTROY(COINS);
     494           3 :         drop(BATTERY, game.loc);
     495           3 :         pspeak(BATTERY, look, FRESH_BATTERIES, true);
     496           3 :         return GO_CLEAROBJ;
     497             :     }
     498             : 
     499        1314 :     if (LIQUID() == obj)
     500           1 :         obj = BOTTLE;
     501        1314 :     if (obj == BOTTLE && LIQUID() != NO_OBJECT) {
     502           9 :         game.place[LIQUID()] = LOC_NOWHERE;
     503             :     }
     504             : 
     505        1314 :     if (obj == BEAR && AT(TROLL)) {
     506          24 :         state_change(TROLL, TROLL_GONE);
     507          24 :         move(TROLL, LOC_NOWHERE);
     508          24 :         move(TROLL + NOBJECTS, IS_FREE);
     509          24 :         move(TROLL2, objects[TROLL].plac);
     510          24 :         move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
     511          24 :         juggle(CHASM);
     512          24 :         drop(obj, game.loc);
     513          24 :         return GO_CLEAROBJ;
     514             :     }
     515             : 
     516        1290 :     if (obj == VASE) {
     517          25 :         if (game.loc != objects[PILLOW].plac) {
     518          25 :             state_change(VASE, AT(PILLOW)
     519             :                          ? VASE_WHOLE
     520             :                          : VASE_DROPPED);
     521          25 :             if (game.prop[VASE] != VASE_WHOLE)
     522           1 :                 game.fixed[VASE] = IS_FIXED;
     523          25 :             drop(obj, game.loc);
     524          25 :             return GO_CLEAROBJ;
     525             :         }
     526             :     }
     527             : 
     528        1265 :     if (obj == CAGE && game.prop[BIRD] == BIRD_CAGED) {
     529           2 :         drop(BIRD, game.loc);
     530             :     }
     531             : 
     532        1265 :     if (obj == BIRD) {
     533         136 :         if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) {
     534           1 :             rspeak(BIRD_BURNT);
     535           1 :             DESTROY(BIRD);
     536           1 :             return GO_CLEAROBJ;
     537             :         }
     538         135 :         if (HERE(SNAKE)) {
     539          58 :             rspeak(BIRD_ATTACKS);
     540          58 :             if (game.closed)
     541           1 :                 return GO_DWARFWAKE;
     542          57 :             DESTROY(SNAKE);
     543             :             /* Set game.prop for use by travel options */
     544          57 :             game.prop[SNAKE] = SNAKE_CHASED;
     545             :         } else
     546          77 :             rspeak(OK_MAN);
     547             : 
     548         134 :         game.prop[BIRD] = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED;
     549         134 :         drop(obj, game.loc);
     550         134 :         return GO_CLEAROBJ;
     551             :     }
     552             : 
     553        1129 :     rspeak(OK_MAN);
     554        1129 :     drop(obj, game.loc);
     555        1129 :     return GO_CLEAROBJ;
     556             : }
     557             : 
     558          47 : static int drink(verb_t verb, obj_t obj)
     559             : /*  Drink.  If no object, assume water and look for it here.  If water is in
     560             :  *  the bottle, drink that, else must be at a water loc, so drink stream. */
     561             : {
     562          48 :     if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER &&
     563           1 :         (LIQUID() != WATER || !HERE(BOTTLE))) {
     564           1 :         return GO_UNKNOWN;
     565             :     }
     566             : 
     567          46 :     if (obj == BLOOD) {
     568          35 :         DESTROY(BLOOD);
     569          35 :         state_change(DRAGON, DRAGON_BLOODLESS);
     570          35 :         game.blooded = true;
     571          35 :         return GO_CLEAROBJ;
     572             :     }
     573             : 
     574          11 :     if (obj != INTRANSITIVE && obj != WATER) {
     575           6 :         rspeak(RIDICULOUS_ATTEMPT);
     576           6 :         return GO_CLEAROBJ;
     577             :     }
     578           5 :     if (LIQUID() == WATER && HERE(BOTTLE)) {
     579           3 :         game.place[WATER] = LOC_NOWHERE;
     580           3 :         state_change(BOTTLE, EMPTY_BOTTLE);
     581           3 :         return GO_CLEAROBJ;
     582             :     }
     583             : 
     584           2 :     speak(actions[verb].message);
     585           2 :     return GO_CLEAROBJ;
     586             : }
     587             : 
     588           6 : static int eat(verb_t verb, obj_t obj)
     589             : /*  Eat.  Intransitive: assume food if present, else ask what.  Transitive: food
     590             :  *  ok, some things lose appetite, rest are ridiculous. */
     591             : {
     592           6 :     switch (obj) {
     593           2 :     case INTRANSITIVE:
     594           2 :         if (!HERE(FOOD))
     595           1 :             return GO_UNKNOWN;
     596             :     /* FALLTHRU */
     597             :     case FOOD:
     598           2 :         DESTROY(FOOD);
     599           2 :         rspeak(THANKS_DELICIOUS);
     600           2 :         break;
     601           2 :     case BIRD:
     602             :     case SNAKE:
     603             :     case CLAM:
     604             :     case OYSTER:
     605             :     case DWARF:
     606             :     case DRAGON:
     607             :     case TROLL:
     608             :     case BEAR:
     609             :     case OGRE:
     610           2 :         rspeak(LOST_APPETITE);
     611           2 :         break;
     612           1 :     default:
     613           1 :         speak(actions[verb].message);
     614             :     }
     615           5 :     return GO_CLEAROBJ;
     616             : }
     617             : 
     618         208 : static int extinguish(verb_t verb, obj_t obj)
     619             : /* Extinguish.  Lamp, urn, dragon/volcano (nice try). */
     620             : {
     621         208 :     if (obj == INTRANSITIVE) {
     622         178 :         if (HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT)
     623         176 :             obj = LAMP;
     624         178 :         if (HERE(URN) && game.prop[URN] == URN_LIT)
     625           1 :             obj = URN;
     626         178 :         if (obj == INTRANSITIVE)
     627           1 :             return GO_UNKNOWN;
     628             :     }
     629             : 
     630         207 :     switch (obj) {
     631           3 :     case URN:
     632           3 :         if (game.prop[URN] != URN_EMPTY) {
     633           2 :             state_change(URN, URN_DARK);
     634             :         } else {
     635           1 :             pspeak(URN, change, URN_DARK, true);
     636             :         }
     637           3 :         break;
     638         200 :     case LAMP:
     639         200 :         state_change(LAMP, LAMP_DARK);
     640         200 :         rspeak(DARK(game.loc) ?
     641             :                PITCH_DARK :
     642             :                NO_MESSAGE);
     643         200 :         break;
     644           2 :     case DRAGON:
     645             :     case VOLCANO:
     646           2 :         rspeak(BEYOND_POWER);
     647           2 :         break;
     648           2 :     default:
     649           2 :         speak(actions[verb].message);
     650             :     }
     651         207 :     return GO_CLEAROBJ;
     652             : }
     653             : 
     654          42 : static int feed(verb_t verb, obj_t obj)
     655             : /*  Feed.  If bird, no seed.  Snake, dragon, troll: quip.  If dwarf, make him
     656             :  *  mad.  Bear, special. */
     657             : {
     658          42 :     switch (obj) {
     659           1 :     case BIRD:
     660           1 :         rspeak(BIRD_PINING);
     661           1 :         break;
     662           4 :     case DRAGON:
     663           4 :         if (game.prop[DRAGON] != DRAGON_BARS)
     664           1 :             rspeak(RIDICULOUS_ATTEMPT);
     665             :         else
     666           3 :             rspeak(NOTHING_EDIBLE);
     667           4 :         break;
     668           2 :     case SNAKE:
     669           2 :         if (!game.closed && HERE(BIRD)) {
     670           1 :             DESTROY(BIRD);
     671           1 :             rspeak(BIRD_DEVOURED);
     672             :         } else
     673           1 :             rspeak(NOTHING_EDIBLE);
     674           2 :         break;
     675           1 :     case TROLL:
     676           1 :         rspeak(TROLL_VICES);
     677           1 :         break;
     678           2 :     case DWARF:
     679           2 :         if (HERE(FOOD)) {
     680           1 :             game.dflag += 2;
     681           1 :             rspeak(REALLY_MAD);
     682             :         } else
     683           1 :             speak(actions[verb].message);
     684           2 :         break;
     685          29 :     case BEAR:
     686          29 :         if (game.prop[BEAR] == BEAR_DEAD) {
     687           1 :             rspeak(RIDICULOUS_ATTEMPT);
     688           1 :             break;
     689             :         }
     690          28 :         if (game.prop[BEAR] == UNTAMED_BEAR) {
     691          27 :             if (HERE(FOOD)) {
     692          26 :                 DESTROY(FOOD);
     693          26 :                 game.fixed[AXE] = IS_FREE;
     694          26 :                 game.prop[AXE] = AXE_HERE;
     695          26 :                 state_change(BEAR, SITTING_BEAR);
     696             :             } else
     697           1 :                 rspeak(NOTHING_EDIBLE);
     698          27 :             break;
     699             :         }
     700           1 :         speak(actions[verb].message);
     701           1 :         break;
     702           2 :     case OGRE:
     703           2 :         if (HERE(FOOD))
     704           1 :             rspeak(OGRE_FULL);
     705             :         else
     706           1 :             speak(actions[verb].message);
     707           2 :         break;
     708           1 :     default:
     709           1 :         rspeak(AM_GAME);
     710             :     }
     711          42 :     return GO_CLEAROBJ;
     712             : }
     713             : 
     714         140 : int fill(verb_t verb, obj_t obj)
     715             : /*  Fill.  Bottle or urn must be empty, and liquid available.  (Vase
     716             :  *  is nasty.) */
     717             : {
     718         140 :     if (obj == VASE) {
     719           3 :         if (LIQLOC(game.loc) == NO_OBJECT) {
     720           1 :             rspeak(FILL_INVALID);
     721           1 :             return GO_CLEAROBJ;
     722             :         }
     723           2 :         if (!TOTING(VASE)) {
     724           1 :             rspeak(ARENT_CARRYING);
     725           1 :             return GO_CLEAROBJ;
     726             :         }
     727           1 :         rspeak(SHATTER_VASE);
     728           1 :         game.prop[VASE] = VASE_BROKEN;
     729           1 :         game.fixed[VASE] = IS_FIXED;
     730           1 :         drop(VASE, game.loc);
     731           1 :         return GO_CLEAROBJ;
     732             :     }
     733             : 
     734         137 :     if (obj == URN) {
     735          26 :         if (game.prop[URN] != URN_EMPTY) {
     736           1 :             rspeak(FULL_URN);
     737           1 :             return GO_CLEAROBJ;
     738             :         }
     739          25 :         if (!HERE(BOTTLE)) {
     740           1 :             rspeak(FILL_INVALID);
     741           1 :             return GO_CLEAROBJ;
     742             :         }
     743          24 :         int k = LIQUID();
     744          24 :         switch (k) {
     745           1 :         case WATER:
     746           1 :             game.prop[BOTTLE] = EMPTY_BOTTLE;
     747           1 :             rspeak(WATER_URN);
     748           1 :             break;
     749          22 :         case OIL:
     750          22 :             game.prop[URN] = URN_DARK;
     751          22 :             game.prop[BOTTLE] = EMPTY_BOTTLE;
     752          22 :             rspeak(OIL_URN);
     753          22 :             break;
     754           1 :         case NO_OBJECT:
     755             :         default:
     756           1 :             rspeak(FILL_INVALID);
     757           1 :             return GO_CLEAROBJ;
     758             :         }
     759          23 :         game.place[k] = LOC_NOWHERE;
     760          23 :         return GO_CLEAROBJ;
     761             :     }
     762         111 :     if (obj != INTRANSITIVE && obj != BOTTLE) {
     763           1 :         speak(actions[verb].message);
     764           1 :         return GO_CLEAROBJ;
     765             :     }
     766         110 :     if (obj == INTRANSITIVE && !HERE(BOTTLE))
     767           1 :         return GO_UNKNOWN;
     768             : 
     769         109 :     if (HERE(URN) && game.prop[URN] != URN_EMPTY) {
     770           1 :         rspeak(URN_NOPOUR);
     771           1 :         return GO_CLEAROBJ;
     772             :     }
     773         108 :     if (LIQUID() != NO_OBJECT) {
     774           1 :         rspeak(BOTTLE_FULL);
     775           1 :         return GO_CLEAROBJ;
     776             :     }
     777         107 :     if (LIQLOC(game.loc) == NO_OBJECT) {
     778           1 :         rspeak(NO_LIQUID);
     779           1 :         return GO_CLEAROBJ;
     780             :     }
     781             : 
     782         106 :     state_change(BOTTLE, (LIQLOC(game.loc) == OIL)
     783             :                  ? OIL_BOTTLE
     784             :                  : WATER_BOTTLE);
     785         106 :     if (TOTING(BOTTLE))
     786         106 :         game.place[LIQUID()] = CARRIED;
     787         106 :     return GO_CLEAROBJ;
     788             : }
     789             : 
     790           8 : static int find(verb_t verb, obj_t obj)
     791             : /* Find.  Might be carrying it, or it might be here.  Else give caveat. */
     792             : {
     793           8 :     if (TOTING(obj)) {
     794           2 :         rspeak(ALREADY_CARRYING);
     795           2 :         return GO_CLEAROBJ;
     796             :     }
     797             : 
     798           6 :     if (game.closed) {
     799           1 :         rspeak(NEEDED_NEARBY);
     800           1 :         return GO_CLEAROBJ;
     801             :     }
     802             : 
     803           9 :     if (AT(obj) ||
     804           8 :         (LIQUID() == obj && AT(BOTTLE)) ||
     805           8 :         obj == LIQLOC(game.loc) ||
     806           1 :         (obj == DWARF && atdwrf(game.loc) > 0)) {
     807           2 :         rspeak(YOU_HAVEIT);
     808           2 :         return GO_CLEAROBJ;
     809             :     }
     810             : 
     811             : 
     812           3 :     speak(actions[verb].message);
     813           3 :     return GO_CLEAROBJ;
     814             : }
     815             : 
     816          45 : static int fly(verb_t verb, obj_t obj)
     817             : /* Fly.  Snide remarks unless hovering rug is here. */
     818             : {
     819          45 :     if (obj == INTRANSITIVE) {
     820           8 :         if (!HERE(RUG)) {
     821           1 :             rspeak(FLAP_ARMS);
     822           1 :             return GO_CLEAROBJ;
     823             :         }
     824           7 :         if (game.prop[RUG] != RUG_HOVER) {
     825           1 :             rspeak(RUG_NOTHING2);
     826           1 :             return GO_CLEAROBJ;
     827             :         }
     828           6 :         obj = RUG;
     829             :     }
     830             : 
     831          43 :     if (obj != RUG) {
     832           1 :         speak(actions[verb].message);
     833           1 :         return GO_CLEAROBJ;
     834             :     }
     835          42 :     if (game.prop[RUG] != RUG_HOVER) {
     836           1 :         rspeak(RUG_NOTHING1);
     837           1 :         return GO_CLEAROBJ;
     838             :     }
     839          41 :     game.oldlc2 = game.oldloc;
     840          41 :     game.oldloc = game.loc;
     841             : 
     842          41 :     if (game.prop[SAPPH] == STATE_NOTFOUND) {
     843          21 :         game.newloc = game.place[SAPPH];
     844          21 :         rspeak(RUG_GOES);
     845             :     } else {
     846          20 :         game.newloc = LOC_CLIFF;
     847          20 :         rspeak(RUG_RETURNS);
     848             :     }
     849          41 :     return GO_TERMINATE;
     850             : }
     851             : 
     852          85 : static int inven(void)
     853             : /* Inventory. If object, treat same as find.  Else report on current burden. */
     854             : {
     855          85 :     bool empty = true;
     856        5950 :     for (obj_t i = 1; i <= NOBJECTS; i++) {
     857       11645 :         if (i == BEAR ||
     858        5780 :             !TOTING(i))
     859        5430 :             continue;
     860         435 :         if (empty) {
     861          84 :             rspeak(NOW_HOLDING);
     862          84 :             empty = false;
     863             :         }
     864         435 :         pspeak(i, touch, -1, false);
     865             :     }
     866          85 :     if (TOTING(BEAR))
     867           1 :         rspeak(TAME_BEAR);
     868          85 :     if (empty)
     869           1 :         rspeak(NO_CARRY);
     870          85 :     return GO_CLEAROBJ;
     871             : }
     872             : 
     873         293 : static int light(verb_t verb, obj_t obj)
     874             : /*  Light.  Applicable only to lamp and urn. */
     875             : {
     876         293 :     if (obj == INTRANSITIVE) {
     877         262 :         int selects = 0;
     878         262 :         if (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0) {
     879         260 :             obj = LAMP;
     880         260 :             selects++;
     881             :         }
     882         262 :         if (HERE(URN) && game.prop[URN] == URN_DARK) {
     883           3 :             obj =  URN;
     884           3 :             selects++;
     885             :         }
     886         262 :         if (selects != 1)
     887           1 :             return GO_UNKNOWN;
     888             :     }
     889             : 
     890         292 :     switch (obj) {
     891          24 :     case URN:
     892          24 :         state_change(URN, game.prop[URN] == URN_EMPTY ?
     893             :                      URN_EMPTY :
     894             :                      URN_LIT);
     895          24 :         break;
     896         266 :     case LAMP:
     897         266 :         if (game.limit < 0) {
     898           1 :             rspeak(LAMP_OUT);
     899           1 :             break;
     900             :         }
     901         265 :         state_change(LAMP, LAMP_BRIGHT);
     902         265 :         if (game.wzdark)
     903         258 :             return GO_TOP;
     904           7 :         break;
     905           2 :     default:
     906           2 :         speak(actions[verb].message);
     907             :     }
     908          34 :     return GO_CLEAROBJ;
     909             : }
     910             : 
     911          47 : static int listen(void)
     912             : /*  Listen.  Intransitive only.  Print stuff based on object sound proprties. */
     913             : {
     914          47 :     vocab_t sound = locations[game.loc].sound;
     915          47 :     if (sound != SILENT) {
     916          12 :         rspeak(sound);
     917          12 :         if (!locations[game.loc].loud)
     918          11 :             rspeak(NO_MESSAGE);
     919          12 :         return GO_CLEAROBJ;
     920             :     }
     921         684 :     for (obj_t i = 1; i <= NOBJECTS; i++) {
     922         446 :         if (!HERE(i) ||
     923         139 :             objects[i].sounds[0] == NULL ||
     924          34 :             game.prop[i] < 0)
     925         307 :             continue;
     926          34 :         int mi =  game.prop[i];
     927             :         /* (ESR) Some unpleasant magic on object states here. Ideally
     928             :          * we'd have liked the bird to be a normal object that we can
     929             :          * use state_change() on; can't do it, because there are
     930             :          * actually two different series of per-state birdsounds
     931             :          * depending on whether player has drunk dragon's blood. */
     932          34 :         if (i == BIRD)
     933          34 :             mi += 3 * game.blooded;
     934          34 :         pspeak(i, hear, mi, true, game.zzword);
     935          34 :         rspeak(NO_MESSAGE);
     936          34 :         if (i == BIRD && mi == BIRD_ENDSTATE)
     937          34 :             DESTROY(BIRD);
     938          34 :         return GO_CLEAROBJ;
     939             :     }
     940           1 :     rspeak(ALL_SILENT);
     941           1 :     return GO_CLEAROBJ;
     942             : }
     943             : 
     944          96 : static int lock(verb_t verb, obj_t obj)
     945             : /* Lock, unlock, no object given.  Assume various things if present. */
     946             : {
     947          96 :     if (obj == INTRANSITIVE) {
     948          13 :         if (HERE(CLAM))
     949           2 :             obj = CLAM;
     950          13 :         if (HERE(OYSTER))
     951           1 :             obj = OYSTER;
     952          13 :         if (AT(DOOR))
     953           1 :             obj = DOOR;
     954          13 :         if (AT(GRATE))
     955           2 :             obj = GRATE;
     956          13 :         if (HERE(CHAIN))
     957           4 :             obj = CHAIN;
     958          13 :         if (obj == INTRANSITIVE) {
     959           3 :             rspeak(NOTHING_LOCKED);
     960           3 :             return GO_CLEAROBJ;
     961             :         }
     962             :     }
     963             : 
     964             :     /*  Lock, unlock object.  Special stuff for opening clam/oyster
     965             :      *  and for chain. */
     966             : 
     967          93 :     switch (obj) {
     968          35 :     case CHAIN:
     969          35 :         if (HERE(KEYS)) {
     970          34 :             return chain(verb);
     971             :         } else
     972           1 :             rspeak(NO_KEYS);
     973           1 :         break;
     974          12 :     case GRATE:
     975          12 :         if (HERE(KEYS)) {
     976          18 :             if (game.closng) {
     977           1 :                 rspeak(EXIT_CLOSED);
     978           1 :                 if (!game.panic)
     979           1 :                     game.clock2 = PANICTIME;
     980           1 :                 game.panic = true;
     981             :             } else {
     982           8 :                 state_change(GRATE, (verb == LOCK) ?
     983             :                              GRATE_CLOSED :
     984             :                              GRATE_OPEN);
     985             :             }
     986             :         } else
     987           3 :             rspeak(NO_KEYS);
     988          12 :         break;
     989          31 :     case CLAM:
     990          31 :         if (verb == LOCK)
     991           1 :             rspeak(HUH_MAN);
     992          30 :         else if (!TOTING(TRIDENT))
     993           1 :             rspeak(CLAM_OPENER);
     994             :         else {
     995          29 :             DESTROY(CLAM);
     996          29 :             drop(OYSTER, game.loc);
     997          29 :             drop(PEARL, LOC_CULDESAC);
     998          29 :             rspeak(PEARL_FALLS);
     999             :         }
    1000          31 :         break;
    1001           5 :     case OYSTER:
    1002           5 :         if (verb == LOCK)
    1003           2 :             rspeak(HUH_MAN);
    1004           3 :         else if (TOTING(OYSTER))
    1005           1 :             rspeak(DROP_OYSTER);
    1006           2 :         else if (!TOTING(TRIDENT))
    1007           1 :             rspeak(OYSTER_OPENER);
    1008             :         else
    1009           1 :             rspeak(OYSTER_OPENS);
    1010           5 :         break;
    1011           5 :     case DOOR:
    1012           5 :         rspeak((game.prop[DOOR] == DOOR_UNRUSTED) ? OK_MAN : RUSTY_DOOR);
    1013           5 :         break;
    1014           2 :     case CAGE:
    1015           2 :         rspeak( NO_LOCK);
    1016           2 :         break;
    1017           1 :     case KEYS:
    1018           1 :         rspeak(CANNOT_UNLOCK);
    1019           1 :         break;
    1020           2 :     default:
    1021           2 :         speak(actions[verb].message);
    1022             :     }
    1023             : 
    1024          59 :     return GO_CLEAROBJ;
    1025             : }
    1026             : 
    1027         126 : static int pour(verb_t verb, obj_t obj)
    1028             : /*  Pour.  If no object, or object is bottle, assume contents of bottle.
    1029             :  *  special tests for pouring water or oil on plant or rusty door. */
    1030             : {
    1031         126 :     if (obj == BOTTLE ||
    1032             :         obj == INTRANSITIVE)
    1033           5 :         obj = LIQUID();
    1034         126 :     if (obj == NO_OBJECT)
    1035           1 :         return GO_UNKNOWN;
    1036         125 :     if (!TOTING(obj)) {
    1037           1 :         speak(actions[verb].message);
    1038           1 :         return GO_CLEAROBJ;
    1039             :     }
    1040             : 
    1041         124 :     if (obj != OIL && obj != WATER) {
    1042           1 :         rspeak(CANT_POUR);
    1043           1 :         return GO_CLEAROBJ;
    1044             :     }
    1045         123 :     if (HERE(URN) && game.prop[URN] == URN_EMPTY)
    1046           1 :         return fill(verb, URN);
    1047         122 :     game.prop[BOTTLE] = EMPTY_BOTTLE;
    1048         122 :     game.place[obj] = LOC_NOWHERE;
    1049         161 :     if (!(AT(PLANT) ||
    1050          41 :           AT(DOOR))) {
    1051           2 :         rspeak(GROUND_WET);
    1052           2 :         return GO_CLEAROBJ;
    1053             :     }
    1054         120 :     if (!AT(DOOR)) {
    1055          83 :         if (obj == WATER) {
    1056             :             /* cycle through the three plant states */
    1057          82 :             state_change(PLANT, MOD(game.prop[PLANT] + 1, 3));
    1058          82 :             game.prop[PLANT2] = game.prop[PLANT];
    1059          82 :             return GO_MOVE;
    1060             :         } else {
    1061           1 :             rspeak(SHAKING_LEAVES);
    1062           1 :             return GO_CLEAROBJ;
    1063             :         }
    1064             :     } else {
    1065          37 :         state_change(DOOR, (obj == OIL) ?
    1066             :                      DOOR_UNRUSTED :
    1067             :                      DOOR_RUSTED);
    1068          37 :         return GO_CLEAROBJ;
    1069             :     }
    1070             : }
    1071             : 
    1072           4 : static int quit(void)
    1073             : /*  Quit.  Intransitive only.  Verify intent and exit if that's what he wants. */
    1074             : {
    1075           4 :     if (yes(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
    1076           3 :         terminate(quitgame);
    1077           1 :     return GO_CLEAROBJ;
    1078             : }
    1079             : 
    1080          12 : static int read(command_t command)
    1081             : /*  Read.  Print stuff based on objtxt.  Oyster (?) is special case. */
    1082             : {
    1083          12 :     if (command.obj == INTRANSITIVE) {
    1084           3 :         command.obj = NO_OBJECT;
    1085         210 :         for (int i = 1; i <= NOBJECTS; i++) {
    1086         207 :             if (HERE(i) && objects[i].texts[0] != NULL && game.prop[i] >= 0)
    1087           3 :                 command.obj = command.obj * NOBJECTS + i;
    1088             :         }
    1089           6 :         if (command.obj > NOBJECTS ||
    1090           6 :             command.obj == NO_OBJECT ||
    1091           6 :             DARK(game.loc))
    1092           2 :             return GO_UNKNOWN;
    1093             :     }
    1094             : 
    1095          10 :     if (DARK(game.loc)) {
    1096           1 :         sspeak(NO_SEE, command.word[0].raw);
    1097           9 :     } else if (command.obj == OYSTER && !game.clshnt && game.closed) {
    1098           1 :         game.clshnt = yes(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]);
    1099          13 :     } else if (objects[command.obj].texts[0] == NULL ||
    1100           5 :                game.prop[command.obj] == STATE_NOTFOUND) {
    1101           3 :         speak(actions[command.verb].message);
    1102             :     } else
    1103           5 :         pspeak(command.obj, study, game.prop[command.obj], true);
    1104          10 :     return GO_CLEAROBJ;
    1105             : }
    1106             : 
    1107          44 : static int reservoir(void)
    1108             : /*  Z'ZZZ (word gets recomputed at startup; different each game). */
    1109             : {
    1110          44 :     if (!AT(RESER) && game.loc != LOC_RESBOTTOM) {
    1111           2 :         rspeak(NOTHING_HAPPENS);
    1112           2 :         return GO_CLEAROBJ;
    1113             :     } else {
    1114          42 :         state_change(RESER,
    1115          42 :                      game.prop[RESER] == WATERS_PARTED ? WATERS_UNPARTED : WATERS_PARTED);
    1116          42 :         if (AT(RESER))
    1117          41 :             return GO_CLEAROBJ;
    1118             :         else {
    1119           1 :             game.oldlc2 = game.loc;
    1120           1 :             game.newloc = LOC_NOWHERE;
    1121           1 :             rspeak(NOT_BRIGHT);
    1122           1 :             return GO_TERMINATE;
    1123             :         }
    1124             :     }
    1125             : }
    1126             : 
    1127          23 : static int rub(verb_t verb, obj_t obj)
    1128             : /* Rub.  Yields various snide remarks except for lit urn. */
    1129             : {
    1130          23 :     if (obj == URN && game.prop[URN] == URN_LIT) {
    1131          21 :         DESTROY(URN);
    1132          21 :         drop(AMBER, game.loc);
    1133          21 :         game.prop[AMBER] = AMBER_IN_ROCK;
    1134          21 :         --game.tally;
    1135          21 :         drop(CAVITY, game.loc);
    1136          21 :         rspeak(URN_GENIES);
    1137           2 :     } else if (obj != LAMP) {
    1138           1 :         rspeak(PECULIAR_NOTHING);
    1139             :     } else {
    1140           1 :         speak(actions[verb].message);
    1141             :     }
    1142          23 :     return GO_CLEAROBJ;
    1143             : }
    1144             : 
    1145          27 : static int say(command_t command)
    1146             : /* Say.  Echo WD2. Magic words override. */
    1147             : {
    1148          44 :     if (command.word[1].type == MOTION &&
    1149          31 :         (command.word[1].id == XYZZY ||
    1150          17 :          command.word[1].id == PLUGH ||
    1151           3 :          command.word[1].id == PLOVER)) {
    1152          17 :         return GO_WORD2;
    1153             :     }
    1154          10 :     if (command.word[1].type == ACTION && command.word[1].id == PART)
    1155           2 :         return reservoir();
    1156             : 
    1157          13 :     if (command.word[1].type == ACTION &&
    1158           9 :         (command.word[1].id == FEE ||
    1159           7 :          command.word[1].id == FIE ||
    1160           5 :          command.word[1].id == FOE ||
    1161           4 :          command.word[1].id == FOO ||
    1162           3 :          command.word[1].id == FUM ||
    1163           1 :          command.word[1].id == PART)) {
    1164           4 :         return bigwords(command.word[1].id);
    1165             :     }
    1166           4 :     sspeak(OKEY_DOKEY, command.word[1].raw);
    1167           4 :     return GO_CLEAROBJ;
    1168             : }
    1169             : 
    1170         129 : static int throw_support(vocab_t spk)
    1171             : {
    1172         129 :     rspeak(spk);
    1173         129 :     drop(AXE, game.loc);
    1174         129 :     return GO_MOVE;
    1175             : }
    1176             : 
    1177         169 : static int throw (command_t command)
    1178             : /*  Throw.  Same as discard unless axe.  Then same as attack except
    1179             :  *  ignore bird, and if dwarf is present then one might be killed.
    1180             :  *  (Only way to do so!)  Axe also special for dragon, bear, and
    1181             :  *  troll.  Treasures special for troll. */
    1182             : {
    1183         169 :     if (!TOTING(command.obj)) {
    1184           2 :         speak(actions[command.verb].message);
    1185           2 :         return GO_CLEAROBJ;
    1186             :     }
    1187         167 :     if (objects[command.obj].is_treasure && AT(TROLL)) {
    1188             :         /*  Snarf a treasure for the troll. */
    1189          28 :         drop(command.obj, LOC_NOWHERE);
    1190          28 :         move(TROLL, LOC_NOWHERE);
    1191          28 :         move(TROLL + NOBJECTS, IS_FREE);
    1192          28 :         drop(TROLL2, objects[TROLL].plac);
    1193          28 :         drop(TROLL2 + NOBJECTS, objects[TROLL].fixd);
    1194          28 :         juggle(CHASM);
    1195          28 :         rspeak(TROLL_SATISFIED);
    1196          28 :         return GO_CLEAROBJ;
    1197             :     }
    1198         139 :     if (command.obj == FOOD && HERE(BEAR)) {
    1199             :         /* But throwing food is another story. */
    1200           1 :         command.obj = BEAR;
    1201           1 :         return (feed(command.verb, command.obj));
    1202             :     }
    1203         138 :     if (command.obj != AXE)
    1204           2 :         return (discard(command.verb, command.obj));
    1205             :     else {
    1206         136 :         if (atdwrf(game.loc) <= 0) {
    1207          10 :             if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS)
    1208           1 :                 return throw_support(DRAGON_SCALES);
    1209           9 :             if (AT(TROLL))
    1210           1 :                 return throw_support(TROLL_RETURNS);
    1211           8 :             if (AT(OGRE))
    1212           1 :                 return throw_support(OGRE_DODGE);
    1213           7 :             if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) {
    1214             :                 /* This'll teach him to throw the axe at the bear! */
    1215           2 :                 drop(AXE, game.loc);
    1216           2 :                 game.fixed[AXE] = IS_FIXED;
    1217           2 :                 juggle(BEAR);
    1218           2 :                 state_change(AXE, AXE_LOST);
    1219           2 :                 return GO_CLEAROBJ;
    1220             :             }
    1221           5 :             command.obj = INTRANSITIVE;
    1222           5 :             return (attack(command));
    1223             :         }
    1224             : 
    1225         126 :         if (randrange(NDWARVES + 1) < game.dflag) {
    1226           7 :             return throw_support(DWARF_DODGES);
    1227             :         } else {
    1228         119 :             int i = atdwrf(game.loc);
    1229         119 :             game.dseen[i] = false;
    1230         119 :             game.dloc[i] = LOC_NOWHERE;
    1231         119 :             return throw_support((++game.dkill == 1) ?
    1232             :                                  DWARF_SMOKE :
    1233             :                                  KILLED_DWARF);
    1234             :         }
    1235             :     }
    1236             : }
    1237             : 
    1238           2 : static int wake(verb_t verb, obj_t obj)
    1239             : /* Wake.  Only use is to disturb the dwarves. */
    1240             : {
    1241           3 :     if (obj != DWARF ||
    1242           1 :         !game.closed) {
    1243           1 :         speak(actions[verb].message);
    1244           1 :         return GO_CLEAROBJ;
    1245             :     } else {
    1246           1 :         rspeak(PROD_DWARF);
    1247           1 :         return GO_DWARFWAKE;
    1248             :     }
    1249             : }
    1250             : 
    1251          85 : static int seed(verb_t verb, const char *arg)
    1252             : /* Set seed */
    1253             : {
    1254          85 :     int32_t seed = strtol(arg, NULL, 10);
    1255          85 :     speak(actions[verb].message, seed);
    1256          85 :     set_seed(seed);
    1257          85 :     --game.turns;
    1258          85 :     return GO_TOP;
    1259             : }
    1260             : 
    1261           1 : static int waste(verb_t verb, turn_t turns)
    1262             : /* Burn turns */
    1263             : {
    1264           1 :     game.limit -= turns;
    1265           1 :     speak(actions[verb].message, (int)game.limit);
    1266           1 :     return GO_TOP;
    1267             : }
    1268             : 
    1269         104 : static int wave(verb_t verb, obj_t obj)
    1270             : /* Wave.  No effect unless waving rod at fissure or at bird. */
    1271             : {
    1272         208 :     if (obj != ROD ||
    1273         204 :         !TOTING(obj) ||
    1274         194 :         (!HERE(BIRD) &&
    1275          76 :          (game.closng ||
    1276          39 :           !AT(FISSURE)))) {
    1277           9 :         speak(((!TOTING(obj)) && (obj != ROD ||
    1278           4 :                                   !TOTING(ROD2))) ?
    1279             :               arbitrary_messages[ARENT_CARRYING] :
    1280             :               actions[verb].message);
    1281           5 :         return GO_CLEAROBJ;
    1282             :     }
    1283             : 
    1284          99 :     if (game.prop[BIRD] == BIRD_UNCAGED && game.loc == game.place[STEPS] && game.prop[JADE] == STATE_NOTFOUND) {
    1285          42 :         drop(JADE, game.loc);
    1286          42 :         game.prop[JADE] = STATE_FOUND;
    1287          42 :         --game.tally;
    1288          42 :         rspeak(NECKLACE_FLY);
    1289          42 :         return GO_CLEAROBJ;
    1290             :     } else {
    1291          57 :         if (game.closed) {
    1292           1 :             rspeak((game.prop[BIRD] == BIRD_CAGED) ?
    1293             :                    CAGE_FLY :
    1294             :                    FREE_FLY);
    1295           1 :             return GO_DWARFWAKE;
    1296             :         }
    1297         112 :         if (game.closng ||
    1298          58 :             !AT(FISSURE)) {
    1299           2 :             rspeak((game.prop[BIRD] == BIRD_CAGED) ?
    1300             :                    CAGE_FLY :
    1301             :                    FREE_FLY);
    1302           2 :             return GO_CLEAROBJ;
    1303             :         }
    1304          54 :         if (HERE(BIRD))
    1305          17 :             rspeak((game.prop[BIRD] == BIRD_CAGED) ?
    1306             :                    CAGE_FLY :
    1307             :                    FREE_FLY);
    1308             : 
    1309          54 :         state_change(FISSURE,
    1310          54 :                      game.prop[FISSURE] == BRIDGED ? UNBRIDGED : BRIDGED);
    1311          54 :         return GO_CLEAROBJ;
    1312             :     }
    1313             : }
    1314             : 
    1315        9598 : int action(command_t command)
    1316             : /*  Analyse a verb.  Remember what it was, go back for object if second word
    1317             :  *  unless verb is "say", which snarfs arbitrary second word.
    1318             :  */
    1319             : {
    1320             :     /* Previously, actions that result in a message, but don't do anything
    1321             :      * further were called "specials". Now they're handled here as normal
    1322             :      * actions. If noaction is true, then we spit out the message and return */
    1323        9598 :     if (actions[command.verb].noaction) {
    1324          19 :         speak(actions[command.verb].message);
    1325          19 :         return GO_CLEAROBJ;
    1326             :     }
    1327             : 
    1328        9579 :     if (command.part == unknown) {
    1329             :         /*  Analyse an object word.  See if the thing is here, whether
    1330             :          *  we've got a verb yet, and so on.  Object must be here
    1331             :          *  unless verb is "find" or "invent(ory)" (and no new verb
    1332             :          *  yet to be analysed).  Water and oil are also funny, since
    1333             :          *  they are never actually dropped at any location, but might
    1334             :          *  be here inside the bottle or urn or as a feature of the
    1335             :          *  location. */
    1336        4219 :         if (HERE(command.obj))
    1337             :             /* FALL THROUGH */;
    1338         193 :         else if (command.obj == DWARF && atdwrf(game.loc) > 0)
    1339             :             /* FALL THROUGH */;
    1340         324 :         else if ((LIQUID() == command.obj && HERE(BOTTLE)) ||
    1341         137 :                  command.obj == LIQLOC(game.loc))
    1342             :             /* FALL THROUGH */;
    1343          32 :         else if (command.obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) {
    1344           1 :             command.obj = URN;
    1345             :             /* FALL THROUGH */;
    1346          31 :         } else if (command.obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) {
    1347           0 :             command.obj = PLANT2;
    1348             :             /* FALL THROUGH */;
    1349          31 :         } else if (command.obj == KNIFE && game.knfloc == game.loc) {
    1350           1 :             game.knfloc = -1;
    1351           1 :             rspeak(KNIVES_VANISH);
    1352           1 :             return GO_CLEAROBJ;
    1353          30 :         } else if (command.obj == ROD && HERE(ROD2)) {
    1354           5 :             command.obj = ROD2;
    1355             :             /* FALL THROUGH */;
    1356          51 :         } else if ((command.verb == FIND ||
    1357          26 :                     command.verb == INVENTORY) && (command.word[1].id == WORD_EMPTY || command.word[1].id == WORD_NOT_FOUND))
    1358             :             /* FALL THROUGH */;
    1359             :         else {
    1360          22 :             sspeak(NO_SEE, command.word[0].raw);
    1361          22 :             return GO_CLEAROBJ;
    1362             :         }
    1363             : 
    1364        4196 :         if (command.verb != 0)
    1365        4194 :             command.part = transitive;
    1366             :     }
    1367             : 
    1368        9556 :     switch (command.part) {
    1369        5274 :     case intransitive:
    1370        5274 :         if (command.word[1].raw[0] != '\0' && command.verb != SAY)
    1371        4246 :             return GO_WORD2;
    1372        1028 :         if (command.verb == SAY)
    1373             :             /* KEYS is not special, anything not NO_OBJECT or INTRANSITIVE
    1374             :              * will do here. We're preventing interpretation as an intransitive
    1375             :              * verb when the word is unknown. */
    1376          28 :             command.obj = command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT;
    1377        1055 :         if (command.obj == NO_OBJECT ||
    1378          27 :             command.obj == INTRANSITIVE) {
    1379             :             /*  Analyse an intransitive verb (ie, no object given yet). */
    1380        1001 :             switch (command.verb) {
    1381          36 :             case CARRY:
    1382          36 :                 return vcarry(command.verb, INTRANSITIVE);
    1383           1 :             case  DROP:
    1384           1 :                 return GO_UNKNOWN;
    1385           1 :             case  SAY:
    1386           1 :                 return GO_UNKNOWN;
    1387           9 :             case  UNLOCK:
    1388           9 :                 return lock(command.verb, INTRANSITIVE);
    1389         154 :             case  NOTHING: {
    1390         154 :                 rspeak(OK_MAN);
    1391         154 :                 return (GO_CLEAROBJ);
    1392             :             }
    1393           4 :             case  LOCK:
    1394           4 :                 return lock(command.verb, INTRANSITIVE);
    1395         262 :             case  LIGHT:
    1396         262 :                 return light(command.verb, INTRANSITIVE);
    1397         178 :             case  EXTINGUISH:
    1398         178 :                 return extinguish(command.verb, INTRANSITIVE);
    1399           1 :             case  WAVE:
    1400           1 :                 return GO_UNKNOWN;
    1401           1 :             case  TAME:
    1402           1 :                 return GO_UNKNOWN;
    1403           2 :             case GO: {
    1404           2 :                 speak(actions[command.verb].message);
    1405           2 :                 return GO_CLEAROBJ;
    1406             :             }
    1407          18 :             case ATTACK:
    1408          18 :                 command.obj = INTRANSITIVE;
    1409          18 :                 return attack(command);
    1410           1 :             case POUR:
    1411           1 :                 return pour(command.verb, INTRANSITIVE);
    1412           2 :             case EAT:
    1413           2 :                 return eat(command.verb, INTRANSITIVE);
    1414           4 :             case DRINK:
    1415           4 :                 return drink(command.verb, INTRANSITIVE);
    1416           1 :             case RUB:
    1417           1 :                 return GO_UNKNOWN;
    1418           1 :             case THROW:
    1419           1 :                 return GO_UNKNOWN;
    1420           4 :             case QUIT:
    1421           4 :                 return quit();
    1422           1 :             case FIND:
    1423           1 :                 return GO_UNKNOWN;
    1424          85 :             case INVENTORY:
    1425          85 :                 return inven();
    1426           1 :             case FEED:
    1427           1 :                 return GO_UNKNOWN;
    1428           1 :             case FILL:
    1429           1 :                 return fill(command.verb, INTRANSITIVE);
    1430           5 :             case BLAST:
    1431           5 :                 blast();
    1432           2 :                 return GO_CLEAROBJ;
    1433           3 :             case SCORE:
    1434           3 :                 score(scoregame);
    1435           3 :                 return GO_CLEAROBJ;
    1436         109 :             case FEE:
    1437             :             case FIE:
    1438             :             case FOE:
    1439             :             case FOO:
    1440             :             case FUM:
    1441         109 :                 return bigwords(command.word[0].id);
    1442           1 :             case BRIEF:
    1443           1 :                 return brief();
    1444           3 :             case READ:
    1445           3 :                 command.obj = INTRANSITIVE;
    1446           3 :                 return read(command);
    1447           1 :             case BREAK:
    1448           1 :                 return GO_UNKNOWN;
    1449           1 :             case WAKE:
    1450           1 :                 return GO_UNKNOWN;
    1451           4 :             case SAVE:
    1452           4 :                 return suspend();
    1453           7 :             case RESUME:
    1454           7 :                 return resume();
    1455           8 :             case FLY:
    1456           8 :                 return fly(command.verb, INTRANSITIVE);
    1457          47 :             case LISTEN:
    1458          47 :                 return listen();
    1459          42 :             case PART:
    1460          42 :                 return reservoir();
    1461           2 :             case SEED:
    1462             :             case WASTE:
    1463           2 :                 rspeak(NUMERIC_REQUIRED);
    1464           2 :                 return GO_TOP;
    1465             :             default: // LCOV_EXCL_LINE
    1466             :                 BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
    1467             :             }
    1468             :         }
    1469             :     /* FALLTHRU */
    1470             :     case transitive:
    1471             :         /*  Analyse a transitive verb. */
    1472        4307 :         switch (command.verb) {
    1473        1933 :         case  CARRY:
    1474        1933 :             return vcarry(command.verb, command.obj);
    1475        1357 :         case  DROP:
    1476        1357 :             return discard(command.verb, command.obj);
    1477          27 :         case  SAY:
    1478          27 :             return say(command);
    1479          72 :         case  UNLOCK:
    1480          72 :             return lock(command.verb, command.obj);
    1481           1 :         case  NOTHING: {
    1482           1 :             rspeak(OK_MAN);
    1483           1 :             return (GO_CLEAROBJ);
    1484             :         }
    1485          11 :         case  LOCK:
    1486          11 :             return lock(command.verb, command.obj);
    1487          31 :         case LIGHT:
    1488          31 :             return light(command.verb, command.obj);
    1489          30 :         case EXTINGUISH:
    1490          30 :             return extinguish(command.verb, command.obj);
    1491         104 :         case WAVE:
    1492         104 :             return wave(command.verb, command.obj);
    1493           1 :         case TAME: {
    1494           1 :             speak(actions[command.verb].message);
    1495           1 :             return GO_CLEAROBJ;
    1496             :         }
    1497           1 :         case GO: {
    1498           1 :             speak(actions[command.verb].message);
    1499           1 :             return GO_CLEAROBJ;
    1500             :         }
    1501         142 :         case ATTACK:
    1502         142 :             return attack(command);
    1503         125 :         case POUR:
    1504         125 :             return pour(command.verb, command.obj);
    1505           4 :         case EAT:
    1506           4 :             return eat(command.verb, command.obj);
    1507          43 :         case DRINK:
    1508          43 :             return drink(command.verb, command.obj);
    1509          23 :         case RUB:
    1510          23 :             return rub(command.verb, command.obj);
    1511         169 :         case THROW:
    1512         169 :             return throw (command);
    1513           1 :         case QUIT: {
    1514           1 :             speak(actions[command.verb].message);
    1515           1 :             return GO_CLEAROBJ;
    1516             :         }
    1517           7 :         case FIND:
    1518           7 :             return find(command.verb, command.obj);
    1519           1 :         case INVENTORY:
    1520           1 :             return find(command.verb, command.obj);
    1521          41 :         case FEED:
    1522          41 :             return feed(command.verb, command.obj);
    1523          36 :         case FILL:
    1524          36 :             return fill(command.verb, command.obj);
    1525           2 :         case BLAST:
    1526           2 :             blast();
    1527           1 :             return GO_CLEAROBJ;
    1528           1 :         case SCORE: {
    1529           1 :             speak(actions[command.verb].message);
    1530           1 :             return GO_CLEAROBJ;
    1531             :         }
    1532           1 :         case FEE:
    1533             :         case FIE:
    1534             :         case FOE:
    1535             :         case FOO:
    1536             :         case FUM: {
    1537           1 :             speak(actions[command.verb].message);
    1538           1 :             return GO_CLEAROBJ;
    1539             :         }
    1540           1 :         case BRIEF: {
    1541           1 :             speak(actions[command.verb].message);
    1542           1 :             return GO_CLEAROBJ;
    1543             :         }
    1544           9 :         case READ:
    1545           9 :             return read(command);
    1546           5 :         case BREAK:
    1547           5 :             return vbreak(command.verb, command.obj);
    1548           2 :         case WAKE:
    1549           2 :             return wake(command.verb, command.obj);
    1550           1 :         case SAVE: {
    1551           1 :             speak(actions[command.verb].message);
    1552           1 :             return GO_CLEAROBJ;
    1553             :         }
    1554           1 :         case RESUME: {
    1555           1 :             speak(actions[command.verb].message);
    1556           1 :             return GO_CLEAROBJ;
    1557             :         }
    1558          37 :         case FLY:
    1559          37 :             return fly(command.verb, command.obj);
    1560           1 :         case LISTEN: {
    1561           1 :             speak(actions[command.verb].message);
    1562           1 :             return GO_CLEAROBJ;
    1563             :         }
    1564             :         // LCOV_EXCL_START
    1565             :         // This case should never happen - here only as placeholder
    1566             :         case PART:
    1567             :             return reservoir();
    1568             :         // LCOV_EXCL_STOP
    1569          85 :         case SEED:
    1570          85 :             return seed(command.verb, command.word[1].raw);
    1571           1 :         case WASTE:
    1572           1 :             return waste(command.verb, (turn_t)atol(command.word[1].raw));
    1573             :         default: // LCOV_EXCL_LINE
    1574             :             BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
    1575             :         }
    1576           2 :     case unknown:
    1577             :         /* Unknown verb, couldn't deduce object - might need hint */
    1578           2 :         sspeak(WHAT_DO, command.word[0].raw);
    1579           2 :         return GO_CLEAROBJ;
    1580             :     default: // LCOV_EXCL_LINE
    1581             :         BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
    1582             :     }
    1583             : }

Generated by: LCOV version 1.13