public void setTarget(int x, int y, int z) {
    checkOrigin();
    // We target the middle of the block, at the very top.
    final Vec3 target = Vec3.createVectorHelper(x + 0.5, y + 1, z + 0.5);

    // Horizontal distance between the origin and target
    final double distHorizontal =
        KNOB_LOB_HORIZONTAL_MUL
            * Math.sqrt(
                Math.pow(target.xCoord - projectileOrigin.xCoord, 2)
                    + Math.pow(target.zCoord - projectileOrigin.zCoord, 2));

    // No vertical multiplier is applied for decline slopes.
    final double distVertical =
        Math.max((target.yCoord - projectileOrigin.yCoord) * KNOB_LOB_VERTICAL_MUL, 0);

    // Calculate the arc of the trajectory
    final float lobScale =
        (float)
            Math.min(
                KNOB_LOB_MAXIMUM_VALUE,
                Math.max(KNOB_LOB_MINIMUM_VALUE, KNOB_LOB_BONUS + distHorizontal + distVertical));

    // Calculate the velocity of the projectile
    final Vec3 velocity =
        TileEntityCannonLogic.calculateTrajectory(projectileOrigin, target, lobScale);

    // m/s applied to item.
    final double speed = velocity.lengthVector();
    targetSpeed.set(speed);

    // reverse the vector to angles for cannon model
    final Vec3 direction = velocity.normalize();
    final double pitch = Math.asin(direction.yCoord);
    final double yaw = Math.atan2(direction.zCoord, direction.xCoord);

    // Set yaw and pitch
    targetYaw.set(Math.toDegrees(yaw) + YAW_OFFSET_DEGREES);
    targetPitch.set(Math.toDegrees(pitch));

    currentYaw = targetYaw.get();
    currentPitch = targetPitch.get();

    // Sync targets
    sync();
  }
  @Override
  public void updateEntity() {
    checkOrigin();

    if (Double.isNaN(currentPitch)) {
      Log.warn("Pitch was NaN");
      currentPitch = 45;
      targetPitch.set(currentPitch);
    }
    if (Double.isNaN(currentYaw)) {
      Log.warn("Yaw was NaN");
      currentYaw = 0;
    }

    super.updateEntity();

    // ugly, need to clean
    currentPitch = currentPitch - ((currentPitch - targetPitch.get()) / KNOB_PITCH_CHANGE_SPEED);
    currentYaw = GeometryUtils.normalizeAngle(currentYaw);

    final double targetYaw = GeometryUtils.normalizeAngle(this.targetYaw.get());
    if (Math.abs(currentYaw - targetYaw) < KNOB_YAW_CHANGE_SPEED) currentYaw = targetYaw;
    else {
      double dist = GeometryUtils.getAngleDistance(currentYaw, targetYaw);
      currentYaw += KNOB_YAW_CHANGE_SPEED * Math.signum(dist);
    }

    currentSpeed = currentSpeed - ((currentSpeed - targetSpeed.get()) / KNOB_VEL_CHANGE_SPEED);

    invalidateMotion();

    if (!worldObj.isRemote) {
      if (worldObj.getTotalWorldTime() % 20 == 0) {
        if (worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord)) {
          ItemStack stack = findStack();
          if (stack != null) fireStack(stack);
        }
      }
    } else {
      if (ticksSinceLastFire < 100) {
        ticksSinceLastFire++;
      }
    }
  }
 public void setYaw(double yaw2) {
   targetYaw.set(yaw2);
   sync();
 }
 public void setPitch(double pitch2) {
   targetPitch.set(pitch2);
   sync();
 }
 public void setSpeed(double speed) {
   targetSpeed.set(speed);
   sync();
 }