/** Updates the status if the entity is currently swimming (in water). */
  private void updateSwimStatus() {
    ArrayList<BlockPosition> blockPositions = gatherAdjacentBlockPositions(getPosition());

    boolean swimming = false, headUnderWater = false;

    for (int i = 0; i < blockPositions.size(); i++) {
      BlockPosition p = blockPositions.get(i);
      byte blockType = _parent.getWorldProvider().getBlockAtPosition(new Vector3d(p.x, p.y, p.z));
      AABB blockAABB = Block.AABBForBlockAt(p.x, p.y, p.z);

      if (BlockManager.getInstance().getBlock(blockType).isLiquid()
          && getAABB().overlaps(blockAABB)) {
        swimming = true;
      }

      Vector3d eyePos = calcEyePosition();
      eyePos.y += 0.25;

      if (BlockManager.getInstance().getBlock(blockType).isLiquid() && blockAABB.contains(eyePos)) {
        headUnderWater = true;
      }
    }

    _headUnderWater = headUnderWater;
    _isSwimming = swimming;
  }
  /**
   * Checks for blocks around the entity.
   *
   * @param origin The original position of the entity
   * @return True if the entity is colliding horizontally
   */
  private boolean horizontalHitTest(Vector3d origin) {
    boolean result = false;
    ArrayList<BlockPosition> blockPositions = gatherAdjacentBlockPositions(origin);

    // Check each block position for collision
    for (int i = 0; i < blockPositions.size(); i++) {
      BlockPosition p = blockPositions.get(i);
      byte blockType = _parent.getWorldProvider().getBlockAtPosition(new Vector3d(p.x, p.y, p.z));
      AABB blockAABB = Block.AABBForBlockAt(p.x, p.y, p.z);

      if (!BlockManager.getInstance().getBlock(blockType).isPenetrable()) {
        if (getAABB().overlaps(blockAABB)) {
          result = true;

          // Calculate the direction from the origin to the current position
          Vector3d direction = new Vector3d(getPosition().x, 0f, getPosition().z);
          direction.x -= origin.x;
          direction.z -= origin.z;

          // Calculate the point of intersection on the block's AABB
          Vector3d blockPoi = blockAABB.closestPointOnAABBToPoint(origin);
          Vector3d entityPoi = generateAABBForPosition(origin).closestPointOnAABBToPoint(blockPoi);

          Vector3d planeNormal =
              blockAABB.normalForPlaneClosestToOrigin(blockPoi, origin, true, false, true);

          // Find a vector parallel to the surface normal
          Vector3d slideVector = new Vector3d(planeNormal.z, 0, -planeNormal.x);
          Vector3d pushBack = new Vector3d();

          pushBack.sub(blockPoi, entityPoi);

          // Calculate the intensity of the diversion alongside the block
          double length = slideVector.dot(direction);

          Vector3d newPosition = new Vector3d();
          newPosition.z = origin.z + pushBack.z * 0.2 + length * slideVector.z;
          newPosition.x = origin.x + pushBack.x * 0.2 + length * slideVector.x;
          newPosition.y = origin.y;

          // Update the position
          getPosition().set(newPosition);
        }
      }
    }

    return result;
  }
  /**
   * Checks for blocks below and above the entity.
   *
   * @param origin The origin position of the entity
   * @return True if a vertical collision was detected
   */
  private boolean verticalHitTest(Vector3d origin) {
    ArrayList<BlockPosition> blockPositions = gatherAdjacentBlockPositions(origin);

    for (int i = 0; i < blockPositions.size(); i++) {
      BlockPosition p = blockPositions.get(i);

      byte blockType1 = _parent.getWorldProvider().getBlockAtPosition(new Vector3d(p.x, p.y, p.z));
      AABB entityAABB = getAABB();

      if (BlockManager.getInstance().getBlock(blockType1).isPenetrable()
          || !entityAABB.overlaps(Block.AABBForBlockAt(p.x, p.y, p.z))) continue;

      double direction = origin.y - getPosition().y;

      if (direction >= 0) getPosition().y = p.y + 0.50001f + entityAABB.getDimensions().y;
      else getPosition().y = p.y - 0.50001f - entityAABB.getDimensions().y;

      return true;
    }

    return false;
  }