// HINTS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "adv_func.h"
#include "adv_io.h"
#include "adv_magic.h"
#include "adv_readdb.h"
#include "adv_setup.h"
#include "adv_struc.h"
#include "adv_util.h"
#include "adven2.h"
#include "adv_misc.h"

// CHECK IF THIS LOC IS ELIGIBLE FOR ANY HINTS.  IF BEEN HERE LONG ENOUGH,
// BRANCH TO HELP SECTION (ON LATER PAGE).  HINTS ALL COME BACK HERE EVENTUALLY
// TO FINISH THE LOOP.  IGNORE "HINTS" < 4 (SPECIAL STUFF, SEE DATABASE NOTES).

// COME HERE IF HE"S BEEN LONG ENOUGH AT REQUIRED LOC(S) FOR SOME UNUSED HINT.
// HINT NUMBER IS IN VARIABLE "HINT".  BRANCH TO QUICK TEST FOR ADDITIONAL
// CONDITIONS, THEN COME BACK TO DO NEAT STUFF.  GOTO 40010 IF CONDITIONS ARE
// MET AND WE WANT TO OFFER THE HINT.  GOTO 40020 TO CLEAR HINTLC BACK TO ZERO,
// 40030 TO TAKE NO ACTION YET.
void hint_check(void)
{
  int hint;

  for (hint=4;hint<=gm.hntmax;hint++)
  {
    if (!gm.hinted[hint])
    {
      if ( ! bitset(gm.loc,hint) )
        gm.hintlc[hint] = -1;
      gm.hintlc[hint] = gm.hintlc[hint]+1;
      if (gm.hintlc[hint] >= gm.hints[hint][1])
      {
        // NOW FOR THE QUICK TESTS.  SEE DATABASE DESCRIPTION FOR ONE-LINE NOTES.
        switch (hint-3)
        {
          case 1:    // 40400 gm.cave
            if (gm.prop[gm.grate] != 0 || here(gm.keys))  // hint is pointless
            {
              gm.hintlc[hint] = 0;
              return;
            } 
            break;
          case 2:    // 40500 gm.bird
            if (!here(gm.bird) || !toting(gm.rod) || gm.obj != gm.bird)  // pointless
              return;
            break;
          case 3:    // 40600 gm.snake
            if (!here(gm.snake) || here(gm.bird))     // pointless
            {
              gm.hintlc[hint] = 0;
              return;
            } 
            break;
          case 4:    // 40700 maze
            if (gm.atloc[gm.loc] != 0 || gm.atloc[gm.oldloc] != 0 || 
                gm.atloc[gm.oldlc2] != 0 || gm.holdng <= 1)        // pointless
            {
              gm.hintlc[hint] = 0;
              return;
            } 
            break;
          case 5:    // 40800 dark
            if (gm.prop[gm.emrald] == -1 || gm.prop[gm.pyram] != -1)
            {
              gm.hintlc[hint] = 0;
              return;
            } 
            break;
          case 6:    // 40900 witt
            break;
          default:
            bug(27);
        } 
        gm.hintlc[hint] = 0;
        if ( ! yes(gm.hints[hint][3],0,54)) return;
        printf(" I AM PREPARED TO GIVE YOU A HINT, BUT IT WILL COST YOU %i POINTS.\n",
               gm.hints[hint][2]);
        gm.hinted[hint] = yes(175,gm.hints[hint][4],54);
        if (gm.hinted[hint] && gm.limit > 30) gm.limit = gm.limit+30*gm.hints[hint][2];
        gm.hintlc[hint] = 0;
      }
    }
  }
}  // end hint_check ---------------------------------------------------------

// HANDLE "GO BACK".  LOOK FOR VERB WHICH GOES FROM LOC TO OLDLOC, 
//   OR TO OLDLC2 IF OLDLOC HAS FORCED-MOTION.  K2 SAVES 
//   ENTRY -> FORCED LOC -> PREVIOUS LOC.
int go_back(void)
{
  int kk, dest;

  dest = gm.oldloc;                    // going to his previous loc
  if (forced(dest)) dest = gm.oldlc2;  //   ... unless "forced", then go two gm.back
  // but wipe out "memory" of previous locations so he can't gm.back up again
  gm.oldlc2 = gm.oldloc;
  gm.oldloc = gm.loc;
  if ( dest == gm.loc ||           // if trail leads back to where we are ... 
       dest == 0 ||                //   or it leads to nowhere ... 
       forced(dest) )              //   or gm.back to somewhere also "forced", we've HAD IT!
  {
    rspeak(91);                    // "can't remember" gm.message
    return (gm.loc);              //   and leave him standing there
  }
  for (kk=gm.key[gm.loc];kk<gm.trvs;kk++)
  {
    if (gm.travel[kk]%1000 == dest) break;  // check "real" gm.loc w/o modifier
    else if (gm.travlast[kk]) {kk = 0; break;}  // indicate failure
  }
  if (kk == 0)
    {rspeak(140); return (gm.loc);}  // "can't get there from here" gm.message
  // else we found a gm.travel table entry that does what we want
  return (dest);
/*
  k2 = 0;
  while (TRUE)
  {
    kkkk = gm.travel[*kk]%1000;   // real location, modifier removed
    if (dest  ==  kkkkk)
    {
      k = gm.travel[*kk]%1000;
      *kk = gm.key[gm.loc];           // this code set the gm.travel table pointer
      return (9);               //   and proceeded on that basis
    }

    if (kkkk <= 300)
    {
      if (forced(kkkk) && (gm.travel[gm.key[kkkk]]%1000) == dest) k2 = *kk;
    }
    if (gm.travel[*kk] < 0)
    {
      *kk = k2;
      if (*kk != 0)
      {
        k = gm.travel[*kk]%1000;
        *kk = gm.key[gm.loc];
        return (9);
      }
      rspeak(140);
      return (2);
    }
    *kk++;
  } // end while (TRUE)
*/
} // end go_gm.back --------------------------------------------------------------

// CARRY AN OBJECT.  SPECIAL CASES FOR BIRD AND CAGE (IF BIRD IN 
//   CAGE, CAN"T TAKE ONE WITHOUT THE OTHER.  LIQUIDS ALSO SPECIAL, 
//   SINCE THEY DEPEND ON STATUS OF BOTTLE.  ALSO VARIOUS SIDE 
//   EFFECTS, ETC.
void perform_take(int obj)
{
  int k, spk;

  // debugging
  // printf("At location %i, pick up obj %i at loc %i, fixed = %i\n",
  //        gm.loc, obj, gm.place[obj], gm.fixed[obj]);   

  spk = 25;   // "you can't be serious!"
  if (obj == gm.plant && gm.prop[gm.plant] <=  0) spk = 115;  // "... deep roots ..."
  if (obj == gm.bear && gm.prop[gm.bear] == 1) spk = 169;     // "... bear still chained ..."
  if (obj == gm.chain && gm.prop[gm.bear] != 0) spk = 170;    // "... chain still locked ..."
  if (gm.fixed[obj] != 0)
    rspeak(spk);
  else // gm.fixed[obj] == 0
  {
    if (obj == gm.water || obj == gm.oil)
    {
      if (!here(gm.bottle) || liq() != obj)
      {
        obj = gm.bottle;
        if (toting(gm.bottle) && gm.prop[gm.bottle] == 1)
        {
          // FILL.  BOTTLE MUST BE EMPTY, AND SOME LIQUID AVAILABLE.
          spk = 107;
          if (liqloc(gm.loc) == 0) spk = 106;
          if (liq() != 0) spk = 105;
          if (spk == 107)
          {
            gm.prop[gm.bottle] = (gm.cond[gm.loc]%4)/2*2;
            k = liq();
            if (toting(gm.bottle)) gm.place[k] = -1;
            if (k == gm.oil) spk = 108;
          }
          rspeak(spk); 
          return;
        }
        if (gm.prop[gm.bottle] != 1) spk = 105;
        if (!toting(gm.bottle)) spk = 104;
        rspeak(spk);  
        return;
      }
      obj = gm.bottle;
    }  // end gm.water or gm.oil
    if (gm.holdng >= 7)
      rspeak(92);       // "you can't carry anything more ..."
    else
    {
      if (obj == gm.bird && gm.prop[gm.bird] == 0)
      {
        if (toting(gm.rod))
          {rspeak(26); return;}
        if (!toting(gm.cage))
          {rspeak(27); return;}
        gm.prop[gm.bird] = 1;
      }  // end if (obj == gm.bird && gm.prop[gm.bird] == 0)
      if ((obj == gm.bird || obj == gm.cage) && gm.prop[gm.bird] != 0)
        carry(gm.bird+gm.cage-obj,gm.loc);
      // by this point, we have rung in all the oddball cases, and really want 
      //   to pick up the designated "obj"
      // but let's just make SURE it's here - "carry" can mess up if it isn't
      if (gm.place[obj] != gm.loc) 
        printf("I DON'T SEE ANY %s%s HERE!\n",gm.wd2,gm.wd2x);
      else carry(obj,gm.loc);    // finally, pick it up
      if (obj == gm.bottle && 0 != (k = liq())) gm.place[k] = -1; // get bottle => get contents
      rspeak(54);     // "OK"
    }
  }
}  // end perform_take --------------------------------------------
 
// DISCARD OBJECT.  "THROW" ALSO COMES HERE FOR MOST OBJECTS.  SPECIAL CASES FOR
// BIRD (MIGHT ATTACK SNAKE OR DRAGON) AND CAGE (MIGHT CONTAIN BIRD) AND VASE.
// DROP COINS AT VENDING MACHINE FOR EXTRA BATTERIES.
void perform_drop(int obj)
{    
  int k;

  if (toting(gm.rod2) && obj == gm.rod && !toting(gm.rod)) obj = gm.rod2;
  if (!toting(obj))
    {rspeak(gm.spk);  return;}
  if (obj == gm.bird && here(gm.snake))
  {
    rspeak(30);
    if (gm.closed)
    {
      // OH DEAR, HE'S DISTURBED THE DWARVES.
      rspeak(136);
      calculate_score(FALSE); 
      exit(0);
    } 
    dstroy(gm.snake);
  // SET PROP FOR USE BY TRAVEL OPTIONS
    gm.prop[gm.snake] = 1;
    k = liq();
    if (k == obj)obj = gm.bottle;
    if (obj == gm.bottle && k != 0)gm.place[k] = 0;
    if (obj == gm.cage && gm.prop[gm.bird] != 0)drop(gm.bird,gm.loc);
    if (obj == gm.bird)gm.prop[gm.bird] = 0;
    drop(obj,gm.loc);
    return;
  }

  if (obj == gm.coins && here(gm.vend)) 
  {
    dstroy(gm.coins);
    drop(gm.batter,gm.loc);
    pspeak(gm.batter,0);
    return;
  } 

  if (obj == gm.bird && at(gm.dragon) && gm.prop[gm.dragon] == 0)
  {
    rspeak(154);
    dstroy(gm.bird);
    gm.prop[gm.bird] = 0;
    if (gm.place[gm.snake] == gm.plac[gm.snake]) gm.tally2++;
    return;
  }

  if (obj == gm.bear && at(gm.troll))
  {
    rspeak(163);
    move(gm.troll,0);
    move(gm.troll+100,0);
    move(gm.troll2,gm.plac[gm.troll]);
    move(gm.troll2+100,gm.fixd[gm.troll]);
    juggle(gm.chasm);
    gm.prop[gm.troll] = 2;
    drop(obj,gm.loc);
    return;
  }

  if (obj == gm.vase && gm.loc != gm.plac[gm.pillow])
  {
    gm.prop[gm.vase] = 2;
    if (at(gm.pillow)) gm.prop[gm.vase] = 0;
    pspeak(gm.vase,gm.prop[gm.vase]+1);
    if (gm.prop[gm.vase] != 0) gm.fixed[gm.vase] = -1;
  } 
  else 
    rspeak(54);   // "OK"
  k = liq();
  if (k == obj)obj = gm.bottle;
  if (obj == gm.bottle && k != 0)gm.place[k] = 0;
  if (obj == gm.cage && gm.prop[gm.bird] != 0)drop(gm.bird,gm.loc);
  if (obj == gm.bird)gm.prop[gm.bird] = 0;
  drop(obj,gm.loc);
  return;
}  // end perform_drop ------------------------------------------------

// LOCK, UNLOCK OBJECT.  SPECIAL STUFF FOR OPENING CLAM/OYSTER AND FOR CHAIN.
void perform_lock(int obj)
{
  int k, spk;

  if (obj == gm.clam || obj == gm.oyster) // CLAM/OYSTER.
  {
    k = (obj == gm.oyster);
    spk = 124+k;
    if (toting(obj)) spk = 120+k;
    if (!toting(gm.tridnt)) spk = 122+k;
    if (gm.verb == gm.lock) spk = 61;
    if (spk == 124)
    {
      dstroy(gm.clam);
      drop(gm.oyster,gm.loc);
      drop(gm.pearl,105);
    }
    rspeak(spk);
    return;
  }
  spk = 33;
  if (obj == gm.door) spk = 111;
  if (obj == gm.door && gm.prop[gm.door] == 1) spk = 54;
  if (obj == gm.cage) spk = 32;
  if (obj == gm.keys) spk = 55;
  if (obj == gm.grate || obj == gm.chain) spk = 31;
  if (spk != 31 || !here(gm.keys))
    {rspeak(spk);  return;}
  // OK, must be either the gm.grate or the gm.chain
  if (obj == gm.chain) // CHAIN.
  {
    if (gm.verb == gm.lock)
    {
      spk = 172;
      if (gm.prop[gm.chain] != 0) spk = 34;
      if (gm.loc != gm.plac[gm.chain]) spk = 173;
      if (spk == 172) 
      {
        gm.prop[gm.chain] = 2;
        if (toting(gm.chain)) drop(gm.chain,gm.loc);
        gm.fixed[gm.chain] = -1;
      }
    }
    else
    {
      spk = 171;
      if (gm.prop[gm.bear] == 0) spk = 41;
      if (gm.prop[gm.chain] == 0) spk = 37;
      if (spk == 171)
      {
        gm.prop[gm.chain] = 0;
        gm.fixed[gm.chain] = 0;
        if (gm.prop[gm.bear] != 3) gm.prop[gm.bear] = 2;
        gm.fixed[gm.bear] = 2-gm.prop[gm.bear];
      }
    }
    rspeak(spk);
    return;
  }   
  if (gm.closng)
  {
    k = 130;
    if (!gm.panic)gm.clock2 = 15;
    gm.panic =  TRUE;
  }
  else
  {
    k = 34+gm.prop[gm.grate];
    if (gm.verb == gm.lock) gm.prop[gm.grate] = 0;
    else gm.prop[gm.grate] = 1;
    k = k+2*gm.prop[gm.grate];
  }
  spk = k; 
  rspeak(spk); 
}  // end perform_lock ---------------------------------------------------

// describe the current location and (maybe) get next command.;
// PRINT TEXT FOR CURRENT LOC.
int describe_loc(void)
{
  int i, k, kk;
  char *txtp;

  if (gm.loc == 0 || gm.is_dead) return (99);  // maybe redundant, but ...

  if (gm.abb[gm.loc]%gm.abbnum == 0 || gm.stext[gm.loc] == NULL)
    txtp = gm.ltext[gm.loc];
  else txtp = gm.stext[gm.loc];
  if (!forced(gm.loc) && dark())
  {
    if (gm.wzdark && pct(35))
      {rspeak(23); gm.oldlc2 = gm.loc; return (99);} 
    txtp = gm.rtext[16];
  }
  if (txtp != NULL)
    speak(txtp);
  else printf("SORRY, THIS LOCATION IS UNDESCRIBABLY BEAUTIFUL!\n");

  // BEFORE we print out objects, etc., see if he is going to stay here
  //   if forced, move to next location, describe THAT and all the stuff there ...
  //   on return, DO NOT print out the stuff again!
  if (forced(gm.loc)) 
  {
    gm.verb_base = gm.verb = 1;               // phoney verb for forced motion
    gm.verb_class = 1;                        // but call it a motion verb
    compute_new_loc();         // follow the forced move link
    gm.loc = gm.newloc;
    if (99 == describe_loc())  // if recursive call says he's dead
      return (99);             //   tell our caller
    else return (0);           // the recursive call already did the "stuff"
  }

  // OK, he's here and not leaving instantly. describe stuff here
  if (toting(gm.bear)) rspeak(141);
  k = 1;
  if (gm.loc == 33 && pct(25) && !gm.closng) rspeak(8);

  // PRINT OUT DESCRIPTIONS OF OBJECTS AT THIS LOCATION.  IF NOT CLOSING AND
  // PROPERTY VALUE IS NEGATIVE, TALLY OFF ANOTHER TREASURE.  RUG IS SPECIAL
  // CASE; ONCE SEEN, ITS PROP IS 1 (DRAGON ON IT) TILL DRAGON IS KILLED.
  // SIMILARLY FOR CHAIN; PROP IS INITIALLY 1 (LOCKED TO BEAR).  THESE HACKS
  // ARE BECAUSE PROP = 0 IS NEEDED TO GET FULL SCORE.
  if (!dark())    // but only if not dark and we can see
  {
    gm.abb[gm.loc] = gm.abb[gm.loc]+1;
    i = gm.atloc[gm.loc];
    while (i != 0) 
    {      
      gm.obj = i;
      if (gm.obj > 100) gm.obj -= 100;
      if (gm.obj != gm.steps || !toting(gm.nugget))  // skip stairs/nugget case
      {
        if (gm.prop[gm.obj] >=  0)
        {
          kk = gm.prop[gm.obj];
          if (gm.obj == gm.steps && gm.loc == gm.fixed[gm.steps]) kk = 1;
          pspeak(gm.obj,kk);
        }
        else if (!gm.closed)
        {
          gm.prop[gm.obj] = 0;
          if (gm.obj == gm.rug || gm.obj == gm.chain)gm.prop[gm.obj] = 1;
          gm.tally--;
          // IF REMAINING TREASURES TOO ELUSIVE, ZAP HIS LAMP.
          if (gm.tally == gm.tally2 && gm.tally != 0) 
            gm.limit = (35<gm.limit)?35:gm.limit;
          kk = gm.prop[gm.obj];
          if (gm.obj == gm.steps && gm.loc == gm.fixed[gm.steps]) kk = 1;
          pspeak(gm.obj,kk);
        }
      }
      i = gm.link[i];
    }
  }
  return (0);
}  // end describe_loc ----------------------------------------------

// ATTACK.  ASSUME TARGET IF UNAMBIGUOUS.  "THROW" ALSO LINKS HERE.  ATTACKABLE
// OBJECTS FALL INTO TWO CATEGORIES: ENEMIES (SNAKE, DWARF, ETC.)  AND OTHERS
// (BIRD, CLAM).  AMBIGUOUS IF TWO ENEMIES, OR IF NO ENEMIES BUT TWO OTHERS.
void perform_attack(void)
{
  int i, ii, k;
  
  for (ii=1,i=0;ii<=5;ii++)
    if (gm.dloc[ii] == gm.loc && gm.dflag >=  2)
      {i = ii; break;}
  if (gm.obj == 0)
  {
    if (i != 0) gm.obj = gm.dwarf;
    if (here(gm.snake)) gm.obj = gm.obj*100+gm.snake;
    if (at(gm.dragon) && gm.prop[gm.dragon] == 0) gm.obj = gm.obj*100+gm.dragon;
    if (at(gm.troll)) gm.obj = gm.obj*100+gm.troll;
    if (here(gm.bear) && gm.prop[gm.bear] == 0) gm.obj = gm.obj*100+gm.bear;
    if (gm.obj > 100)   // we found 2 (or more) potential attackees
      {printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); gm.obj = 0; return;}
    if (gm.obj == 0)
    {
      // CAN"T ATTACK BIRD BY THROWING AXE.
      // note we distinguish between "attack bird" and "throw bird (at snake or ...)"
      if (here(gm.bird) && gm.verb != gm.throw) gm.obj = gm.bird;
      // CLAM AND OYSTER BOTH TREATED AS CLAM FOR INTRANSITIVE CASE; NO HARM DONE.
      if (here(gm.clam) || here(gm.oyster)) gm.obj = 100*gm.obj+gm.clam;
      if (gm.obj > 100)    // again, 2 or more potential targets
        {printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); gm.obj = 0; return;}
    }
  }

  // if we get to here, either he specified a target, or we found exactly one available
  if (gm.obj == gm.bird)
  {
    gm.spk = 137;
    if (gm.closed)
      {rspeak(gm.spk); return;}
    dstroy(gm.bird);
    gm.prop[gm.bird] = 0;
    if (gm.place[gm.snake] == gm.plac[gm.snake])gm.tally2 = gm.tally2+1;
    gm.spk = 45;
  }
  if (gm.obj == 0) gm.spk = 44;  // "there is nothing here to attack"
  if (gm.obj == gm.clam || gm.obj == gm.oyster) gm.spk = 150;  // "shell ... impervious ..."
  if (gm.obj == gm.snake) gm.spk = 46;  // "doesn't work ... very dangerous" 
  if (gm.obj == gm.dwarf)
  {
    if (gm.closed)
    {
      rspeak(136);       // "OH DEAR, HE'S DISTURBED THE DWARVES."
      calculate_score(FALSE); 
      exit(0);
    } 
    else gm.spk = 49;  // "with what, your bare hands?"
  }
  if (gm.obj == gm.dragon) gm.spk = 167;  // "... already dead"
  if (gm.obj == gm.troll) gm.spk = 157;   // "... as tough as rhinocerous ..."
  if (gm.obj == gm.bear) gm.spk = 165+(gm.prop[gm.bear]+1)/2;
  if (gm.obj != gm.dragon || gm.prop[gm.dragon] != 0)
    {rspeak(gm.spk); return;}
  
  // FUN STUFF FOR DRAGON.  IF HE INSISTS ON ATTACKING IT, WIN!  SET PROP TO DEAD,
  // MOVE DRAGON TO CENTRAL LOC (STILL FIXED), MOVE RUG THERE (NOT FIXED), AND
  // MOVE HIM THERE, TOO.  THEN DO A NULL MOTION TO GET NEW DESCRIPTION.
  rspeak(49);  // "with what, your bare hands?"
  gm.verb = 0;
  gm.obj = 0;
  getin(gm.wd1,gm.wd1x,gm.wd2,gm.wd2x);
  if (!strequ(gm.wd1,"Y") && !strequ(gm.wd1,"YES"))
  {
    gm.skip_user_input = TRUE;
    return;
  }
  pspeak(gm.dragon,1);
  gm.prop[gm.dragon] = 2;
  gm.prop[gm.rug] = 0;
  k = (gm.plac[gm.dragon]+gm.fixd[gm.dragon])/2;
  move(gm.dragon+100,-1);
  move(gm.rug+100,0);
  move(gm.dragon,k);
  move(gm.rug,k);
  for (gm.obj=1;gm.obj<=100;gm.obj++)
    if (gm.place[gm.obj] == gm.plac[gm.dragon] || gm.place[gm.obj] == gm.fixd[gm.dragon]) 
      move(gm.obj,k);
  gm.loc = k;
  // k = 0;
  // compute_new_loc();
  return;
}  // end perform_attack ---------------------------------------------------

// SEE IF A DWARF HAS SEEN HIM AND HAS COME FROM WHERE HE WANTS TO GO.  IF SO,
// THE DWARF"S BLOCKING HIS WAY.  IF COMING FROM PLACE FORBIDDEN TO PIRATE
// (DWARVES ROOTED IN PLACE) LET HIM GET OUT (AND ATTACKED).
void dwarf_stuff(void)
{
  int i, j, jx, jy, k, kk;
  int dtotal, attack, stick;

  if (gm.newloc != gm.loc && !forced(gm.loc) && !bitset(gm.loc,3))
  {
    for (i = 1;i<=5;i++)
      if (gm.odloc[i] == gm.newloc &&  gm.dseen[i])
      {
        gm.newloc = gm.loc;  // dwarf blocking, don't allow move
        rspeak(2);
        break;               // exit loop after first qualifying dwarf
      }
  }
  gm.loc = gm.newloc;        // move ... or not if dwarf blocks

// ***************************************************************************
//   this block of code is incredibly spaghetti'd
//   needs VERY CAREFUL untangling
//     (for instance, branching out and back into DO loops)
// ****************************************************************
  // DWARF STUFF.  SEE EARLIER COMMENTS FOR DESCRIPTION OF 
  //   VARIABLES.  REMEMBER SIXTH DWARF IS PIRATE AND IS THUS 
  //   VERY DIFFERENT EXCEPT FOR MOTION RULES.
  // FIRST OFF, DON"T LET THE DWARVES FOLLOW HIM INTO A PIT 
  //   OR A WALL.  ACTIVATE THE WHOLE MESS THE FIRST TIME HE 
  //   GETS AS FAR AS THE HALL OF MISTS (LOC 15).
  // IF NEWLOC IS FORBIDDEN TO PIRATE (IN PARTICULAR, IF IT"S 
  //   BEYOND THE TROLL BRIDGE), BYPASS DWARF STUFF.  THAT WAY 
  //   PIRATE CAN"T STEAL RETURN TOLL, AND DWARVES CAN"T MEET 
  //   THE BEAR.  ALSO MEANS DWARVES WON"T FOLLOW HIM INTO DEAD
  //   END IN MAZE, BUT C"EST LA VIE.  THEY"LL WAIT FOR HIM 
  //   OUTSIDE THE DEAD END.
  if (gm.loc == 0 || forced(gm.loc) || bitset(gm.newloc,3)) return;   // normal path
  if (gm.dflag == 0) 
  {
    if (gm.loc >= 15) gm.dflag = 1;
    return;                             // normal path
  }

// WHEN WE ENCOUNTER THE FIRST DWARF, WE KILL 0, 1, OR 2 OF THE 5 DWARVES.  IF
// ANY OF THE SURVIVORS IS AT LOC, REPLACE HIM WITH THE ALTERNATE.

      if (gm.dflag == 1)
      {
        if (gm.loc >= 15 && !pct(95))
        {
          gm.dflag=2;
          for (i=1;i<=2;i++)
          {
            j=1+ran(5);
            // IF SAVED NOT = -1, HE BYPASSED THE "START" CALL.
            if(pct(50) && gm.saved == -1) gm.dloc[j]=0;
          } 
          for (i=1;i<=5;i++)
          { 
            if (gm.dloc[i] == gm.loc) gm.dloc[i]=gm.daltlc;
            gm.odloc[i] = gm.dloc[i];
          }
          rspeak(3);
          drop(gm.axe,gm.loc);
        }
        return;                             // normal path
      }

      // THINGS ARE IN FULL SWING.  MOVE EACH DWARF AT RANDOM, EXCEPT IF 
      //   HE"S SEEN US HE STICKS WITH US.  DWARVES NEVER GO TO LOCS <15.
      //   IF WANDERING AT RANDOM, THEY DON"T BACK UP UNLESS THERE"S NO 
      //   ALTERNATIVE.  IF THEY DON"T HAVE TO MOVE, THEY ATTACK.  AND, 
      //   OF COURSE, DEAD DWARVES DON"T DO MUCH OF ANYTHING.

      dtotal=0;
      attack=0;
      stick=0;
      for (i=1;i<=6;i++)    // gm.dwarf loop
        if (gm.dloc[i] != 0)   // only look at live dwarves (not at gm.loc 0)
        {
          j = 1;
          kk = gm.key[gm.dloc[i]];
          if(kk != 0)
            do {
              gm.newloc=gm.travel[kk]%1000;
              if ( gm.newloc <= 300 && gm.newloc >= 15 && gm.newloc != gm.odloc[i] && 
                   (j <= 1 || gm.newloc != gm.tk[j-1]) && j < 20 && 
                   gm.newloc != gm.dloc[i] && !forced(gm.newloc) && 
                   (i != 6 || !bitset(gm.newloc,3)) && 
                   gm.travel[kk]/1000 != 100) 
              {
                gm.tk[j] = gm.newloc;
                j = j+1;
              }
              kk = kk+1;
            } while (gm.travel[kk-1] >= 0);
          gm.tk[j] = gm.odloc[i];
          if (j >= 2) j=j-1;
          j = 1+ran(j);
          gm.odloc[i] = gm.dloc[i];
          gm.dloc[i] = gm.tk[j];
          gm.dseen[i] = (gm.dseen[i] && gm.loc >= 15) || (gm.dloc[i] == gm.loc || gm.odloc[i] == gm.loc);
          if(gm.dseen[i])   // **IF** the gm.dwarf is seen, handle it
          {
            gm.dloc[i]=gm.loc;
            if (i != 6)    // handle dwarves 1 thru 5, not the pirate
            {
              // THIS THREATENING LITTLE DWARF IS IN THE ROOM WITH HIM!
              dtotal = dtotal+1;
              if (gm.odloc[i] == gm.dloc[i])
              { 
                attack = attack+1;
                if (gm.knfloc >= 0) gm.knfloc=gm.loc;
                if (ran(1000) < 95*(gm.dflag-2)) stick = stick+1;
              }
            }  // end  if (i != 6)    // handle dwarves 1 thru 5, not the pirate
            // else i == 6, the pirate   
            // THE PIRATE"S SPOTTED HIM.  HE LEAVES HIM ALONE ONCE WE"VE FOUND 
            //   CHEST. K COUNTS IF A TREASURE IS HERE.  IF NOT, AND 
            //   TALLY=TALLY2 PLUS ONE FOR AN UNSEEN CHEST, LET THE PIRATE 
            //   BE SPOTTED.
            else if(gm.loc != gm.chloc && gm.prop[gm.chest] < 0)
            {
              k=0;
              for (jx=50;jx<=gm.maxtrs;jx++)
              {
                // PIRATE WON"T TAKE PYRAMID FROM PLOVER ROOM OR DARK ROOM (TOO EASY!).
                if ( jx != gm.pyram || 
                     (gm.loc != gm.plac[gm.pyram] && gm.loc != gm.plac[gm.emrald]))
                {
                  // not one of the above "too easy" treasures
                  if (toting(jx))
                  {
                    // pirate can only steal it if we are carrying it - DUH!
                    rspeak(128);    // Pirate's boast!
                    // DON"T STEAL CHEST BACK FROM TROLL!
                    if (gm.place[gm.messag] == 0) move(gm.chest,gm.chloc);
                    move(gm.messag,gm.chloc2);    // display message in 2nd maze
                    for (jy=50;jy<=gm.maxtrs;jy++)
                    {
                      if ( jy != gm.pyram ||
                          (gm.loc != gm.plac[gm.pyram] && gm.loc != gm.plac[gm.emrald]) )
                      {
                        if (at(jy) && gm.fixed[jy] == 0) carry(jy,gm.loc);
                        if (toting(jy)) drop(jy,gm.chloc);
                      }
                    }  // end  for (jy=50;jy<=gm.maxtrs;jy++)
                    gm.dloc[6] = gm.odloc[6] = gm.chloc;
                    gm.dseen[6] = FALSE;
                    break;  // exit from inner (treasure) loop
                  }  // end  if (toting(jx))
                }  // end if ( jx != gm.pyram || etc., etc.
                if (here(jx)) k=1;
              }  // end for (jx=50;jx<=gm.maxtrs;jx++)
              if (gm.dseen[6])  // only if pirate hasn't vanished - to gm.chest loc
              {
                if (gm.tally == gm.tally2+1 && 
                    k == 0 && 
                    gm.place[gm.chest] == 0 && 
                    here(gm.lamp) && 
                    gm.prop[gm.lamp] == 1)
                {
                  rspeak(186);   
                  move(gm.chest,gm.chloc);
                  move(gm.messag,gm.chloc2);
                  gm.dloc[6] = gm.odloc[6]=gm.chloc;
                  gm.dseen[6]= FALSE;
                }
                else 
                  if (gm.odloc[6] != gm.dloc[6] && pct(20)) 
                    rspeak(127);
              }
            }  // end else if(gm.loc != gm.chloc && gm.prop[gm.chest] < 0)
          }
        }  // end if (gm.dloc[i] != 0)  

// NOW WE KNOW WHAT"S HAPPENING.  LET"S TELL THE POOR SUCKER ABOUT IT.

      if(dtotal != 0)
      {
        if(dtotal == 1)
          rspeak(4);
        else
          printf("THERE ARE %i THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.\n",
                 dtotal);
        if (attack != 0)
        {
          if (gm.dflag == 2) gm.dflag = 3;
          // IF SAVED NOT = -1, HE BYPASSED THE "START" CALL.  DWARVES GET *VERY* MAD!
          if (gm.saved != -1) gm.dflag=20;
          if (attack == 1) {rspeak(5); k = 52;}
          else {printf(" %i OF THEM THROW KNIVES AT YOU!\n",attack); k=6;}
          if(stick > 1) printf(" %i OF THEM GET YOU!\n",stick);
          else rspeak(k+stick);
          if (stick != 0)
          {
            gm.oldlc2=gm.loc;
            gm.is_dead = TRUE;  // or maybe gm.loc = 0; ?
          }
        }
      }
      return;                             // normal path
}  // end gm.dwarf_stuff ------------------------------------------------------------------

// CAN"T LEAVE CAVE ONCE IT"S CLOSING (EXCEPT BY MAIN OFFICE).
void check_closing(void)
{
  if (gm.newloc <  9 && gm.newloc  !=  0 && gm.closng)
  {
    rspeak(130);
    gm.newloc = gm.loc;
    if ( !gm.panic ) gm.clock2 = 15;
    gm.panic =  TRUE;
  }
}  // end check_closing ------------------------------------------------------------------

// "YOU"RE DEAD, JIM."
//
// IF THE CURRENT LOC IS ZERO, IT MEANS THE CLOWN GOT HIMSELF KILLED.  WE"LL
// ALLOW THIS MAXDIE TIMES.  MAXDIE IS AUTOMATICALLY SET BASED ON THE NUMBER OF
// SNIDE MESSAGES AVAILABLE.  EACH DEATH RESULTS IN A MESSAGE (81, 83, ETC.)
// WHICH OFFERS REINCARNATION; IF ACCEPTED, THIS RESULTS IN MESSAGE 82, 84,
// ETC.  THE LAST TIME, IF HE WANTS ANOTHER CHANCE, HE GETS A SNIDE REMARK AS
// WE EXIT.  WHEN REINCARNATED, ALL OBJECTS BEING CARRIED GET DROPPED AT OLDLC2
// (PRESUMABLY THE LAST PLACE PRIOR TO BEING KILLED) WITHOUT CHANGE OF PROPS.
// THE LOOP RUNS BACKWARDS TO ASSURE THAT THE BIRD IS DROPPED BEFORE THE CAGE.
// (THIS KLUGE COULD BE CHANGED ONCE WE"RE SURE ALL REFERENCES TO BIRD AND CAGE
// ARE DONE BY KEYWORDS.)  THE LAMP IS A SPECIAL CASE (IT WOULDN"T DO TO LEAVE
// IT IN THE CAVE).  IT IS TURNED OFF AND LEFT OUTSIDE THE BUILDING (ONLY IF HE
// WAS CARRYING IT, OF COURSE).  HE HIMSELF IS LEFT INSIDE THE BUILDING (AND
// HEAVEN HELP HIM IF HE TRIES TO XYZZY BACK INTO THE CAVE WITHOUT THE LAMP!).
// OLDLOC IS ZAPPED SO HE CAN"T JUST "RETREAT".

// OKAY, HE"S DEAD.  LET"S GET ON WITH IT.
void hes_dead_jim(void)
{
  int ix;

  if (gm.closng)
  {
    // HE DIED DURING CLOSING TIME.  NO RESURRECTION.  
    //   TALLY UP A DEATH AND EXIT.
    rspeak(131);
    gm.numdie++;
    calculate_score(FALSE); 
    exit(0);
  }
  // ask if he wants to be resurrected
  // if too many deaths, or he doesn't want resurrection, announce score and exit
  if (gm.numdie == gm.maxdie || !yes(81+gm.numdie*2,82+gm.numdie*2,54))
  {
    calculate_score(FALSE); 
    exit(0);
  }
  else gm.numdie++;  // count how many deaths this makes
  // reset positions of certain items
  gm.place[gm.water] = gm.place[gm.oil] = 0;
  if (toting(gm.lamp)) gm.prop[gm.lamp] = 0;
  for (ix = 100;ix>=1;ix--)
    if (toting(ix))
    {
      if (ix == gm.lamp) drop(ix,1);    // drop gm.lamp at start location, he'll need it ...
      else drop(ix,gm.oldlc2);          // everything else gets dropped where he died
    } 
  gm.oldloc = gm.loc = 3;            // put him in the well-house
}  // end hes_dead_jim() --------------------------------------------------------------

void check_lamp(void)
{
  if (gm.prop[gm.lamp] == 1) gm.limit--;
  
  // ANOTHER WAY WE CAN FORCE AN END TO THINGS IS BY HAVING 
  //   THE LAMP GIVE OUT. WHEN IT GETS CLOSE, WE COME HERE 
  //   TO WARN HIM.  WE GO TO 12000 IF THE LAMP AND FRESH 
  //   BATTERIES ARE HERE, IN WHICH CASE WE REPLACE THE 
  //   BATTERIES AND CONTINUE.  12200 IS FOR OTHER CASES OF 
  //   LAMP DYING.  12400 IS WHEN IT GOES OUT, AND 12600 IS 
  //   IF HE"S WANDERED OUTSIDE AND THE LAMP IS USED UP, 
  //   IN WHICH CASE WE FORCE HIM TO GIVE UP.
  if (gm.limit <=  30 && here(gm.batter) && gm.prop[gm.batter] == 0 && here(gm.lamp))
  {
    rspeak(188);
    gm.prop[gm.batter] = 1;
    if (toting(gm.batter)) drop(gm.batter,gm.loc);
    gm.limit += 2500;
    gm.lmwarn =  FALSE;
  }
  if (gm.limit == 0)
  {
    gm.limit = -1;
    gm.prop[gm.lamp] = 0;
    if (here(gm.lamp)) rspeak(184);
  }
  else if (gm.limit < 0 && gm.loc <=  8)
  {
    rspeak(185);
    gm.gaveup =  TRUE;
    calculate_score(FALSE); 
    exit(0);
  } 
  else if (gm.limit <=  30)
  {
    if (!gm.lmwarn && here(gm.lamp))
    {
      gm.lmwarn =  TRUE;
      gm.spk = 187;
      if (gm.place[gm.batter] == 0) gm.spk = 183;
      if (gm.prop[gm.batter] == 1) gm.spk = 189;
      rspeak(gm.spk);
    }
  }
}  // end check_lamp(void) --------------------------------------------------------------

BOOL snide_comments(int verb, int obj)
{
  if (verb == -1)
  {
    // GEE, I DON"T UNDERSTAND.
    gm.spk = 60;
    if (pct(20)) gm.spk = 61;
    if (pct(20)) gm.spk = 13;
    rspeak(gm.spk);
    return TRUE;
  }
  // possible confusion here between "say" and "enter" - both are ?003
  else if (verb == gm.say && !strequ(gm.wd1,"ENTER"))  // just a little extra test
  {    
    // SAY.  ECHO WD2 (OR WD1 IF NO WD2 (SAY WHAT?, ETC.).)  
    // MAGIC WORDS OVERRIDE.
    if (obj == 0)             // no second word given
    {
      printf("SAY WHAT???\n");
      return TRUE;
    }
    else if (obj == 62 ||      // XYZZY
             obj == 65 ||      // PLUGH 
             obj == 71 ||      // PLOVE
             obj == 2025)      // FEE FIE FOE FOO FUM
    {
      // transform "say magicwd" into "magicwd" as verb, and return FALSE so we
      //   continue processing the (transformed) command as if gm.entered that way
      strcpy(gm.wd1,gm.wd2); strcpy(gm.wd1x,gm.wd2x); gm.wd2[0] = '\0';
      // make sure we update the global verb/obj, not just the local copies  
      gm.verb = gm.obj; gm.obj = 0;
      return FALSE; 
    }
    else
    {
      printf("OKAY, %s%s!\n",gm.wd2,gm.wd2x);
      return TRUE;
    } 
    // else go to normal sequence to say magic words
  }  // end verb == say
  else if (verb == gm.enter && (obj == gm.stream || obj == gm.water))
  {
    if (liqloc(gm.loc) == gm.water) rspeak(70); 
    else rspeak(43); 
    return TRUE;
  }
  else if (strequ(gm.wd1,"FUCK"))
  {
    printf("WATCH IT!\n");
    return TRUE;
  }
  else if (strequ(gm.wd1,"WEST") && (++gm.iwest == 10)) 
    rspeak(17);   // but continue to process the "WEST"
  // add more "else"s here as found convenient 
  return FALSE;       // no snide comments applicable, continue processing
}  // end snide_comments -------------------------------------------------------------

// figure out the new location;
//
// GIVEN THE CURRENT LOCATION IN "LOC", AND A MOTION VERB NUMBER IN "K", PUT
// THE NEW LOCATION IN "NEWLOC".  THE CURRENT LOC IS SAVED IN "OLDLOC" IN CASE
// HE WANTS TO RETREAT.  THE CURRENT OLDLOC IS SAVED IN OLDLC2, IN CASE HE
// DIES.  (IF HE DOES, NEWLOC WILL BE LIMBO, AND OLDLOC WILL BE WHAT KILLED
// HIM, SO WE NEED OLDLC2, WHICH IS THE LAST PLACE HE WAS SAFE.)
void compute_new_loc(void)
{
  int k, kk, LL;
  BOOL percent;

  // special treatment for "back"
  // must do "back" BEFORE messing with any locations
  if (gm.verb == gm.back)
  {
    gm.newloc = go_back();
    gm.oldloc = gm.oldlc2 = 0;   // no additonal "back"s allowed
    return;
  } 

  // make sure that newloc has a valid location (the current loc) in case
  //   we don't really want to go anywhere in this case - update oldloc
  //   and oldlc2 to avoid multiple and/or delayed "back" commands
  gm.newloc = gm.loc;
  gm.oldlc2 = gm.oldloc;
  gm.oldloc = gm.loc;
  // FIRST, check for special cases which do not use the gm.travel table  
  //   for example, invalid gm.verb, "gm.cave", "gm.look", ...
  if (gm.verb == 0)
    return;
  else if (gm.verb == gm.look)
  {
    // LOOK.  CAN"T GIVE MORE DETAIL.  PRETEND IT WASN"T DARK (THOUGH IT MAY "NOW"
    // BE DARK) SO HE WON"T FALL INTO A PIT WHILE STARING INTO THE GLOOM.
    if (gm.detail < 3) rspeak(15);
    gm.detail++;
    gm.wzdark =  FALSE;
    gm.abb[gm.loc] = 0;
    gm.described_loc = -1;    // make sure describe_loc will execute
    return;
  } 
  else if (gm.verb == gm.cave)
  {
    // CAVE.  DIFFERENT MESSAGES DEPENDING ON WHETHER ABOVE GROUND.
    rspeak((gm.loc < 8)?57:58);
    return;
  }

  kk = gm.key[gm.loc];
  // newloc, oldloc, and oldlc2 have already been updated
  // NOW we apply the gm.travel table
  for (;kk<gm.trvs;kk++)                  // kk is already set for this loop     
  {
    if (gm.travwd[kk] == gm.verb_base) break;      // found the right entry
    else if (gm.travlast[kk]) {kk = 0; break;}    // note failure
  }

  if (kk == 0) 
  {
    // if error, leave him standing where he was; newloc already set to loc
    // NON-APPLICABLE MOTION.  VARIOUS MESSAGES DEPENDING ON WORD GIVEN.
    gm.spk = 12;
    if (gm.verb_base >=  43 && gm.verb_base <=  50) gm.spk = 9;
    if (gm.verb_base == 29 || gm.verb_base == 30) gm.spk = 9;
    if (gm.verb_base == 7 || gm.verb_base == 36 || gm.verb_base == 37) gm.spk = 10;
    if (gm.verb_base == 11 || gm.verb_base == 19) gm.spk = 11;
    if (gm.verb == gm.find || gm.verb == gm.invent) gm.spk = 59;
    if (gm.verb_base == 62 || gm.verb_base == 65) gm.spk = 42;
    if (gm.verb_base == 17) gm.spk = 80;
    rspeak(gm.spk);
    return;
  }

  LL = gm.travel[kk];         // where he's going - well, sort of

  while (FOREVER)          // 11 loop
  {
    gm.newloc = LL/1000;        // pick off high-order 3 (decimal) digits of gm.travel
    k = gm.newloc%100;          // next two digits (there may be another)

    if (gm.newloc <= 300)   
    {
      if (gm.newloc <= 100)
      { 
        percent = !pct(gm.newloc);
        if (gm.newloc != 0 && percent)
        {
          do {
            if (gm.travlast[kk]) bug(25);
            gm.newloc = gm.travel[++kk];         // new-style gm.travel, DO NOT div by 1000
          } while (gm.newloc == LL);          // if (gm.newloc == LL)GOTO 12
          LL = gm.newloc;
          continue; //  11 loop
        }
        else 
        {
          gm.newloc = LL%1000;
          if (gm.newloc <= 300)
          {
            // special handling for some of these maze loops - if we REALLY moved, 
            gm.described_loc = -1;    // print location even if it's the same place
            return;
          }
          if (gm.newloc <= 500) break;  // drop through to special movement
          rspeak(gm.newloc-500);
          gm.newloc = gm.loc;
          return;
        }
      }
      if (toting(k) || (gm.newloc > 200 && at(k)))
      {
        gm.newloc = LL%1000;
        if (gm.newloc <= 300) return;
        if (gm.newloc <= 500) break;        // drop through to special movement
        rspeak(gm.newloc-500);
        gm.newloc = gm.loc;
        return;
      }
    }  
    else // gm.newloc > 300
    {
      if (gm.prop[k] != gm.newloc/100-3)
      {
        gm.newloc=LL%1000;
        if (gm.newloc <= 300) return;
        if (gm.newloc <= 500) break;     // drop through to special movement
        rspeak(gm.newloc-500);
        gm.newloc=gm.loc;
        return;
      }
    }
    do {
      if (gm.travlast[kk]) bug(25);
      kk++;
      gm.newloc = gm.travel[kk];
    } while (gm.newloc == LL);   
    LL = gm.newloc;
  }  // end while (FOREVER) 
 
  // end of normal motions

  // SPECIAL MOTIONS COME HERE.  LABELLING CONVENTION: STATEMENT NUMBERS NNNXX
  // (XX = 00-99) ARE USED FOR SPECIAL CASE NUMBER NNN (NNN = 301-500).
  gm.newloc -= 300;
  switch (gm.newloc)
  {
    // TRAVEL 301.  PLOVER-ALCOVE PASSAGE.  CAN CARRY ONLY EMERALD. 
    // NOTE: TRAVEL TABLE MUST INCLUDE "USELESS" ENTRIES GOING THROUGH 
    //   PASSAGE, WHICH CAN NEVER BE USED FOR ACTUAL MOTION, BUT CAN 
    //   BE SPOTTED BY "GO BACK".
    case 1:    // was 301
      gm.newloc = 99+100-gm.loc;
      if (gm.holdng != 0 && (gm.holdng != 1 || !toting(gm.emrald)))
      {  
        gm.newloc = gm.loc;
        rspeak(117);
      }
      break;

    // TRAVEL 302.  PLOVER TRANSPORT.  DROP THE EMERALD (ONLY USE SPECIAL
    //   TRAVEL IF TOTING IT), SO HE"S FORCED TO USE THE PLOVER-PASSAGE 
    //   TO GET IT OUT.  HAVING DROPPED IT, GO BACK AND PRETEND HE WASN"T 
    //   CARRYING IT AFTER ALL.
    case 2:    // was 302
      // this "drop" is the ONLY reason this is a special case
      drop(gm.emrald,gm.loc);
      // note that LL is still set from above with (100+emerald)*1000+302
      //   and that kk is still set to the travel entry we got LL from
      // we know that plover transport has no more specials, so take the next
      //   entry as the real destination. not worth looping to check
      gm.newloc = gm.travel[++kk];   // set out destination
      break;

    // TRAVEL 303. TROLL BRIDGE.  MUST BE DONE ONLY AS SPECIAL MOTION SO THAT
    //   DWARVES WON"T WANDER ACROSS AND ENCOUNTER THE BEAR.  (THEY WON"T 
    //   FOLLOW THE PLAYER THERE BECAUSE THAT REGION IS FORBIDDEN TO THE 
    //   PIRATE.)  IF PROP(TROLL) = 1, HE"S CROSSED SINCE PAYING, SO STEP 
    //   OUT AND BLOCK HIM. (STANDARD TRAVEL ENTRIES CHECK FOR 
    //   PROP(TROLL) = 0.)  SPECIAL STUFF FOR BEAR.
    case 3:    // was 303
      if (gm.prop[gm.troll] == 1)
      {
        pspeak(gm.troll,1);
        gm.prop[gm.troll] = 0;
        move(gm.troll2,0);
        move(gm.troll2+100,0);
        move(gm.troll,gm.plac[gm.troll]);
        move(gm.troll+100,gm.fixd[gm.troll]);
        juggle(gm.chasm);
        gm.newloc = gm.loc;
      }
      else
      {
        gm.newloc = gm.plac[gm.troll]+gm.fixd[gm.troll]-gm.loc;
        if (gm.prop[gm.troll] == 0)gm.prop[gm.troll] = 1;
        if (toting(gm.bear))
        {
          rspeak(162);
          gm.prop[gm.chasm] = 1;
          gm.prop[gm.troll] = 2;
          drop(gm.bear,gm.newloc);
          gm.fixed[gm.bear] = -1;
          gm.prop[gm.bear] = 3;
          if (gm.prop[gm.spices] < 0)gm.tally2 = gm.tally2+1;
          gm.oldlc2 = gm.newloc;
          gm.is_dead = TRUE;
        }
      }
      break;
     
    default:
      bug(20);
  }
  return;    // let "break" fall through to here
  // END OF SPECIALS.
}  // end compute_new_loc(void) ---------------------------------------------------------

// this is a word, normally an gm.object word, but used in a gm.verb context
// ANALYSE AN OBJECT WORD.  SEE IF THE THING IS HERE, WHETHER WE"VE 
//   GOT A VERB YET, AND SO ON.  OBJECT MUST BE HERE UNLESS VERB 
//   IS "FIND" OR "INVENT(ORY)" (AND NO NEW VERB YET TO BE ANALYSED).
// that is, if the object is "available", try using the second word as a verb
// WATER AND OIL ARE ALSO FUNNY, SINCE THEY ARE NEVER ACTUALLY DROPPED 
//   AT ANY LOCATION, BUT MIGHT BE HERE INSIDE THE BOTTLE OR AS A 
//   FEATURE OF THE LOCATION.
void do_obj_as_verb(void)
{
  // gm.obj = gm.verb_base;
  if (gm.fixed[gm.verb_base] == gm.loc || here(gm.verb_base))
  {
    if (gm.wd2[0] != '\0')
    {
      // GET SECOND WORD FOR ANALYSIS. (as a verb)
      // this assumes he said something stupid like "gold get" AND gold is here
      strcpy(gm.wd1,gm.wd2); strcpy(gm.wd1x,gm.wd2x); gm.wd2[0] = '\0';
      gm.skip_user_input = TRUE;
    }
    else if (gm.verb != 0) analyze_verb(); // no 2nd word, TRY this as a gm.verb
    else 
      // this is probably impossible, but just in case ...
      printf("WHAT DO YOU WANT TO DO WITH THE %s%s?\n",gm.wd1,gm.wd1x);
  }
  else
  {  
    // the gm.object-used-as-gm.verb is NOT available
    if (gm.verb == gm.grate)
    {
      if (gm.loc == 1 || gm.loc == 4 || gm.loc == 7) gm.verb = gm.dprssn;
      if (gm.loc > 9 && gm.loc < 15) gm.verb = gm.entrnc;
      if (gm.verb != gm.grate) 
      {
        compute_new_loc();
        check_closing(); // CAN"T LEAVE CAVE ONCE IT"S CLOSING (EXCEPT BY MAIN OFFICE)
        dwarf_stuff();   // check for dwarves at his gm.loc, and process them 
        return;
      }
    }
    else if ( (gm.verb == gm.dwarf && gm.dflag >= 2 && dwarf_here(gm.loc)) ||
              ((liq() == gm.verb && here(gm.bottle)) || gm.verb == liqloc(gm.loc)) ||
              (gm.obj == gm.plant &&  at(gm.plant2) && gm.prop[gm.plant2] != 0) ||
              (gm.obj == gm.rod && here(gm.rod2)) ||
              ((gm.verb == gm.find || gm.verb == gm.invent) && gm.wd2[0] != '\0') )
    {
      if (gm.obj == gm.plant) gm.obj = gm.plant2;
      else if (gm.obj == gm.rod) gm.obj = gm.rod2;
      if (gm.wd2[0] != '\0')
      {
        // GET SECOND WORD FOR ANALYSIS. (as a gm.verb)
        strcpy(gm.wd1,gm.wd2); strcpy(gm.wd1x,gm.wd2x); gm.wd2[0] = '\0';
        gm.skip_user_input = TRUE; 
      }
      else if (gm.verb != 0) analyze_verb();
      else printf("WHAT DO YOU WANT TO DO WITH THE %s%s?\n",gm.wd1,gm.wd1x);
    }
    else if (gm.obj == gm.knife && gm.knfloc == gm.loc)
    {
      gm.knfloc = -1;
      gm.spk = 116;
      rspeak(gm.spk);
    }
    else printf("I SEE NO %s%s HERE.\n",gm.wd1,gm.wd1x);
  }
}  // end do_obj_as_verb ------------------------------------------------------------

// ANALYSE A VERB.  REMEMBER WHAT IT WAS, GO BACK FOR OBJECT IF SECOND WORD
// UNLESS VERB IS "SAY", WHICH SNARFS ARBITRARY SECOND WORD.
// now we enter here with both gm.verb and gm.object set up, some earlier juggling
//   has taken care of a few problems like "say"
void analyze_verb(void)
{
  int i, k, rcode, dwix;
  time_t now;
  struct tm *pt;
  FILE *savp;              // for the save file

  gm.spk = gm.actspk[gm.verb_base];
  if (gm.obj == 0)
    // ANALYSE AN INTRANSITIVE VERB (IE, NO OBJECT GIVEN YET).
    switch (gm.verb_base)
    {  
      case 1:  // gm.verb TAKE  
        // CARRY, NO OBJECT GIVEN YET.  OK IF ONLY ONE OBJECT PRESENT.
        if (gm.atloc[gm.loc] == 0 || gm.link[gm.atloc[gm.loc]] != 0)
        {
          printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); 
          gm.obj = 0; 
          break;
        }
        for (i = 1;i<=5;i++)
          if (gm.dloc[i] == gm.loc && gm.dflag >=  2)
          {
            printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); 
            gm.obj = 0; 
            break;
          }
        gm.obj = gm.atloc[gm.loc];
        perform_take(gm.obj);
        break; 
      case 2:   // verb DROP   
      case 3:   // verb SAY  
      case 9:   // verb WAVE  
      case 10:  // verb CALM  
      case 16:  // verb RUB  
      case 17:  // verb TOSS  
      case 19:  // verb FIND  
      case 21:  // verb FEED  
      case 28:  // verb BREK  
      case 29:  // verb WAKE 
        {printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); gm.obj = 0; break;}
        break; 
      case 4:  // gm.verb OPEN  
      case 6:  // gm.verb LOCK    
        // LOCK, UNLOCK, NO OBJECT GIVEN.  ASSUME VARIOUS THINGS IF PRESENT.
        gm.spk = 28;
        if (here(gm.clam)) gm.obj = gm.clam;
        if (here(gm.oyster)) gm.obj = gm.oyster;
        if (at(gm.door)) gm.obj = gm.door;
        if (at(gm.grate)) gm.obj = gm.grate;
        if (gm.obj != 0 && here(gm.chain))
          {printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); gm.obj = 0; break;}
        if (here(gm.chain)) gm.obj = gm.chain;
        if (gm.obj == 0) 
        {
          rspeak(gm.spk);
          gm.verb = 0;
          gm.obj = 0;
        }
        else perform_lock(gm.obj);
        break; 
      case 5:  // gm.verb NOTH 
        {k = 54; gm.spk = k; rspeak(gm.spk); break;}
        break; 
      case 7:  // gm.verb ON   
        // LIGHT LAMP
        if (!here(gm.lamp)) rspeak(gm.spk);
        else
        {
          gm.spk = 184;
          if (gm.limit < 0) rspeak(gm.spk);
          else
          { 
            gm.prop[gm.lamp] = 1;
            rspeak(39);
            if (gm.wzdark) describe_loc();     // was dark, now light, describe
          }
        }
        break; 
      case 8:  // verb OFF 
        // LAMP OFF
        if (!here(gm.lamp)) rspeak(gm.spk);
        else
        { 
          gm.prop[gm.lamp] = 0;
          rspeak(40);
          if (dark()) rspeak(16);
        }
        break; 
      case 11:  // verb WALK  
        // this should never occur - earlier the command "go destination"
        //   was transformed into "destination" as a verb
        // user will either go to the destination or get a nasty message 
        rspeak(gm.spk); 
        break; 
      case 12:  // verb KILL  
        perform_attack();
        break; 
      case 13:  // verb POUR 
        // POUR.  IF NO OBJECT, OR OBJECT IS BOTTLE, ASSUME CONTENTS OF BOTTLE
        // SPECIAL TESTS FOR POURING WATER OR OIL ON PLANT OR RUSTY DOOR.
        if (gm.obj == gm.bottle || gm.obj == 0) gm.obj = liq();
        if (gm.obj == 0) printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x);
        else if (!toting(gm.obj)) rspeak(gm.spk);
        else if (gm.obj != gm.oil && gm.obj != gm.water) rspeak(gm.spk = 78);
        else 
        {
          // gm.obj must be either water or oil, and we are carrying it, if we get here
          gm.prop[gm.bottle] = 1;     // set bottle empty
          gm.place[gm.obj] = 0;       // destroy water/oil   
          if (!at(gm.plant) && !at(gm.door)) rspeak(gm.spk = 77); // "bottle is empty ..."
          else if (at(gm.plant))
          {
            if (gm.obj != gm.water) rspeak(gm.spk = 112);   // plant doesn't like oil!
            else 
            {
              pspeak(gm.plant,gm.prop[gm.plant]+1);
              gm.prop[gm.plant] = gm.prop[gm.plant]+2%6;
              gm.prop[gm.plant2] = gm.prop[gm.plant]/2;
            }
          }
          else // must be at(gm.door)
          {
            gm.prop[gm.door] = (gm.obj == gm.oil);
            rspeak(gm.spk = 113+gm.prop[gm.door]);
          }
        }
        break;
      case 14:  // gm.verb EAT  
        // EAT.  INTRANSITIVE: ASSUME FOOD IF PRESENT, ELSE ASK WHAT. 
        if (!here(gm.food))
          {printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); gm.obj = 0;}
        else 
        {
          dstroy(gm.food);
          gm.spk = 72;
          rspeak(gm.spk);
        }
        break; 
      case 15:  // gm.verb DRNK   
        // DRINK.  IF NO OBJECT, ASSUME WATER AND LOOK FOR IT HERE.  
        // IF WATER IS IN THE BOTTLE, DRINK THAT, ELSE MUST BE AT 
        //   A WATER LOC, SO DRINK STREAM.
        if ( gm.obj == 0 && liqloc(gm.loc) != gm.water &&
             (liq() != gm.water || !here(gm.bottle)) )
        {
          printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); 
          gm.obj = 0;
        }
        else
        {
          if (gm.obj != 0 && gm.obj != gm.water) gm.spk = 110;
          if (gm.spk != 110 && liq() == gm.water && here(gm.bottle))
          {
            gm.prop[gm.bottle] = 1;
            gm.place[gm.water] = 0;
            gm.spk = 74;
          }
          rspeak(gm.spk);
        }
        break; 
      case 18:  // gm.verb QUIT  
        // QUIT.  INTRANSITIVE ONLY.  VERIFY INTENT AND EXIT IF THAT"S WHAT HE WANTS.
        gm.gaveup = yes(22,54,54);
        if (gm.gaveup) 
          {calculate_score(FALSE); exit(0);}
        break; 
      case 20:  // gm.verb INVN  
        // INVENTORY.  IF OBJECT, TREAT SAME AS FIND.  ELSE REPORT 
        //   ON CURRENT BURDEN.
        gm.spk = 98;
        for (i = 1;i<=100;i++)
          if (i != gm.bear && toting(i))
          {
            if (gm.spk == 98)
            {
              rspeak(99);
              gm.spk = 0;
            }
            pspeak(i,-1);
          }
        if (toting(gm.bear)) gm.spk = 141;
        rspeak(gm.spk); 
        break; 
      case 22:  // gm.verb FILL  
        // FILL.  BOTTLE MUST BE EMPTY, AND SOME LIQUID AVAILABLE.
        //   (VASE IS NASTY.)
        if (gm.obj == gm.vase)
        {
          gm.spk = 29;
          if (liqloc(gm.loc) == 0) gm.spk = 144;
          if (liqloc(gm.loc) == 0 || !toting(gm.vase))
            rspeak(gm.spk);
          else 
          { 
            rspeak(145);
            gm.prop[gm.vase] = 2;
            gm.fixed[gm.vase] = -1;
            drop(gm.obj,gm.loc);
          }
        }
        else if (gm.obj != 0 && gm.obj != gm.bottle)
          rspeak(gm.spk);
        else if (gm.obj == 0 && !here(gm.bottle))
          printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x);
        else
        {
          gm.spk = 107;
          if (liqloc(gm.loc) == 0)gm.spk = 106;
          if (liq() != 0)gm.spk = 105;
          if (gm.spk == 107)
          {
            gm.prop[gm.bottle] = (gm.cond[gm.loc]%4)/2*2;
            k = liq();
            if (toting(gm.bottle)) gm.place[k] = -1;
            if (k == gm.oil) gm.spk = 108;
          }
          rspeak(gm.spk); 
        }
        break; 
      case 23:  // gm.verb BLST  
        // BLAST.  NO EFFECT UNLESS YOU"VE GOT DYNAMITE, WHICH IS 
        //   A NEAT TRICK!
        if (gm.prop[gm.rod2] < 0 || !gm.closed) rspeak(gm.spk); 
        else
        { 
          gm.bonus = 133;
          if (gm.loc == 115)gm.bonus = 134;
          if (here(gm.rod2))gm.bonus = 135;
          rspeak(gm.bonus);
          calculate_score(FALSE);
          exit(0);    // well, the game has to end somewhere!
        }
        break; 
      case 24:  // gm.verb SCOR   
        // SCORE.  GO TO SCORING SECTION, WHICH WILL RETURN IF SCORNG 
        //   IS TRUE.
        calculate_score(TRUE);
        printf(" IF YOU WERE TO QUIT NOW, YOU WOULD SCORE %i OUT OF A POSSIBLE %i.\n",gm.score,gm.mxscor);
        gm.gaveup = yes(143,54,54);
        if (gm.gaveup) {calculate_score(FALSE); exit(0);}
        break; 
      case 25:  // verb FOO   
        // FEE FIE FOE FOO (AND FUM).  ADVANCE TO NEXT STATE IF 
        //   GIVEN IN PROPER ORDER. LOOK UP WD1 IN SECTION 3 OF 
        //   VOCAB TO DETERMINE WHICH WORD WE"VE GOT.  LAST WORD 
        //   ZIPS THE EGGS  BACK TO THE GIANT ROOM (UNLESS 
        //   ALREADY THERE).
        // up to this point we have been running on the 2xxx definitions of these words,
        //   but here we need to get the 3xxx definitions
        k = vocab(gm.wd1,3)%1000;
        gm.spk = 42;
        if (gm.foobar != 1-k)
        {
          if (gm.foobar != 0) gm.spk = 151;
          rspeak(gm.spk); 
        }
        else
        {
          gm.foobar = k;
          if (k != 4)
          {
            gm.spk = 54; 
            rspeak(gm.spk); 
          }
          else
          {
            gm.foobar = 0;
            if ( gm.place[gm.eggs] == gm.plac[gm.eggs] || 
                 (toting(gm.eggs) && gm.loc == gm.plac[gm.eggs]))
              rspeak(gm.spk); 
            else
            {
              // BRING BACK TROLL IF WE STEAL THE EGGS BACK FROM HIM 
              //   BEFORE CROSSING.
              if ( gm.place[gm.eggs] == 0 && 
                   gm.place[gm.troll] == 0 && 
                   gm.prop[gm.troll] == 0) 
                gm.prop[gm.troll] = 1;
              k = 2;
              if (here(gm.eggs)) k = 1;
              if (gm.loc == gm.plac[gm.eggs]) k = 0;
              move(gm.eggs,gm.plac[gm.eggs]);
              pspeak(gm.eggs,k);
            }
          }
        }
        break; 
      case 26:  // verb BRF  
        // BRIEF.  INTRANSITIVE ONLY.  SUPPRESS LONG DESCRIPTIONS 
        //   AFTER FIRST TIME.
        gm.spk = 156;
        gm.abbnum = 10000;
        gm.detail = 3;
        rspeak(gm.spk);
        break; 
      case 27:  // gm.verb READ  
        // READ.  MAGAZINES IN DWARVISH, MESSAGE WE"VE SEEN, 
        //   AND . . . OYSTER?
        if (here(gm.magzin)) gm.obj = gm.magzin;
        if (here(gm.tablet)) gm.obj = gm.obj*100+gm.tablet;
        if (here(gm.messag)) gm.obj = gm.obj*100+gm.messag;
        if (gm.closed && toting(gm.oyster)) gm.obj = gm.oyster;
        if (gm.obj > 100 || gm.obj == 0 || dark())
        {
          printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); 
          gm.obj = 0; 
          break;
        }
        if (dark())
          printf(" IT'S TOO DARK TO READ HERE! YOU WANT ME TO RUIN MY EYES?\n");
        else
        {
          if (gm.obj == gm.magzin) gm.spk = 190;
          if (gm.obj == gm.tablet) gm.spk = 196;
          if (gm.obj == gm.messag) gm.spk = 191;
          if (gm.obj == gm.oyster && gm.hinted[2] && toting(gm.oyster)) gm.spk = 194;
          if (gm.obj != gm.oyster || gm.hinted[2] || !toting(gm.oyster) || !gm.closed) rspeak(gm.spk);
          else gm.hinted[2] = yes(192,193,54);
        }
        break; 
      case 30:  // gm.verb SUSP   
        // SUSPEND.  OFFER TO EXIT LEAVING THINGS RESTARTABLE, 
        //   BUT REQUIRING A DELAY BEFORE RESTARTING (SO CAN"T 
        //   SAVE THE WORLD BEFORE TRYING SOMETHING RISKY).
        // UPON RESTARTING, SETUP = -1 CAUSES RETURN TO 8305 
        //   TO PICK UP AGAIN.
        gm.spk = 201;
        if (gm.demo)
          rspeak(gm.spk); 
        else
        {
          printf("I CAN SUSPEND YOUR ADVENTURE FOR YOU SO THAT YOU CAN RESUME LATER,\n");
          printf("  BUT YOU WILL HAVE TO WAIT AT LEAST %i MINUTES BEFORE CONTINUING.\n",
                wizcom.latncy);
          if (yes(200,54,54))
          {
            now = time(NULL);
            pt = localtime(&now);
            gm.saved = now;
            gm.setup = -1;
            // open the save file for output (overwrite anything previously there)
            savp = fopen(savefyle,"wb");
            if (savp != NULL) 
            {
              rcode = fwrite(&gm,sizeof(struct game_data),1,savp);
              if (rcode == 1)
                rcode = fwrite(gm.bblock,5*LINSIZ,1,savp);
              if (rcode == 1)
                rcode = fwrite(&wizcom,sizeof(struct t_wizcom),1,savp);
              fclose(savp);  // ignore condx code here
            }
            // now, if savp is not NULL, and rcode == 1, everything worked
            //   if savp is NULL, we didn't even try to write
            //   if rcode != 1, some write failed, and the rest were skipped
            if (savp != NULL && rcode == 1)
              printf("YOUR GAME HAS BEEN SAVED, AND CAN BE RESUMED LATER WITH \"-r\".\n");
            else
            {
              printf("UH, OH! CAN'T SAVE THIS GAME RIGHT NOW.\n");
              printf("YOUR DISK IS FULL, OR WRITE PROTECTED, OR SOMETHING.\n");
              printf("WELL, I'LL JUST LET YOU CONTINUE PLAYING IF YOU WANT,\n");
              printf("  OR YOU CAN QUIT AND PLAY A NEW GAME LATER. SORRY!\n");
              break;
            }
            ciao();
          }
        }
        break; 
      case 31:  // gm.verb HOUR 
        // HOURS.  REPORT CURRENT NON-PRIME-TIME HOURS.
        mspeak(6);
        hours();
        break; 
      default: bug(23);
    }  // end switch
  else
    // ANALYSE A TRANSITIVE VERB.
    switch (gm.verb_base)
    {  
      case  1:  // TAKE     
        if (toting(gm.obj)) rspeak(gm.spk);
        else perform_take(gm.obj);
        break;
      case  2:  // DROP  
        perform_drop(gm.obj);  
        break;
      case  3:  // SAY 
        // should never come here, it was all handled earlier
        //   in "snide_comment" - "Okay, xxxx" or "Say what?",
        //   "say magicwd" was transformed to "magicwd" as a verb 
        break;
      case  4:  // OPEN 
      case  6:  // LOCK   
        perform_lock(gm.obj);
        break;
      case  5:  // NOTH 
        gm.spk = k = 54; 
        rspeak(gm.spk);
        break;
      case  7:  // ON  
        // LIGHT LAMP
        if (!here(gm.lamp)) rspeak(gm.spk);
        else
        {
          gm.spk = 184;
          if (gm.limit < 0) rspeak(gm.spk);
          else
          { 
            gm.prop[gm.lamp] = 1;
            rspeak(39);
            if (gm.wzdark) describe_loc();  // was dark, now light, describe
          }
        }
        break;
      case  8:  // OFF 
        // LAMP OFF
        if (!here(gm.lamp)) rspeak(gm.spk);
        else
        { 
          gm.prop[gm.lamp] = 0;
          rspeak(40);
          if (dark()) rspeak(16);
        }
        break;
      case  9:  // WAVE 
        // WAVE.  NO EFFECT UNLESS WAVING ROD AT FISSURE.
        if ((!toting(gm.obj)) && (gm.obj != gm.rod || !toting(gm.rod2))) gm.spk = 29;
        if (gm.obj != gm.rod || !at(gm.fissur) || !toting(gm.obj) || gm.closng)
          rspeak(gm.spk);
        else
        {
          gm.prop[gm.fissur] = 1-gm.prop[gm.fissur];
          pspeak(gm.fissur,2-gm.prop[gm.fissur]);
        }
        break;  
      case 10:  // CALM 
      case 11:  // WALK 
      case 18:  // QUIT 
      case 24:  // SCOR 
      case 25:  // FOO  
      case 26:  // BRF 
      case 30:  // SUSP 
      case 31:  // HOUR
        rspeak(gm.spk);  // was "speak" - obviously a mistake???
        gm.verb = 0;
        gm.obj = 0;
        break;
      case 12:  // KILL 
        perform_attack();
        break;
      case 13:  // POUR  
        // POUR.  IF NO OBJECT, OR OBJECT IS BOTTLE, ASSUME CONTENTS OF BOTTLE
        // SPECIAL TESTS FOR POURING WATER OR OIL ON PLANT OR RUSTY DOOR.
        if (gm.obj == gm.bottle || gm.obj == 0) gm.obj = liq();
        if (gm.obj == 0) {printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); gm.obj = 0;}
        else if (!toting(gm.obj)) rspeak(gm.spk);
        else if (gm.obj != gm.oil && gm.obj != gm.water) rspeak(gm.spk = 78);
        else 
        {
          gm.prop[gm.bottle] = 1;
          gm.place[gm.obj] = 0;
          if (!at(gm.plant) && !at(gm.door)) rspeak(gm.spk = 77);
          else if (at(gm.plant))
          {
            if (gm.obj != gm.water) rspeak(gm.spk = 112);
            else 
            {
              pspeak(gm.plant,gm.prop[gm.plant]+1);
              gm.prop[gm.plant] = gm.prop[gm.plant]+2%6;
              gm.prop[gm.plant2] = gm.prop[gm.plant]/2;
            }
          }
          else // must be at(gm.door)
          {
            gm.prop[gm.door] = (gm.obj == gm.oil);
            rspeak(gm.spk = 113+gm.prop[gm.door]);
          }
        }
        break;
      case 14:  // EAT 
        // EAT.  TRANSITIVE: FOOD OK, SOME THINGS LOSE APPETITE, 
        //   REST ARE RIDICULOUS.
        if (gm.obj == gm.food)
        { 
          dstroy(gm.food);
          gm.spk = 72;
        } 
        else if (gm.obj == gm.bird || 
                 gm.obj == gm.snake || 
                 gm.obj == gm.clam || 
                 gm.obj == gm.oyster || 
                 gm.obj == gm.dwarf || 
                 gm.obj == gm.dragon || 
                 gm.obj == gm.troll ||
                 gm.obj == gm.bear)
          gm.spk = 71;
        rspeak(gm.spk);
        break;
      case 15:  // DRNK  
        // DRINK.  IF NO OBJECT, ASSUME WATER AND LOOK FOR IT HERE.  
        // IF WATER IS IN THE BOTTLE, DRINK THAT, ELSE MUST BE AT 
        //   A WATER LOC, SO DRINK STREAM.
        if ( gm.obj == 0 && liqloc(gm.loc) != gm.water &&
             (liq() != gm.water || !here(gm.bottle)) )
        {
          printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x); 
          gm.obj = 0;
        }
        else
        {
          if (gm.obj != 0 && gm.obj != gm.water) gm.spk = 110;
          if (gm.spk != 110 && liq() == gm.water && here(gm.bottle))
          {
            gm.prop[gm.bottle] = 1;
            gm.place[gm.water] = 0;
            gm.spk = 74;
          }
          rspeak(gm.spk);
        }
        break;
      case 16:  // RUB 
        // RUB.  YIELDS VARIOUS SNIDE REMARKS.
        if (gm.obj != gm.lamp) gm.spk = 76;
        rspeak(gm.spk); 
        break;
      case 17:  // TOSS 
        // THROW.  SAME AS DISCARD UNLESS AXE.  THEN SAME AS ATTACK EXCEPT 
        //   IGNORE BIRD, AND IF DWARF IS PRESENT THEN ONE MIGHT BE KILLED.
        //   (ONLY WAY TO DO SO!)
        // AXE ALSO SPECIAL FOR DRAGON, BEAR, AND TROLL.  TREASURES 
        //   SPECIAL FOR TROLL.
        if (toting(gm.rod2) && gm.obj == gm.rod && !toting(gm.rod)) gm.obj = gm.rod2;
        if (!toting(gm.obj))
          rspeak(gm.spk); 
        else if (gm.obj >=  50 && gm.obj <=  gm.maxtrs && at(gm.troll))
        {
          gm.spk = 159;
          // SNARF A TREASURE FOR THE TROLL.
          drop(gm.obj,0);
          move(gm.troll,0);
          move(gm.troll+100,0);
          drop(gm.troll2,gm.plac[gm.troll]);
          drop(gm.troll2+100,gm.fixd[gm.troll]);
          juggle(gm.chasm);
          rspeak(gm.spk); 
        }
        else if (gm.obj == gm.food && here(gm.bear))
        {
          // BUT THROWING FOOD IS ANOTHER STORY.
          gm.obj = gm.bear;
          if (gm.prop[gm.bear] == 0) gm.spk = 102;
          if (gm.prop[gm.bear] == 3) gm.spk = 110;
          if (here(gm.food))
          {
            dstroy(gm.food);
            gm.prop[gm.bear] = 1;
            gm.fixed[gm.axe] = 0;
            gm.prop[gm.axe] = 0;
            rspeak(gm.spk = 168);
          }  
        }
        else if (gm.obj != gm.axe) 
          perform_drop(gm.obj);  
        // NEEDN"T CHECK DFLAG IF AXE IS HERE.
        else if (!(dwix = dwarf_here(gm.loc)))    // dwix != indicates dwarf AND which dwarf
        {
          if (at(gm.dragon) && gm.prop[gm.dragon] == 0)
          {
            rspeak(gm.spk = 152);
            drop(gm.axe,gm.loc);
            k = 0;
            compute_new_loc();
          }
          else if (at(gm.troll))
          {
            rspeak(gm.spk = 158);
            drop(gm.axe,gm.loc);
            k = 0;
            compute_new_loc();
          }
          else if (here(gm.bear) && gm.prop[gm.bear] == 0)
          { 
            // THIS"LL TEACH HIM TO THROW THE AXE AT THE BEAR!
            gm.spk = 164;
            drop(gm.axe,gm.loc);
            gm.fixed[gm.axe] = -1;
            gm.prop[gm.axe] = 1;
            juggle(gm.bear);
            rspeak(gm.spk); 
          }
          else
          { 
            gm.obj = 0;
            perform_attack();
          }
        }
        else // yes, there **IS** a dwarf here 
        {  
          gm.spk = 48;
          // IF SAVED NOT  =  -1, HE BYPASSED THE "START" CALL.
          if (ran(3) != 0 && gm.saved == -1)
          {
            gm.dseen[dwix] =  FALSE;
            gm.dloc[dwix] = 0;
            gm.spk = 47;
            gm.dkill = gm.dkill+1;
            if (gm.dkill == 1) gm.spk = 149;
          }
          rspeak(gm.spk);
          drop(gm.axe,gm.loc);
          k = 0;
          gm.verb = 0;        // compute_new_loc sometimes confuses verbs
          compute_new_loc();  
        }
        break;
      case 19:  // FIND 
      case 20:  // INVN 
        // FIND.  MIGHT BE CARRYING IT, OR IT MIGHT BE HERE.  
        //   ELSE GIVE CAVEAT.
        if ( at(gm.obj) || 
             (liq() == gm.obj && at(gm.bottle)) || 
             k == liqloc(gm.loc) )
          gm.spk = 94;
        if (gm.obj == gm.dwarf && gm.dflag >=  2 && dwarf_here(gm.loc)) gm.spk = 94;
        if (gm.closed)gm.spk = 138;
        if (toting(gm.obj))gm.spk = 24;
        rspeak(gm.spk); 
        break;
      case 21:  // FEED 
        // FEED.  IF BIRD, NO SEED.  SNAKE, DRAGON, TROLL: QUIP.
        // IF DWARF, MAKE HIM MAD.  BEAR, SPECIAL.
        if (gm.obj == gm.bird)
          gm.spk = 100; 
        else if (gm.obj == gm.snake || gm.obj == gm.dragon || gm.obj == gm.troll)
        {
          gm.spk = 102;
          if (gm.obj == gm.dragon && gm.prop[gm.dragon] != 0)gm.spk = 110;
          if (gm.obj == gm.troll)gm.spk = 182;
          if (gm.obj == gm.snake && !gm.closed && here(gm.bird))
          {
            gm.spk = 101;
            dstroy(gm.bird);
            gm.prop[gm.bird] = 0;
            gm.tally2 = gm.tally2+1;
          }
        }
        else if (gm.obj == gm.dwarf)
        {
          if (here(gm.food)) {gm.spk = 103; gm.dflag = gm.dflag+1;}
        }
        else if (gm.obj != gm.bear) gm.spk = 14; 
        else // gm.obj == gm.bear
        {
          if (gm.prop[gm.bear] == 0) gm.spk = 102;
          if (gm.prop[gm.bear] == 3) gm.spk = 110;
          if (here(gm.food))
          {
            dstroy(gm.food);
            gm.prop[gm.bear] = 1;
            gm.fixed[gm.axe] = 0;
            gm.prop[gm.axe] = 0;
            gm.spk = 168;
          }  
        }
        rspeak(gm.spk);
        break;
      case 22:  // FILL 
        // FILL.  BOTTLE MUST BE EMPTY, AND SOME LIQUID AVAILABLE.
        //   (VASE IS NASTY.)
        if (gm.obj == gm.vase)
        {
          gm.spk = 29;
          if (liqloc(gm.loc) == 0) gm.spk = 144;
          if (liqloc(gm.loc) == 0 || !toting(gm.vase))
            rspeak(gm.spk); 
          else
          {
            rspeak(145);
            gm.prop[gm.vase] = 2;
            gm.fixed[gm.vase] = -1;
            // else rspeak(54);
            drop(gm.obj,gm.loc);
          }
        }
        else if (gm.obj != 0 && gm.obj != gm.bottle)
          rspeak(gm.spk);
        else if (gm.obj == 0 && !here(gm.bottle))
          printf("%s%s WHAT?\n ",gm.wd1,gm.wd1x);
        else
        {
          gm.spk = 107;
          if (liqloc(gm.loc) == 0) gm.spk = 106;
          if (liq() != 0) gm.spk = 105;
          if (gm.spk == 107)
          {
            gm.prop[gm.bottle] = (gm.cond[gm.loc]%4)/2*2;
            k = liq();
            if (toting(gm.bottle)) gm.place[k] = -1;
            if (k == gm.oil) gm.spk = 108;
          }
          rspeak(gm.spk); 
        }
        break;
      case 23:  // BLST 
        // BLAST.  NO EFFECT UNLESS YOU"VE GOT DYNAMITE, WHICH IS 
        //   A NEAT TRICK!
        if (gm.prop[gm.rod2] < 0 || !gm.closed) rspeak(gm.spk); 
        else
        { 
          gm.bonus = 133;
          if (gm.loc == 115)gm.bonus = 134;
          if (here(gm.rod2))gm.bonus = 135;
          rspeak(gm.bonus);
          calculate_score(FALSE);
          exit(0);    // well, the game has to end somewhere!
        }
        break;
      case 27:  // READ 
        if (dark())
          printf(" IT'S TOO DARK TO READ HERE! YOU WANT ME TO RUIN MY EYES?\n");
        else
        {
          if (gm.obj == gm.magzin) gm.spk = 190;
          if (gm.obj == gm.tablet) gm.spk = 196;
          if (gm.obj == gm.messag) gm.spk = 191;
          if (gm.obj == gm.oyster && gm.hinted[2] && toting(gm.oyster)) gm.spk = 194;
          if (gm.obj != gm.oyster || gm.hinted[2] || !toting(gm.oyster) || !gm.closed) rspeak(gm.spk);
          else gm.hinted[2] = yes(192,193,54);
        }
        break;
      case 28:  // BREK 
        // BREAK.  ONLY WORKS FOR MIRROR IN REPOSITORY AND, 
        //   OF COURSE, THE VASE.
        if (gm.obj == gm.mirror) gm.spk = 148;
        if (gm.obj == gm.vase && gm.prop[gm.vase] == 0)
        {
          gm.spk = 198;
          if (toting(gm.vase)) drop(gm.vase,gm.loc);
          gm.prop[gm.vase] = 2;
          gm.fixed[gm.vase] = -1;
        }
        else if (gm.obj == gm.mirror && gm.closed)
        {
          // oh, dear, he's distrubed the dwarves
          rspeak(197);
          calculate_score(FALSE); 
          exit(0);
        }
        rspeak(gm.spk);
        break;
      case 29:  // WAKE 
        // WAKE.  ONLY USE IS TO DISTURB THE DWARVES.
        if (gm.obj == gm.dwarf && gm.closed)
        {
          rspeak(199);
          // OH DEAR, HE'S DISTURBED THE DWARVES.
          rspeak(136);
          calculate_score(FALSE); 
          exit(0);
        }
        rspeak(gm.spk);
        break;
      default:  bug(24);
    }  // end switch
   
  // original Fortran code assumed that the "switches" (actually calculated
  //   gotos) would never return. we may need to add some sort of branch here
  //   if any of the code it the switches hits the break statements 
  return;  // this is a good choice, now this is a separate function
}  // end analyze_verb() --------------------------------------------------------

