All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bukkit.Location.kt Maven / Gradle / Ivy

The newest version!
package org.bukkit

import io.github.uinnn.serializer.serial.LocationSerializer
import net.chestmc.common.interfaces.Localizable
import kotlinx.serialization.Serializable
import net.chestmc.Coordinate
import org.bukkit.block.Block
import org.bukkit.configuration.serialization.ConfigurationSerializable
import org.bukkit.util.NumberConversions
import org.bukkit.util.Vector
import java.util.*
import kotlin.math.*

/**
 * Represents a 3-dimensional position in a world
 */
@Serializable(LocationSerializer::class)
open class Location(
  var world: World,
  var x: Double,
  var y: Double,
  var z: Double,
  var pitch: Float = 0f,
  var yaw: Float = 0f,
) : Cloneable, ConfigurationSerializable, Comparable, Localizable {

  /**
   * Backend access to the [Localizable] implementation.
   */
  override var location: Location = this
    set(value) = throw IllegalAccessException("Tryed to change the location backend access.")

  /**
   * Gets the chunk at the represented location.
   */
  val chunk: Chunk get() = world.getChunkAt(this)

  /**
   * Gets the block at the represented location.
   */
  val block: Block get() = world.getBlockAt(this)

  /**
   * Gets the floored value of the X component, indicating the block that
   * this location is contained with.
   */
  val blockX: Int get() = locToBlock(x)

  /**
   * Gets the floored value of the Y component, indicating the block that
   * this location is contained with.
   */
  val blockY: Int get() = locToBlock(y)

  /**
   * Gets the floored value of the Z component, indicating the block that
   * this location is contained with.
   */
  val blockZ: Int get() = locToBlock(z)

  /**
   * Constructs a new location with the specified world and coordinates.
   */
  constructor(world: World, x: Number, y: Number, z: Number) :
    this(world, x.toDouble(), y.toDouble(), z.toDouble(), 0f, 0f)

  /**
   * Constructs a new location with the specified world and coordinates.
   */
  constructor(world: String, x: Number, y: Number, z: Number) :
    this(Bukkit.getWorld(world), x, y, z)

  /**
   * Constructs a new location with the specified world, coordinates and direction.
   */
  constructor(world: String, x: Number, y: Number, z: Number, pitch: Number, yaw: Number) :
    this(Bukkit.getWorld(world), x.toDouble(), y.toDouble(), z.toDouble(), pitch.toFloat(), yaw.toFloat())

  /**
   * Constructs a new location with the specified world and coordinates.
   */
  constructor(world: UUID, x: Number, y: Number, z: Number) :
    this(Bukkit.getWorld(world), x, y, z)

  /**
   * Constructs a new location with the specified world, coordinates and direction.
   */
  constructor(world: UUID, x: Number, y: Number, z: Number, pitch: Number, yaw: Number) :
    this(Bukkit.getWorld(world), x.toDouble(), y.toDouble(), z.toDouble(), pitch.toFloat(), yaw.toFloat())

  /**
   * Gets a unit-vector pointing in the direction that this Location is facing.
   */
  var direction: Vector
    get() {
      val vector = Vector()

      val rotX: Double = yaw.toDouble()
      val rotY: Double = pitch.toDouble()
      val xz = cos(Math.toRadians(rotY))

      vector.y = -sin(Math.toRadians(rotY))
      vector.x = -xz * sin(Math.toRadians(rotX))
      vector.z = xz * cos(Math.toRadians(rotX))

      return vector
    }
  set(value) {
    setDirection(value)
  }

  /**
   * Sets the [yaw] and [pitch] to point in the direction of the vector.
   */
  fun setDirection(vector: Vector): Location {
    val pi = 2 * Math.PI
    val x = vector.x
    val z = vector.z

    if (x == 0.0 && z == 0.0) {
      pitch = if (vector.y > 0) -90f else 90.toFloat()
      return this
    }

    val theta = atan2(-x, z)
    yaw = Math.toDegrees((theta + pi) % pi).toFloat()

    val x2 = NumberConversions.square(x)
    val z2 = NumberConversions.square(z)
    val xz = sqrt(x2 + z2)
    pitch = Math.toDegrees(atan(-vector.y / xz)).toFloat()

    return this
  }

  /**
   * Adds the location by another.
   */
  fun add(vec: Location): Location {
    x += vec.x
    y += vec.y
    z += vec.z
    return this
  }

  /**
   * Adds the location by a vector.
   */
  fun add(vec: Vector): Location {
    x += vec.x
    y += vec.y
    z += vec.z
    return this
  }

  /**
   * Adds the location by another. Not world-aware.
   */
  fun add(x: Double, y: Double, z: Double): Location {
    this.x += x
    this.y += y
    this.z += z
    return this
  }

  /**
   * Subtracts the location by another.
   */
  fun subtract(vec: Location): Location {
    x -= vec.x
    y -= vec.y
    z -= vec.z
    return this
  }

  /**
   * Subtracts the location by a vector.
   */
  fun subtract(vec: Vector): Location {
    x -= vec.x
    y -= vec.y
    z -= vec.z
    return this
  }

  /**
   * Subtracts the location by another. Not world-aware and
   * orientation independent.
   */
  fun subtract(x: Double, y: Double, z: Double): Location {
    this.x -= x
    this.y -= y
    this.z -= z
    return this
  }

  /**
   * Gets the magnitude of the location, defined as sqrt(x^2+y^2+z^2). The
   * value of this method is not cached and uses a costly square-root
   * function, so do not repeatedly call this method to get the location's
   * magnitude. NaN will be returned if the inner result of the sqrt()
   * function overflows, which will be caused if the length is too long. Not
   * world-aware and orientation independent.
   */
  fun length(): Double =
    sqrt(NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z))


  /**
   * Gets the magnitude of the location squared. Not world-aware and
   * orientation independent.
   */
  fun lengthSquared(): Double =
    NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z)


  /**
   * Get the distance between this location and another. The value of this
   * method is not cached and uses a costly square-root function, so do not
   * repeatedly call this method to get the location's magnitude. NaN will
   * be returned if the inner result of the sqrt() function overflows, which
   * will be caused if the distance is too long.
   */
  fun distance(o: Location): Double = sqrt(distanceSquared(o))

  /**
   * Get the squared distance between this location and another.
   */
  fun distanceSquared(o: Location): Double =
    NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z)


  /**
   * Performs scalar multiplication, multiplying all components with a
   * scalar. Not world-aware.
   */
  fun multiply(m: Double): Location {
    x *= m
    y *= m
    z *= m
    return this
  }

  /**
   * Zero this location's components. Not world-aware.
   */
  fun zero(): Location {
    x = 0.0
    y = 0.0
    z = 0.0
    return this
  }

  override fun equals(other: Any?): Boolean {
    if (other == null) {
      return false
    }
    if (javaClass != other.javaClass) {
      return false
    }
    val other = other as Location
    if (world != other.world) {
      return false
    }
    if (java.lang.Double.doubleToLongBits(x) != java.lang.Double.doubleToLongBits(other.x)) {
      return false
    }
    if (java.lang.Double.doubleToLongBits(y) != java.lang.Double.doubleToLongBits(other.y)) {
      return false
    }
    if (java.lang.Double.doubleToLongBits(z) != java.lang.Double.doubleToLongBits(other.z)) {
      return false
    }
    return if (java.lang.Float.floatToIntBits(pitch) != java.lang.Float.floatToIntBits(other.pitch)) {
      false
    } else java.lang.Float.floatToIntBits(yaw) == java.lang.Float.floatToIntBits(other.yaw)
  }

  override fun hashCode(): Int {
    var hash = 3
    hash = 19 * hash + world.hashCode()
    hash = 19 * hash + (java.lang.Double.doubleToLongBits(x) xor (java.lang.Double.doubleToLongBits(x) ushr 32)).toInt()
    hash = 19 * hash + (java.lang.Double.doubleToLongBits(y) xor (java.lang.Double.doubleToLongBits(y) ushr 32)).toInt()
    hash = 19 * hash + (java.lang.Double.doubleToLongBits(z) xor (java.lang.Double.doubleToLongBits(z) ushr 32)).toInt()
    hash = 19 * hash + java.lang.Float.floatToIntBits(pitch)
    hash = 19 * hash + java.lang.Float.floatToIntBits(yaw)
    return hash
  }

  override fun toString(): String = "Location{world=$world,x=$x,y=$y,z=$z,pitch=$pitch,yaw=$yaw}"

  /**
   * Converts this location to a coordinate.
   */
  fun toCoordinate(): Coordinate = Coordinate.from(this)

  /**
   * Constructs a new [Vector] based on this Location
   *
   * @return New Vector containing the coordinates represented by this
   * Location
   */
  open fun toVector(): Vector = Vector(x, y, z)

  public override fun clone(): Location {
    return try {
      super.clone() as Location
    } catch (e: CloneNotSupportedException) {
      throw Error(e)
    }
  }

  override fun compareTo(other: Location): Int {
    if ((x == other.x || y == other.y) && z == other.z) return 0
    return if (x < other.x && y < other.y && z < other.z) -1 else 0
  }

  @Utility
  override fun serialize(): Map {
    val data: MutableMap = HashMap()
    data["world"] = world.name
    data["x"] = x
    data["y"] = y
    data["z"] = z
    data["yaw"] = yaw
    data["pitch"] = pitch
    return data
  }

  companion object {

    /**
     * Safely converts a double (location coordinate) to an int (block coordinate)
     */
    @JvmStatic
    fun locToBlock(loc: Double): Int = NumberConversions.floor(loc)

    /**
     * Required method for deserialization.
     */
    @JvmStatic
    fun deserialize(args: Map): Location {
      val world = Bukkit.getWorld(args["world"] as String?) ?: throw IllegalArgumentException("unknown world")
      return Location(world,
        NumberConversions.toDouble(args["x"]),
        NumberConversions.toDouble(args["y"]),
        NumberConversions.toDouble(args["z"]),
        NumberConversions.toFloat(args["yaw"]),
        NumberConversions.toFloat(args["pitch"]))
    }

    /**
     * Converts the specified string to a location.
     */
    fun from(string: String): Location {
      val split = string.split('|')
      return Location(split[0], split[1].toDouble(), split[2].toDouble(), split[3].toDouble())
    }

    /**
     * Converts the specified string to a location.
     */
    fun from(world: World, string: String): Location {
      val split = string.split('|')
      return Location(world, split[0].toDouble(), split[1].toDouble(), split[2].toDouble())
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy