import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.prefs.Preferences;

/**
 * Colossal Cave is my rewrite of a Unix C version of the classic text adventure game by Jerry D. Pohl that was based on
 * a BDS C version by Jay R. Jaegar that was, itself, based on the expanded FORTRAN version by Don Woods that was derived
 * from the original FORTRAN code by Willie Crowther.
 * See: http://rickadams.org/adventure/e_downloads.html
 */

public class Adventure extends JFrame implements Runnable {
  private static final boolean  listObjects = false;       // If true, list the location of all the objects to System.out
  private static boolean        showMoves = false;         // If true, display locations and available moves to System.out
  private static boolean        lithiumBatteries = false;  // If true, batteries in lamp last 5000 turns;
  private static boolean        noDwarvesOrPirate = false; // If true, dwarves and pirate are disabled
  private static final Font     font = new Font("Courier", Font.PLAIN, 20);
  private Teleprinter           teleprinter;
  private TextField             input;
  private final Thread          ticker = new Thread(this);
  private String                inText = "";

  private static final int MAXOBJ     = 100;          //	max # of objects in cave
  private static final int MAXTRAV    = (16 + 1);     //	max # of travel directions from loc
  private static final int DWARFMAX   = 7;            //	max # of nasty dwarves
  private static final int MAXDIE     = 3;            //	max # of deaths before close
  private static final int MAXTRS     = 79;           //	max # of

  //	Object indexes (1 based)
  private static final int KEYS       = 1;
  private static final int LAMP       = 2;
  private static final int GRATE      = 3;
  private static final int CAGE       = 4;
  private static final int ROD        = 5;
  private static final int ROD2       = 6;
  private static final int STEPS      = 7;
  private static final int BIRD       = 8;
  private static final int DOOR       = 9;
  private static final int PILLOW     = 10;
  private static final int SNAKE      = 11;
  private static final int FISSURE    = 12;
  private static final int TABLET     = 13;
  private static final int CLAM       = 14;
  private static final int OYSTER     = 15;
  private static final int MAGAZINE   = 16;
  private static final int DWARF      = 17;
  private static final int KNIFE      = 18;
  private static final int FOOD       = 19;
  private static final int BOTTLE     = 20;
  private static final int WATER      = 21;
  private static final int OIL        = 22;
  private static final int MIRROR     = 23;
  private static final int PLANT      = 24;
  private static final int PLANT2     = 25;           // 26, 27 not used
  private static final int AXE        = 28;           // 29, 30 not used
  private static final int DRAGON     = 31;
  private static final int CHASM      = 32;
  private static final int TROLL      = 33;
  private static final int TROLL2     = 34;
  private static final int BEAR       = 35;
  private static final int MESSAGE    = 36;         // 37 not used
  private static final int VEND       = 38;
  private static final int BATTERIES  = 39;         // 40 - 49 not used
  private static final int NUGGET     = 50;         // 51 - 53 not used
  private static final int COINS      = 54;
  private static final int CHEST      = 55;
  private static final int EGGS       = 56;
  private static final int TRIDENT    = 57;
  private static final int VASE       = 58;
  private static final int EMERALD    = 59;
  private static final int PYRAMID    = 60;
  private static final int PEARL      = 61;
  private static final int RUG        = 62;
  private static final int SPICES     = 63;
  private static final int CHAIN      = 64;

  //	Verb definitions
  private static final int NULLX      = 21;
  private static final int BACK       = 8;
  private static final int LOOK       = 57;
  private static final int CAVE       = 67;
  private static final int ENTRANCE   = 64;
  private static final int DEPRESSION = 63;

  //	Action verb definitions
  private static final int TAKE       = 1;
  private static final int DROP       = 2;
  private static final int SAY        = 3;
  private static final int OPEN       = 4;
  private static final int NOTHING    = 5;
  private static final int LOCK       = 6;
  private static final int ON         = 7;
  private static final int OFF        = 8;
  private static final int WAVE       = 9;
  private static final int CALM       = 10;
  private static final int WALK       = 11;
  private static final int KILL       = 12;
  private static final int POUR       = 13;
  private static final int EAT        = 14;
  private static final int DRINK      = 15;
  private static final int RUB        = 16;
  private static final int THROW      = 17;
  private static final int QUIT       = 18;
  private static final int FIND       = 19;
  private static final int INVENTORY  = 20;
  private static final int FEED       = 21;
  private static final int FILL       = 22;
  private static final int BLAST      = 23;
  private static final int SCORE      = 24;
  private static final int FOO        = 25;
  private static final int BRIEF      = 26;
  private static final int READ       = 27;
  private static final int BREAK      = 28;
  private static final int WAKE       = 29;
  private static final int SUSPEND    = 30;
  private static final int HOURS      = 31;
  private static final int LOG        = 32;

  //	BIT mapping of "locationStatus" array which indicates location status
  static final int LIGHT              = 1;          // If light present
  static final int IS_OIL             = 2;          // If liquid is Oil, else water
  static final int LIQUID             = 4;          // if liquid present
  static final int NOPIRAT            = 8;          // If no Pirate present
  static final int FORCED_MOVE        = 2;          // Shares bit with WATOIL


  //	Database variables
  private Actions[]   travel = new Actions[MAXTRAV];
  private int[]       actmsg = new int[32];         //	action messages

  //	English variables
  private int         verb, object, motion;
  private String      word1;
  private String      word2;

  //	Play variables
  private int     loc, oldloc, oldloc2, newloc;     //	location variables
  private int     tally, tally2;                    //	item counts
  private int     limit;                            //	time limit until batteries run down
  private int     holding;                          //	count of held items
  private int     detail;                           //	LOOK count
  private int     knfloc;                           //	knife location
  private int     clock, clock2;                    //	timing variables
  private int     dflag;                            //	dwarf flag (0, 1, 2 . . .)
  private int     daltloc;                          //	alternate appearance
  private boolean dkill;                            //	dwarves killed
  private int     chloc, chloc2;                    //	chest locations
  private int     bonus;                            //	to pass to end
  private int     numdie;                           //	number of deaths
  private int     object1;                          //	to help intrans.
  private int     foobar;                           //	fie fie foe foo...
  private boolean gaveup;                           //	true if he quit early
  private boolean lmwarn;                           //	lamp warning flag
  private boolean wzdark, closing, closed;          //	game state flags
  private boolean panic = false;                            //	??

  private int[] objectLocation = new int[MAXOBJ];   //	object location
  private int[] fixedObjLocation = new int[MAXOBJ]; //	second object loc
  private int[] objectStatus = new int[MAXOBJ];     //	status of object
  private int[] odloc = new int[DWARFMAX];          //	dwarf old locations
  private int[] dloc = new int[DWARFMAX];           //	dwarf locations
  private boolean[] dseen = new boolean[DWARFMAX];  //	dwarf seen flag

  static class Actions {
    int tdest;
    int tverb;
    int tcond;
  }

  private Adventure () throws Exception {
    super("Colossal Cave Adventure");
    JPanel inPanel = new JPanel(new BorderLayout());
    Border border = BorderFactory.createEmptyBorder(1, 6, 1, 6);
    border = BorderFactory.createCompoundBorder(border, BorderFactory.createTitledBorder("Type Commands Here"));
    inPanel.setBorder(border);
    input = new TextField();
    inPanel.add(input, BorderLayout.CENTER);
    input.setFont(font);
    input.requestFocus();
    teleprinter = new Teleprinter();
    teleprinter.setFont(font);
    JScrollPane scroll = new JScrollPane(teleprinter);
    scroll.setBorder(BorderFactory.createEmptyBorder(1, 6, 1, 6));
    setLayout(new BorderLayout(2, 2));
    add(scroll, BorderLayout.CENTER);
    add(inPanel, BorderLayout.SOUTH);
    teleprinter.setColumns(60);
    teleprinter.setRows(18);
    // Add window close handler
    addWindowListener(new WindowAdapter() {
      public void windowClosing (WindowEvent ev) {
        System.exit(0);
      }
    });
    setLocationRelativeTo(null);
    pack();
    setResizable(false);
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    Preferences prefs = Preferences.userRoot().node(this.getClass().getName());
    setLocation(prefs.getInt("window.x", 100), prefs.getInt("window.y", 100));
    // Track window resize/move events and save in prefs
    addComponentListener(new ComponentAdapter() {
      public void componentMoved (ComponentEvent ev) {
        Rectangle bounds = ev.getComponent().getBounds();
        prefs.putInt("window.x", bounds.x);
        prefs.putInt("window.y", bounds.y);
      }
    });
    // Setup "Teleprinter" Menu
    JMenuBar menuBar = new JMenuBar();
    JMenu menu = new JMenu("Teleprinter");
    ButtonGroup group = new ButtonGroup();
    int baudRate = prefs.getInt("baud.rate", 2400);
    teleprinter.setSpeed(baudRate);
    for (int baud : new int[] {300, 1200, 2400, 0}) {
      JRadioButtonMenuItem item = new JRadioButtonMenuItem(baud > 0 ? Integer.toString(baud) : "Full Speed");
      item.addActionListener(ev -> {
        teleprinter.setSpeed(baud);
        prefs.putInt("baud.rate", baud);
      });
      menu.add(item);
      group.add(item);
      item.setSelected(baud == baudRate);
    }
    menuBar.add(menu);
    setJMenuBar(menuBar);
    setVisible(true);
    ticker.start();
    input.addActionListener(e -> {
      inText = input.getText();
      input.setText("");
      teleprinter.appendText("\n" + inText + "\n");
      synchronized (ticker) {
        ticker.notifyAll();
      }
    });
    // Add pop up cheat menu
    teleprinter.addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed (MouseEvent ev) {
        if (ev.isPopupTrigger()) {
          JPopupMenu popup = new JPopupMenu();
          JCheckBoxMenuItem mItem1 = new JCheckBoxMenuItem("Lithium Batteries");
          mItem1.setState(lithiumBatteries);
          mItem1.addActionListener(e1 -> lithiumBatteries = mItem1.getState());
          popup.add(mItem1);
          JCheckBoxMenuItem mItem2 = new JCheckBoxMenuItem("No Dwarves or Pirate");
          mItem2.setState(noDwarvesOrPirate);
          mItem2.addActionListener(e12 -> noDwarvesOrPirate = mItem2.getState());
          popup.add(mItem2);
          JCheckBoxMenuItem mItem3 = new JCheckBoxMenuItem("Show Legal Moves");
          mItem3.setState(noDwarvesOrPirate);
          mItem3.addActionListener(e12 -> showMoves = mItem3.getState());
          popup.add(mItem3);
          popup.show(ev.getComponent(), ev.getX(), ev.getY());
        }
      }
    });
  }

  public static void main (String[] args) throws Exception {
    new Adventure();
  }

  private void print (String str) {
    teleprinter.appendText(str + "\n");
  }

  public void run () {
    initplay();
    printResponse(1);                                 // Somewhere nearby is Colossal Cave...
    while (true) {
      doTurn();
    }
  }

  //	Initialize adventure play variables

  private void initIntArray (int[] dst, int[][] src) {
    for (int[] ints : src) {
      System.arraycopy(ints, 1, dst, ints[0], ints.length - 1);
    }
  }

  private void initplay () {
    // Initialize objectLocation[] array (objectLocation[object] = location)
    initIntArray(objectLocation, new int[][] {
      {1, 3, 3, 8, 10, 11, 0, 14, 13, 94, 96},            //
      {11, 19, 17, 101, 103, 0, 106, 0, 0, 3, 3},
      {23, 109, 25, 23, 111, 35, 0, 97},
      {31, 119, 117, 117, 0, 130, 0, 126, 140, 0, 96},
      {50, 18, 27, 28, 29, 30},
      {56, 92, 95, 97, 100, 101, 0, 119, 127, 130}});
    if (listObjects) {
      // DEBUG: List where objects are placed
      System.out.println("Object locations");
      for (int ii = 0; ii < objectLocation.length; ii++) {
        int objLoc = objectLocation[ii];
        if (objLoc > 0) {
          String objName = ObjectText.list[ii - 1][0];
          String locName = stripPrefix(Map.cave[objLoc - 1].shortText);
          System.out.println(ii + "\t" + objName + " \t-> " + locName + " - room: " + objLoc);
        }
      }
    }
    // Initialize fixedObjLocation[] array (fixedObjLocation[object] = location)
    initIntArray(fixedObjLocation, new int[][]{
      {3, 9, 0, 0, 0, 15, 0, -1},
      {11, -1, 27, -1, 0, 0, 0, -1},
      {23, -1, -1, 67, -1, 110, 0, -1, -1},
      {31, 121, 122, 122, 0, -1, -1, -1, -1, 0, -1},
      //{62, 121, -1}});                                            // bug fix that prevented taking spices
      {62, 121}});
    if (listObjects) {
      // DEBUG: List where fixed objects are placed
      System.out.println("Fixed Object locations");
      for (int ii = 0; ii < fixedObjLocation.length; ii++) {
        int objLoc = fixedObjLocation[ii];
        if (objLoc > 0) {
          String objName = ObjectText.list[ii - 1][0];
          String locName = stripPrefix(Map.cave[objLoc - 1].shortText);
          System.out.println(ii + "\t" + objName + " \t-> " + locName + " - room: " + objLoc);
        }
      }
    }
    // Initialize default verb messages
    initIntArray(actmsg, new int[][]{
      {0, 0, 24, 29, 0, 33, 0, 33, 38, 38, 42, 14},
      {11, 43, 110, 29, 110, 73, 75, 29, 13, 59, 59},
      {21, 174, 109, 67, 13, 147, 155, 195, 146, 110, 13, 13}});
    for (int ii = 0; ii < MAXTRAV; ii++) {
      travel[ii] = new Actions();
    }
    // Initialize various flags and other variables
    for (Map.Room room : Map.cave) {
      room.visited = false;
    }
    for (int ii = 50; ii < objectStatus.length; ii++) {
      objectStatus[ii] = -1;
    }
    wzdark = closed = closing = false;
    holding = detail = 0;
    limit = lithiumBatteries ? 5000 : 100;                // number of turns until batteries die
    tally = 15;
    tally2 = 0;
    newloc = 3;
    loc = oldloc = oldloc2 = 1;
    knfloc = 0;
    chloc = 114;
    chloc2 = 140;
    // set up dwarf locations
    int[] dwarfLocations = {0, 19, 27, 33, 44, 64, 114};
    System.arraycopy(dwarfLocations, 0, dloc, 0, dwarfLocations.length);
    // clear dwarf old locations
    dkill = false;
    clock = 30;
    clock2 = 50;
    panic = false;
    bonus = 0;
    numdie = 0;
    daltloc = 18;
    lmwarn = false;
    foobar = 0;
    dflag = 0;
    gaveup = false;
  }

  private void doTurn () {
    //	if closing, then he can't leave except via the main office.
    if (newloc < 9 && newloc != 0 && closing) {
      printResponse(130);                               // I don't know where the cave is, but...
      newloc = loc;
      if (!panic) {
        clock2 = 15;
      }
      panic = true;
    }
    //	see if a dwarf has seen him and has come from where he wants to go.
    if (newloc != loc && !forcedMove(loc) && (Map.cave[loc - 1].info & NOPIRAT) == 0) {
      for (int ii = 1; ii < (DWARFMAX - 1); ++ii) {
        if (odloc[ii] == newloc && dseen[ii]) {
          newloc = loc;
          printResponse(2);                             // A little dwarf with a big knife blocks your way.
          break;
        }
      }
    }
    if (!noDwarvesOrPirate) {
      dwarves();                                          //	& special dwarf (pirate who steals)
    }
    //if (loc != newloc) {
      loc = newloc;
      if (loc == 0) {                                   //	check for death
        death();
        return;
      }
      if (forcedMove(loc)) {                            // check for forcedMove move
        describeLocation();
        doMove();
        return;
      }
      if (wzdark && isDark() && pct(35)) {              // check for wandering in the dark
        printResponse(23);                              // You fell into a pit and broke every bone in your body!
        oldloc2 = loc;
        death();
        return;
      }
      describeLocation();                               // describe his situation
      if (!isDark()) {
        Map.cave[loc - 1].visited = true;
        printVisibleItems();
      }
    //}
    if (closed) {
      if (objectStatus[OYSTER] < 0 && isCarryingObject(OYSTER)) {
        printObjectDescription(OYSTER, 1);
      }
      for (int ii = 1; ii < objectStatus.length; ++ii) {
        if (isCarryingObject(ii) && objectStatus[ii] < 0) {
          objectStatus[ii] = -1 - objectStatus[ii];
        }
      }
    }
    wzdark = isDark();
    if (knfloc > 0 && knfloc != loc) {
      knfloc = 0;
    }
    if (stimer()) {          //	as the grains of sand slip by
      return;
    }
    while (!analyzeTwoWordSentence()) {     //	retrieve player instructions
    }
    if (motion != 0) {       //	execute player instructions
      doMove();
    } else if (object != 0) {
      doobj();              // act on object
    } else {
      intransitiveVerb();
    }
  }

  //	Routine to describe current location

  private void describeLocation () {
    if (isCarryingObject(BEAR)) {
      printResponse(141);                               // You are being followed by a very large, tame bear.
    }
    if (isDark()) {
      printResponse(16);                                // It is now pitch dark.  If you proceed...
    } else if (Map.cave[loc - 1].visited) {
      printShortLocation(loc);
    } else {
      printLongLocation(loc);
    }
    if (loc == 33 && pct(25) && !closing) {
      printResponse(8);                                 // A hollow voice says 'Plugh'.
    }
    if (showMoves) {
      // Cheat features for debugging
      getMoves(loc);
      System.out.println(Map.cave[loc - 1].shortText + " - room " + loc);
      for (int kk = 0; travel[kk].tdest >= 0; ++kk) {
        int rdest = travel[kk].tdest;
        int rverb = travel[kk].tverb;
        switch (rverb) {
        case 45:    // North
          showDirections("N  ", rdest);
          break;
        case 46:    // South
          showDirections("S  ", rdest);
          break;
        case 43:    // East
          showDirections("E  ", rdest);
          break;
        case 44:    // West
          showDirections("W  ", rdest);
          break;
        case 50:    // NW
          showDirections("NW ", rdest);
          break;
        case 47:    // NE
          showDirections("NE ", rdest);
          break;
        case 49:    // SW
          showDirections("SW ", rdest);
          break;
        case 48:    // SE
          showDirections("SE ", rdest);
          break;
        case 29:    // Up
          showDirections("Up ", rdest);
          break;
        case 30:    // Down
          showDirections("Dn ", rdest);
          break;
        }
      }
    }
  }

  private String stripPrefix (String loc) {
    String[] strip = {"You're in ", "You are on the ", "You are in a ", "You are in the ", "You are at one end of an ",
                      "You're at ", "You're ", "You are in a ", "on "};
    for (String prefix : strip) {
      if (loc.startsWith(prefix)) {
        return loc.substring(prefix.length());
      }
    }
    return loc;
  }

  private void showDirections (String dir, int rdest) {
    try {
      String tmp = "  " + dir + "to room " + rdest;
      System.out.print(tmp);
      for (int ii = tmp.length(); ii < 20; ii++) {
        System.out.print(" ");
      }
      String desc = rdest > 0 ? Map.cave[rdest - 1].shortText : "";
      System.out.println(stripPrefix(desc));
    } catch (Exception ignored) {}
  }

  //	Routine to describe visible items

  private void printVisibleItems () {
    for (int ii = 1; ii < objectStatus.length; ii++) {
      if (at(ii)) {
        if (ii == STEPS && isCarryingObject(NUGGET)) {
          continue;
        }
        if (objectStatus[ii] < 0) {
          if (closed) {
            continue;
          } else {
            objectStatus[ii] = 0;
            if (ii == RUG || ii == CHAIN) {
              ++objectStatus[ii];
            }
            --tally;
          }
        }
        int state;
        if (ii == STEPS && loc == fixedObjLocation[STEPS]) {
          state = 1;
        } else {
          state = objectStatus[ii];
        }
        printObjectDescription(ii, state);
      }
    }
    if (tally == tally2 && tally != 0 && limit > 35) {
      limit = 35;
    }
  }

  //	Routine to handle motion requests

  private void doMove () {
    getMoves(loc);
    switch (motion) {
    case NULLX:
      break;
    case BACK:
      goBack();
      break;
    case LOOK:
      if (detail++ < 3) {
        printResponse(15);                              // Sorry, but I am not allowed to give more detail.
      }
      wzdark = false;
      Map.cave[loc - 1].visited = false;
      newloc = loc;
      //loc = 0;
      break;
    case CAVE:
      if (loc < 8) {
        printResponse(57);                              // I don't know where the cave is, but...
      } else {
        printResponse(58);                              // I need more detailed instructions to do that.
      }
      break;
    default:
      oldloc2 = oldloc;
      oldloc = loc;
      movePlayer();
    }
  }

  //	Routine to handle request to return from whence we came!

  private void goBack () {
    int want, temp;
    Actions[] travelSave = new Actions[MAXTRAV];
    for (int ii = 0; ii < travelSave.length; ii++) {
      travelSave[ii] = new Actions();
    }
    if (forcedMove(oldloc)) {
      want = oldloc2;
    } else {
      want = oldloc;
    }
    oldloc2 = oldloc;
    oldloc = loc;
    int k2 = 0;
    if (want == loc) {
      printResponse(91);                                // Sorry, but I no longer seem to remember...
      return;
    }
    copytrv(travel, travelSave);
    for (int kk = 0; travel[kk].tdest != -1; ++kk) {
      if (travel[kk].tcond == 0 && travel[kk].tdest == want) {
        motion = travel[kk].tverb;
        movePlayer();
        return;
      }
      if (travel[kk].tcond == 0) {
        k2 = kk;
        temp = travel[kk].tdest;
        getMoves(temp);
        if (forcedMove(temp) && travel[0].tdest == want) {
          k2 = temp;
        }
        copytrv(travelSave, travel);
      }
    }
    if (k2 != 0) {
      motion = travel[k2].tverb;
      movePlayer();
    } else {
      printResponse(140);                               // You can't get there from here.
    }
  }

  //	Routine to copy a travel array

  private void copytrv (Actions[] trav1, Actions[] trav2) {
    for (int ii = 0; ii < MAXTRAV; ++ii) {
      trav2[ii].tdest = trav1[ii].tdest;
      trav2[ii].tverb = trav1[ii].tverb;
      trav2[ii].tcond = trav1[ii].tcond;
    }
  }

  //	Routine to figure out a new location
  //	given current location and a motion.

  private void movePlayer () {
    int hitflag;
    int rdest = 0, rverb, rcond, robject;
    newloc = loc;
    int mvflag = hitflag = 0;
    int pctt = rand() % 100;
    for (int kk = 0; travel[kk].tdest >= 0 && mvflag == 0; ++kk) {
      rdest = travel[kk].tdest;
      rverb = travel[kk].tverb;
      rcond = travel[kk].tcond;
      robject = rcond % 100;
      if ((rverb != 1) && (rverb != motion) && hitflag == 0) {
        continue;
      }
      ++hitflag;
      switch (rcond / 100) {
      case 0:
        if ((rcond == 0) || (pctt < rcond)) {
          ++mvflag;
        }
        break;
      case 1:
        if (robject == 0) {
          ++mvflag;
        } else if (isCarryingObject(robject)) {
          ++mvflag;
        }
        break;
      case 2:
        if (isCarryingObject(robject) || at(robject)) {
          ++mvflag;
        }
        break;
      case 3:
      case 4:
      case 5:
      case 7:
        if (objectStatus[robject] != (rcond / 100) - 3) {
          ++mvflag;
        }
        break;
      default:
        bug(37);
      }
    }
    if (mvflag == 0) {
      badMove();
    } else if (rdest > 500) {
      printResponse(rdest - 500);
    } else if (rdest > 300) {
      specialMove(rdest);
    } else {
      newloc = rdest;
    }
  }

  //	The player tried a poor move option.

  private void badMove () {
    if (motion >= 43 && motion <= 50) {                 // for unavailable compass moves
      printResponse(9);                                 // There is no way to go that direction.
    } else if (verb == FIND || verb == INVENTORY) {
      printResponse(59);                                // I can only tell you what you see...
    } else {
      switch (motion) {
      case 7:                                           // forward, onward
      case 36:                                          // left
      case 37:                                          // right
        printResponse(10);                              // I am unsure how you are facing.
        break;
      case 11:                                          // exit, leave, outside, out
      case 19:                                          // in, inside, inward
        printResponse(11);                              // I don't know in from out here.
        break;
      case 62:                                          // xyzzy
      case 65:                                          // plugh
        printResponse(42);                              // Nothing happens.
        break;
      case 17:                                          // crawl
        printResponse(80);                              // Which way?
        break;
      default:
        printResponse(12);                              // I don't know how to apply that word here.
      }
    }
  }

  //	Routine to handle very special movement.

  private void specialMove (int rdest) {
    switch (rdest - 300) {
    case 1:                                             //	plover movement via alcove
      // If carryng nothing, or only the emerald
      if (holding == 0 || (holding == 1 && isCarryingObject(EMERALD))) {
        newloc = (99 + 100) - loc;                      // moves between room 99 and 100
      } else {
        printResponse(117);                             // Something you're carrying won't fit through the tunnel...
      }
      break;
    case 2:                                             //	trying to remove plover, bad route
      dropObject(EMERALD, loc);
      break;
    case 3:                                             //	troll bridge
      if (objectStatus[TROLL] == 1) {                   // If troll is active
        printObjectDescription(TROLL, 1);               // "The troll steps out from beneath the bridge and blocks your way."
        objectStatus[TROLL] = 0;
        moveObj(TROLL2, 0);                             // Destroy Troll 2
        moveObj((TROLL2 + MAXOBJ), 0);
        moveObj(TROLL, 117);                            // Move troll to room 117
        moveObj((TROLL + MAXOBJ), 122);
        juggle(CHASM);                                  // Does nothing...
        newloc = loc;
      } else {
        newloc = (loc == 117 ? 122 : 117);              // If at loc 117, move to 122, or vice versa
        if (objectStatus[TROLL] == 0) {
          ++objectStatus[TROLL];
        }
        if (isCarryingObject(BEAR)) {                   // Can't cross with bear
          printResponse(162);                           // Just as you reach the other side, the bridge buckles...
          objectStatus[CHASM] = 1;
          objectStatus[TROLL] = 2;
          dropObject(BEAR, newloc);
          fixedObjLocation[BEAR] = -1;
          objectStatus[BEAR] = 3;
          if (objectStatus[SPICES] < 0) {
            ++tally2;
          }
          oldloc2 = newloc;
          death();                                      // death with a chance of resurrection
        }
      }
      break;
    default:
      bug(38);
    }
  }

  //	Routine to handle player's demise via waking up the dwarves...

  private void dwarfend () {
    death();
    normend();
  }

  //	normal end of game

  private void normend () {
    score();
    //	exit();
  }

  //	scoring

  private void score () {
    int score;
    int tt = 0;
    for (int ii = 50; ii <= MAXTRS; ++ii) {
      int kk;
      if (ii == CHEST) {
        kk = 14;
      } else if (ii > CHEST) {
        kk = 16;
      } else {
        kk = 12;
      }
      if (objectStatus[ii] >= 0) {
        tt += 2;
      }
      if (objectLocation[ii] == 3 && objectStatus[ii] == 0) {
        tt += kk - 2;
      }
    }
    print("Treasures: " + (score = tt));
    tt = (MAXDIE - numdie) * 10;
    if (tt != 0) {
      print("Survival: " + tt);
    }
    score += tt;
    if (!gaveup) {
      score += 4;
    }
    tt = (dflag != 0) ? 25 : 0;
    if (tt != 0) {
      print("Getting well in: " + tt);
    }
    score += tt;
    tt = closing ? 25 : 0;
    if (tt != 0) {
      print("Masters section: " + tt);
    }
    score += tt;
    if (closed) {
      if (bonus == 0) {
        tt = 10;
      } else if (bonus == 135) {
        tt = 25;
      } else if (bonus == 134) {
        tt = 30;
      } else if (bonus == 133) {
        tt = 45;
      }
      print("Bonus: " + tt);
      score += tt;
    }
    if (objectLocation[MAGAZINE] == 108) {
      score += 1;
    }
    score += 2;
    print("Score: " + score);
  }

  //	Routine to handle the passing on of one
  //	of the player's incarnations...

  private void death () {
    if (!closing) {
      boolean yea = askYesNoQuestion(81 + numdie * 2, 82 + numdie * 2, 54);
      if (++numdie >= MAXDIE || !yea) {
        normend();
      }
      objectLocation[WATER] = 0;
      objectLocation[OIL] = 0;
      if (isCarryingObject(LAMP)) {
        objectStatus[LAMP] = 0;
      }
      for (int jj = 1; jj < MAXOBJ; ++jj) {
        int ii = MAXOBJ - jj;
        if (isCarryingObject(ii)) {
          dropObject(ii, ii == LAMP ? 1 : oldloc2);
        }
      }
      newloc = 3;
      oldloc = loc;
      return;
    }
    //	closing -- no resurrection...
    printResponse(131);                                 // It looks as though you're dead.
    ++numdie;
    normend();
  }

  //	Routine to process an object.

  private void doobj () {
    if (fixedObjLocation[object] == loc || objectPresent(object)) {     //	Is object objectPresent?  if so, transitive
      trobj();
    } else if (object == GRATE) {
      if (loc == 1 || loc == 4 || loc == 7) {                           //	Did he give grate as destination?
        motion = DEPRESSION;
        doMove();
      } else if (loc > 9 && loc < 15) {
        motion = ENTRANCE;
        doMove();
      }
    }
    //	Is it a dwarf he is after?
    else if (dwarvesPresent() != 0 && dflag >= 2) {
      object = DWARF;
      trobj();
    } else if ((liq() == object && objectPresent(BOTTLE)) ||
          liqloc(loc) == object) {                                        //	Is he trying to get/use a liquid?
      trobj();
    } else if (object == PLANT && at(PLANT2) &&
          objectStatus[PLANT2] == 0) {                                    // Reference to plant
      object = PLANT2;
      trobj();
    } else if (object == KNIFE && knfloc == loc) {                        //	Is he trying to grab a knife?
      printResponse(116);                                                 // The Dwarves' knives vanish as they strike the walls of the cave.
      knfloc = -1;
    } else if (object == ROD && objectPresent(ROD2)) {                    //	Is he trying to get at dynamite?
      object = ROD2;
      trobj();
    } else {
      print("I see no " + getObjectName() + " here.");
    }
  }

  //	Routine to process an object being referred to.

  private void trobj () {
    if (verb != 0) {
      trverb();
    } else {
      print("What do you want to do with the " + getObjectName() + "?");
    }
  }

  //	Routine to print word corresponding to object

  private String getObjectName () {
    int[] typeVal = new int[2];
    analyzeWord(word1, typeVal);
    return (typeVal[0] == 1 ? word1 : word2);
  }

  //	Handle dwarf and pirate

  private void dwarves () {
    int attack, stick, dtotal;

    //	See if dwarves allowed here
    if (newloc == 0 || forcedMove(newloc) || (Map.cave[newloc - 1].info & NOPIRAT) != 0) {
      return;
    }
    //	See if dwarves are active.
    if (dflag == 0) {
      if (newloc > 15) {
        ++dflag;
      }
      return;
    }
    //	If first close encounter (of 3rd kind) kill 0, 1 or 2
    if (dflag == 1) {
      if (newloc < 15 || pct(95)) {
        return;
      }
      ++dflag;
      for (int ii = 1; ii < 3; ++ii) {
        if (pct(50)) {
          dloc[rand() % 5 + 1] = 0;
        }
      }
      for (int ii = 1; ii < (DWARFMAX - 1); ++ii) {
        if (dloc[ii] == newloc) {
          dloc[ii] = daltloc;
        }
        odloc[ii] = dloc[ii];
      }
      printResponse(3);                                 // A little dwarf just walked around a corner,...
      dropObject(AXE, newloc);
      return;
    }
    dtotal = attack = stick = 0;
    for (int ii = 1; ii < DWARFMAX; ++ii) {
      if (dloc[ii] == 0) {
        continue;
      }
      //	move a dwarf at random.  we don't have a matrix around to do it
      //	as in the original version...
      int jj = 0;
      for (int tryy = 1; tryy < 20; ++tryy) {
        jj = rand() % 106 + 15;                //	allowed area
        if (jj != odloc[ii] && jj != dloc[ii] &&
            !(ii == (DWARFMAX - 1) && (Map.cave[jj - 1].info & NOPIRAT) != 0)) {
          break;
        }
      }
      if (jj == 0) {
        jj = odloc[ii];
      }
      odloc[ii] = dloc[ii];
      dloc[ii] = jj;
      dseen[ii] = (dseen[ii] && newloc >= 15) ||
        dloc[ii] == newloc || odloc[ii] == newloc;
      if (!dseen[ii]) {
        continue;
      }
      dloc[ii] = newloc;
      if (ii == 6) {
        dopirate();
      } else {
        ++dtotal;
        if (odloc[ii] == dloc[ii]) {
          ++attack;
          if (knfloc >= 0) {
            knfloc = newloc;
          }
          if ((rand() % 1000) < (95 * (dflag - 2))) {
            ++stick;
          }
        }
      }
    }
    if (dtotal == 0) {
      return;
    }
    if (dtotal > 1) {
      print("There are " + dtotal + " threatening little dwarves in the room with you!");
    } else {
      printResponse(4);                                 // There is a threatening little dwarf in the room with you!
    }
    if (attack == 0) {
      return;
    }
    if (dflag == 2) {
      ++dflag;
    }
    int kk;
    if (attack > 1) {
      print("" + attack + " of them throw knives at you!!");
      kk = 6;
    } else {
      printResponse(5);                                 // One sharp, nasty knife is thrown at you!
      kk = 52;
    }
    if (stick <= 1) {
      printResponse(stick + kk);
      if (stick == 0) {
        return;
      }
    } else {
      print("" + stick + " of them get you !!!");
    }
    oldloc2 = newloc;
    death();
  }

  //	pirate stuff

  private void dopirate () {
    if (newloc == chloc || objectStatus[CHEST] >= 0) {
      return;
    }
    int kk = 0;
    for (int jj = 50; jj <= MAXTRS; ++jj) {
      if (jj != PYRAMID || (newloc != objectLocation[PYRAMID] && newloc != objectLocation[EMERALD])) {
        if (isCarryingObject(jj)) {
          stealit();
          return;
        }
        if (objectPresent(jj)) {
          ++kk;
        }
      }
    }
    if (tally == (tally2 + 1) && kk == 0 && objectLocation[CHEST] == 0 &&
      objectPresent(LAMP) && objectStatus[LAMP] == 1) {
      printResponse(186);                               // There are faint rustling noises from the darkness behind you. (etc)
      moveObj(CHEST, chloc);
      moveObj(MESSAGE, chloc2);
      dloc[6] = chloc;
      odloc[6] = chloc;
      dseen[6] = false;
      return;
    }
    if (odloc[6] != dloc[6] && pct(20)) {
      printResponse(127);                               // There are faint rustling noises from the darkness behind you.
    }
  }

  //	Steal Treasure

  private void stealit () {
    printResponse(128);                                 // Out from the shadows behind you pounces a bearded pirate!
    if (objectLocation[MESSAGE] == 0) {
      moveObj(CHEST, chloc);
    }
    moveObj(MESSAGE, chloc2);
    for (int jj = 50; jj <= MAXTRS; ++jj) {
      if (jj == PYRAMID && (newloc == objectLocation[PYRAMID] || newloc == objectLocation[EMERALD])) {
        continue;
      }
      if (at(jj) && fixedObjLocation[jj] == 0) {
        carryObject(jj);
      }
      if (isCarryingObject(jj)) {
        dropObject(jj, chloc);
      }
    }
    dloc[6] = chloc;
    odloc[6] = chloc;
    dseen[6] = false;
  }

  //	Special time limit stuff...

  private boolean stimer () {
    foobar = foobar > 0 ? -foobar : 0;
    if (tally == 0 && loc >= 15 && loc != 33) {
      --clock;
    }
    if (clock == 0) {
      //	Start closing the cave
      objectStatus[GRATE] = 0;
      objectStatus[FISSURE] = 0;
      for (int ii = 1; ii < DWARFMAX; ++ii) {
        dseen[ii] = false;
      }
      moveObj(TROLL, 0);
      moveObj((TROLL + MAXOBJ), 0);
      moveObj(TROLL2, 117);
      moveObj((TROLL2 + MAXOBJ), 122);
      juggle(CHASM);
      if (objectStatus[BEAR] != 3) {
        destroyObject(BEAR);
      }
      objectStatus[CHAIN] = 0;
      fixedObjLocation[CHAIN] = 0;
      objectStatus[AXE] = 0;
      fixedObjLocation[AXE] = 0;
      printResponse(129);                               // Cave closing soon.
      clock = -1;
      closing = true;
      return false;
    }
    if (clock < 0) {
      --clock2;
    }
    if (clock2 == 0) {
      //	set up storage room...
      //	and close the cave...
      objectStatus[BOTTLE] = putObject(BOTTLE, 115, 1);
      objectStatus[PLANT] = putObject(PLANT, 115, 0);
      objectStatus[OYSTER] = putObject(OYSTER, 115, 0);
      objectStatus[LAMP] = putObject(LAMP, 115, 0);
      objectStatus[ROD] = putObject(ROD, 115, 0);
      objectStatus[DWARF] = putObject(DWARF, 115, 0);
      loc = 115;
      oldloc = 115;
      newloc = 115;
      putObject(GRATE, 116, 0);
      objectStatus[SNAKE] = putObject(SNAKE, 116, 1);
      objectStatus[BIRD] = putObject(BIRD, 116, 1);
      objectStatus[CAGE] = putObject(CAGE, 116, 0);
      objectStatus[ROD2] = putObject(ROD2, 116, 0);
      objectStatus[PILLOW] = putObject(PILLOW, 116, 0);
      objectStatus[MIRROR] = putObject(MIRROR, 115, 0);
      fixedObjLocation[MIRROR] = 116;
      for (int ii = 1; ii < MAXOBJ; ++ii) {
        if (isCarryingObject(ii)) {
          destroyObject(ii);
        }
      }
      printResponse(132);                               // The cave is now closed.
      closed = true;
      return true;
    }
    if (objectStatus[LAMP] == 1) {
      --limit;
    }
    if (limit <= 30 &&
      objectPresent(BATTERIES) && objectStatus[BATTERIES] == 0 &&
      objectPresent(LAMP)) {
      printResponse(188);                               // Your lamp is getting dim.. (replacing batteries)
      objectStatus[BATTERIES] = 1;
      if (isCarryingObject(BATTERIES)) {
        dropObject(BATTERIES, loc);
      }
      limit += 2500;
      lmwarn = false;
      return false;
    }
    if (limit == 0) {
      --limit;
      objectStatus[LAMP] = 0;
      if (objectPresent(LAMP)) {
        printResponse(184);                             // Your lamp has run out of power.
      }
      return false;
    }
    if (limit < 0 && loc <= 8) {
      printResponse(185);                               // There's not much point in wandering around out here (no lamp)
      gaveup = true;
      normend();
    }
    if (limit <= 30) {
      if (lmwarn || !objectPresent(LAMP)) {
        return false;
      }
      lmwarn = true;
      if (objectLocation[BATTERIES] == 0) {
        printResponse(183);                             // Your lamp is getting dim..
      } else if (objectStatus[BATTERIES] == 1) {
        printResponse(189);                             // Your lamp is getting dim, and you're out of spare batteries.
      } else {
        printResponse(187);                             // Your lamp is getting dim. (get batteries)
      }
      return false;
    }
    return false;
  }

  //	random number seed

  // private static long rnum = 0;                      // Note: fixed seed always repeated sequence
  private Random random = new Random();                 // Use Java's random number generation instead

  //	get random number

  private int rand () {
    //rnum = rnum * 0x41C64E6D + 0x3039;                // Old random logic
    //return ((int) (rnum >> 16) & 0x7FFF);
    return random.nextInt() &  0x7FFF;
  }

  //	Routine to process a transitive verb

  private void trverb () {
    switch (verb) {
    case CALM:
    case WALK:
    case QUIT:
    case SCORE:
    case FOO:
    case BRIEF:
    case SUSPEND:
    case HOURS:
    case LOG:
      actspk(verb);
      break;
    case TAKE:
      takeObject();
      break;
    case DROP:
      dropObject();
      break;
    case OPEN:
    case LOCK:
      openObject();
      break;
    case SAY:
      saySomething();
      break;
    case NOTHING:
      printResponse(54);                                // ok.
      break;
    case ON:
      turnObjectOn();
      break;
    case OFF:
      turnObjectOff();
      break;
    case WAVE:
      waveObject();
      break;
    case KILL:
      killObject();
      break;
    case POUR:
      pourObject();
      break;
    case EAT:
      eatObject();
      break;
    case DRINK:
      drinkObject();
      break;
    case RUB:
      if (object != LAMP) {
        printResponse(76);                              // Peculiar.  Nothing unexpected happens.
      } else {
        actspk(RUB);
      }
      break;
    case THROW:
      throwObject();
      break;
    case FEED:
      feedObject();
      break;
    case FIND:
    case INVENTORY:
      findObject();
      break;
    case FILL:
      fillObject();
      break;
    case READ:
      readObject();
      break;
    case BLAST:
      verbBlast();
      break;
    case BREAK:
      breakObject();
      break;
    case WAKE:
      wakeObject();
      break;
    default:
      print("This verb is not implemented yet.");
    }
  }

  //	CARRY TAKE etc.

  private void takeObject () {
    if (isCarryingObject(object)) {
      actspk(verb);
      return;
    }
    //	Special case objects and fixedObjLocation objects
    int msg = 25;                                       // You can't be serious!
    if (object == PLANT && objectStatus[PLANT] <= 0) {
      msg = 115;                                        // The plant has exceptionally deep roots and cannot be pulled free.
    }
    if (object == BEAR && objectStatus[BEAR] == 1) {
      msg = 169;                                        // The bear is still chained to the wall.
    }
    if (object == CHAIN && objectStatus[BEAR] != 0) {
      msg = 170;                                        // The chain is still locked.
    }
    if (fixedObjLocation[object] != 0) {
      printResponse(msg);
      return;
    }
    //	Special case for liquids
    if (object == WATER || object == OIL) {
      if (!objectPresent(BOTTLE) || liq() != object) {
        object = BOTTLE;
        if (isCarryingObject(BOTTLE) && objectStatus[BOTTLE] == 1) {
          fillObject();
          return;
        }
        if (objectStatus[BOTTLE] != 1) {
          msg = 105;                                    // Your bottle is already full.
        }
        if (!isCarryingObject(BOTTLE)) {
          msg = 104;                                    // You have nothing in which to carry it.
        }
        printResponse(msg);
        return;
      }
      object = BOTTLE;
    }
    if (holding >= 7) {
      printResponse(92);                                // You can't carry anything more.
      return;
    }
    //	Special case for bird.
    if (object == BIRD && objectStatus[BIRD] == 0) {
      if (isCarryingObject(ROD)) {
        printResponse(26);                              // The bird was unafraid when you entered, but...
        return;
      }
      if (!isCarryingObject(CAGE)) {
        printResponse(27);                              // You can catch the bird, but you cannot carry it.
        return;
      }
      objectStatus[BIRD] = 1;
    }
    if ((object == BIRD || object == CAGE) && objectStatus[BIRD] != 0) {
      carryObject((BIRD + CAGE) - object);
    }
    carryObject(object);
    //	Handle liquid in bottle
    int ii = liq();
    if (object == BOTTLE && ii != 0) {
      objectLocation[ii] = -1;
    }
    printResponse(54);                                  // ok.
  }

  //	DROP etc.

  private void dropObject () {
    //	Check for dynamite
    if (isCarryingObject(ROD2) && object == ROD && !isCarryingObject(ROD)) {
      object = ROD2;
    }
    if (!isCarryingObject(object)) {
      actspk(verb);
      return;
    }
    //	Snake and bird
    if (object == BIRD && objectPresent(SNAKE)) {
      printResponse(30);                                // The little bird attacks the green snake, and...
      if (closed) {
        dwarfend();
      }
      destroyObject(SNAKE);
      objectStatus[SNAKE] = -1;
    }
    //	Coins and vending machine
    else if (object == COINS && objectPresent(VEND)) {
      destroyObject(COINS);
      dropObject(BATTERIES, loc);
      printObjectDescription(BATTERIES, 0);
      return;
    }
    //	Bird and dragon (ouch!!)
    else if (object == BIRD && at(DRAGON) && objectStatus[DRAGON] == 0) {
      printResponse(154);                               // The little bird attacks the green dragon, and...
      destroyObject(BIRD);
      objectStatus[BIRD] = 0;
      if (objectLocation[SNAKE] != 0) {
        ++tally2;
      }
      return;
    }
    //	Bear and troll
    if (object == BEAR && at(TROLL)) {
      printResponse(163);                               // The bear lumbers toward the troll, who...
      moveObj(TROLL, 0);
      moveObj((TROLL + MAXOBJ), 0);
      moveObj(TROLL2, 117);
      moveObj((TROLL2 + MAXOBJ), 122);
      juggle(CHASM);
      objectStatus[TROLL] = 2;
    }
    //	Vase
    else if (object == VASE) {
      if (loc == 96) {
        printResponse(54);                              // ok.
      } else {
        objectStatus[VASE] = at(PILLOW) ? 0 : 2;
        printObjectDescription(VASE, objectStatus[VASE] + 1);
        if (objectStatus[VASE] != 0) {
          fixedObjLocation[VASE] = -1;
        }
      }
    }
    //	Handle liquid and bottle
    int ii = liq();
    if (ii == object) {
      object = BOTTLE;
    }
    if (object == BOTTLE && ii != 0) {
      objectLocation[ii] = 0;
    }
    //	Handle bird and cage
    if (object == CAGE && objectStatus[BIRD] != 0) {
      dropObject(BIRD, loc);
    }
    if (object == BIRD) {
      objectStatus[BIRD] = 0;
    }
    dropObject(object, loc);
  }

  //	LOCK, UNLOCK, OPEN, CLOSE etc.

  private void openObject () {
    switch (object) {
    case CLAM:
    case OYSTER:
      int oyclam = (object == OYSTER ? 1 : 0);
      if (verb == LOCK) {
        printResponse(61);                              // What?
      } else if (!isCarryingObject(TRIDENT)) {
        printResponse(22 + oyclam);
      } else if (isCarryingObject(object)) {
        printResponse(120 + oyclam);
      } else {
        destroyObject(CLAM);
        dropObject(OYSTER, loc);
        dropObject(PEARL, 105);
        printResponse(124 + oyclam);
      }
      break;
    case DOOR:
      printResponse(objectStatus[DOOR] == 1 ? 54 : 111);
      break;
    case CAGE:
      printResponse(32);                                // It has no lock.
      break;
    case KEYS:
      printResponse(55);                                // You can't unlock the keys.
      break;
    case CHAIN:
      if (!objectPresent(KEYS)) {
        printResponse(31);
      } else if (verb == LOCK) {
        if (objectStatus[CHAIN] != 0) {
          printResponse(34);
        } else if (loc != 130) {
          printResponse(173);                           // There is nothing here to which the chain can be locked.
        } else {
          objectStatus[CHAIN] = 2;
          if (isCarryingObject(CHAIN)) {
            dropObject(CHAIN, loc);
          }
          fixedObjLocation[CHAIN] = -1;
          printResponse(172);                           // The chain is now locked.
        }
      } else {
        if (objectStatus[BEAR] == 0) {
          printResponse(41);                            // There is no way to get past the bear to unlock the chain
        } else if (objectStatus[CHAIN] == 0) {
          printResponse(37);                            // It was already unlocked.
        } else {
          objectStatus[CHAIN] = 0;
          fixedObjLocation[CHAIN] = 0;
          if (objectStatus[BEAR] != 3) {
            objectStatus[BEAR] = 2;
          }
          fixedObjLocation[BEAR] = 2 - objectStatus[BEAR];
          printResponse(171);                           // The chain is now unlocked.
        }
      }
      break;
    case GRATE:
      if (!objectPresent(KEYS)) {
        printResponse(31);                              // You have no keys!
      } else if (closing) {
        if (!panic) {
          clock2 = 15;
          panic = true;
        }
        printResponse(130);                             // This exit is closed.  Please leave via main office.
      } else {
        int msg = 34 + objectStatus[GRATE];
        objectStatus[GRATE] = (verb == LOCK ? 0 : 1);
        printResponse(msg + 2 * objectStatus[GRATE]);
      }
      break;
    default:
      printResponse(33);                                // I don't know how to lock or unlock such a thing.
    }
  }

  //	SAY etc.

  private void saySomething () {
    int[] typeVal = new int[2];
    analyzeWord(word1, typeVal);
    print("Okay.  " + (typeVal[1] == SAY ? word2 : word1));
  }

  //	ON etc.

  private void turnObjectOn () {
    if (!objectPresent(LAMP)) {
      actspk(verb);
    } else if (limit < 0) {
      printResponse(184);                               // Your lamp has run out of power.
    } else {
      objectStatus[LAMP] = 1;
      printResponse(39);                                // Your lamp is now on.
      if (wzdark) {
        wzdark = false;
        describeLocation();
      }
    }
  }

  //	OFF etc.

  private void turnObjectOff () {
    if (!objectPresent(LAMP)) {
      actspk(verb);
    } else {
      objectStatus[LAMP] = 0;
      printResponse(40);                                // Your lamp is now off.
    }
  }

  // WAVE etc.

  private void waveObject () {
    if (!isCarryingObject(object) && (object != ROD ||
        !isCarryingObject(ROD2))) {
      printResponse(29);                                // You aren't carrying it!
    } else if (object != ROD || !at(FISSURE) ||
        !isCarryingObject(object) || closing) {
      actspk(verb);
    } else {
      objectStatus[FISSURE] = 1 - objectStatus[FISSURE];
      printObjectDescription(FISSURE, 2 - objectStatus[FISSURE]);
    }
  }

  // ATTACK, KILL etc.

  private void killObject () {
    switch (object) {
    case BIRD:
      if (closed) {
        printResponse(137);                             // Oh, leave the poor unhappy bird alone.
      } else {
        destroyObject(BIRD);
        objectStatus[BIRD] = 0;
        if (objectLocation[SNAKE] == 19) {
          ++tally2;
        }
        printResponse(45);                              // The little bird is now dead.  Its body disappears.
      }
      break;
    case 0:
      printResponse(44);                                // here is nothing here to attack.
      break;
    case CLAM:
    case OYSTER:
      printResponse(150);                               // The shell is very strong and impervious to attack.
      break;
    case SNAKE:
      printResponse(46);                                // Attacking the snake both doesn't work and is very dangerous.
      break;
    case DWARF:
      if (closed) {
        dwarfend();
      }
      printResponse(49);                                // With what? Your bare hands?
      break;
    case TROLL:
      printResponse(157);                               // The troll fends off your blows effortlessly.
      break;
    case BEAR:
      printResponse(165 + (objectStatus[BEAR] + 1) / 2);
      break;
    case DRAGON:
      if (objectStatus[DRAGON] != 0) {
        printResponse(167);                             // For crying out loud, the poor thing is already dead!
        break;
      }
      if (!askYesNoQuestion(49, 0, 0)) {
        break;
      }
      printObjectDescription(DRAGON, 1);
      objectStatus[DRAGON] = 2;
      objectStatus[RUG] = 0;
      moveObj((DRAGON + MAXOBJ), -1);
      moveObj((RUG + MAXOBJ), 0);
      moveObj(DRAGON, 120);
      moveObj(RUG, 120);
      for (int ii = 1; ii < MAXOBJ; ++ii) {
        if (objectLocation[ii] == 119 || objectLocation[ii] == 121) {
          moveObj(ii, 120);
        }
      }
      newloc = 120;
      return;
    default:
      actspk(verb);
    }
  }

  //	POUR

  private void pourObject () {
    if (object == BOTTLE || object == 0) {
      object = liq();
    }
    if (object == 0) {
      needObject();
      return;
    }
    if (!isCarryingObject(object)) {
      actspk(verb);
      return;
    }
    if (object != OIL && object != WATER) {
      printResponse(78);                                // You can't pour that.
      return;
    }
    objectStatus[BOTTLE] = 1;
    objectLocation[object] = 0;
    if (at(PLANT)) {
      if (object != WATER) {
        printResponse(112);                             // The plant indignantly shakes the oil off its leaves...
      } else {
        printObjectDescription(PLANT, objectStatus[PLANT] + 1);
        objectStatus[PLANT] = (objectStatus[PLANT] + 2) % 6;
        objectStatus[PLANT2] = objectStatus[PLANT] / 2;
        describeLocation();
      }
    } else if (at(DOOR)) {
      objectStatus[DOOR] = (object == OIL ? 1 : 0);
      printResponse(113 + objectStatus[DOOR]);
    } else {
      printResponse(77);                                // Your bottle is empty and the ground is wet.
    }
  }

  //	EAT

  private void eatObject () {
    switch (object) {
    case FOOD:
      destroyObject(FOOD);
      printResponse(72);                                // Thank you.  It was delicious!
      break;
    case BIRD:
    case SNAKE:
    case CLAM:
    case OYSTER:
    case DWARF:
    case DRAGON:
    case TROLL:
    case BEAR:
      printResponse(71);                                // I think I just lost my appetite.
      break;
    default:
      actspk(verb);
    }
  }

  //	DRINK

  private void drinkObject () {
    if (object != WATER) {
      printResponse(110);                               // Don't be ridiculous!
    } else if (liq() != WATER || !objectPresent(BOTTLE)) {
      actspk(verb);
    } else {
      objectStatus[BOTTLE] = 1;
      objectLocation[WATER] = 0;
      printResponse(74);                                // The bottle of water is now empty.
    }
  }

  //	THROW etc.

  private void throwObject () {
    if (isCarryingObject(ROD2) && object == ROD && !isCarryingObject(ROD)) {
      object = ROD2;
    }
    if (!isCarryingObject(object)) {
      actspk(verb);
      return;
    }
    //	Treasure to troll
    if (at(TROLL) && object >= 50 && object < MAXOBJ) {   // If object is treasure, troll takes it
      printResponse(159);                                 // The troll catches your treasure and scurries away out of sight.
      dropObject(object, 0);                              // Destroy treasure
      moveObj(TROLL, 0);                                  // Destroy troll
      moveObj((TROLL + MAXOBJ), 0);                       //   "      "
      dropObject(TROLL2, 117);                            // Move Phony troll to 117
      dropObject((TROLL2 + MAXOBJ), 122);                 // Set Phony troll's fixed location to 122
      juggle(CHASM);                                      // Does nothing...
      return;
    }
    //	Feed the bears...
    if (object == FOOD && objectPresent(BEAR)) {
      object = BEAR;
      feedObject();
      return;
    }
    //	If not axe, same as dropObject...
    if (object != AXE) {
      dropObject();
      return;
    }
    int ii;
    if ((ii = dwarvesPresent()) != 0) {
      // AXE is THROWN at a dwarf...
      if (pct(33)) {
        dseen[ii] = false;
        dloc[ii] = 0;
        if (!dkill) {
          dkill = true;
          printResponse(149);                           // You killed a little Dwarf.
        } else {
          printResponse(47);                            // You killed a little dwarf.
        }
      } else {
        printResponse(48);                              // You attack a little dwarf, but he dodges out of the way.
      }
    } else if (at(DRAGON) &&
        objectStatus[DRAGON] == 0) {
      // AXE is THROWN at a dragon...
      printResponse(152);                               // The axe bounces harmlessly off the dragon's thick scales.
    } else if (at(TROLL)) {
      //AXE is THROWN at the troll...
      printResponse(158);                               // The troll deftly catches the axe, examines it...
    } else if (objectPresent(BEAR) &&
        objectStatus[BEAR] == 0) {
      // AXE is THROWN at the bear...
      printResponse(164);                               // The axe misses and lands near the bear...
      dropObject(AXE, loc);
      fixedObjLocation[AXE] = -1;
      objectStatus[AXE] = 1;
      juggle(BEAR);
      return;
    } else {
      // Otherwise it is an attack
      verb = KILL;
      object = 0;
      intransitiveVerb();
      return;
    }
    //	Handle the left over axe...
    dropObject(AXE, loc);
    describeLocation();
  }

  //	INVENTORY, FIND etc.

  private void findObject () {
    if (isCarryingObject(object)) {
      printResponse(24);                                // You are already carrying it!
    } else if (closed) {
      printResponse(138);                               // I daresay whatever you want is around here somewhere.
    } else if (dwarvesPresent() != 0 &&
        dflag >= 2 && object == DWARF) {
      printResponse(94);                                // I believe what you want is right here with you.
    } else if (at(object) ||
      (liq() == object && objectPresent(BOTTLE)) ||
      object == liqloc(loc)) {
      printResponse(94);                                // I believe what you want is right here with you.
    } else {
      actspk(verb);
    }
  }

  //	FILL

  private void fillObject () {
    switch (object) {
    case BOTTLE:
      if (liq() != 0) {
        printResponse(105);                             // Your bottle is already full.
      } else if (liqloc(loc) == 0) {
        printResponse(106);                             // There is nothing here with which to fill the bottle.
      } else {
        objectStatus[BOTTLE] = Map.cave[loc - 1].info & IS_OIL;
        int ii = liq();
        if (isCarryingObject(BOTTLE)) {
          objectLocation[ii] = -1;
        }
        printResponse(ii == OIL ? 108 : 107);           // Your bottle is now full of oil. / Your bottle is now full of water.
      }
      break;
    case VASE:
      if (liqloc(loc) == 0) {
        printResponse(144);                             // There is nothing here with which to fill the vase.
        break;
      }
      if (!isCarryingObject(VASE)) {
        printResponse(29);                              // You aren't carrying it!
        break;
      }
      printResponse(145);                               // The sudden change in temperature has delicately shattered the vase.
      dropObject();
      return;
    default:
      printResponse(29);                                // You aren't carrying it!
    }
  }

  //	FEED

  private void feedObject () {
    switch (object) {
    case BIRD:
      printResponse(100);                               // It's not hungry (It's merely pinin' for the Fjords).
      break;
    case DWARF:
      if (!objectPresent(FOOD)) {
        actspk(verb);
        return;
      }
      ++dflag;
      printResponse(103);                               // You fool, Dwarves eat only coal!
      break;
    case BEAR:
      if (!objectPresent(FOOD)) {
        if (objectStatus[BEAR] == 0) {
          printResponse(102);                           // There's nothing here it wants to eat (Except perhaps you).
        } else if (objectStatus[BEAR] == 3) {
          printResponse(110);                           // Don't be ridiculous!
        } else {
          actspk(verb);
          return;
        }
        break;
      }
      destroyObject(FOOD);
      objectStatus[BEAR] = 1;
      fixedObjLocation[AXE] = 0;
      objectStatus[AXE] = 0;
      printResponse(168);                               // The bear eagerly wolfs down your food, after which...
      break;
    case DRAGON:
      if (objectStatus[DRAGON] != 0) {
        printResponse(110);                             // Don't be ridiculous!
      } else {
        printResponse(102);                             // There's nothing here it wants to eat (Except perhaps you).
      }
      break;
    case TROLL:
      printResponse(182);                               // Gluttony is not one of the Troll's vices.
      break;
    case SNAKE:
      if (closed || !objectPresent(BIRD)) {
        printResponse(102);                             // There's nothing here it wants to eat (Except perhaps you).
        break;
      }
      destroyObject(BIRD);
      objectStatus[BIRD] = 0;
      ++tally2;
      printResponse(101);                               // The snake has now devoured your bird.
      break;
    default:
      printResponse(14);                                // I'm game.  Would you care to explain how?
    }
  }

  //	READ etc.

  private void readObject () {
    if (isDark()) {
      print("I see no " + getObjectName() + " here.");
      return;
    }
    switch (object) {
    case MAGAZINE:
      printResponse(190);                               // I'm afraid the magazine is written in Dwarvish.
      break;
    case TABLET:
      printResponse(196);                               // 'Congratulations on bringing light into the dark-room!'
      break;
    case MESSAGE:
      printResponse(191);                               // This is not the maze where the pirate leaves his treasure chest.
      break;
    case OYSTER:
      if (!isCarryingObject(OYSTER) || !closed) {
        break;
      }
      askYesNoQuestion(192, 193, 54);
      return;
    default:
      actspk(verb);
    }
  }

  //	BLAST etc.

  private void verbBlast () {
    if (objectStatus[ROD2] < 0 || !closed) {
      actspk(verb);
    } else {
      bonus = 133;                                      // You march through the hole and find yourself in the main office
      if (loc == 115) {
        bonus = 134;                                    // A river of molten lava pours in through the hole (killing you)
      }
      if (objectPresent(ROD2)) {
        bonus = 135;                                    // you are suddenly splashed across the walls of the room
      }
      printResponse(bonus);
      normend();
    }
  }

  //	BREAK etc.

  private void breakObject () {
    if (object == MIRROR) {
      if (closed) {
        dwarfend();
        printResponse(197);                             // You strike the mirror a resounding blow,
      } else {
        printResponse(148);                             // It is too far up for you to reach.
      }
    } else if (object == VASE && objectStatus[VASE] == 0) {
      if (isCarryingObject(VASE)) {
        dropObject(VASE, loc);
      }
      objectStatus[VASE] = 2;
      fixedObjLocation[VASE] = -1;
      printResponse(198);                               // You have taken the vase and hurled it delicately to the ground.
    } else {
      actspk(verb);
    }
  }

  //	WAKE etc.

  private void wakeObject () {
    if (object != DWARF || !closed) {
      actspk(verb);
    } else {
      printResponse(199);                               // You prod the nearest Dwarf, who wakes up grumpily,
      dwarfend();
    }
  }

  //	Routine to speak default verb message

  private void actspk (int verb) {
    if (verb < 1 || verb > 31) {
      bug(39);
    }
    int ii = actmsg[verb];
    if (ii != 0) {
      printResponse(ii);
    }
  }

  //	Routine to indicate no reasonable
  //	object for verb found.  Used mostly by
  //	intransitive verbs.

  private void needObject () {
    int[] typeVal = new int[2];
    analyzeWord(word1, typeVal);
    print((typeVal[0] == 2 ? word1 : word2) + " what?");
  }


  //	Routines to process intransitive verbs

  private void intransitiveVerb () {
    switch (verb) {
    case DROP:
    case SAY:
    case WAVE:
    case CALM:
    case RUB:
    case THROW:
    case FIND:
    case FEED:
    case BREAK:
    case WAKE:
      needObject();                                     // No applicable object available
      break;
    case TAKE:
      verbTake();
      break;
    case OPEN:
    case LOCK:
      verbOpen();
      break;
    case NOTHING:
      printResponse(54);                                // ok.
      break;
    case ON:
    case OFF:
    case POUR:
      trverb();
      break;
    case WALK:
      actspk(verb);
      break;
    case KILL:
      verbKill();
      break;
    case EAT:
      verbEat();
      break;
    case DRINK:
      verbDrink();
      break;
    case QUIT:
      verbQuit();
      break;
    case FILL:
      verbFill();
      break;
    case BLAST:
      verbBlast();
      break;
    case SCORE:
      score();
      break;
    case FOO:
      verbFoo();
      break;
    case INVENTORY:
      verbInventory();
      break;
    case READ:
      verbRead();
      break;
    default:
      print("This intransitive not implemented yet");
    }
  }

  //	CARRY, TAKE etc.

  private void verbTake () {
    int anobj = 0;
    for (int item = 1; item < MAXOBJ; ++item) {
      if (objectLocation[item] == loc) {
        if (anobj != 0) {
          needObject();
          return;
        }
        anobj = item;
      }
    }
    if (anobj == 0 || (dwarvesPresent() != 0 && dflag >= 2)) {
      needObject();
      return;
    }
    object = anobj;
    takeObject();
  }

  //	OPEN, LOCK, UNLOCK

  private void verbOpen () {
    if (objectPresent(CLAM)) {
      object = CLAM;
    }
    if (objectPresent(OYSTER)) {
      object = OYSTER;
    }
    if (at(DOOR)) {
      object = DOOR;
    }
    if (at(GRATE)) {
      object = GRATE;
    }
    if (objectPresent(CHAIN)) {
      if (object != 0) {
        needObject();
        return;
      }
      object = CHAIN;
    }
    if (object == 0) {
      printResponse(28);                                // There is nothing here with a lock!
      return;
    }
    openObject();
  }

  //	ATTACK, KILL etc

  private void verbKill () {
    object1 = 0;
    if (dwarvesPresent() != 0 && dflag >= 2) {
      object = DWARF;
    }
    if (objectPresent(SNAKE)) {
      addobj(SNAKE);
    }
    if (at(DRAGON) && objectStatus[DRAGON] == 0) {
      addobj(DRAGON);
    }
    if (at(TROLL)) {
      addobj(TROLL);
    }
    if (objectPresent(BEAR) && objectStatus[BEAR] == 0) {
      addobj(BEAR);
    }
    if (object1 != 0) {
      needObject();
      return;
    }
    if (object != 0) {
      killObject();
      return;
    }
    if (objectPresent(BIRD) && verb != THROW) {
      object = BIRD;
    }
    if (objectPresent(CLAM) || objectPresent(OYSTER)) {
      addobj(CLAM);
    }
    if (object1 != 0) {
      needObject();
      return;
    }
    killObject();
  }

  //	EAT

  private void verbEat () {
    if (!objectPresent(FOOD)) {
      needObject();
    } else {
      object = FOOD;
      eatObject();
    }
  }

  //	DRINK

  private void verbDrink () {
    if (liqloc(loc) != WATER &&
      (liq() != WATER || !objectPresent(BOTTLE))) {
      needObject();
    } else {
      object = WATER;
      drinkObject();
    }
  }

  //	QUIT

  private void verbQuit () {
    if (gaveup = askYesNoQuestion(22, 54, 54)) {
      normend();
    }
  }

  //	FILL

  private void verbFill () {
    if (!objectPresent(BOTTLE)) {
      needObject();
    } else {
      object = BOTTLE;
      fillObject();
    }
  }

  //	Handle fee fie foe foo...

  private void verbFoo () {
    int kk = getWordAction(word1, 3000);
    int msg = 42;                                       // Nothing happens.
    if (foobar != 1 - kk) {
      if (foobar != 0) {
        msg = 151;                                      // What's the matter, can't you read?
      }
      printResponse(msg);
      return;
    }
    foobar = kk;
    if (kk != 4) {
      return;
    }
    foobar = 0;
    if (objectLocation[EGGS] == 92 || (isCarryingObject(EGGS) && loc == 92)) {
      printResponse(msg);
      return;
    }
    if (objectLocation[EGGS] == 0 && objectLocation[TROLL] == 0 && objectStatus[TROLL] == 0) {
      objectStatus[TROLL] = 1;
    }
    if (objectPresent(EGGS)) {
      kk = 1;
    } else if (loc == 92) {
      kk = 0;
    } else {
      kk = 2;
    }
    moveObj(EGGS, 92);
    printObjectDescription(EGGS, kk);
  }

  //	Read etc...

  private void verbRead () {
    if (objectPresent(MAGAZINE)) {
      object = MAGAZINE;
    }
    if (objectPresent(TABLET)) {
      object = object * 100 + TABLET;
    }
    if (objectPresent(MESSAGE)) {
      object = object * 100 + MESSAGE;
    }
    if (object > 100 || object == 0 || isDark()) {
      needObject();
      return;
    }
    readObject();
  }

  //	INVENTORY

  private void verbInventory () {
    int msg = 98;                                       // You're not carrying anything.
    for (int ii = 1; ii < MAXOBJ; ++ii) {
      if (ii == BEAR || !isCarryingObject(ii)) {
        continue;
      }
      if (msg != 0) {
        printResponse(99);                              // You are currently holding the following:
      }
      msg = 0;
      printObjectDescription(ii, -1);
    }
    if (isCarryingObject(BEAR)) {
      msg = 141;                                        // You are being followed by a very large, tame bear.
    }
    if (msg != 0) {
      printResponse(msg);
    }
  }

  //	Ensure uniqueness as objects are searched
  //	out for an intransitive verb

  private void addobj (int obj) {
    if (object1 != 0) {
      return;
    }
    if (object != 0) {
      object1 = -1;
      return;
    }
    object = obj;
  }

  private boolean analyzeTwoWordSentence () {
    int[] typeVal1 = {-1, -1}, typeVal2 = {-1, -1};
    verb = object = motion = 0;
    getWords();
    if (word1 != null && word1.length() == 0) {
      return false;                                 //	ignore whitespace
    }
    if (!analyzeWord(word1, typeVal1)) {            //	check word1
      return false;                                 //	didn't know it
    }
    if (typeVal1[0] == 2 && typeVal1[1] == SAY) {
      verb = SAY;                                   //	repeat word & act upon if..
      object = 1;
      return true;
    }
    if (word2 != null && word2.length() > 0) {
      if (!analyzeWord(word2, typeVal2)) {
        return false;                               //	didn't know it
      }
    }
    //	check his grammar
    if ((typeVal1[0] == 3) && (typeVal2[0] == 3) && (typeVal1[1] == 51) && (typeVal2[1] == 51)) {
      outwords();
      return false;
    } else if (typeVal1[0] == 3) {
      printResponse(typeVal1[1]);
      return false;
    } else if (typeVal2[0] == 3) {
      printResponse(typeVal2[1]);
      return false;
    } else if (typeVal1[0] == 0) {
      if (typeVal2[0] == 0) {
        print("bad grammar...");
        return false;
      } else {
        motion = typeVal1[1];
      }
    } else if (typeVal2[0] == 0) {
      motion = typeVal2[1];
    } else if (typeVal1[0] == 1) {
      object = typeVal1[1];
      if (typeVal2[0] == 2) {
        verb = typeVal2[1];
      }
      if (typeVal2[0] == 1) {
        print("bad grammar...");
        return false;
      }
    } else if (typeVal1[0] == 2) {
      verb = typeVal1[1];
      if (typeVal2[0] == 1) {
        object = typeVal2[1];
      }
      if (typeVal2[0] == 2) {
        print("bad grammar...");
        return false;
      }
    } else {
      bug(36);
    }
    return true;
  }


  private boolean analyzeWord (String word, int[] typeVal) {
    int wordval;
    //	make sure I understand
    if ((wordval = getWordAction(word, 0)) == -1) {
      switch (rand() % 3) {
      case 0:
        printResponse(60);                              // I don't know that word.
        break;
      case 1:
        printResponse(61);                              // What?
        break;
      default:
        printResponse(13);                              // I don't understand that!
      }
      return false;
    }
    typeVal[0] = wordval / 1000;
    typeVal[1] = wordval % 1000;
    return true;
  }

  //	retrieve input line (max 80 chars), convert to lower case
  //	& rescan for first two words.

  private void getWords () {
    synchronized (ticker) {
      try {
        ticker.wait();
      } catch (InterruptedException ignored) {
      }
    }
    StringTokenizer parser = new StringTokenizer(inText);
    try {
      word1 = word2 = "";
      if (parser.hasMoreTokens()) {
        word1 = parser.nextToken();
      }
      if (parser.hasMoreTokens()) {
        word2 = parser.nextToken();
      }
    } catch (NoSuchElementException ignored) {
    }
  }

  //	Output adventure word list (motion/0xxx & verb/2xxx) only
  //	6 words/line pausing at 20th line until keyboard active

  private void outwords () {
/*
		int		jj, line;
		char	words[80];

		jj = line = 0;
		for (int ii = 0; ii < MAXWC; ++ii) {
			if ((wc[ii].acode < 1000) || ((wc[ii].acode < 3000) && (wc[ii].acode > 1999))) {
				// printf("%-12s", wc[ii].aword);
				if ((++jj == 6) || (ii == MAXWC-1)) {
					jj = 0;
					fputc('\n', stdout);
					if (++line == 20) {
						line = 0;
						// printf("\n\007Enter <RETURN>");
						// printf(" to continue\n\n");
						fgets(words, 80, stdin);
					}
				}
			}
		}
*/
  }

  //	Routine to fill travel array for a given location

  private void getMoves (int loc) {
    int[][] locs = Map.cave[loc - 1].moves;
    for (int ii = 0; ii < locs.length; ++ii) {
      travel[ii].tverb = locs[ii][0];
      travel[ii].tdest = locs[ii][1];
      travel[ii].tcond = locs[ii][2];
      travel[ii + 1].tdest = -1;        //	end of array
    }
  }

  private boolean askYesNoQuestion (int msg1, int msg2, int msg3) {
    if (msg1 != 0) {
      printResponse(msg1);
    }
    synchronized (ticker) {
      try {
        ticker.wait();
      } catch (InterruptedException ignored) {
      }
    }
    StringTokenizer parser = new StringTokenizer(inText);
    try {
      word1 = word2 = "";
      if (parser.hasMoreTokens()) {
        word1 = parser.nextToken();
      }
      word1 = word1.toLowerCase();
    } catch (NoSuchElementException ignored) {
    }
    if (word1.equals("n") || word1.equals("no")) {
      if (msg3 != 0) {
        printResponse(msg3);
      }
      return false;
    }
    if (msg2 != 0) {
      printResponse(msg2);
    }
    return true;
  }

  //	Print a location description from "advent4.txt"

  private void printResponse (int msg) {
    print(Responses.list[msg - 1]);
  }

  //	Print an item message for a given state from "advent3.txt"

  private void printObjectDescription (int item, int state) {        // state = -1, 0, 1, 2
    String[] strs = ObjectText.list[item - 1];
    int idx = state + 1;
    if (idx < strs.length) {
      print(strs[idx]);
    }
  }

  //	Print a long location description from "advent1.txt"

  private void printLongLocation (int loc) {
    print(Map.cave[loc - 1].longText);
  }

  //	Print a short location description from "advent2.txt"

  private void printShortLocation (int loc) {
    print(Map.cave[loc - 1].shortText);
  }

  //	look-up vocabulary word in lex-ordered table.  words may have two entries with different codes.
  //  If minimum acceptable value = 0, then return minimum of different codes.  last word CANNOT have
  //  two entries(due to binary sort).
  //	  word is the word to look up.
  //	  val  is the minimum acceptable value,
  //		if != 0 return %1000

  private int getWordAction (String word, int val) {
    int v1;
    if ((v1 = binarySearch(word, Vocabulary.wc.length)) >= 0) {
      int v2 = binarySearch(word, Vocabulary.wc.length - 1);
      if (v2 < 0) {
        v2 = v1;
      }
      if (val == 0) {
        return (Math.min(Vocabulary.wc[v1].acode, Vocabulary.wc[v2].acode));
      }
      if (val <= Vocabulary.wc[v1].acode) {
        return (Vocabulary.wc[v1].acode % 1000);
      } else if (val <= Vocabulary.wc[v2].acode) {
        return (Vocabulary.wc[v2].acode % 1000);
      } else {
        return (-1);
      }
    }
    return (-1);
  }

  private int binarySearch (String w, int maxwc) {
    int lo, mid, hi, check;
    lo = 0;
    hi = maxwc - 1;
    while (lo <= hi) {
      mid = (lo + hi) / 2;
      if ((check = Vocabulary.compare(w, mid)) < 0) {
        hi = mid - 1;
      } else if (check > 0) {
        lo = mid + 1;
      } else {
        return (mid);
      }
    }
    return (-1);
  }

  //	Routine to test for darkness

  private boolean isDark () {
    return ((Map.cave[loc - 1].info & LIGHT) == 0 && (objectStatus[LAMP] == 0 || !objectPresent(LAMP)));
  }

  //	Routine to tell if an item is present.

  private boolean objectPresent (int item) {
    return (objectLocation[item] == loc || isCarryingObject(item));
  }

  //	Routine to tell if an item is being carried.

  private boolean isCarryingObject (int item) {
    return (objectLocation[item] == -1);
  }

  //	Routine to tell if a location causes
  //	a forcedMove move.

  private boolean forcedMove (int atloc) {
    return (Map.cave[atloc - 1].info == FORCED_MOVE);
  }

  //	Routine true x% of the time.

  private boolean pct (int x) {
    return (rand() % 100 < x);
  }

  //	Routine to tell if player is on
  //	either side of a two sided object.

  private boolean at (int item) {
    return (objectLocation[item] == loc || fixedObjLocation[item] == loc);
  }

  //	Routine to destroy an object

  private void destroyObject (int obj) {
    moveObj(obj, 0);
  }

  //	Routine to move an object

  private void moveObj (int obj, int where) {
    //int from = (obj < MAXOBJ) ? objectLocation[obj] : fixedObjLocation[obj];
    int from = (obj < MAXOBJ) ? objectLocation[obj] : fixedObjLocation[obj - MAXOBJ];   // Bug fix for original C code
    if (from > 0 && from <= 300) {
      carryObject(obj);
    }
    dropObject(obj, where);
  }

  //	Juggle an object (currently a no-op)

  private void juggle (int loc) {
    // What is intended here?
  }

  //	Routine to carry an object

  private void carryObject (int obj) {
    if (obj < MAXOBJ) {
      if (objectLocation[obj] == -1) {
        return;
      }
      objectLocation[obj] = -1;
      ++holding;
    }
  }

  //	Routine to drop an object

  private void dropObject (int obj, int where) {
    if (obj < MAXOBJ) {
      if (objectLocation[obj] == -1) {
        --holding;
      }
      objectLocation[obj] = where;
    } else {
      fixedObjLocation[obj - MAXOBJ] = where;
    }
  }

  //	Routine to move an object and return a
  //	value used to set the negated objectStatus values
  //	for the repository.

  private int putObject (int obj, int where, int pval) {
    moveObj(obj, where);
    return ((-1) - pval);
  }

  //	Routine to check for presence of dwarves..

  private int dwarvesPresent () {
    for (int ii = 1; ii < (DWARFMAX - 1); ++ii) {
      if (dloc[ii] == loc) {
        return ii;
      }
    }
    return 0;
  }

  //	Determine liquid in the bottle

  private int liq () {
    int ii = objectStatus[BOTTLE];
    int jj = -1 - ii;
    return (liq2(Math.max(ii, jj)));
  }

  //	Determine liquid at a location

  private int liqloc (int loc) {
    if ((Map.cave[loc - 1].info & LIQUID) != 0) {
      return (liq2(Map.cave[loc - 1].info & IS_OIL));
    } else {
      return (liq2(1));
    }
  }

  //	Convert 0 to WATER
  //			    1 to nothing
  //			    2 to OIL

  private int liq2 (int pbottle) {
    return ((1 - pbottle) * WATER + (pbottle >> 1) * (WATER + OIL));
  }

  //	Fatal error routine

  private void bug (int n) {
    System.out.println("Fatal error number " + n);
    //	exit();
  }
}
