LCOV - code coverage report
Current view: top level - open-adventure - main.c (source / functions) Hit Total Coverage
Test: advent.info Lines: 649 650 99.8 %
Date: 2017-09-17 15:40:10 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  * There used to be a note that said this:
       3             :  *
       4             :  * The author - Don Woods - apologises for the style of the code; it
       5             :  * is a result of running the original Fortran IV source through a
       6             :  * home-brew Fortran-to-C converter.
       7             :  *
       8             :  * Now that the code has been restructured into something much closer
       9             :  * to idiomatic C, the following is more appropriate:
      10             :  *
      11             :  * ESR apologizes for the remaing gotos (now confined to one function
      12             :  * in this file - there used to be over 350 of them, *everywhere*).
      13             :  * Applying the Structured Program Theorem can be hard.
      14             :  */
      15             : 
      16             : #include <stdlib.h>
      17             : #include <stdio.h>
      18             : #include <stdbool.h>
      19             : #include <getopt.h>
      20             : #include <signal.h>
      21             : #include <string.h>
      22             : #include <ctype.h>
      23             : #include "advent.h"
      24             : #include "dungeon.h"
      25             : 
      26             : #define DIM(a) (sizeof(a)/sizeof(a[0]))
      27             : 
      28             : // LCOV_EXCL_START
      29             : // exclude from coverage analysis because it requires interactivity to test
      30             : static void sig_handler(int signo)
      31             : {
      32             :     if (signo == SIGINT) {
      33             :         if (settings.logfp != NULL)
      34             :             fflush(settings.logfp);
      35             :     }
      36             :     exit(EXIT_FAILURE);
      37             : }
      38             : // LCOV_EXCL_STOP
      39             : 
      40             : /*
      41             :  * MAIN PROGRAM
      42             :  *
      43             :  *  Adventure (rev 2: 20 treasures)
      44             :  *  History: Original idea & 5-treasure version (adventures) by Willie Crowther
      45             :  *           15-treasure version (adventure) by Don Woods, April-June 1977
      46             :  *           20-treasure version (rev 2) by Don Woods, August 1978
      47             :  *              Errata fixed: 78/12/25
      48             :  *           Revived 2017 as Open Adventure.
      49             :  */
      50             : 
      51             : static bool do_command(void);
      52             : 
      53          95 : int main(int argc, char *argv[])
      54             : {
      55             :     int ch;
      56             : 
      57             :     /*  Options. */
      58             : 
      59             : #ifndef ADVENT_NOSAVE
      60          95 :     const char* opts = "l:or:";
      61          95 :     const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename]\n";
      62          95 :     FILE *rfp = NULL;
      63             : #else
      64             :     const char* opts = "l:o";
      65             :     const char* usage = "Usage: %s [-l logfilename] [-o]\n";
      66             : #endif
      67         197 :     while ((ch = getopt(argc, argv, opts)) != EOF) {
      68           8 :         switch (ch) {
      69           3 :         case 'l':
      70           3 :             settings.logfp = fopen(optarg, "w");
      71           3 :             if (settings.logfp == NULL)
      72           1 :                 fprintf(stderr,
      73             :                         "advent: can't open logfile %s for write\n",
      74             :                         optarg);
      75           3 :             signal(SIGINT, sig_handler);
      76           3 :             break;
      77           1 :         case 'o':
      78           1 :             settings.oldstyle = true;
      79           1 :             settings.prompt = false;
      80           1 :             break;
      81             : #ifndef ADVENT_NOSAVE
      82           3 :         case 'r':
      83           3 :             rfp = fopen(optarg, "r");
      84           3 :             if (rfp == NULL)
      85           1 :                 fprintf(stderr,
      86             :                         "advent: can't open save file %s for read\n",
      87             :                         optarg);
      88           3 :             break;
      89             : #endif
      90           1 :         default:
      91           1 :             fprintf(stderr,
      92             :                     usage, argv[0]);
      93           1 :             fprintf(stderr,
      94             :                     "        -l create a log file of your game named as specified'\n");
      95           1 :             fprintf(stderr,
      96             :                     "        -o 'oldstyle' (no prompt, no command editing, displays 'Initialising...')\n");
      97             : #ifndef ADVENT_NOSAVE
      98           1 :             fprintf(stderr,
      99             :                     "        -r restore from specified saved game file\n");
     100             : #endif
     101           1 :             exit(EXIT_FAILURE);
     102             :             break;
     103             :         }
     104             :     }
     105             : 
     106             :     /*  Initialize game variables */
     107          94 :     long seedval = initialise();
     108             : 
     109             : #ifndef ADVENT_NOSAVE
     110          94 :     if (!rfp) {
     111          92 :         game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
     112          92 :         if (game.novice)
     113           1 :             game.limit = NOVICELIMIT;
     114             :     } else {
     115           2 :         restore(rfp);
     116             :     }
     117             : #else
     118             :     game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
     119             :     if (game.novice)
     120             :         game.limit = NOVICELIMIT;
     121             : #endif
     122             : 
     123          94 :     if (settings.logfp)
     124           2 :         fprintf(settings.logfp, "seed %ld\n", seedval);
     125             : 
     126             :     /* interpret commands until EOF or interrupt */
     127             :     for (;;) {
     128       26914 :         if (!do_command())
     129          67 :             break;
     130             :     }
     131             :     /* show score and exit */
     132          67 :     terminate(quitgame);
     133             : }
     134             : 
     135             : /*  Check if this loc is eligible for any hints.  If been here long
     136             :  *  enough, display.  Ignore "HINTS" < 4 (special stuff, see database
     137             :  *  notes). */
     138       17783 : static void checkhints(void)
     139             : {
     140       17783 :     if (conditions[game.loc] >= game.conds) {
     141       45006 :         for (int hint = 0; hint < NHINTS; hint++) {
     142       41557 :             if (game.hinted[hint])
     143          16 :                 continue;
     144       41541 :             if (!CNDBIT(game.loc, hint + 1 + COND_HBASE))
     145       37320 :                 game.hintlc[hint] = -1;
     146       41541 :             ++game.hintlc[hint];
     147             :             /*  Come here if he's been long enough at required loc(s) for some
     148             :              *  unused hint. */
     149       41541 :             if (game.hintlc[hint] >= hints[hint].turns) {
     150             :                 int i;
     151             : 
     152         771 :                 switch (hint) {
     153           3 :                 case 0:
     154             :                     /* cave */
     155           3 :                     if (game.prop[GRATE] == GRATE_CLOSED && !HERE(KEYS))
     156           2 :                         break;
     157           1 :                     game.hintlc[hint] = 0;
     158           1 :                     return;
     159          27 :                 case 1: /* bird */
     160          27 :                     if (game.place[BIRD] == game.loc && TOTING(ROD) && game.oldobj == BIRD)
     161           1 :                         break;
     162          26 :                     return;
     163          27 :                 case 2: /* snake */
     164          27 :                     if (HERE(SNAKE) && !HERE(BIRD))
     165           1 :                         break;
     166          26 :                     game.hintlc[hint] = 0;
     167          26 :                     return;
     168           2 :                 case 3: /* maze */
     169           3 :                     if (game.atloc[game.loc] == NO_OBJECT &&
     170           2 :                         game.atloc[game.oldloc] == NO_OBJECT &&
     171           2 :                         game.atloc[game.oldlc2] == NO_OBJECT &&
     172           1 :                         game.holdng > 1)
     173           1 :                         break;
     174           1 :                     game.hintlc[hint] = 0;
     175           1 :                     return;
     176          10 :                 case 4: /* dark */
     177          10 :                     if (game.prop[EMERALD] != STATE_NOTFOUND && game.prop[PYRAMID] == STATE_NOTFOUND)
     178           1 :                         break;
     179           9 :                     game.hintlc[hint] = 0;
     180           9 :                     return;
     181           2 :                 case 5: /* witt */
     182           2 :                     break;
     183          26 :                 case 6: /* urn */
     184          26 :                     if (game.dflag == 0)
     185           1 :                         break;
     186          25 :                     game.hintlc[hint] = 0;
     187          25 :                     return;
     188           2 :                 case 7: /* woods */
     189           3 :                     if (game.atloc[game.loc] == NO_OBJECT &&
     190           2 :                         game.atloc[game.oldloc] == NO_OBJECT &&
     191           1 :                         game.atloc[game.oldlc2] == NO_OBJECT)
     192           1 :                         break;
     193           1 :                     return;
     194           4 :                 case 8: /* ogre */
     195           4 :                     i = atdwrf(game.loc);
     196           4 :                     if (i < 0) {
     197           1 :                         game.hintlc[hint] = 0;
     198           1 :                         return;
     199             :                     }
     200           3 :                     if (HERE(OGRE) && i == 0)
     201           1 :                         break;
     202           2 :                     return;
     203         668 :                 case 9: /* jade */
     204         668 :                     if (game.tally == 1 && game.prop[JADE] < 0)
     205           1 :                         break;
     206         667 :                     game.hintlc[hint] = 0;
     207         667 :                     return;
     208             :                 default: // LCOV_EXCL_LINE
     209             :                     BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
     210             :                 }
     211             : 
     212             :                 /* Fall through to hint display */
     213          12 :                 game.hintlc[hint] = 0;
     214          12 :                 if (!yes(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
     215           1 :                     return;
     216          11 :                 rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty);
     217          11 :                 game.hinted[hint] = yes(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
     218          10 :                 if (game.hinted[hint] && game.limit > WARNTIME)
     219           9 :                     game.limit += WARNTIME * hints[hint].penalty;
     220             :             }
     221             :         }
     222             :     }
     223             : }
     224             : 
     225         964 : static bool spotted_by_pirate(int i)
     226             : {
     227         964 :     if (i != PIRATE)
     228         762 :         return false;
     229             : 
     230             :     /*  The pirate's spotted him.  Pirate leaves him alone once we've
     231             :      *  found chest.  K counts if a treasure is here.  If not, and
     232             :      *  tally=1 for an unseen chest, let the pirate be spotted.  Note
     233             :      *  that game.place[CHEST] = LOC_NOWHERE might mean that he's thrown
     234             :      *  it to the troll, but in that case he's seen the chest
     235             :      *  (game.prop[CHEST] == STATE_FOUND). */
     236         384 :     if (game.loc == game.chloc ||
     237         182 :         game.prop[CHEST] != STATE_NOTFOUND)
     238          60 :         return true;
     239         142 :     int snarfed = 0;
     240         142 :     bool movechest = false, robplayer = false;
     241        9940 :     for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
     242        9798 :         if (!objects[treasure].is_treasure)
     243        6958 :             continue;
     244             :         /*  Pirate won't take pyramid from plover room or dark
     245             :          *  room (too easy!). */
     246        2982 :         if (treasure == PYRAMID && (game.loc == objects[PYRAMID].plac ||
     247         142 :                                     game.loc == objects[EMERALD].plac)) {
     248           1 :             continue;
     249             :         }
     250        5634 :         if (TOTING(treasure) ||
     251        5585 :             HERE(treasure))
     252          49 :             ++snarfed;
     253        2839 :         if (TOTING(treasure)) {
     254          44 :             movechest = true;
     255          44 :             robplayer = true;
     256             :         }
     257             :     }
     258             :     /* Force chest placement before player finds last treasure */
     259         142 :     if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == LOC_NOWHERE && HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT) {
     260           1 :         rspeak(PIRATE_SPOTTED);
     261           1 :         movechest = true;
     262             :     }
     263             :     /* Do things in this order (chest move before robbery) so chest is listed
     264             :      * last at the maze location. */
     265         142 :     if (movechest) {
     266          28 :         move(CHEST, game.chloc);
     267          28 :         move(MESSAG, game.chloc2);
     268          28 :         game.dloc[PIRATE] = game.chloc;
     269          28 :         game.odloc[PIRATE] = game.chloc;
     270          28 :         game.dseen[PIRATE] = false;
     271             :     } else {
     272             :         /* You might get a hint of the pirate's presence even if the
     273             :          * chest doesn't move... */
     274         114 :         if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20))
     275          11 :             rspeak(PIRATE_RUSTLES);
     276             :     }
     277         142 :     if (robplayer) {
     278          27 :         rspeak(PIRATE_POUNCES);
     279        1890 :         for (int treasure = 1; treasure <= NOBJECTS; treasure++) {
     280        1863 :             if (!objects[treasure].is_treasure)
     281        1323 :                 continue;
     282         567 :             if (!(treasure == PYRAMID && (game.loc == objects[PYRAMID].plac ||
     283          27 :                                           game.loc == objects[EMERALD].plac))) {
     284         540 :                 if (AT(treasure) && game.fixed[treasure] == IS_FREE)
     285           1 :                     carry(treasure, game.loc);
     286         540 :                 if (TOTING(treasure))
     287          45 :                     drop(treasure, game.chloc);
     288             :             }
     289             :         }
     290             :     }
     291             : 
     292         142 :     return true;
     293             : }
     294             : 
     295       13504 : static bool dwarfmove(void)
     296             : /* Dwarves move.  Return true if player survives, false if he dies. */
     297             : {
     298             :     int kk, stick, attack;
     299       13504 :     loc_t tk[21];
     300             : 
     301             :     /*  Dwarf stuff.  See earlier comments for description of
     302             :      *  variables.  Remember sixth dwarf is pirate and is thus
     303             :      *  very different except for motion rules. */
     304             : 
     305             :     /*  First off, don't let the dwarves follow him into a pit or a
     306             :      *  wall.  Activate the whole mess the first time he gets as far
     307             :      *  as the Hall of Mists (what INDEEP() tests).  If game.newloc
     308             :      *  is forbidden to pirate (in particular, if it's beyond the
     309             :      *  troll bridge), bypass dwarf stuff.  That way pirate can't
     310             :      *  steal return toll, and dwarves can't meet the bear.  Also
     311             :      *  means dwarves won't follow him into dead end in maze, but
     312             :      *  c'est la vie.  They'll wait for him outside the dead end. */
     313       27002 :     if (game.loc == LOC_NOWHERE ||
     314       26255 :         FORCED(game.loc) ||
     315       12757 :         CNDBIT(game.newloc, COND_NOARRR))
     316        1050 :         return true;
     317             : 
     318             :     /* Dwarf activity level ratchets up */
     319       12454 :     if (game.dflag == 0) {
     320        3375 :         if (INDEEP(game.loc))
     321          67 :             game.dflag = 1;
     322        3375 :         return true;
     323             :     }
     324             : 
     325             :     /*  When we encounter the first dwarf, we kill 0, 1, or 2 of
     326             :      *  the 5 dwarves.  If any of the survivors is at game.loc,
     327             :      *  replace him with the alternate. */
     328        9079 :     if (game.dflag == 1) {
     329        1931 :         if (!INDEEP(game.loc) ||
     330        1749 :             (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) ||
     331          16 :                          PCT(85))))
     332         983 :             return true;
     333          55 :         game.dflag = 2;
     334         165 :         for (int i = 1; i <= 2; i++) {
     335         110 :             int j = 1 + randrange(NDWARVES - 1);
     336         110 :             if (PCT(50))
     337          62 :                 game.dloc[j] = 0;
     338             :         }
     339             : 
     340             :         /* Alternate initial loc for dwarf, in case one of them
     341             :         *  starts out on top of the adventurer. */
     342         330 :         for (int i = 1; i <= NDWARVES - 1; i++) {
     343         275 :             if (game.dloc[i] == game.loc)
     344           4 :                 game.dloc[i] = DALTLC; //
     345         275 :             game.odloc[i] = game.dloc[i];
     346             :         }
     347          55 :         rspeak(DWARF_RAN);
     348          55 :         drop(AXE, game.loc);
     349          55 :         return true;
     350             :     }
     351             : 
     352             :     /*  Things are in full swing.  Move each dwarf at random,
     353             :      *  except if he's seen us he sticks with us.  Dwarves stay
     354             :      *  deep inside.  If wandering at random, they don't back up
     355             :      *  unless there's no alternative.  If they don't have to
     356             :      *  move, they attack.  And, of course, dead dwarves don't do
     357             :      *  much of anything. */
     358        8041 :     game.dtotal = 0;
     359        8041 :     attack = 0;
     360        8041 :     stick = 0;
     361       56287 :     for (int i = 1; i <= NDWARVES; i++) {
     362       48246 :         if (game.dloc[i] == 0)
     363       23909 :             continue;
     364             :         /*  Fill tk array with all the places this dwarf might go. */
     365       24337 :         unsigned int j = 1;
     366       24337 :         kk = tkey[game.dloc[i]];
     367       24337 :         if (kk != 0)
     368             :             do {
     369      126246 :                 enum desttype_t desttype = travel[kk].desttype;
     370      126246 :                 game.newloc = travel[kk].destval;
     371             :                 /* Have we avoided a dwarf encounter? */
     372      126246 :                 if (desttype != dest_goto)
     373       10334 :                     continue;
     374      115912 :                 else if (!INDEEP(game.newloc))
     375        1990 :                     continue;
     376      113922 :                 else if (game.newloc == game.odloc[i])
     377       32958 :                     continue;
     378       80964 :                 else if (j > 1 && game.newloc == tk[j - 1])
     379       14440 :                     continue;
     380       66524 :                 else if (j >= DIM(tk) - 1)
     381             :                     /* This can't actually happen. */
     382             :                     continue; // LCOV_EXCL_LINE
     383       66524 :                 else if (game.newloc == game.dloc[i])
     384        3466 :                     continue;
     385       63058 :                 else if (FORCED(game.newloc))
     386        7457 :                     continue;
     387       55601 :                 else if (i == PIRATE && CNDBIT(game.newloc, COND_NOARRR))
     388        2576 :                     continue;
     389       53025 :                 else if (travel[kk].nodwarves)
     390         349 :                     continue;
     391       52676 :                 tk[j++] = game.newloc;
     392             :             } while
     393      126246 :             (!travel[kk++].stop);
     394       24337 :         tk[j] = game.odloc[i];
     395       24337 :         if (j >= 2)
     396       21194 :             --j;
     397       24337 :         j = 1 + randrange(j);
     398       24337 :         game.odloc[i] = game.dloc[i];
     399       24337 :         game.dloc[i] = tk[j];
     400       48928 :         game.dseen[i] = (game.dseen[i] && INDEEP(game.loc)) ||
     401       47166 :                         (game.dloc[i] == game.loc ||
     402       23539 :                          game.odloc[i] == game.loc);
     403       24337 :         if (!game.dseen[i])
     404       23373 :             continue;
     405         964 :         game.dloc[i] = game.loc;
     406         964 :         if (spotted_by_pirate(i))
     407         202 :             continue;
     408             :         /* This threatening little dwarf is in the room with him! */
     409         762 :         ++game.dtotal;
     410         762 :         if (game.odloc[i] == game.dloc[i]) {
     411         161 :             ++attack;
     412         161 :             if (game.knfloc >= 0)
     413         161 :                 game.knfloc = game.loc;
     414         161 :             if (randrange(1000) < 95 * (game.dflag - 2))
     415           2 :                 ++stick;
     416             :         }
     417             :     }
     418             : 
     419             :     /*  Now we know what's happening.  Let's tell the poor sucker about it. */
     420        8041 :     if (game.dtotal == 0)
     421        7319 :         return true;
     422         722 :     rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal);
     423         722 :     if (attack == 0)
     424         567 :         return true;
     425         155 :     if (game.dflag == 2)
     426          48 :         game.dflag = 3;
     427         155 :     if (attack > 1) {
     428           6 :         rspeak(THROWN_KNIVES, attack);
     429           6 :         rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick);
     430             :     } else {
     431         149 :         rspeak(KNIFE_THROWN);
     432         149 :         rspeak(stick ? GETS_YOU : MISSES_YOU);
     433             :     }
     434         155 :     if (stick == 0)
     435         153 :         return true;
     436           2 :     game.oldlc2 = game.loc;
     437           2 :     return false;
     438             : }
     439             : 
     440             : /*  "You're dead, Jim."
     441             :  *
     442             :  *  If the current loc is zero, it means the clown got himself killed.
     443             :  *  We'll allow this maxdie times.  NDEATHS is automatically set based
     444             :  *  on the number of snide messages available.  Each death results in
     445             :  *  a message (obituaries[n]) which offers reincarnation; if accepted,
     446             :  *  this results in message obituaries[0], obituaries[2], etc.  The
     447             :  *  last time, if he wants another chance, he gets a snide remark as
     448             :  *  we exit.  When reincarnated, all objects being carried get dropped
     449             :  *  at game.oldlc2 (presumably the last place prior to being killed)
     450             :  *  without change of props.  The loop runs backwards to assure that
     451             :  *  the bird is dropped before the cage.  (This kluge could be changed
     452             :  *  once we're sure all references to bird and cage are done by
     453             :  *  keywords.)  The lamp is a special case (it wouldn't do to leave it
     454             :  *  in the cave). It is turned off and left outside the building (only
     455             :  *  if he was carrying it, of course).  He himself is left inside the
     456             :  *  building (and heaven help him if he tries to xyzzy back into the
     457             :  *  cave without the lamp!).  game.oldloc is zapped so he can't just
     458             :  *  "retreat". */
     459             : 
     460          23 : static void croak(void)
     461             : /*  Okay, he's dead.  Let's get on with it. */
     462             : {
     463          23 :     if (game.numdie < 0)
     464           0 :         game.numdie = 0;
     465          23 :     const char* query = obituaries[game.numdie].query;
     466          23 :     const char* yes_response = obituaries[game.numdie].yes_response;
     467          23 :     ++game.numdie;
     468          23 :     if (game.closng) {
     469             :         /*  He died during closing time.  No resurrection.  Tally up a
     470             :          *  death and exit. */
     471           1 :         rspeak(DEATH_CLOSING);
     472           1 :         terminate(endgame);
     473          22 :     } else if ( !yes(query, yes_response, arbitrary_messages[OK_MAN])
     474          15 :                 || game.numdie == NDEATHS)
     475           8 :         terminate(endgame);
     476             :     else {
     477          11 :         game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
     478          11 :         if (TOTING(LAMP))
     479           3 :             game.prop[LAMP] = LAMP_DARK;
     480         770 :         for (int j = 1; j <= NOBJECTS; j++) {
     481         759 :             int i = NOBJECTS + 1 - j;
     482         759 :             if (TOTING(i)) {
     483             :                 /* Always leave lamp where it's accessible aboveground */
     484          10 :                 drop(i, (i == LAMP) ? LOC_START : game.oldlc2);
     485             :             }
     486             :         }
     487          11 :         game.oldloc = game.loc = game.newloc = LOC_BUILDING;
     488             :     }
     489          11 : }
     490             : 
     491        1378 : static bool traveleq(int a, int b)
     492             : /* Are two travel entries equal for purposes of skip after failed condition? */
     493             : {
     494        1378 :     return (travel[a].condtype == travel[b].condtype)
     495         620 :            && (travel[a].condarg1 == travel[b].condarg1)
     496         556 :            && (travel[a].condarg2 == travel[b].condarg2)
     497         554 :            && (travel[a].desttype == travel[b].desttype)
     498        1932 :            && (travel[a].destval == travel[b].destval);
     499             : }
     500             : 
     501             : /*  Given the current location in "game.loc", and a motion verb number in
     502             :  *  "motion", put the new location in "game.newloc".  The current loc is saved
     503             :  *  in "game.oldloc" in case he wants to retreat.  The current
     504             :  *  game.oldloc is saved in game.oldlc2, in case he dies.  (if he
     505             :  *  does, game.newloc will be limbo, and game.oldloc will be what killed
     506             :  *  him, so we need game.oldlc2, which is the last place he was
     507             :  *  safe.) */
     508             : 
     509       13359 : static void playermove( int motion)
     510             : {
     511       13359 :     int scratchloc, travel_entry = tkey[game.loc];
     512       13359 :     game.newloc = game.loc;
     513       13359 :     if (travel_entry == 0)
     514             :         BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE
     515       13359 :     if (motion == NUL)
     516         263 :         return;
     517       13096 :     else if (motion == BACK) {
     518             :         /*  Handle "go back".  Look for verb which goes from game.loc to
     519             :          *  game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion.
     520             :          *  te_tmp saves entry -> forced loc -> previous loc. */
     521           7 :         motion = game.oldloc;
     522           7 :         if (FORCED(motion))
     523           1 :             motion = game.oldlc2;
     524           7 :         game.oldlc2 = game.oldloc;
     525           7 :         game.oldloc = game.loc;
     526           7 :         if (CNDBIT(game.loc, COND_NOBACK)) {
     527           2 :             rspeak(TWIST_TURN);
     528           2 :             return;
     529             :         }
     530           5 :         if (motion == game.loc) {
     531           1 :             rspeak(FORGOT_PATH);
     532           1 :             return;
     533             :         }
     534             : 
     535           4 :         int te_tmp = 0;
     536          30 :         for (;;) {
     537          34 :             enum desttype_t desttype = travel[travel_entry].desttype;
     538          34 :             scratchloc = travel[travel_entry].destval;
     539          34 :             if (desttype != dest_goto || scratchloc != motion) {
     540          32 :                 if (desttype == dest_goto) {
     541          32 :                     if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == motion)
     542           1 :                         te_tmp = travel_entry;
     543             :                 }
     544          32 :                 if (!travel[travel_entry].stop) {
     545          30 :                     ++travel_entry;     /* go to next travel entry for this location */
     546          30 :                     continue;
     547             :                 }
     548             :                 /* we've reached the end of travel entries for game.loc */
     549           2 :                 travel_entry = te_tmp;
     550           2 :                 if (travel_entry == 0) {
     551           1 :                     rspeak(NOT_CONNECTED);
     552           1 :                     return;
     553             :                 }
     554             :             }
     555             : 
     556           3 :             motion = travel[travel_entry].motion;
     557           3 :             travel_entry = tkey[game.loc];
     558           3 :             break; /* fall through to ordinary travel */
     559             :         }
     560       13089 :     } else if (motion == LOOK) {
     561             :         /*  Look.  Can't give more detail.  Pretend it wasn't dark
     562             :          *  (though it may now be dark) so he won't fall into a
     563             :          *  pit while staring into the gloom. */
     564          42 :         if (game.detail < 3)
     565          35 :             rspeak(NO_MORE_DETAIL);
     566          42 :         ++game.detail;
     567          42 :         game.wzdark = false;
     568          42 :         game.abbrev[game.loc] = 0;
     569          42 :         return;
     570       13047 :     } else if (motion == CAVE) {
     571             :         /*  Cave.  Different messages depending on whether above ground. */
     572           2 :         rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL);
     573           2 :         return;
     574             :     } else {
     575             :         /* none of the specials */
     576       13045 :         game.oldlc2 = game.oldloc;
     577       13045 :         game.oldloc = game.loc;
     578             :     }
     579             : 
     580             :     /* Look for a way to fulfil the motion verb passed in - travel_entry indexes
     581             :      * the beginning of the motion entries for here (game.loc). */
     582             :     for (;;) {
     583      128669 :         if (T_TERMINATE(travel[travel_entry]) ||
     584       46745 :             travel[travel_entry].motion == motion)
     585             :             break;
     586       34962 :         if (travel[travel_entry].stop) {
     587             :             /*  Couldn't find an entry matching the motion word passed
     588             :              *  in.  Various messages depending on word given. */
     589         524 :             switch (motion) {
     590         504 :             case EAST:
     591             :             case WEST:
     592             :             case SOUTH:
     593             :             case NORTH:
     594             :             case NE:
     595             :             case NW:
     596             :             case SW:
     597             :             case SE:
     598             :             case UP:
     599             :             case DOWN:
     600         504 :                 rspeak(BAD_DIRECTION);
     601         504 :                 break;
     602           1 :             case FORWARD:
     603             :             case LEFT:
     604             :             case RIGHT:
     605           1 :                 rspeak(UNSURE_FACING);
     606           1 :                 break;
     607           2 :             case OUTSIDE:
     608             :             case INSIDE:
     609           2 :                 rspeak(NO_INOUT_HERE);
     610           2 :                 break;
     611           7 :             case XYZZY:
     612             :             case PLUGH:
     613           7 :                 rspeak(NOTHING_HAPPENS);
     614           7 :                 break;
     615           1 :             case CRAWL:
     616           1 :                 rspeak(WHICH_WAY);
     617           1 :                 break;
     618           9 :             default:
     619           9 :                 rspeak(CANT_APPLY);
     620             :             }
     621         524 :             return;
     622             :         }
     623       34438 :         ++travel_entry;
     624             :     }
     625             : 
     626             :     /* (ESR) We've found a destination that goes with the motion verb.
     627             :      * Next we need to check any conditional(s) on this destination, and
     628             :      * possibly on following entries. */
     629             :     do {
     630           1 :         for (;;) { /* L12 loop */
     631         832 :             for (;;) {
     632       13357 :                 enum condtype_t condtype = travel[travel_entry].condtype;
     633       13357 :                 long condarg1 = travel[travel_entry].condarg1;
     634       13357 :                 long condarg2 = travel[travel_entry].condarg2;
     635       13357 :                 if (condtype < cond_not) {
     636             :                     /* YAML N and [pct N] conditionals */
     637       12525 :                     if (condtype == cond_goto || condtype == cond_pct) {
     638       12414 :                         if (condarg1 == 0 ||
     639         347 :                             PCT(condarg1))
     640             :                             break;
     641             :                         /* else fall through */
     642             :                     }
     643             :                     /* YAML [with OBJ] clause */
     644         458 :                     else if (TOTING(condarg1) ||
     645         132 :                              (condtype == cond_with && AT(condarg1)))
     646             :                         break;
     647             :                     /* else fall through to check [not OBJ STATE] */
     648         832 :                 } else if (game.prop[condarg1] != condarg2)
     649         444 :                     break;
     650             : 
     651             :                 /* We arrive here on conditional failure.
     652             :                  * Skip to next non-matching destination */
     653         832 :                 int te_tmp = travel_entry;
     654             :                 do {
     655        1377 :                     if (travel[te_tmp].stop)
     656             :                         BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
     657        1377 :                     ++te_tmp;
     658             :                 } while
     659        1377 :                 (traveleq(travel_entry, te_tmp));
     660         832 :                 travel_entry = te_tmp;
     661             :             }
     662             : 
     663             :             /* Found an eligible rule, now execute it */
     664       12525 :             enum desttype_t desttype = travel[travel_entry].desttype;
     665       12525 :             game.newloc = travel[travel_entry].destval;
     666       12525 :             if (desttype == dest_goto)
     667       12140 :                 return;
     668             : 
     669         385 :             if (desttype == dest_speak) {
     670             :                 /* Execute a speak rule */
     671         241 :                 rspeak(game.newloc);
     672         241 :                 game.newloc = game.loc;
     673         241 :                 return;
     674             :             } else {
     675         144 :                 switch (game.newloc) {
     676          67 :                 case 1:
     677             :                     /* Special travel 1.  Plover-alcove passage.  Can carry only
     678             :                      * emerald.  Note: travel table must include "useless"
     679             :                      * entries going through passage, which can never be used
     680             :                      * for actual motion, but can be spotted by "go back". */
     681         134 :                     game.newloc = (game.loc == LOC_PLOVER)
     682             :                                   ? LOC_ALCOVE
     683          67 :                                   : LOC_PLOVER;
     684         133 :                     if (game.holdng > 1 ||
     685          99 :                         (game.holdng == 1 && !TOTING(EMERALD))) {
     686           2 :                         game.newloc = game.loc;
     687           2 :                         rspeak(MUST_DROP);
     688             :                     }
     689          67 :                     return;
     690           1 :                 case 2:
     691             :                     /* Special travel 2.  Plover transport.  Drop the
     692             :                      * emerald (only use special travel if toting
     693             :                      * it), so he's forced to use the plover-passage
     694             :                      * to get it out.  Having dropped it, go back and
     695             :                      * pretend he wasn't carrying it after all. */
     696           1 :                     drop(EMERALD, game.loc);
     697           1 :                     int te_tmp = travel_entry;
     698             :                     do {
     699           1 :                         if (travel[te_tmp].stop)
     700             :                             BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE
     701           1 :                         ++te_tmp;
     702             :                     } while
     703           1 :                     (traveleq(travel_entry, te_tmp));
     704           1 :                     travel_entry = te_tmp;
     705           1 :                     continue; /* goto L12 */
     706          76 :                 case 3:
     707             :                     /* Special travel 3.  Troll bridge.  Must be done
     708             :                      * only as special motion so that dwarves won't
     709             :                      * wander across and encounter the bear.  (They
     710             :                      * won't follow the player there because that
     711             :                      * region is forbidden to the pirate.)  If
     712             :                      * game.prop[TROLL]=TROLL_PAIDONCE, he's crossed
     713             :                      * since paying, so step out and block him.
     714             :                      * (standard travel entries check for
     715             :                      * game.prop[TROLL]=TROLL_UNPAID.)  Special stuff
     716             :                      * for bear. */
     717          76 :                     if (game.prop[TROLL] == TROLL_PAIDONCE) {
     718          25 :                         pspeak(TROLL, look, TROLL_PAIDONCE, true);
     719          25 :                         game.prop[TROLL] = TROLL_UNPAID;
     720          25 :                         move(TROLL2, LOC_NOWHERE);
     721          25 :                         move(TROLL2 + NOBJECTS, IS_FREE);
     722          25 :                         move(TROLL, objects[TROLL].plac);
     723          25 :                         move(TROLL + NOBJECTS, objects[TROLL].fixd);
     724          25 :                         juggle(CHASM);
     725          25 :                         game.newloc = game.loc;
     726          25 :                         return;
     727             :                     } else {
     728          51 :                         game.newloc = objects[TROLL].plac + objects[TROLL].fixd - game.loc;
     729          51 :                         if (game.prop[TROLL] == TROLL_UNPAID)
     730          27 :                             game.prop[TROLL] = TROLL_PAIDONCE;
     731          51 :                         if (!TOTING(BEAR))
     732          50 :                             return;
     733           1 :                         state_change(CHASM, BRIDGE_WRECKED);
     734           1 :                         game.prop[TROLL] = TROLL_GONE;
     735           1 :                         drop(BEAR, game.newloc);
     736           1 :                         game.fixed[BEAR] = IS_FIXED;
     737           1 :                         game.prop[BEAR] = BEAR_DEAD;
     738           1 :                         game.oldlc2 = game.newloc;
     739           1 :                         croak();
     740           1 :                         return;
     741             :                     }
     742             :                 default: // LCOV_EXCL_LINE
     743             :                     BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
     744             :                 }
     745             :             }
     746             :             break; /* Leave L12 loop */
     747             :         }
     748             :     } while
     749             :     (false);
     750             : }
     751             : 
     752       17715 : static bool closecheck(void)
     753             : /*  Handle the closing of the cave.  The cave closes "clock1" turns
     754             :  *  after the last treasure has been located (including the pirate's
     755             :  *  chest, which may of course never show up).  Note that the
     756             :  *  treasures need not have been taken yet, just located.  Hence
     757             :  *  clock1 must be large enough to get out of the cave (it only ticks
     758             :  *  while inside the cave).  When it hits zero, we branch to 10000 to
     759             :  *  start closing the cave, and then sit back and wait for him to try
     760             :  *  to get out.  If he doesn't within clock2 turns, we close the cave;
     761             :  *  if he does try, we assume he panics, and give him a few additional
     762             :  *  turns to get frantic before we close.  When clock2 hits zero, we
     763             :  *  transport him into the final puzzle.  Note that the puzzle depends
     764             :  *  upon all sorts of random things.  For instance, there must be no
     765             :  *  water or oil, since there are beanstalks which we don't want to be
     766             :  *  able to water, since the code can't handle it.  Also, we can have
     767             :  *  no keys, since there is a grate (having moved the fixed object!)
     768             :  *  there separating him from all the treasures.  Most of these
     769             :  *  problems arise from the use of negative prop numbers to suppress
     770             :  *  the object descriptions until he's actually moved the objects. */
     771             : {
     772             :     /* If a turn threshold has been met, apply penalties and tell
     773             :      * the player about it. */
     774       88575 :     for (int i = 0; i < NTHRESHOLDS; ++i) {
     775       70860 :         if (game.turns == turn_thresholds[i].threshold + 1) {
     776          24 :             game.trnluz += turn_thresholds[i].point_loss;
     777          24 :             speak(turn_thresholds[i].message);
     778             :         }
     779             :     }
     780             : 
     781             :     /*  Don't tick game.clock1 unless well into cave (and not at Y2). */
     782       17715 :     if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2)
     783         974 :         --game.clock1;
     784             : 
     785             :     /*  When the first warning comes, we lock the grate, destroy
     786             :      *  the bridge, kill all the dwarves (and the pirate), remove
     787             :      *  the troll and bear (unless dead), and set "closng" to
     788             :      *  true.  Leave the dragon; too much trouble to move it.
     789             :      *  from now until clock2 runs out, he cannot unlock the
     790             :      *  grate, move to any location outside the cave, or create
     791             :      *  the bridge.  Nor can he be resurrected if he dies.  Note
     792             :      *  that the snake is already gone, since he got to the
     793             :      *  treasure accessible only via the hall of the mountain
     794             :      *  king. Also, he's been in giant room (to get eggs), so we
     795             :      *  can refer to it.  Also also, he's gotten the pearl, so we
     796             :      *  know the bivalve is an oyster.  *And*, the dwarves must
     797             :      *  have been activated, since we've found chest. */
     798       17715 :     if (game.clock1 == 0) {
     799          13 :         game.prop[GRATE] = GRATE_CLOSED;
     800          13 :         game.prop[FISSURE] = UNBRIDGED;
     801          91 :         for (int i = 1; i <= NDWARVES; i++) {
     802          78 :             game.dseen[i] = false;
     803          78 :             game.dloc[i] = LOC_NOWHERE;
     804             :         }
     805          13 :         move(TROLL, LOC_NOWHERE);
     806          13 :         move(TROLL + NOBJECTS, IS_FREE);
     807          13 :         move(TROLL2, objects[TROLL].plac);
     808          13 :         move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
     809          13 :         juggle(CHASM);
     810          13 :         if (game.prop[BEAR] != BEAR_DEAD)
     811          13 :             DESTROY(BEAR);
     812          13 :         game.prop[CHAIN] = CHAIN_HEAP;
     813          13 :         game.fixed[CHAIN] = IS_FREE;
     814          13 :         game.prop[AXE] = AXE_HERE;
     815          13 :         game.fixed[AXE] = IS_FREE;
     816          13 :         rspeak(CAVE_CLOSING);
     817          13 :         game.clock1 = -1;
     818          13 :         game.closng = true;
     819          13 :         return true;
     820       17702 :     } else if (game.clock1 < 0)
     821         551 :         --game.clock2;
     822       17702 :     if (game.clock2 == 0) {
     823             :         /*  Once he's panicked, and clock2 has run out, we come here
     824             :          *  to set up the storage room.  The room has two locs,
     825             :          *  hardwired as LOC_NE and LOC_SW.  At the ne end, we
     826             :          *  place empty bottles, a nursery of plants, a bed of
     827             :          *  oysters, a pile of lamps, rods with stars, sleeping
     828             :          *  dwarves, and him.  At the sw end we place grate over
     829             :          *  treasures, snake pit, covey of caged birds, more rods, and
     830             :          *  pillows.  A mirror stretches across one wall.  Many of the
     831             :          *  objects come from known locations and/or states (e.g. the
     832             :          *  snake is known to have been destroyed and needn't be
     833             :          *  carried away from its old "place"), making the various
     834             :          *  objects be handled differently.  We also drop all other
     835             :          *  objects he might be carrying (lest he have some which
     836             :          *  could cause trouble, such as the keys).  We describe the
     837             :          *  flash of light and trundle back. */
     838           9 :         game.prop[BOTTLE] = put(BOTTLE, LOC_NE, EMPTY_BOTTLE);
     839           9 :         game.prop[PLANT] = put(PLANT, LOC_NE, PLANT_THIRSTY);
     840           9 :         game.prop[OYSTER] = put(OYSTER, LOC_NE, STATE_FOUND);
     841           9 :         game.prop[LAMP] = put(LAMP, LOC_NE, LAMP_DARK);
     842           9 :         game.prop[ROD] = put(ROD, LOC_NE, STATE_FOUND);
     843           9 :         game.prop[DWARF] = put(DWARF, LOC_NE, 0);
     844           9 :         game.loc = LOC_NE;
     845           9 :         game.oldloc = LOC_NE;
     846           9 :         game.newloc = LOC_NE;
     847             :         /*  Leave the grate with normal (non-negative) property.
     848             :          *  Reuse sign. */
     849           9 :         put(GRATE, LOC_SW, 0);
     850           9 :         put(SIGN, LOC_SW, 0);
     851           9 :         game.prop[SIGN] = ENDGAME_SIGN;
     852           9 :         game.prop[SNAKE] = put(SNAKE, LOC_SW, SNAKE_CHASED);
     853           9 :         game.prop[BIRD] = put(BIRD, LOC_SW, BIRD_CAGED);
     854           9 :         game.prop[CAGE] = put(CAGE, LOC_SW, STATE_FOUND);
     855           9 :         game.prop[ROD2] = put(ROD2, LOC_SW, STATE_FOUND);
     856           9 :         game.prop[PILLOW] = put(PILLOW, LOC_SW, STATE_FOUND);
     857             : 
     858           9 :         game.prop[MIRROR] = put(MIRROR, LOC_NE, STATE_FOUND);
     859           9 :         game.fixed[MIRROR] = LOC_SW;
     860             : 
     861         630 :         for (int i = 1; i <= NOBJECTS; i++) {
     862         621 :             if (TOTING(i))
     863           9 :                 DESTROY(i);
     864             :         }
     865             : 
     866           9 :         rspeak(CAVE_CLOSED);
     867           9 :         game.closed = true;
     868           9 :         return true;
     869             :     }
     870             : 
     871       17693 :     return false;
     872             : }
     873             : 
     874       17693 : static void lampcheck(void)
     875             : /* Check game limit and lamp timers */
     876             : {
     877       17693 :     if (game.prop[LAMP] == LAMP_BRIGHT)
     878       11644 :         --game.limit;
     879             : 
     880             :     /*  Another way we can force an end to things is by having the
     881             :      *  lamp give out.  When it gets close, we come here to warn him.
     882             :      *  First following arm checks if the lamp and fresh batteries are
     883             :      *  here, in which case we replace the batteries and continue.
     884             :      *  Second is for other cases of lamp dying.  Even after it goes
     885             :      *  out, he can explore outside for a while if desired. */
     886       17693 :     if (game.limit <= WARNTIME) {
     887         896 :         if (HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) {
     888           2 :             rspeak(REPLACE_BATTERIES);
     889           2 :             game.prop[BATTERY] = DEAD_BATTERIES;
     890             : #ifdef __unused__
     891             :             /* This code from the original game seems to have been faulty.
     892             :              * No tests ever passed the guard, and with the guard removed
     893             :              * the game hangs when the lamp limit is reached.
     894             :              */
     895             :             if (TOTING(BATTERY))
     896             :                 drop(BATTERY, game.loc);
     897             : #endif
     898           2 :             game.limit += BATTERYLIFE;
     899           2 :             game.lmwarn = false;
     900         894 :         } else if (!game.lmwarn && HERE(LAMP)) {
     901          19 :             game.lmwarn = true;
     902          19 :             if (game.prop[BATTERY] == DEAD_BATTERIES)
     903           1 :                 rspeak(MISSING_BATTERIES);
     904          18 :             else if (game.place[BATTERY] == LOC_NOWHERE)
     905          17 :                 rspeak(LAMP_DIM);
     906             :             else
     907           1 :                 rspeak(GET_BATTERIES);
     908             :         }
     909             :     }
     910       17693 :     if (game.limit == 0) {
     911          13 :         game.limit = -1;
     912          13 :         game.prop[LAMP] = LAMP_DARK;
     913          13 :         if (HERE(LAMP))
     914          13 :             rspeak(LAMP_OUT);
     915             :     }
     916       17693 : }
     917             : 
     918       13104 : static void listobjects(void)
     919             : /*  Print out descriptions of objects at this location.  If
     920             :  *  not closing and property value is negative, tally off
     921             :  *  another treasure.  Rug is special case; once seen, its
     922             :  *  game.prop is RUG_DRAGON (dragon on it) till dragon is killed.
     923             :  *  Similarly for chain; game.prop is initially CHAINING_BEAR (locked to
     924             :  *  bear).  These hacks are because game.prop=0 is needed to
     925             :  *  get full score. */
     926             : {
     927       13104 :     if (!DARK(game.loc)) {
     928       12802 :         ++game.abbrev[game.loc];
     929       26528 :         for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) {
     930       13726 :             obj_t obj = i;
     931       13726 :             if (obj > NOBJECTS)
     932        1063 :                 obj = obj - NOBJECTS;
     933       13726 :             if (obj == STEPS && TOTING(NUGGET))
     934          35 :                 continue;
     935       13691 :             if (game.prop[obj] < 0) {
     936         836 :                 if (game.closed)
     937         158 :                     continue;
     938         678 :                 game.prop[obj] = STATE_FOUND;
     939         678 :                 if (obj == RUG)
     940          51 :                     game.prop[RUG] = RUG_DRAGON;
     941         678 :                 if (obj == CHAIN)
     942          27 :                     game.prop[CHAIN] = CHAINING_BEAR;
     943         678 :                 --game.tally;
     944             :                 /*  Note: There used to be a test here to see whether the
     945             :                  *  player had blown it so badly that he could never ever see
     946             :                  *  the remaining treasures, and if so the lamp was zapped to
     947             :                  *  35 turns.  But the tests were too simple-minded; things
     948             :                  *  like killing the bird before the snake was gone (can never
     949             :                  *  see jewelry), and doing it "right" was hopeless.  E.G.,
     950             :                  *  could cross troll bridge several times, using up all
     951             :                  *  available treasures, breaking vase, using coins to buy
     952             :                  *  batteries, etc., and eventually never be able to get
     953             :                  *  across again.  If bottle were left on far side, could then
     954             :                  *  never get eggs or trident, and the effects propagate.  So
     955             :                  *  the whole thing was flushed.  anyone who makes such a
     956             :                  *  gross blunder isn't likely to find everything else anyway
     957             :                  *  (so goes the rationalisation). */
     958             :             }
     959       13533 :             int kk = game.prop[obj];
     960       13533 :             if (obj == STEPS)
     961         874 :                 kk = (game.loc == game.fixed[STEPS])
     962             :                      ? STEPS_UP
     963         437 :                      : STEPS_DOWN;
     964       13533 :             pspeak(obj, look, kk, true);
     965             :         }
     966             :     }
     967       13104 : }
     968             : 
     969       13504 : static bool do_command()
     970             : /* Get and execute a command */
     971             : {
     972             :     static command_t command;
     973             : 
     974             :     /*  Can't leave cave once it's closing (except by main office). */
     975       13504 :     if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
     976           1 :         rspeak(EXIT_CLOSED);
     977           1 :         game.newloc = game.loc;
     978           1 :         if (!game.panic)
     979           1 :             game.clock2 = PANICTIME;
     980           1 :         game.panic = true;
     981             :     }
     982             : 
     983             :     /*  See if a dwarf has seen him and has come from where he
     984             :      *  wants to go.  If so, the dwarf's blocking his way.  If
     985             :      *  coming from place forbidden to pirate (dwarves rooted in
     986             :      *  place) let him get out (and attacked). */
     987       13504 :     if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, COND_NOARRR)) {
     988       67440 :         for (size_t i = 1; i <= NDWARVES - 1; i++) {
     989       56202 :             if (game.odloc[i] == game.newloc && game.dseen[i]) {
     990           6 :                 game.newloc = game.loc;
     991           6 :                 rspeak(DWARF_BLOCK);
     992           6 :                 break;
     993             :             }
     994             :         }
     995             :     }
     996       13504 :     game.loc = game.newloc;
     997             : 
     998       13504 :     if (!dwarfmove())
     999           2 :         croak();
    1000             : 
    1001             :     /*  Describe the current location and (maybe) get next command. */
    1002             : 
    1003         361 :     for (;;) {
    1004       13863 :         if (game.loc == 0)
    1005           6 :             croak();
    1006       13859 :         const char* msg = locations[game.loc].description.small;
    1007       13859 :         if (MOD(game.abbrev[game.loc], game.abbnum) == 0 ||
    1008             :             msg == 0)
    1009        6588 :             msg = locations[game.loc].description.big;
    1010       13859 :         if (!FORCED(game.loc) && DARK(game.loc)) {
    1011             :             /*  The easiest way to get killed is to fall into a pit in
    1012             :              *  pitch darkness. */
    1013         316 :             if (game.wzdark && PCT(35)) {
    1014          14 :                 rspeak(PIT_FALL);
    1015          14 :                 game.oldlc2 = game.loc;
    1016          14 :                 croak();
    1017           8 :                 continue;       /* back to top of main interpreter loop */
    1018             :             }
    1019         302 :             msg = arbitrary_messages[PITCH_DARK];
    1020             :         }
    1021       13845 :         if (TOTING(BEAR))
    1022         178 :             rspeak(TAME_BEAR);
    1023       13845 :         speak(msg);
    1024       13845 :         if (FORCED(game.loc)) {
    1025         741 :             playermove(HERE);
    1026         741 :             return true;
    1027             :         }
    1028       13104 :         if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
    1029         266 :             rspeak(SAYS_PLUGH);
    1030             : 
    1031       13104 :         listobjects();
    1032             : 
    1033       17783 : Lclearobj:
    1034       17783 :         game.oldobj = command.obj;
    1035             : 
    1036       17783 :         checkhints();
    1037             : 
    1038             :         /*  If closing time, check for any objects being toted with
    1039             :          *  game.prop < 0 and stash them.  This way objects won't be
    1040             :          *  described until they've been picked up and put down
    1041             :          *  separate from their respective piles. */
    1042       17782 :         if (game.closed) {
    1043          59 :             if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
    1044           2 :                 pspeak(OYSTER, look, 1, true);
    1045        4130 :             for (size_t i = 1; i <= NOBJECTS; i++) {
    1046        4071 :                 if (TOTING(i) && game.prop[i] < 0)
    1047           9 :                     game.prop[i] = STASHED(i);
    1048             :             }
    1049             :         }
    1050       17782 :         game.wzdark = DARK(game.loc);
    1051       17782 :         if (game.knfloc > 0 && game.knfloc != game.loc)
    1052         128 :             game.knfloc = 0;
    1053             : 
    1054             :         /* Preserve state from last command for reuse when required */
    1055       17782 :         command_t preserve = command;
    1056             : 
    1057             :         // Get command input from user
    1058       17782 :         if (!get_command_input(&command))
    1059          67 :             return false;
    1060             : 
    1061             : #ifdef GDEBUG
    1062             :         /* Needs to stay synced with enum word_type_t */
    1063             :         const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION", "NUMERIC"};
    1064             :         /* needs to stay synced with enum speechpart */
    1065             :         const char *roles[] = {"unknown", "intransitive", "transitive"};
    1066             :         printf("Preserve: role = %s type1 = %s, id1 = %ld, type2 = %s, id2 = %ld\n",
    1067             :                roles[preserve.part],
    1068             :                types[preserve.word[0].type],
    1069             :                preserve.word[0].id,
    1070             :                types[preserve.word[1].type],
    1071             :                preserve.word[1].id);
    1072             :         printf("Command: role = %s type1 = %s, id1 = %ld, type2 = %s, id2 = %ld\n",
    1073             :                roles[command.part],
    1074             :                types[command.word[0].type],
    1075             :                command.word[0].id,
    1076             :                types[command.word[1].type],
    1077             :                command.word[1].id);
    1078             : #endif
    1079             : 
    1080             :         /* Handle of objectless action followed by actionless object */
    1081       17715 :         if (preserve.word[0].type == ACTION && preserve.word[1].type == NO_WORD_TYPE && command.word[1].id == 0)
    1082         826 :             command.verb = preserve.verb;
    1083             : 
    1084             : #ifdef BROKEN
    1085             :         /* Handling of actionless object followed by objectless action */
    1086             :         if (preserve.type1 == OBJECT && preserve.type2 == NO_WORD_TYPE && command.id2 == 0)
    1087             :             command.obj = preserve.obj;
    1088             : #endif
    1089             : 
    1090       17715 :         ++game.turns;
    1091             : 
    1092       17715 :         if (closecheck()) {
    1093          22 :             if (game.closed)
    1094           9 :                 return true;
    1095             :         } else
    1096       17693 :             lampcheck();
    1097             : 
    1098       17706 :         if (command.word[0].type == MOTION && command.word[0].id == ENTER
    1099           7 :             && (command.word[1].id == STREAM || command.word[1].id == WATER)) {
    1100           2 :             if (LIQLOC(game.loc) == WATER)
    1101           1 :                 rspeak(FEET_WET);
    1102             :             else
    1103           1 :                 rspeak(WHERE_QUERY);
    1104             : 
    1105           2 :             goto Lclearobj;
    1106             :         }
    1107             : 
    1108       17704 :         if (command.word[0].type == OBJECT) {
    1109         134 :             if (command.word[0].id == GRATE) {
    1110           3 :                 command.word[0].type = MOTION;
    1111           6 :                 if (game.loc == LOC_START ||
    1112           5 :                     game.loc == LOC_VALLEY ||
    1113           2 :                     game.loc == LOC_SLIT) {
    1114           1 :                     command.word[0].id = DEPRESSION;
    1115             :                 }
    1116           6 :                 if (game.loc == LOC_COBBLE ||
    1117           6 :                     game.loc == LOC_DEBRIS ||
    1118           6 :                     game.loc == LOC_AWKWARD ||
    1119           5 :                     game.loc == LOC_BIRD ||
    1120           2 :                     game.loc == LOC_PITTOP) {
    1121           1 :                     command.word[0].id = ENTRANCE;
    1122             :                 }
    1123             :             }
    1124         134 :             if ((command.word[0].id == WATER || command.word[0].id == OIL) && (command.word[1].id == PLANT || command.word[1].id == DOOR)) {
    1125         122 :                 if (AT(command.word[1].id)) {
    1126         121 :                     command.word[1] = command.word[0];
    1127         121 :                     command.word[0].id = POUR;
    1128         121 :                     command.word[0].type = ACTION;
    1129         121 :                     strncpy(command.word[0].raw, "pour", LINESIZE - 1);
    1130             :                 }
    1131             :             }
    1132         134 :             if (command.word[0].id == CAGE && command.word[1].id == BIRD && HERE(CAGE) && HERE(BIRD)) {
    1133           6 :                 command.word[0].id = CARRY;
    1134           6 :                 command.word[0].type = ACTION;
    1135             :             }
    1136             : 
    1137             :             /* From OV to VO form */
    1138         138 :             if (command.word[0].type == OBJECT && command.word[1].type == ACTION) {
    1139           1 :                 command_word_t stage = command.word[0];
    1140           1 :                 command.word[0] = command.word[1];
    1141           1 :                 command.word[1] = stage;
    1142             :             }
    1143             :         }
    1144             : 
    1145       35407 : Lookup:
    1146       21967 :         if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) {
    1147         107 :             if (++game.iwest == 10)
    1148           2 :                 rspeak(W_IS_WEST);
    1149             :         }
    1150       21967 :         if (strncasecmp(command.word[0].raw, "go", sizeof("go")) == 0 && command.word[1].id != WORD_EMPTY) {
    1151          16 :             if (++game.igo == 10)
    1152           1 :                 rspeak(GO_UNNEEDED);
    1153             :         }
    1154       21967 :         if (command.word[0].id == WORD_NOT_FOUND) {
    1155             :             /* Gee, I don't understand. */
    1156          14 :             sspeak(DONT_KNOW, command.word[0].raw);
    1157          14 :             goto Lclearobj;
    1158             :         }
    1159       21953 :         switch (command.word[0].type) {
    1160       12355 :         case NO_WORD_TYPE: // FIXME: treating NO_WORD_TYPE as a motion word is confusing
    1161             :         case MOTION:
    1162       12355 :             playermove(command.word[0].id);
    1163       12355 :             return true;
    1164        4219 :         case OBJECT:
    1165        4219 :             command.part = unknown;
    1166        4219 :             command.obj = command.word[0].id;
    1167        4219 :             break;
    1168        5379 :         case ACTION:
    1169        5379 :             if (command.word[1].type == NUMERIC)
    1170          86 :                 command.part = transitive;
    1171             :             else
    1172        5293 :                 command.part = intransitive;
    1173        5379 :             command.verb = command.word[0].id;
    1174        5379 :             break;
    1175             :         case NUMERIC: // LCOV_EXCL_LINE
    1176             :         default: // LCOV_EXCL_LINE
    1177             :             BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
    1178             :         }
    1179        9598 :         switch (action(command)) {
    1180          42 :         case GO_TERMINATE:
    1181          42 :             return true;
    1182         263 :         case GO_MOVE:
    1183         263 :             playermove(NUL);
    1184         263 :             return true;
    1185         353 :         case GO_TOP:
    1186         353 :             continue;   /* back to top of main interpreter loop */
    1187        4263 :         case GO_WORD2:
    1188             : #ifdef GDEBUG
    1189             :             printf("Word shift\n");
    1190             : #endif /* GDEBUG */
    1191             :             /* Get second word for analysis. */
    1192        4263 :             command.word[0] = command.word[1];
    1193        4263 :             command.word[1] = empty_command_word;
    1194        4263 :             goto Lookup;
    1195          25 :         case GO_UNKNOWN:
    1196             :             /*  Random intransitive verbs come here.  Clear obj just in case
    1197             :              *  (see attack()). */
    1198          25 :             command.word[0].raw[0] = toupper(command.word[0].raw[0]);
    1199          25 :             sspeak(DO_WHAT, command.word[0].raw);
    1200          25 :             command.obj = 0;
    1201             :         // Fallthrough
    1202        4663 :         case GO_CLEAROBJ:
    1203        4663 :             goto Lclearobj;
    1204           5 :         case GO_DWARFWAKE:
    1205             :             /*  Oh dear, he's disturbed the dwarves. */
    1206           5 :             rspeak(DWARVES_AWAKEN);
    1207           5 :             terminate(endgame);
    1208             :         default: // LCOV_EXCL_LINE
    1209             :             BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
    1210             :         }
    1211             :     }
    1212             : }
    1213             : 
    1214             : /* end */

Generated by: LCOV version 1.13