/**
   * Handle direction press to invoke or disable auto-walk.
   *
   * @param direction The direction to move/face
   * @param user The entity to set/check
   */
  private synchronized void processAutoWalk(final Direction direction, final User user) {
    RPAction walkAction = new RPAction();
    final boolean facing = direction == user.getDirection();

    /* Correct facing direction if necessary. */
    if (!facing) {
      RPAction faceAction = new RPAction();
      faceAction.put(TYPE, FACE);
      faceAction.put(DIR, direction.get());
      this.client.send(faceAction);
    }

    /* Check if player is already using auto-walk. */
    if (!user.getRPObject().has(AUTOWALK)) {
      walkAction.put(TYPE, WALK);

    } else if (facing) {
      /* Player can press key of current walking direction to toggle
       * auto-walk off.
       */
      walkAction.put(TYPE, WALK);
      walkAction.put(MODE, "stop");
    }

    /* Send auto-walk action to the server. */
    if (walkAction.has(TYPE)) {
      /* Toggle auto-walk. */
      this.client.send(walkAction);
    }
  }
  /**
   * Initialize this entity for an object.
   *
   * @param base The object.
   * @see #release()
   */
  @Override
  public void initialize(final RPObject base) {
    double speed;

    super.initialize(base);

    if (base.has("dir")) {
      setDirection(Direction.build(base.getInt("dir")));
    }

    if (base.has("speed")) {
      speed = base.getDouble("speed");
    } else {
      speed = 0.0;
    }

    dx = direction.getdx() * speed;
    dy = direction.getdy() * speed;
  }
  /**
   * When entity moves, it will be called with the data.
   *
   * @param x new x coordinate
   * @param y new y coordinate
   * @param direction new direction
   * @param speed new speed
   */
  private void onMove(final int x, final int y, final Direction direction, final double speed) {

    double oldx = this.x;
    double oldy = this.y;
    setSpeed(direction.getdx() * speed, direction.getdy() * speed);

    if ((Direction.LEFT.equals(direction)) || (Direction.RIGHT.equals(direction))) {
      this.y = y;
      if (compareDouble(this.x, x, 1.0)) {
        // make the movement look more nicely: + this.dx * 0.1
        this.dx = calcDeltaMovement(this.x + this.dx * 0.1, x, direction.getdx()) * speed;
      } else {
        this.x = x;
      }
      this.dy = 0;
    } else if ((Direction.UP.equals(direction)) || (Direction.DOWN.equals(direction))) {
      this.x = x;
      this.dx = 0;
      if (compareDouble(this.y, y, 1.0)) {
        // make the movement look more nicely: + this.dy * 0.1
        this.dy = calcDeltaMovement(this.y + this.dy * 0.1, y, direction.getdy()) * speed;
      } else {
        this.y = y;
      }
    } else {
      // placing entities
      this.x = x;
      this.y = y;
    }

    // Call onPosition only if the entity actually moved. Also always call
    // on partial coordinates - those are always predicted rather than real
    // and thus should always be a result of prediction. However, the
    // client collision detection does not always agree with that of the
    // server, so relying on just the coordinate change checks can miss
    // entities stopping when they collide with each other.
    if (!compareDouble(this.x, oldx, EPSILON)
        || !compareDouble(this.y, oldy, EPSILON)
        || !compareDouble(oldx, (int) oldx, EPSILON)
        || !compareDouble(oldy, (int) oldy, EPSILON)) {
      onPosition(x, y);
    }
  }
  /**
   * Process attribute changes that may affect positioning. This is needed because different
   * entities may want to process coordinate changes more gracefully.
   *
   * @param base The previous values.
   * @param diff The changes.
   */
  @Override
  protected void processPositioning(final RPObject base, final RPObject diff) {
    // Real movement case
    final int oldx = base.getInt("x");
    final int oldy = base.getInt("y");

    int newX = oldx;
    int newY = oldy;

    if (diff.has("x")) {
      newX = diff.getInt("x");
    }
    if (diff.has("y")) {
      newY = diff.getInt("y");
    }

    Direction tempDirection;

    if (diff.has("dir")) {
      tempDirection = Direction.build(diff.getInt("dir"));
      setDirection(tempDirection);
    } else if (base.has("dir")) {
      tempDirection = Direction.build(base.getInt("dir"));
      setDirection(tempDirection);
    } else {
      tempDirection = Direction.STOP;
    }

    double speed;

    /*
     * Speed change must be fired only after the new speed has been stored
     * (done in onMove())
     */
    boolean speedChanged = false;

    if (diff.has("speed")) {
      speed = diff.getDouble("speed");
      speedChanged = true;
    } else if (base.has("speed")) {
      speed = base.getDouble("speed");
    } else {
      speed = 0;
    }

    onMove(newX, newY, tempDirection, speed);

    if (speedChanged) {
      fireChange(PROP_SPEED);
    }

    boolean positionChanged = false;
    if ((Direction.STOP.equals(tempDirection)) || (speed == 0)) {
      setSpeed(0.0, 0.0);

      /*
       * Try to ensure relocation in the case the client and server were
       * in disagreement about the position at the moment of stopping.
       */
      if (!(compareDouble(y, newY, EPSILON) && compareDouble(x, newX, EPSILON))) {
        positionChanged = true;
      }

      // Store the new position before signaling it with onPosition().
      x = newX;
      y = newY;
    }

    /*
     * Change in position?
     */
    if (positionChanged || ((oldx != newX) && (oldy != newY))) {
      onPosition(newX, newY);
    }
  }
  @Override
  public void keyPressed(final KeyEvent e) {
    final int keyCode = e.getKeyCode();

    /* Ignore if the key is already pressed down. */
    if (!client.keyIsPressed(keyCode)) {
      /* Add keyCode to pressedStateKeys list. */
      client.onKeyPressed(keyCode);

      if (e.isShiftDown()) {
        /*
         * We are going to use shift to move to previous/next line of text
         * with arrows so we just ignore the keys if shift is pressed.
         */
        return;
      }

      switch (keyCode) {
        case KeyEvent.VK_R:
          if (e.isControlDown()) {
            /*
             * Ctrl+R Remove text bubbles
             */
            screen.clearTexts();
          }
          break;

        case KeyEvent.VK_LEFT:
        case KeyEvent.VK_RIGHT:
        case KeyEvent.VK_UP:
        case KeyEvent.VK_DOWN:
          /*
           * Ctrl means face, otherwise move. Alt turns on auto-walk.
           */
          final Direction direction = keyCodeToDirection(e.getKeyCode());

          /* TODO: Remove MOTION condition when auto-walk testing is
           * finished.
           *
           * Check if the player is currently using auto-walk or the Alt
           * key is pressed.
           */
          User user = User.get();
          if ((user.getRPObject().has(AUTOWALK) || e.isAltDown()) && Testing.MOVEMENT) {
            /* Face direction pressed and toggle auto-walk. */
            this.processAutoWalk(direction, user);
          } else {
            if (e.isAltGraphDown()) {
              if (System.currentTimeMillis() - lastAction > 1000) {
                final EntityView<?> view =
                    screen.getEntityViewAt(
                        user.getX() + direction.getdx(), user.getY() + direction.getdy());

                if (view != null) {
                  final IEntity entity = view.getEntity();
                  if (!entity.equals(user)) {
                    view.onAction();
                    lastAction = System.currentTimeMillis();
                  }
                }
              }
            }

            this.processDirectionPress(direction, e.isControlDown());
          }
          break;
        case KeyEvent.VK_0:
        case KeyEvent.VK_1:
        case KeyEvent.VK_2:
        case KeyEvent.VK_3:
        case KeyEvent.VK_4:
        case KeyEvent.VK_5:
        case KeyEvent.VK_6:
        case KeyEvent.VK_7:
        case KeyEvent.VK_8:
        case KeyEvent.VK_9:
          switchToSpellCastingState(e);
          break;
      }
    }
  }