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: 2018-03-12 03:52:32 Functions: 32 32 100.0 %

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

Generated by: LCOV version 1.13