Package soc.qase.bot

Source Code of soc.qase.bot.BasicBot

//--------------------------------------------------
// Name:      BasicBot.java
// Author:      Bernard.Gorman@computing.dcu.ie
//--------------------------------------------------

package soc.qase.bot;

import java.io.File;
import java.util.Vector;

import soc.qase.ai.waypoint.Waypoint;
import soc.qase.ai.waypoint.WaypointMap;
import soc.qase.ai.waypoint.WaypointMapGenerator;
import soc.qase.com.Proxy;
import soc.qase.file.bsp.BSPBrush;
import soc.qase.file.bsp.BSPEntity;
import soc.qase.file.bsp.BSPLeaf;
import soc.qase.file.bsp.BSPParser;
import soc.qase.file.pak.PAKParser;
import soc.qase.info.Server;
import soc.qase.info.User;
import soc.qase.state.Action;
import soc.qase.state.Angles;
import soc.qase.state.Entity;
import soc.qase.state.Inventory;
import soc.qase.state.Origin;
import soc.qase.state.Player;
import soc.qase.state.PlayerGun;
import soc.qase.state.Velocity;
import soc.qase.state.World;
import soc.qase.tools.Utils;
import soc.qase.tools.vecmath.Vector2f;
import soc.qase.tools.vecmath.Vector3f;

/*-------------------------------------------------------------------*/
/**  An abstract class which provides all the base functionality needed
*  by the game agent. All that is required is to provide a means of
*  handling server updates, as in the ObserverBot and PollingBot classes.
*  BasicBot also provides tailored, embedded access to the BSPParser and
*  WaypointMap classes - methods in this class simply relay calls to the
*  the BSPParser or Waypoint object as appropriate, with certain parameters
*  pre-defined to the most useful values from the perspective of game
*  agents (e.g. the bounding box used to trace through the level is set to
*  the size of the agent's in-game character's bounding box). Users can
*  also obtain a pointer to the underlying object, thereby allowing full
*  access to all their facilities.
@see ObserverBot
@see PollingBot */
/*-------------------------------------------------------------------*/
public abstract class BasicBot extends Thread implements Bot
{
  private User user = null;
  protected Proxy proxy = null;

  private Angles angles = new Angles(0, 0, 0);
  private Angles delta_Angles = new Angles(0, 0, 0);

  private Velocity velocity = new Velocity(0, 0, 0);
  private Action action = new Action(false, false, false);

  private boolean connected = false;
  private boolean threadSafe = false;
  private boolean traceFromView = false;

  protected boolean ctfTeamAssigned = false;

  private boolean mapNotFound = false;
  private static String q2HomeDir = null;

  protected WaypointMap wpMap = null;
  protected BSPParser bsp = new BSPParser();

  private float sphereRadius = 18.0f;
  private boolean globalAngles = true;
  private static final Vector3f BOUNDING_MAX = new Vector3f(9, 25, 9);
  private static final Vector3f BOUNDING_MIN = new Vector3f(-9, -25, -9);

/*-------------------------------------------------------------------*/
/**  Default constructor. Creates the agent using default parameters. */
/*-------------------------------------------------------------------*/
  public BasicBot()
  {
    user = new User("QASE_Bot", "female/athena", 65535, 1, 90, User.HAND_RIGHT, "");
    commonSetup(false, true);
  }

/*-------------------------------------------------------------------*/
/**  Constructor allowing the user to specify a name and skin (appearance)
*  for the agent.
@param botName name of the character during game session
@param botSkin specifies the character's in-game appearance */
/*-------------------------------------------------------------------*/
  public BasicBot(String botName, String botSkin)
  {
    user = new User((botName == null ? "QASE_BasicBot" : botName), (botSkin == null ? "female/athena" : botSkin), 65535, 1, 90, User.HAND_RIGHT, "");
    commonSetup(false, true);
  }

/*-------------------------------------------------------------------*/
/**  Constructor allowing the user to specify a name, skin, and whether
*  it should manually track its inventory.
@param botName name of the character during game session
@param botSkin specifies the character's in-game appearance
@param trackInv if true, the agent will manually track its inventory */
/*-------------------------------------------------------------------*/
  public BasicBot(String botName, String botSkin, boolean trackInv)
  {
    user = new User((botName == null ? "QASE_BasicBot" : botName), (botSkin == null ? "female/athena" : botSkin), 65535, 1, 90, User.HAND_RIGHT, "");
    commonSetup(false, trackInv);
  }

/*-------------------------------------------------------------------*/
/**  Constructor allowing the user to specify a name, skin, whether the
*  agent should operate in high thread safety mode, and whether it
*  should manually track its inventory.
@param botName name of the character during game session
@param botSkin specifies the character's in-game appearance
@param highThreadSafety if true, enables high thread safety mode
@param trackInv if true, the agent will manually track its inventory */
/*-------------------------------------------------------------------*/
  public BasicBot(String botName, String botSkin, boolean highThreadSafety, boolean trackInv)
  {
    user = new User((botName == null ? "QASE_BasicBot" : botName), (botSkin == null ? "female/athena" : botSkin), 65535, 1, 90, User.HAND_RIGHT, "");
    commonSetup(highThreadSafety, trackInv);
  }

/*-------------------------------------------------------------------*/
/**  Constructor allowing the user to specify a name, skin, server password,
*  whether the agent should operate in high thread safety mode, and whether
*  it should manually track its inventory.
@param botName name of the character during game session
@param botSkin specifies the character's in-game appearance
@param password the password of the server, if necessary
@param highThreadSafety if true, enables high thread safety mode
@param trackInv if true, the agent will manually track its inventory */
/*-------------------------------------------------------------------*/
  public BasicBot(String botName, String botSkin, String password, boolean highThreadSafety, boolean trackInv)
  {
    user = new User((botName == null ? "QASE_BasicBot" : botName), (botSkin == null ? "female/athena" : botSkin), 65535, 1, 90, User.HAND_RIGHT, password);
    commonSetup(highThreadSafety, trackInv);
  }

/*-------------------------------------------------------------------*/
/**  Constructor allowing the user to specify a name, skin, connection
*  receive rate, type of messages received from server, field of view,
*  which hand the agent should hold its gun in, server password,
*  whether the agent should operate in high thread safety mode, and whether
*  it should manually track its inventory.
@param botName name of the character during game session
@param botSkin specifies the character's in-game appearance
@param recvRate rate at which the client communicates with server
@param msgLevel specifies which server messages to register interest in
@param fov specifies the agent's field of vision
@param hand specifies the hand in which the agent hold its gun
@param password the password of the server, if necessary
@param highThreadSafety if true, enables high thread safety mode
@param trackInv if true, the agent will manually track its inventory */
/*-------------------------------------------------------------------*/
  public BasicBot(String botName, String botSkin, int recvRate, int msgLevel, int fov, int hand, String password, boolean highThreadSafety, boolean trackInv)
  {
    user = new User((botName == null ? "QASE_BasicBot" : botName), (botSkin == null ? "female/athena" : botSkin), (recvRate < 0 ? 65535 : recvRate), (msgLevel < 0 ? 1 : msgLevel), (fov < 0 ? 90 : fov), (hand < 0 ? User.HAND_RIGHT : hand), (password == null ? "" : password));
    commonSetup(highThreadSafety, trackInv);
  }

  private void commonSetup(boolean highThreadSafety, boolean trackInv)
  {
    threadSafe = highThreadSafety;
    proxy = new Proxy(user, highThreadSafety, trackInv);
  }

// all abstract methods to be supplied by derived classes
// custom bots should synchronize on World if high thread safety is enabled
/*-------------------------------------------------------------------*/
/**  Connect to a game server. To be implemented by derived classes.
@param host a String representation of the host machine's IP address
@param port the port on which the game server is running */
/*-------------------------------------------------------------------*/
  public abstract boolean connect(String host, int port);

/*-------------------------------------------------------------------*/
/**  Connect to a CTF game server. To be implemented by derived classes.
@param host a String representation of the host machine's IP address
@param port the port on which the game server is running
@param ctfTeam the team to join; one of the CTF constants found in
*  soc.qase.info.Server */
/*-------------------------------------------------------------------*/
  public abstract boolean connect(String host, int port, int ctfTeam);

/*-------------------------------------------------------------------*/
/**  Disconnect from the server. To be implemented by derived classes.*/
/*-------------------------------------------------------------------*/
  public abstract void disconnect();

/*-------------------------------------------------------------------*/
/**  The core AI routine. To be implemented by derived classes.
@param world a World object representing the current gamestate */
/*-------------------------------------------------------------------*/
  public abstract void runAI(World world);

/*-------------------------------------------------------------------*/
/**  Flag the agent as being connected to or disconnected from the game server.
@param value true if the agent is connected, false otherwise */
/*-------------------------------------------------------------------*/
  protected void setConnected(boolean value)
  {
    connected = value;
  }

/*-------------------------------------------------------------------*/
/**  Check whether the agent is connected to the server.
@return value true if the agent is connected, false otherwise */
/*-------------------------------------------------------------------*/
  public boolean isConnected()
  {
    return connected;
  }

/*-------------------------------------------------------------------*/
/**  Set the agent to operate in low or high thread safety mode. In
*  high thread safety mode, the gamestate object is locked while
*  QASE is reading from it, preventing errors which may occur due to
*  updates arriving in the middle of the AI cycle. Since BasicBot does
*  not implement a server-handling mechanism, it is the responsibility
*  of derived classes to correctly observe this contract (as is the case
*  with ObserverBot and PollingBot). Note that, because the Proxy object
*  is implicitly locked while updating all registered ObserverBots, high
*  thread safety is not required except in cases where external threads
*  may intervene in mid-AI cycle.
@param highThreadSafety true to enable high safety mode, false to disable
@see ObserverBot
@see PollingBot*/
/*-------------------------------------------------------------------*/
  public void setHighThreadSafety(boolean highThreadSafety)
  {
    threadSafe = highThreadSafety;
    proxy.setHighThreadSafety(highThreadSafety);
  }

/*-------------------------------------------------------------------*/
/**  Check whether the agent is operating in high thread-safety mode.
@return true if the agent is in high safety mode, false otherwise */
/*-------------------------------------------------------------------*/
  public boolean getHighThreadSafety()
  {
    return threadSafe;
  }

/*-------------------------------------------------------------------*/
/**  Specifies whether the agent should automatically request a full
*  listing of its inventory on each frame. This can be used in place
*  of manual inventory tracking - it ensures complete accuracy, at
*  the cost of increasing the amount of network traffic per update.
*  Also note that recorded DM2 files of agents using this approach
*  will display an inventory window every second frame.
@param refresh turn auto inventory refresh on/off */
/*-------------------------------------------------------------------*/
  public void setAutoInventoryRefresh(boolean refresh)
  {
    proxy.setAutoInventoryRefresh(refresh);
  }

/*-------------------------------------------------------------------*/
/**  Returns the User object associated with this bot, which contains
*  information regarding the agent's in-game name, skin, FoV, etc.
@return the User object containing details of the bot's
*  configuration, or null if no such object exists */
/*-------------------------------------------------------------------*/
  protected User getPlayerInfo()
  {
    return user;
  }

/*-------------------------------------------------------------------*/
/**  Returns the Player object associated with this bot, which can then
*  be further queried for information.
@return the Player object containing full details of the bot's
*  current state, or null if no such object exists */
/*-------------------------------------------------------------------*/
  protected Player getPlayerState()
  {
    return ((proxy == null || proxy.getWorld() == null) ? null : proxy.getWorld().getPlayer());
  }

/*-------------------------------------------------------------------*/
/**  Returns the Server object associated with the current game session,
*  which contains information such as the server version, the map name,
*  and whether the server is running CTF or a regular deathmatch.
@return the Server object associated with the current session */
/*-------------------------------------------------------------------*/
  protected Server getServerInfo()
  {
    return (proxy == null ? null : proxy.getServer());
  }

/*-------------------------------------------------------------------*/
/**  Check whether the agent is currently alive and active in the game.
@return true if the agent is active and alive, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isBotAlive()
  {
    return proxy != null && proxy.inGame() && proxy.getWorld().getPlayer().isAlive();
  }

/*-------------------------------------------------------------------*/
/**  Get the agent's current position.
@return the current position, or null if the agent is not
*  currently connected */
/*-------------------------------------------------------------------*/
  protected Origin getPosition()
  {
    return ((proxy == null || proxy.getWorld() == null) ? null : proxy.getWorld().getPlayer().getPosition());
  }

/*-------------------------------------------------------------------*/
/**  Get the agent's current orientation.
@return the current orientation, or null if the agent is not
*  currently connected */
/*-------------------------------------------------------------------*/
  protected Angles getOrientation()
  {
    return ((proxy == null || proxy.getWorld() == null) ? null : proxy.getWorld().getPlayer().getOrientation());
  }

/*-------------------------------------------------------------------*/
/**  Get the agent's current health.
@return the agent's health value, or Integer.MIN_VALUE if the agent
*  is not currently connected */
/*-------------------------------------------------------------------*/
  protected int getHealth()
  {
    return ((proxy != null && proxy.inGame()) ? proxy.getWorld().getPlayer().getHealth() : Integer.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Get the amount of armor currently held by the agent.
@return the current armor value, or Integer.MIN_VALUE if the agent
*  is not currently connected */
/*-------------------------------------------------------------------*/
  protected int getArmor()
  {
    return ((proxy != null && proxy.inGame()) ? proxy.getWorld().getPlayer().getArmor() : Integer.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Get the inventory index of the current weapon.
@return the index of the current weapon as indicated by the constants
*  in the Inventory class, or Integer.MIN_VALUE if the agent is not
*  currently connected */
/*-------------------------------------------------------------------*/
  protected int getWeaponIndex()
  {
    return ((proxy != null && proxy.inGame()) ? proxy.getWorld().getPlayer().getWeaponIndex() : Integer.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Get the amount of ammo the agent has for the current weapon.
@return the current ammo, or Integer.MIN_VALUE if the agent
*  is not currently connected */
/*-------------------------------------------------------------------*/
  protected int getAmmo()
  {
    return ((proxy != null && proxy.inGame()) ? proxy.getWorld().getPlayer().getAmmo() : Integer.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Determines whether the player is currently firing his gun.
@return true if the player is firing, false otherwise. */
/*-------------------------------------------------------------------*/
  public boolean isFiring()
  {
    return proxy != null && proxy.inGame() && proxy.getWorld().getPlayer().isFiring();
  }

/*-------------------------------------------------------------------*/
/**  Switch the agent to a specified team during a CTF match (assuming in-game
*  team switching is enabled).
@param ctfTeam the team to join; one of the CTF constants found in
*  soc.qase.info.Server */
/*-------------------------------------------------------------------*/
  protected void setCTFTeam(int ctfTeam)
  {
    ctfTeamAssigned = true;
    sendConsoleCommand("team " + Server.CTF_STRINGS[(Math.abs(ctfTeam) < 2 ? Math.abs(ctfTeam) : (int)Math.round(Math.random()))]);
  }

/*-------------------------------------------------------------------*/
/**  Resolve the CTF team number of the local agent, if the current server
*  is running the CTF mod.
@return the team number of the local agent; 0 = RED, 1 = BLUE */
/*-------------------------------------------------------------------*/
  protected int getCTFTeamNumber()
  {
    return (proxy == null ? Integer.MIN_VALUE : proxy.getCTFTeamNumber());
  }

/*-------------------------------------------------------------------*/
/**  Resolve the CTF team name of the local agent, if the current server
*  is running the CTF mod.
@return the team name of the local agent; either RED, BLUE or null
*  if the agent is not currently on a team. */
/*-------------------------------------------------------------------*/
  protected String getCTFTeamString()
  {
    return (proxy == null ? null : proxy.getCTFTeamString());
  }

/*-------------------------------------------------------------------*/
/**  Determined whether the given player Entity is on the same CTF team
*  as the local agent.
@param otherPlayer the Entity representing the player to be checked
@return true if otherPlayer is on the same CTF team as the local agent,
*  false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isOnSameCTFTeam(Entity otherPlayer)
  {
    return (getCTFTeamNumber() >= 0 && getCTFTeamNumber() == otherPlayer.getCTFTeamNumber());
  }

/*-------------------------------------------------------------------*/
/**  Get the specified angle, as defined by the constants in the
*  Angles class.
@param angleType the angle to return
@see soc.qase.state.Angles */
/*-------------------------------------------------------------------*/
  protected float getAngle(int angleType)
  {
    return angles.get(angleType);
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's angles in each plane.
@param yaw the new yaw angle
@param pitch the new pitch angle
@param roll the new roll angle */
/*-------------------------------------------------------------------*/
  protected void setAngle(float yaw, float pitch, float roll)
  {
    angles.set(yaw, pitch, roll);
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's angles in the given plane, as defined by the constants
*  in the Angles class.
@param angleType the angle to change
@param value the new angle to set
@see soc.qase.state.Angles */
/*-------------------------------------------------------------------*/
  protected void setAngle(int angleType, float value)
  {
    angles.set(angleType, value);
  }

/*-------------------------------------------------------------------*/
/**  Get the current velocity in the given direction, as specified by
*  the constants in the Velocity class.
@param velocityType the direction (forward/right) whose magnitude
*  is required
@return the magnitude of the agent's velocity in the given direction
@see soc.qase.state.Velocity */
/*-------------------------------------------------------------------*/
  protected int getVelocity(int velocityType)
  {
    return velocity.get(velocityType);
  }

/*-------------------------------------------------------------------*/
/**  Get the current walk state of the bot. This will be one of WALK_STOPPED,
*  WALK_NORMAL or WALK_RUN, as specified in the PlayerMove class.
@return the agent's current walk state
@see soc.qase.state.PlayerMove */
/*-------------------------------------------------------------------*/
  protected int getWalkState()
  {
    return getPlayerState().getWalkState();
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's velocity in each direction.
@param forward the new forward velocity
@param right the new right velocity
@param up the new vertical velocity */
/*-------------------------------------------------------------------*/
  protected void setVelocity(int forward, int right, int up)
  {
    velocity.set(forward, right, up);
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's velocity in a given direction, as given by the
*  constants in the Velocity class.
@param velocityType the direction whose velocity is to be set
@param value the magnitude of the new velocity
@see soc.qase.state.Velocity */
/*-------------------------------------------------------------------*/
  protected void setVelocity(int velocityType, int value)
  {
    velocity.set(velocityType, value);
  }

/*-------------------------------------------------------------------*/
/**  Get the agent's current action settings, as specified by the constants
*  in the Action class.
@param actionType the action to return
@return true if the given action is active, false otherwise
@see soc.qase.state.Action */
/*-------------------------------------------------------------------*/
  protected boolean getAction(int actionType)
  {
    return action.get(actionType);
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's current actions.
@param attack the action represents an attack
@param use the action represents usage of current item
@param any the action represents any button press
@see soc.qase.state.Action */
/*-------------------------------------------------------------------*/
  protected void setAction(boolean attack, boolean use, boolean any)
  {
    action.set(attack, use, any);
  }

/*-------------------------------------------------------------------*/
/**  Set one of the agent's actions, as specified by the constants in
*  the Action class.
@param actionType the action to return
@param value true if the given action should be active, false otherwise
@see soc.qase.state.Action */
/*-------------------------------------------------------------------*/
  protected void setAction(int actionType, boolean value)
  {
    action.set(actionType, value);
  }

/*-------------------------------------------------------------------*/
/**  Specify whether the agent should fire its weapon on the next frame.
@param attack true if the agent should fire, false otherwise
@see soc.qase.state.Action */
/*-------------------------------------------------------------------*/
  protected void setAttack(boolean attack)
  {
    action.setAttack(attack);
  }

/*-------------------------------------------------------------------*/
/**  Specify whether the agent should use the current item
@param use true if the agent should use the item, false otherwise
@see soc.qase.state.Action */
/*-------------------------------------------------------------------*/
  protected void setUse(boolean use)
  {
    action.setUse(use);
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's posture to POSTURE_CROUCH, POSTURE_STAND or
*  POSTURE_JUMP; these constants are found in the PlayerMove class.
@param postureState specifies the bot's posture (crouch/stand/jump)
@see soc.qase.state.PlayerMove */
/*-------------------------------------------------------------------*/
  protected void setPosture(int postureState)
  {
    velocity.setUp(postureState * 300);
  }

/*-------------------------------------------------------------------*/
/**  Specify whether the bot should jump. A value of 'true' causes the
*  bot to jump, while 'false' returns it to a normal standing posture.
@param jump indicates whether or not the agent should jump */
/*-------------------------------------------------------------------*/
  protected void setJump(boolean jump)
  {
    velocity.setUp(jump ? 300 : 0);
  }

/*-------------------------------------------------------------------*/
/**  Specify whether the bot should crouch. A value of 'true' causes the
*  bot to crouch, while 'false' returns it to a normal standing posture.
@param crouch indicates whether or not the agent should crouch */
/*-------------------------------------------------------------------*/
  protected void setCrouch(boolean crouch)
  {
    velocity.setUp(crouch ? -300 : 0);
  }

/*-------------------------------------------------------------------*/
/**  Check whether agent is currently jumping.
@return true if jumping, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isJumping()
  {
    return proxy != null && proxy.inGame() && proxy.getWorld().getPlayer().isJumping();
  }

/*-------------------------------------------------------------------*/
/**  Check whether agent is currently crouching.
@return true if crouching, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isCrouching()
  {
    return proxy != null && proxy.inGame() && proxy.getWorld().getPlayer().isCrouching();
  }

/*-------------------------------------------------------------------*/
/**  Get the agent's current posture, as one of the POSTURE constants
*  in the PlayerMove class (POSTURE_CROUCH = -1, POSTURE_NORMAL = 0,
*  POSTURE_JUMP = 1;).
@return one of the POSTURE constants in PlayerMove */
/*-------------------------------------------------------------------*/
  protected int getPosture()
  {
    return  (proxy != null && proxy.inGame() ? proxy.getWorld().getPlayer().getPosture() : Integer.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Check whether agent is currently in water.
@return true if in water, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isUnderWater()
  {
    return proxy != null && proxy.inGame() && proxy.getWorld().getPlayer().isUnderWater();
  }

/*-------------------------------------------------------------------*/
/**  Determines the amount of time remaining until the player begins
*  drowning, or Long.MIN_VALUE if the player is not underwater. If
*  the player has a Rebreather or Environment Suit active - meaning
*  that they can breathe underwater - the value returned will indicate
*  the total amount of time until drowning, taking the active item
*  into account.
@return the time remaining until the player starts to drown in
*  milliseconds, or Long.MIN_VALUE if the player is not underwater */
/*-------------------------------------------------------------------*/
  protected long timeUntilDrowning()
  {
    return (proxy != null && proxy.inGame() ? proxy.getWorld().getPlayer().timeUntilDrowning() : Long.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Checks whether the agent is currently drowning.
@return true if the player is drowning, false otherwise. */
/*-------------------------------------------------------------------*/
  protected boolean isDrowning()
  {
    return proxy != null && proxy.inGame() && proxy.getWorld().getPlayer().isDrowning();
  }

/*-------------------------------------------------------------------*/
/**  Reactivate the agent in the game world upon its death. */
/*-------------------------------------------------------------------*/
  protected void respawn()
  {
    angles.setYaw(0);
    angles.setRoll(0);
    angles.setPitch(0);

    velocity.setForward(0);
    velocity.setRight(0);

    action.setAttack(true);

    while(!isBotAlive())
    {
      proxy.sendMovement(angles, velocity, action);

      try
      {
        Thread.sleep(10);
      }
      catch(InterruptedException ie)
      {  }
    }

    pacify();
  }

/*-------------------------------------------------------------------*/
/**  Stop all agent activities. */
/*-------------------------------------------------------------------*/
  protected void pacify()
  {
    action.setAny(false);
    action.setUse(false);
    action.setAttack(false);

    velocity.setRight(0);
    velocity.setForward(0);

    angles.setYaw(0);
    angles.setRoll(0);
    angles.setPitch(0);

    proxy.sendMovement(angles, velocity, action);
  }

  private float[] botAngles = new float[2];
  private Vector2f perp = new Vector2f(0, 0);
  private Vector2f moveDir2f = new Vector2f(0, 0), aimDir2f = new Vector2f(0, 0);

/*-------------------------------------------------------------------*/
/**  Convenience method to facilitate the separation of movement and
*  aiming, and allow both to be specified in global co-ordinates. Also
*  allows the programmer to specify the bot's 'posture' - that is, whether
*  it is standing, crouching or jumping. The postureState parameter should
*  be one of the POSTURE_CROUCH, POSTURE_NORMAL or POSTURE_JUMP constants
*  from the PlayerMove class.
@param moveDir the direction in which to move
@param aimDir the direction in which to aim
@param vel the agent's total velocity
@param postureState indicates the bot's posture (crouch/stand/jump)
@see soc.qase.state.PlayerMove */
/*-------------------------------------------------------------------*/
  protected void setBotMovement(Vector3f moveDir, Vector3f aimDir, float vel, int postureState)
  {
    if(moveDir == null && aimDir == null)
      return;
    else if(moveDir == null)
      moveDir = new Vector3f(aimDir);
    else if(aimDir == null)
      aimDir = new Vector3f(moveDir);

    moveDir2f.set(moveDir.x, moveDir.y);
    aimDir2f.set(aimDir.x, aimDir.y);

    if(vel < 0)
      vel = (int)moveDir2f.length();

    moveDir2f.normalize();
    aimDir2f.normalize();

    moveDir2f.scale(vel);
    perp.set(aimDir2f.y, -aimDir2f.x);

    velocity.setForward((int)Math.round(aimDir2f.x * moveDir2f.x + aimDir2f.y * moveDir2f.y));
    velocity.setRight((int)Math.round(perp.x * moveDir2f.x + perp.y * moveDir2f.y));
    setPosture(postureState);

    botAngles = Utils.calcAngles(aimDir);

    angles.setYaw(botAngles[0]);
    angles.setPitch(botAngles[1]);
  }

/*-------------------------------------------------------------------*/
/**  Convenience method to facilitate the separation of movement and
*  aiming, and allow both to be specified in global co-ordinates. Also
*  allows the programmer to specify the bot's 'posture' - that is, whether
*  it is standing, crouching or jumping. The postureState parameter should
*  be one of the POSTURE_CROUCH, POSTURE_NORMAL or POSTURE_JUMP constants
*  from the PlayerMove class.<br>
<br>
*  Instead of providing an explicit velocity as above, the programmer
*  specifies the current walk state (WALK_STOPPED, WALK_NORMAL, WALK_RUN)
*  using the constants defined in the PlayerMove class. The method computes
*  the correct velocity based on whether the agent is on land or submerged,
*  and then passes the call to setBotMovement(Vector3f, Vector3f, float).
*  To account for the possibility of the programmer passing
*  an explicit velocity as an int rather than float, the call will be
*  passed directly to the above method if walkState exceeds the range
*  of the constant values (i.e. 0, 1, 2).
@param moveDir the direction in which to move
@param aimDir the direction in which to aim
@param walkState the discrete movement speed to use
@param postureState indicates the bot's posture (crouch/stand/jump)
@see soc.qase.state.PlayerMove */
/*-------------------------------------------------------------------*/
  protected void setBotMovement(Vector3f moveDir, Vector3f aimDir, int walkState, int postureState)
  {
    float vel = 0f; // assume stopped

    if(walkState == 0 || walkState > 2)
      vel = (float)walkState;
    else if(proxy.getWorld().getPlayer().isUnderWater()) // in water
      vel = (walkState == 1 ? 110f : 300f);
    else // moving, on land
      vel = (walkState == 1 ? 200f : 300f);

    setBotMovement(moveDir, aimDir, vel, postureState);
  }

/*-------------------------------------------------------------------*/
/**  Instruct the agent to use a global or local co-ordinate system. In
*  Quake 2, the agent operates using a set of local axes that are rotated
*  relative to the global co-ordinate system depending on the player's
*  orientation when spawning. Generally, programmers are advised to work
*  in global co-ordinates (the default setting), but may occasionally need
*  or want to work using the local axes instead.
@param use true if the agent should use global co-ordinates, false
*  if it should use local */
/*-------------------------------------------------------------------*/
  public void useGlobalAngles(boolean use)
  {
    globalAngles = use;
  }

/*-------------------------------------------------------------------*/
/**  Check whether the agent is using a global or local co-ordinate system.
@return true if the agent is using global co-ordinates, false otherwise */
/*-------------------------------------------------------------------*/
  public boolean getUseGlobalAngles()
  {
    return globalAngles;
  }

/*-------------------------------------------------------------------*/
/**  Activate the agent's hand grenades. */
/*-------------------------------------------------------------------*/
  protected void activateGrenades()
  {
    useItem(PlayerGun.GRENADES);
  }

/*-------------------------------------------------------------------*/
/**  Change the active weapon. For use with the constants stored in the
*  PlayerGun class.
@param gun the index of the gun to activate
@see soc.qase.state.PlayerGun */
/*-------------------------------------------------------------------*/
  protected void changeWeapon(int gun)
  {
    changeWeaponByInventoryIndex(gun);
  }

/*-------------------------------------------------------------------*/
/**  Change the current weapon by specifying a number from 0-9. The
*  argument should be one of the KEY_WEAPON constants defined in the
*  User class.
@param gun the keyboard index of the weapon to activate */
/*-------------------------------------------------------------------*/
  protected void changeWeaponByKeyboardIndex(int gun)
  {
    useItem((gun >= 6 ? gun + 7 : (gun > 0 ? gun + 6 : 17)));
  }

/*-------------------------------------------------------------------*/
/**  Change the current weapon by specifying an inventory index.
*  Typically uses the constants from the Inventory class.
@param gun the inventory index of the weapon to activate
@see soc.qase.state.Inventory */
/*-------------------------------------------------------------------*/
  protected void changeWeaponByInventoryIndex(int gun)
  {
    useItem(gun);
  }

/*-------------------------------------------------------------------*/
/**  Change the current weapon by specifying the index of the associated
*  weapon model in the Model subsection of the Config table, i.e. the
*  value returned by calling playerGun.getIndex().
@param gun the index of the weapon to activate
@see soc.qase.info.Config */
/*-------------------------------------------------------------------*/
  protected void changeWeaponByGunModelIndex(int gun)
  {
    useItem(PlayerGun.getGunInventoryIndex(gun));
  }

/*-------------------------------------------------------------------*/
/**  Use the specified item. Items are specified by inventory index,
*  using the constants from the Inventory class.
@param item the inventory index of the weapon to activate
@see soc.qase.state.Inventory */
/*-------------------------------------------------------------------*/
  protected void useItem(int item)
  {
    if (proxy != null && proxy.inGame())
      proxy.useItem(item);
  }

/*-------------------------------------------------------------------*/
/**  Checks whether or not a particular timed buff (invulnerability,
*  quad damage, environment suit, etc) is currently active on the
*  bot; if so, returns the time remaining until the buff expires.
*  If null is passed, then the time remaining for any active buff is
*  returned. If no such buff is active, Integer.MIN_VALUE is returned.
*  The string passed to this method should be one of the appropriate
*  ICON_XYZ constants from the PlayerStatus class, e.g. ICON_INVULNERABILITY,
*  ICON_ENVIRONMENT_SUIT, ICON_QUAD_DAMAGE; this is because  the buff
*  is checked by examining the contents of the TIMER_ICON field in
*  the player's status array. Note that the time is a floored integer
*  representation of the real value - that is, a 30-second buff will
*  begin at 29 and end with 0 being returned for the final second it
*  is active.
@param buffIcon icon string of a particular timed buff, or null to check for
*  any buff
@return the time remaining on the active buff, in seconds; or
*  Integer.MIN_VALUE if no such buff is found */
/*-------------------------------------------------------------------*/
  protected int checkTimedBuff(String buffIcon)
  {
    return (proxy != null && proxy.inGame() ? proxy.getWorld().getPlayer().getPlayerStatus().checkTimedBuff(buffIcon) : Integer.MIN_VALUE);
  }

/*-------------------------------------------------------------------*/
/**  Request a full copy of the agent's current inventory. Used when
*  auto inventory refresh is enabled.*/
/*-------------------------------------------------------------------*/
  protected void refreshInventory()
  {
    proxy.refreshInventory();
  }

/*-------------------------------------------------------------------*/
/**  Get count for specified inventory item.
@param itemIndex item index. Should be one of the constants defined
*  in the Inventory class.
@return item index count. */
/*-------------------------------------------------------------------*/
  protected int getInventoryItemCount(int itemIndex)
  {
    World world;
    Inventory inv;

    if(proxy == null || (world = proxy.getWorld()) == null || (inv = world.getInventory()) == null)
      return -1;

    return inv.getCount(itemIndex);
  }

/*-------------------------------------------------------------------*/
/**  Get count for specified range of inventory items.
@param startItemIndex item index at which to start. Should be one of
*  the constants defined in the Inventory class.
@param endItemIndex item index at which to end, inclusive. Should be
*  one of the constants defined in the Inventory class.
@return an array giving a count of each item in the range, or
*  null if the inventory does not exist. */
/*-------------------------------------------------------------------*/
  protected int[] getInventoryItemCount(int startItemIndex, int endItemIndex)
  {
    World world;
    Inventory inv;

    if(proxy == null || (world = proxy.getWorld()) == null || (inv = world.getInventory()) == null)
      return null;

    return inv.getCount(startItemIndex, endItemIndex);
  }

/*-------------------------------------------------------------------*/
/**  Get count for specified inventory item. Note that this string-matching
*  method is somewhat slower that the index methods above.
@param item plain english string of the item as seen in-game, e.g.
*  rocket launcher, hyperblaster, chaingun.
@return item count. */
/*-------------------------------------------------------------------*/
  protected int getInventoryItemCount(String item)
  {
    World world;
    Inventory inv;

    if(proxy == null || (world = proxy.getWorld()) == null || (inv = world.getInventory()) == null)
      return -1;

    return inv.getCount(item);
  }

/*-------------------------------------------------------------------*/
/**  Determines whether the agent is in possession of the specified item.
@param itemIndex item index. Should be one of the constants defined
*  in the Inventory class.
@return true if the agents possesses one or more of the specified
*  item, false otherwise. */
/*-------------------------------------------------------------------*/
  protected boolean hasItem(int itemIndex)
  {
    return getInventoryItemCount(itemIndex) > 0;
  }

/*-------------------------------------------------------------------*/
/**  Determines whether the agent is in possession of the specified item.
*  Note that this string-matching method is somewhat slower that the
*  index method above.
@param item plain english string of the item as seen in-game, e.g.
*  rocket launcher, hyperblaster, chaingun.
@return true if the agents possesses one or more of the specified
*  item, false otherwise. */
/*-------------------------------------------------------------------*/
  protected boolean hasItem(String item)
  {
    return getInventoryItemCount(item) > 0;
  }

/*-------------------------------------------------------------------*/
/**  Send a non-blocking console message to connected host.
@param command message to send */
/*-------------------------------------------------------------------*/
  protected void sendConsoleCommand(String command)
  {
    if(proxy != null && proxy.inGame())
      proxy.sendConsoleCommand(command);
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest entity in the game world. Returns only entities
*  which are currently active.
@return nearest entity */
/*-------------------------------------------------------------------*/
  protected Entity getNearestEntity()
  {
    if(proxy == null || proxy.getWorld() == null)
      return null;

    return getNearestEntity(proxy.getWorld().getEntities());
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest entity in the game world. Returns only entities
*  which are currently active.
@param cat the category of entity to search for. Should be one of
*  the CAT_ constants in the Entity class, or null to search for all
*  entity categories.
@param type the type of entity to search for. Should be one of the
*  TYPE_ constants in the Entity class, or null to search for all
*  entity types.
@param subType the subtype of entity to search for. Should be one
*  of the SUBTYPE_ constants in the Entity class, or null to search
*  for all entity subtypes.
@return nearest entity */
/*-------------------------------------------------------------------*/
  protected Entity getNearestEntity(String cat, String type, String subType)
  {
    if(proxy == null || proxy.getWorld() == null)
      return null;

    return getNearestEntity(proxy.getWorld().getEntities(cat, type, subType, true));
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest entity in the game world from those contained in
*  the argument Vector.
@param filteredEntities a Vector containing a selection of entities
*  from the gamestate.
@return nearest entity */
/*-------------------------------------------------------------------*/
  protected Entity getNearestEntity(Vector filteredEntities)
  {
    Entity tempEntity = null;
    Entity nearestEntity = null;

    Origin currentPos = getPosition();

    float tempDistance = 0;
    float distance = Float.MAX_VALUE;

    for(int i = 0; i < filteredEntities.size(); i++)
    {
      tempEntity = (Entity)filteredEntities.elementAt(i);
      tempDistance = currentPos.distance(tempEntity.getOrigin());

      if(tempDistance < distance && tempDistance > 0)
      {
        distance = tempDistance;
        nearestEntity = tempEntity;
      }
    }

    return nearestEntity;
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest enemy in the game world. Returns only entities
*  which are currently active.
@return nearest enemy */
/*-------------------------------------------------------------------*/
  protected Entity getNearestOpponent()
  {
    if(proxy == null || proxy.getWorld() == null)
      return null;

    return getNearestEntity(proxy.getWorld().getOpponents());
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest item in the game world. Returns only entities
*  which are currently active.
@param type the type of item to search for. Should be one of the
*  TYPE_ constants in the Entity class, or null to search for all
*  item types.
@param subType the subtype of item to search for. Should be one of
*  the SUBTYPE_ constants in the Entity class, or null to search for all
*  item subtypes.
@return nearest item */
/*-------------------------------------------------------------------*/
  protected Entity getNearestItem(String type, String subType)
  {
    if(proxy == null || proxy.getWorld() == null)
      return null;

    return getNearestEntity(proxy.getWorld().getEntities(Entity.CAT_ITEMS, type, subType, true));
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest item in the game world. Returns only entities
*  which are currently active.
@param type the type of weapon to search for. Should be one of the
*  TYPE_ constants in the Entity class, or null to search for all
*  weapon types.
@return nearest weapon */
/*-------------------------------------------------------------------*/
  protected Entity getNearestWeapon(String type)
  {
    if(proxy == null || proxy.getWorld() == null)
      return null;

    return getNearestEntity(proxy.getWorld().getEntities(Entity.CAT_WEAPONS, type, null, true));
  }

/*-------------------------------------------------------------------*/
/**  Find the nearest object in the game world. Returns only entities
*  which are currently active.
@param type the type of object to search for. Should be one of the
*  TYPE_ constants in the Entity class, or null to search for all
*  object types.
@param subType the subtype of object to search for. Should be one of
*  the SUBTYPE_ constants in the Entity class, or null to search for all
*  object subtypes.
@return nearest item */
/*-------------------------------------------------------------------*/
  protected Entity getNearestObject(String type, String subType)
  {
    if(proxy == null || proxy.getWorld() == null)
      return null;

    return getNearestEntity(proxy.getWorld().getEntities(Entity.CAT_OBJECTS, type, subType, true));
  }

/*-------------------------------------------------------------------*/
/**  Set the agent's WaypointMap to be the specified WaypointMap.
@param wp the WaypointMap that the agent should use */
/*-------------------------------------------------------------------*/
  protected void setWaypointMap(WaypointMap wp)
  {
    wpMap = wp;
  }

/*-------------------------------------------------------------------*/
/**  Return the agent's WaypointMap, thereby allowing full access to its
*  facilities.
@return the agent's WaypointMap */
/*-------------------------------------------------------------------*/
  protected WaypointMap getWaypointMap()
  {
    return wpMap;
  }

/*-------------------------------------------------------------------*/
/**  Load a WaypointMap object from a file created using the saveMap method.
*  The resulting WaypointMap is set as the agent's navigation map, to be
*  used when any waypoint-related BasicBot methods are invoked. A reference
*  to the object is also returned for convenience.
@param filename the path and name of the saved waypoint file
@return the WaypointMap which was loaded from file, or null if the loading
*  process failed
@see soc.qase.ai.waypoint.WaypointMap#saveMap(String) */
/*-------------------------------------------------------------------*/
  protected WaypointMap loadWaypointMap(String filename)
  {
    return (wpMap = WaypointMap.loadMap(filename));
  }

/*-------------------------------------------------------------------*/
/**  Generate a WaypointMap by analysing a recorded DM2 demo. The resulting
*  WaypointMap is set as the agent's navigation map, to be used when
*  any waypoint-related BasicBot methods are invoked. A reference to the
*  object is also returned for convenience. This method records the
*  positions at which items were collected in the demo, and holds these
*  locations constant through the clustering process.
@param dm2Filename filename of the DM2 demo to analyse
@param fNumWaypoints the number of nodes to generate for the WaypointMap.
*  If this number is less than 1, it is treated as a percentage of the
*  total number of observed player positions. If it is 1 or greater, it
*  is treated as an absolute number of nodes to generate.
@return the resulting WaypointMap
@see soc.qase.ai.waypoint.WaypointMapGenerator#generate(String, float) */
/*-------------------------------------------------------------------*/
  protected WaypointMap generateWaypointMap(String dm2Filename, float fNumWaypoints)
  {
    return (wpMap = WaypointMapGenerator.generate(dm2Filename, fNumWaypoints));
  }

/*-------------------------------------------------------------------*/
/**  Get the closest Waypoint to the agent's current position at which
*  an item of the the specified type resides. The category, type and
*  subtype are typically passed using the constants found in the Entity
*  class.
@param cat the category of item to search for, or null to search for
*  entities of any category
@param type the type of item to search for, or null to search for
*  entities of any type
@param subType the subtype of item to search for, or null to search
*  for entities of any subtype
@return the closest Waypoint at which a matching item exists
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint findClosestEntity(String cat, String type, String subType)
  {
    if(wpMap == null)
      return null;

    return wpMap.findClosestEntity(getPosition(), cat, type, subType);
  }

/*-------------------------------------------------------------------*/
/**  Get the closest Waypoint to the agent's current position at which
*  the closest enemy player is located.
@return the closest Waypoint to the nearest enemy player
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint findClosestOpponent()
  {
    Entity nearestOpponent = getNearestOpponent();

    if(wpMap == null || nearestOpponent == null)
      return null;

    return wpMap.findClosestWaypoint(nearestOpponent.getOrigin());
  }

/*-------------------------------------------------------------------*/
/**  Get the closest Waypoint to the agent's current position at which
*  an item of the the given type resides. The item type is specified
*  by inventory index; see the Inventory class for a list of inventory
*  constants.
@param itemInventoryIndex the inventory index corresponding to the
*  item to search for
@return the closest Waypoint at which a matching item exists
@see soc.qase.state.Inventory */
/*-------------------------------------------------------------------*/
  protected Waypoint findClosestItem(int itemInventoryIndex)
  {
    if(wpMap == null)
      return null;

    return wpMap.findClosestItem(getPosition(), itemInventoryIndex);
  }

/*-------------------------------------------------------------------*/
/**  Get the closest Waypoint to the agent's current position at which
*  an item of the the specified type resides. The type and subtype are
*  typically passed using the constants found in the Entity class.
@param type the type of item to search for, or null to search for
*  items of any type
@param subType the subtype of item to search for, or null to search
*  for items of any subtype
@return the closest Waypoint at which a matching item exists
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint findClosestItem(String type, String subType)
  {
    return findClosestEntity(Entity.CAT_ITEMS, type, subType);
  }

/*-------------------------------------------------------------------*/
/**  Get the closest Waypoint to the agent's current position at which
*  a weapon of the the specified type resides. The type is typically
*  passed using the constants found in the Entity class.
@param type the type of weapon to search for, or null to search for
*  weapons of any type
@return the closest Waypoint at which a matching weapon exists
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint findClosestWeapon(String type)
  {
    return findClosestEntity(Entity.CAT_WEAPONS, type, null);
  }

/*-------------------------------------------------------------------*/
/**  Get the closest Waypoint to the agent's current position at which
*  an object of the the specified type resides. The type and subtype are
*  typically passed using the constants found in the Entity class.
@param type the type of object to search for, or null to search for
*  objects of any type
@param subType the subtype of object to search for, or null to search
*  for objects of any subtype
@return the closest Waypoint at which a matching object exists
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint findClosestObject(String type, String subType)
  {
    return findClosestEntity(Entity.CAT_OBJECTS, type, subType);
  }

/*-------------------------------------------------------------------*/
/**  Find the shortest path between the Waypoints closest to the agent's
*  current position and the specified location. This uses the
*  previously-generated cost and predecessor matrices.
@param to the position to which we need a path
@return a Waypoint array indicating the shortest path between the
*  two Waypoints closest to the start and end positions */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPath(Origin to)
  {
    if(wpMap == null)
      return null;

    return wpMap.findShortestPath(getPosition(), to);
  }

/*-------------------------------------------------------------------*/
/**  Get the path through the waypoint graph from the current position to
*  the closest Waypoint at which an entity of the the given type resides.
*  The category, type and subtype are typically passed using the
*  constants found in the Entity class.
@param cat the category of entity to search for, or null to search for
*  entities of any category
@param type the type of entity to search for, or null to search for
*  entities of any type
@param subType the subtype of entity to search for, or null to search for
*  entities of any subtype
@return a Waypoint array indicating the shortest path
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPathToEntity(String cat, String type, String subType)
  {
    if(wpMap == null)
      return null;

    return wpMap.findShortestPathToEntity(getPosition(), cat, type, subType);
  }

/*-------------------------------------------------------------------*/
/**  Get the path through the waypoint graph from the current position to
*  the closest Waypoint at which an enemy player is located.
@return a Waypoint array indicating the shortest path
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPathToOpponent()
  {
    Entity nearestOpponent = getNearestOpponent();
    return (nearestOpponent == null ? null : findShortestPath(nearestOpponent.getOrigin()));
  }

/*-------------------------------------------------------------------*/
/**  Get the path through the waypoint graph from the current position to
*  the closest Waypoint at which an item of the the given type resides.
*  The item type is specified by inventory index; see the Inventory class
*  for a list of inventory constants
@param itemInventoryIndex the inventory index corresponding to the
*  item to search for
@return a Waypoint array indicating the shortest path
@see soc.qase.state.Inventory */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPathToItem(int itemInventoryIndex)
  {
    if(wpMap == null)
      return null;

    return wpMap.findShortestPathToItem(getPosition(), itemInventoryIndex);
  }

/*-------------------------------------------------------------------*/
/**  Get the path through the waypoint graph from the current position to
*  the closest Waypoint at which an item of the the given type resides.
*  The category, type and subtype are typically passed using the
*  constants found in the Entity class.
@param type the type of item to search for, or null to search for
*  entities of any type
@param subType the subtype of item to search for, or null to search for
*  entities of any subtype
@return a Waypoint array indicating the shortest path
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPathToItem(String type, String subType)
  {
    return findShortestPathToEntity(Entity.CAT_ITEMS, type, subType);
  }

/*-------------------------------------------------------------------*/
/**  Get the path through the waypoint graph from the current position to
*  the closest Waypoint at which a weapon of the the given type resides.
*  The type is typically passed using the constants found in the Entity
*  class.
@param type the type of weapon to search for, or null to search for
*  weapons of any type
@return a Waypoint array indicating the shortest path
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPathToWeapon(String type)
  {
    return findShortestPathToEntity(Entity.CAT_WEAPONS, type, null);
  }

/*-------------------------------------------------------------------*/
/**  Get the path through the waypoint graph from the current position to
*  the closest Waypoint at which an object of the the given type resides.
*  The type and subtype are typically passed using the constants found
*  in the Entity class.
@param type the type of object to search for, or null to search for
*  objects of any type
@param subType the subtype of object to search for, or null to search for
*  objects of any subtype
@return a Waypoint array indicating the shortest path
@see soc.qase.state.Entity */
/*-------------------------------------------------------------------*/
  protected Waypoint[] findShortestPathToObject(String type, String subType)
  {
    return findShortestPathToEntity(Entity.CAT_OBJECTS, type, subType);
  }

/*-------------------------------------------------------------------*/
/**  Return the BSP parser object, thereby allowing full access to its
*  facilities.
@return the agent's BSP parser */
/*-------------------------------------------------------------------*/
  protected BSPParser getBSPParser()
  {
    if(isBotAlive() && (bsp.isMapLoaded() || readMap()))
      return bsp;
    else
      return null;
  }

/*-------------------------------------------------------------------*/
/** Returns all Item entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getItems(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getItems(vect)}

/*-------------------------------------------------------------------*/
/** Returns all entities which possess in-game models.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getModels(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getModels(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Weapon entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getWeapons(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getWeapons(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Monster entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getMonsters(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getMonsters(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Door entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getDoors(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getDoors(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Lift entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getLifts(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getLifts(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Button entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getButtons(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getButtons(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Illusory entities (i.e. visible but non-interactive).
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getIllusion(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getIllusion(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Conveyor belts or trains.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getConveyors(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getConveyors(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Teleport entities, both single-player and DeathMatch.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getTeleports(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getTeleports(vect)}

/*-------------------------------------------------------------------*/
/** Returns all single-player teleporters.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getNormalTeleports(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getNormalTeleports(vect)}

/*-------------------------------------------------------------------*/
/** Returns DeathMatch teleporter entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getDMTeleports(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getDMTeleports(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Secret Door entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getSecretDoors(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getSecretDoors(vect)}

/*-------------------------------------------------------------------*/
/** Returns all path corner entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getPathCorners(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getPathCorners(vect)}

/*-------------------------------------------------------------------*/
/** Returns all walkover-button entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getWalkovers(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getWalkovers(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Teleport destination entities.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getTeleportDestinations(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getTeleportDestinations(vect)}

/*-------------------------------------------------------------------*/
/** Returns all Misc object entities (exploding barrels, etc).
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getMiscObjects(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getMiscObjects(vect)}

/*-------------------------------------------------------------------*/
/** Returns all spawn points, regardless of single or multi-player.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getStartPositions(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getStartPositions(vect)}

/*-------------------------------------------------------------------*/
/** Returns all single-player spawn points.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getPlayerStartPositions(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getPlayerStartPositions(vect)}

/*-------------------------------------------------------------------*/
/** Returns all deathmatch spawn points.
@param vect the Vector into which the entities will be added
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getDMStartPositions(Vector vect)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getDMStartPositions(vect)}

/*-------------------------------------------------------------------*/
/** Returns an array of BSPLeaf objects representing the locations
*  at which environmental features - water, lava, etc - matching
*  the specified criteria are found. The argument should be a bitwise
*  OR of one or more of the CONTENTS constants found in the BSPBrush
*  class, e.g. CONTENTS_SLIME, CONTENTS_LAVA, CONTENTS_WATER. Each
*  BSPLeaf contains two Vector3f objects specifying the minimum and
*  maximum extents of the space occupied by the feature. For
*  convenience, separate getLavaLocations, getWaterLocations,
*  getSlimeLocations, getMistLocations and getWindowLocations
*  methods are provided.
@param brushBits a bitwise OR of the features to search for
@return the BSPLeaf objects which match said bitwise OR, null
*  if no matching leaves were found */
/*-------------------------------------------------------------------*/
  protected BSPLeaf[] getEnvironmentFeatureLocations(int brushBits)
  {
    if(!isBotAlive() || (!bsp.isMapLoaded() && !readMap()))
      return null;

    Vector envFeatureLeaves = new Vector();
    BSPLeaf[] bspLeaves = bsp.leafLump.leaves;

    for(int i = 0; i < bspLeaves.length; i++)
    {
      if((bspLeaves[i].brushOr & brushBits) != 0)
        envFeatureLeaves.add(bspLeaves[i]);
    }

    return (BSPLeaf[])envFeatureLeaves.toArray(new BSPLeaf[0]);
  }

/*-------------------------------------------------------------------*/
/** Returns an array of BSPLeaf objects representing the locations
*  at which lava pools were found in the game environment.
@return the BSPLeaf objects corresponding to lava pools, or null
*  if no such pools were found */
/*-------------------------------------------------------------------*/
  protected BSPLeaf[] getLavaLocations()
  {
    return getEnvironmentFeatureLocations(BSPBrush.CONTENTS_LAVA);
  }

/*-------------------------------------------------------------------*/
/** Returns an array of BSPLeaf objects representing the locations
*  at which water pools were found in the game environment.
@return the BSPLeaf objects corresponding to water pools, or null
*  if no such pools were found */
/*-------------------------------------------------------------------*/
  protected BSPLeaf[] getWaterLocations()
  {
    return getEnvironmentFeatureLocations(BSPBrush.CONTENTS_WATER);
  }

/*-------------------------------------------------------------------*/
/** Returns an array of BSPLeaf objects representing the locations
*  at which poison slime pools were found in the game environment.
@return the BSPLeaf objects corresponding to slime pools, or null
*  if no such pools were found */
/*-------------------------------------------------------------------*/
  protected BSPLeaf[] getSlimeLocations()
  {
    return getEnvironmentFeatureLocations(BSPBrush.CONTENTS_SLIME);
  }

/*-------------------------------------------------------------------*/
/** Returns an array of BSPLeaf objects representing the locations
*  at which mist coulds were found in the game environment.
@return the BSPLeaf objects corresponding to mist clouds, or null
*  if no such clouds were found */
/*-------------------------------------------------------------------*/
  protected BSPLeaf[] getMistLocations()
  {
    return getEnvironmentFeatureLocations(BSPBrush.CONTENTS_MIST);
  }

/*-------------------------------------------------------------------*/
/** Returns an array of BSPLeaf objects representing the locations
*  at which windows were found in the game environment.
@return the BSPLeaf objects corresponding to windows, or null
*  if no windows were found */
/*-------------------------------------------------------------------*/
  protected BSPLeaf[] getWindowLocations()
  {
    return getEnvironmentFeatureLocations(BSPBrush.CONTENTS_WINDOW);
  }

/*-------------------------------------------------------------------*/
/** Returns all entities of the specified type. The supplied entity ID
*  should match one of the integer constants found in BSPEntity.
@param vect the Vector into which the entities will be added
@param entID the type of entity to find and return; should be one
*  of the integer constants from BSPEntity
@return a reference to the newly-populated vect for convenience */
/*-------------------------------------------------------------------*/
  protected Vector getEntityType(Vector vect, int entID)
  {  return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? null : bsp.getEntityType(vect, entID)}

  private Origin o = null;
  private Vector3f bbmin = null, bbmax = null;

/*-------------------------------------------------------------------*/
/** Determines whether the agent is currently riding one of the lift
*  entities specified in the BSP file of the map.
@return a reference to the BSPEntity describing the lift if the
*  agent is on one, null otherwise. */
/*-------------------------------------------------------------------*/
  protected BSPEntity isOnLift()
  {
    if(!isBotAlive() || (!bsp.isMapLoaded() && !readMap()))
      return null;

    o = proxy.getWorld().getPlayer().getPlayerMove().getOrigin();

    for(int i = 0; i < bsp.entitiesLump.entities.length; i++)
    {
      if(bsp.entitiesLump.entities[i].isLift)
      {
        bbmin = bsp.entitiesLump.entities[i].model.bboxMin;
        bbmax = bsp.entitiesLump.entities[i].model.bboxMax;

        if(o.getX() >= bbmin.x && o.getX() <= bbmax.x && o.getY() >= bbmin.y && o.getY() <= bbmax.y && o.getZ() >= bbmin.z && o.getZ() <= bbmax.z + 50)
          return bsp.entitiesLump.entities[i];
      }
    }

    return null;
  }

/*-------------------------------------------------------------------*/
/**  Indicate whether visibility-checking functions should simply start
*  from the actual location of the player entity, or from its point-of-view
*  (i.e. the "camera" position). Defaults to FALSE.
@param useVO if true, checks visibility of other points in the game
*  environment from the bot's POV; if false, from the bot's location in
*  the game world (i.e. the centre of the agent model) */
/*-------------------------------------------------------------------*/
  protected void useViewOffset(boolean useVO)
  {
    traceFromView = useVO;
  }

  private Vector3f dir = new Vector3f(0, 0, 0);
  private Vector3f pos = new Vector3f(0, 0, 0);
  private Vector3f oppPos = new Vector3f(0, 0, 0);

/*-------------------------------------------------------------------*/
/**  Check whether a particular entity is visible from the player's
*  current position.
@param e the entity whose visibility will be checked
@return true if visible, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isVisible(Entity e)
  {
    return (e != null && isVisible(e.getOrigin()));
  }

/*-------------------------------------------------------------------*/
/**  Check whether a particular point in the environment is visible from
*  the player's current position.
@param o the point whose visibility will be checked
@return true if visible, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isVisible(Origin o)
  {
    return (o != null && isVisible(new Vector3f(o)));
  }

/*-------------------------------------------------------------------*/
/**  Check whether a particular point in the environment is visible from
*  the player's current position.
@param v the point whose visibility will be checked
@return true if visible, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isVisible(Vector3f v)
  {
    if(v == null || proxy == null || proxy.getWorld() == null) return false;

    pos.set(proxy.getWorld().getPlayer().getPlayerMove().getOrigin());
    if(traceFromView) pos.add(proxy.getWorld().getPlayer().getPlayerView().getViewOffset());

    return !isBotAlive() || (!bsp.isMapLoaded() && !readMap()) ? false : bsp.isVisible(pos, v);
  }

/*-------------------------------------------------------------------*/
/**  Check whether the nearest enemy in the game is currently visible.
@return true if visible, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isNearestOpponentVisible()
  {
    return isNearestOpponentVisible(false);
  }

/*-------------------------------------------------------------------*/
/**  Check whether the nearest enemy in the game is currently visible.
@param withinFOV if true, constrain visibility check to agent's
*  line-of-sight
@return true if visible, false otherwise */
/*-------------------------------------------------------------------*/
  protected boolean isNearestOpponentVisible(boolean withinFOV)
  {
    if(!isBotAlive() || (!bsp.isMapLoaded() && !readMap()))
      return false;

    Entity nearestOpponent = getNearestOpponent();

    if(nearestOpponent != null)
    {
      oppPos.set(nearestOpponent.getOrigin());
      dir.sub(oppPos, pos);

      if(traceFromView) pos.add(proxy.getWorld().getPlayer().getPlayerView().getViewOffset());
      return (withinFOV ? Utils.calcAngles(dir)[0] <= proxy.getWorld().getPlayer().getPlayerView().getFOV() / 2.0 && bsp.isVisible(pos, oppPos) : bsp.isVisible(pos, oppPos));
    }
    else
      return false;
  }

/*-------------------------------------------------------------------*/
/**  Projects a bounding-box through the game world in a given direction
*  from the agent's current position, and returns the location of the
*  first collision with solid geometry. Finds and loads the current game
*  map transparently when called, using BSPParser.
@param dir direction to sweep bounding box
@param maxDist maximum distance across which to sweep
@return the location of the first collision
@see soc.qase.file.bsp.BSPParser */
/*-------------------------------------------------------------------*/
  protected Vector3f getObstacleLocation(Vector3f dir, float maxDist)
  {
    return getObstacleLocation(dir, BSPParser.TRACE_BOX, BSPBrush.CONTENTS_SOLID, maxDist);
  }

/*-------------------------------------------------------------------*/
/**  Get the radius of the sphere used to trace through the level. By
*  default this is 18.0 units, or the same width as the player's
*  bounding box.
@return the sphere radius */
/*-------------------------------------------------------------------*/
  protected float getTraceSphereRadius()
  {
    return sphereRadius;
  }

/*-------------------------------------------------------------------*/
/**  Set the radius of the sphere used to trace through the level. This
*  is permitted to be variable to account for different projectile sizes.
@param radius the new sphere radius */
/*-------------------------------------------------------------------*/
  protected void setTraceSphereRadius(float radius)
  {
    sphereRadius = radius;
  }

/*-------------------------------------------------------------------*/
/**  Projects a bounding-box, sphere or line through the game world in
*  a given direction from the agent's current position, and returns
*  the location of the first collision with geometry matching the given
*  type (specified using the constants in BSPBrush). Finds and loads
*  the current game map transparently when called, using BSPParser.
@param dir direction to sweep bounding box
@param traceType the type of trace, specified using the constants
*  found in BSPParser
@param brushType the type of brush to check against, specified by
*  the constants found in BSPBrush. Allows the agent to check for
*  different types of terrain
@param maxDist maximum distance across which to sweep
@return the location of the first collision
@see soc.qase.file.bsp.BSPParser
@see soc.qase.file.bsp.BSPBrush*/
/*-------------------------------------------------------------------*/
  protected Vector3f getObstacleLocation(Vector3f dir, int traceType, int brushType, float maxDist)
  {
    if(!isBotAlive() || (!bsp.isMapLoaded() && !readMap()))
      return null;

    pos.set(proxy.getWorld().getPlayer().getPlayerMove().getOrigin());
    if(traceFromView) pos.add(proxy.getWorld().getPlayer().getPlayerView().getViewOffset());

    bsp.setBrushType(brushType);

    if(traceType == BSPParser.TRACE_LINE)
      return bsp.getObstacleLocation(pos, dir, maxDist);
    else if(traceType == BSPParser.TRACE_SPHERE)
      return bsp.getObstacleLocation(pos, dir, sphereRadius, maxDist);
    else if(traceType == BSPParser.TRACE_BOX)
      return bsp.getObstacleLocation(pos, dir, BOUNDING_MIN, BOUNDING_MAX, maxDist);
    else
      return null;
  }

/*-------------------------------------------------------------------*/
/**  Projects a bounding-box through the game world in a given direction
*  from the agent's current position, and returns the distance to the
*  nearest solid geometry. Finds and loads the current game map
*  transparently when called, using BSPParser.
@param dir direction to sweep bounding box
@param maxDist maximum distance across which to sweep
@return the location of the first collision
@see soc.qase.file.bsp.BSPParser */
/*-------------------------------------------------------------------*/
  protected float getObstacleDistance(Vector3f dir, float maxDist)
  {
    return getObstacleDistance(dir, BSPParser.TRACE_BOX, BSPBrush.CONTENTS_SOLID, maxDist);
  }

/*-------------------------------------------------------------------*/
/**  Projects a bounding-box, sphere or line through the game world in
*  a given direction from the agent's current position, and returns
*  the distance to the nearest geometry matching the given type
*  (specified using the constants in BSPBrush). Finds and loads the
*  current game map transparently when called, using BSPParser.
@param dir direction to sweep bounding box
@param traceType the type of trace, specified using the constants
*  found in BSPParser
@param brushType the type of brush to check against, specified by
*  the constants found in BSPBrush. Allows the agent to check for
*  different types of terrain
@param maxDist maximum distance across which to sweep
@return the location of the first collision
@see soc.qase.file.bsp.BSPParser
@see soc.qase.file.bsp.BSPBrush*/
/*-------------------------------------------------------------------*/
  protected float getObstacleDistance(Vector3f dir, int traceType, int brushType, float maxDist)
  {
    if(!isBotAlive() || (!bsp.isMapLoaded() && !readMap()))
      return Float.NaN;

    pos.set(proxy.getWorld().getPlayer().getPlayerMove().getOrigin());
    if(traceFromView) pos.add(proxy.getWorld().getPlayer().getPlayerView().getViewOffset());

    bsp.setBrushType(brushType);

    if(traceType == BSPParser.TRACE_LINE)
      return bsp.getObstacleDistance(pos, dir, maxDist);
    else if(traceType == BSPParser.TRACE_SPHERE)
      return bsp.getObstacleDistance(pos, dir, sphereRadius, maxDist);
    else if(traceType == BSPParser.TRACE_BOX)
      return bsp.getObstacleDistance(pos, dir, BOUNDING_MIN, BOUNDING_MAX, maxDist);
    else
      return Float.NaN;
  }

/*-------------------------------------------------------------------*/
/**  Set the Quake 2 home directory. Used when locating the local BSP files
*  containing the game geometry. Two alternatives to calling this method
*  exist; the user can pass the folder location to the JVM as a variable
*  called QUAKE2 using the -D switch (eg 'java -DQUAKE2="C:/quake2"'), or
*  - the preferred approach - an environment variable called 'QUAKE2' can
*  be declared which points to the home folder.
@param q2hd the location of the Quake 2 home folder */
/*-------------------------------------------------------------------*/
  public static void setQuake2HomeDirectory(String q2hd)
  {
    q2HomeDir = q2hd;
  }

/*-------------------------------------------------------------------*/
/**  Return the Quake 2 home folder location.
@return the location of the Quake 2 home folder */
/*-------------------------------------------------------------------*/
  public static String getQuake2HomeDirectory()
  {
    return q2HomeDir;
  }

/*-------------------------------------------------------------------*/
/**  Read a local geometry file into memory. The path to the BSP file can
*  begin with the alias 'Q2HOME' (eg 'Q2HOME/maps/bsp1.bsp') which will
*  automatically be resolved to the actual Quake 2 home folder using
*  findQuake2HomeDirectory. If the correct BSP file is know before the
*  agent connects to the server, this method can be used to pre-load it.
@param filename the location of the BSP file
@return true if the file was successfully read, false otherwise
@see #setQuake2HomeDirectory
@see #findQuake2HomeDirectory */
/*-------------------------------------------------------------------*/
  protected boolean readMap(String filename)
  {
    if(filename.substring(0, 6).equalsIgnoreCase("Q2HOME"))
    {
      if(q2HomeDir == null)
        findQuake2HomeDirectory();

      filename = q2HomeDir + filename.substring(6);
    }

    return bsp.load(filename);
  }

/*-------------------------------------------------------------------*/
/**  Read the current game map into memory. This will automatically
*  deduce the name of the map, and will then search all possible
*  locations in decreasing order of likelihood, including within PAK
*  archives. Uses PAKParser and BSPParser.
@return true if the map was successfully found and loaded, false
*  otherwise
@see soc.qase.file.pak.BSPParser
@see soc.qase.file.pak.PAKParser */
/*-------------------------------------------------------------------*/
  private boolean readMap()
  {
    if(!isBotAlive() || mapNotFound)
      return false;

    try
    String pathAndFileName = null;
      String gameDir = proxy.getServer().getGameDirectory();
      String mapName = proxy.getServer().getMapName();
 
      if(gameDir == null || gameDir.length() == 0)
        gameDir = "baseq2";
 
      if(q2HomeDir == null || q2HomeDir.length() == 0)
        findQuake2HomeDirectory();
 
      // try to load BSP assuming filename == mapName
      if(!bsp.load(q2HomeDir + "/" + gameDir + "/maps/" + mapName + ".bsp"))
      {
        String pakBSPFilename = null;
        String pakDir = q2HomeDir + "/" + gameDir + "/";
 
        // search PAKs assuming filename == mapName
        for(int i = 0; i < 10; i++)
        {
          pakBSPFilename = PAKParser.findFileFromPAK(pakDir + "pak" + i + ".pak", mapName + ".bsp");
 
          if(pakBSPFilename != null)
          {
            bsp.load(pakDir + "pak" + i + ".pak#" + pakBSPFilename);
            break;
          }
        }
      }
 
      // search in BSP files for map name
      if(!bsp.isMapLoaded())
      {
        File bspDir = new File(q2HomeDir + "/" + gameDir + "/maps");
        String[] fileList = bspDir.list();
 
        for(int i = 0; i < fileList.length; i++)
        {
          if(fileList[i].toLowerCase().indexOf(".bsp") != -1 && BSPParser.isMapNameInFile(q2HomeDir + "/" + gameDir + "/maps/" + fileList[i], mapName))
          {
            bsp.load(q2HomeDir + "/" + gameDir + "/maps/" + fileList[i]);
            break;
          }
        }
      }
 
      // search in PAK files for map name
      if(!bsp.isMapLoaded())
      {
        String foundFile = null;
        String pakDir = q2HomeDir + "/" + gameDir + "/";
 
        for(int i = 0; i < 10; i++)
        {
          foundFile = PAKParser.findBSPFileFromPAK(pakDir + "pak" + i + ".pak", mapName);
 
          if(foundFile != null)
          {
            bsp.load(pakDir + "pak" + i + ".pak#" + foundFile);
            break;
          }
        }
      }
 
      mapNotFound = !bsp.isMapLoaded();
    }
    catch(Exception e)
    {  }

    return bsp.isMapLoaded();
  }

  private String findQuake2HomeDirectory()
  {
    q2HomeDir = System.getProperty("QUAKE2");

/*    // System.getenv not valid on most J2SE platforms
    if(q2Dir == null || q2Dir.length() == 0)
    {
      try
      {
        q2Dir = System.getenv("QUAKE2");
      }
      catch(Exception e)
      {  }
    }
*/

    if(q2HomeDir == null || q2HomeDir.length() == 0)
      q2HomeDir = Utils.parseEnvironmentVariables("QUAKE2");

    if(q2HomeDir == null || q2HomeDir.length() == 0)
      q2HomeDir = "c:/quake2";

    return q2HomeDir;
  }

/*-------------------------------------------------------------------*/
/**  Check if the proxy is recording the current game.
@return true if the proxy is recording the current game,
*  otherwise false. */
/*-------------------------------------------------------------------*/
  public boolean isRecording()
  {
    return proxy.isRecording();
  }

  protected void sendMovement()
  {
    if(globalAngles)
    {
      delta_Angles = proxy.getWorld().getPlayer().getPlayerMove().getDeltaAngles();
      angles.setYaw(angles.getYaw() - delta_Angles.getYaw());
      angles.setPitch(angles.getPitch() - delta_Angles.getPitch());
      angles.setRoll(-delta_Angles.getRoll());
    }

    proxy.sendMovement(angles, velocity, action);
  }
}
TOP

Related Classes of soc.qase.bot.BasicBot

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.