2010-12-27 02:11:52 +00:00
|
|
|
package org.bukkit;
|
|
|
|
|
2011-02-18 16:28:42 +00:00
|
|
|
import org.bukkit.block.Block;
|
2012-02-29 16:07:47 -06:00
|
|
|
import org.bukkit.util.NumberConversions;
|
2011-01-15 13:43:09 -08:00
|
|
|
import org.bukkit.util.Vector;
|
|
|
|
|
2010-12-27 02:11:52 +00:00
|
|
|
/**
|
|
|
|
* Represents a 3-dimensional position in a world
|
|
|
|
*/
|
2010-12-29 03:19:12 +00:00
|
|
|
public class Location implements Cloneable {
|
2010-12-27 02:11:52 +00:00
|
|
|
private World world;
|
|
|
|
private double x;
|
|
|
|
private double y;
|
|
|
|
private double z;
|
2010-12-28 16:59:25 +00:00
|
|
|
private float pitch;
|
|
|
|
private float yaw;
|
2010-12-27 02:11:52 +00:00
|
|
|
|
2011-02-19 22:47:23 +00:00
|
|
|
/**
|
|
|
|
* Constructs a new Location with the given coordinates
|
|
|
|
*
|
|
|
|
* @param world The world in which this location resides
|
|
|
|
* @param x The x-coordinate of this new location
|
|
|
|
* @param y The y-coordinate of this new location
|
|
|
|
* @param z The z-coordinate of this new location
|
|
|
|
*/
|
2010-12-27 02:11:52 +00:00
|
|
|
public Location(final World world, final double x, final double y, final double z) {
|
2010-12-28 16:59:25 +00:00
|
|
|
this(world, x, y, z, 0, 0);
|
|
|
|
}
|
|
|
|
|
2011-02-19 22:47:23 +00:00
|
|
|
/**
|
|
|
|
* Constructs a new Location with the given coordinates and direction
|
|
|
|
*
|
|
|
|
* @param world The world in which this location resides
|
|
|
|
* @param x The x-coordinate of this new location
|
|
|
|
* @param y The y-coordinate of this new location
|
|
|
|
* @param z The z-coordinate of this new location
|
|
|
|
* @param yaw The absolute rotation on the x-plane, in degrees
|
|
|
|
* @param pitch The absolute rotation on the y-plane, in degrees
|
|
|
|
*/
|
2010-12-29 01:15:16 +00:00
|
|
|
public Location(final World world, final double x, final double y, final double z, final float yaw, final float pitch) {
|
2010-12-27 02:11:52 +00:00
|
|
|
this.world = world;
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.z = z;
|
2010-12-28 16:59:25 +00:00
|
|
|
this.pitch = pitch;
|
|
|
|
this.yaw = yaw;
|
2010-12-27 02:11:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the world that this location resides in
|
|
|
|
*
|
|
|
|
* @param world New world that this location resides in
|
|
|
|
*/
|
|
|
|
public void setWorld(World world) {
|
|
|
|
this.world = world;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the world that this location resides in
|
|
|
|
*
|
|
|
|
* @return World that contains this location
|
|
|
|
*/
|
|
|
|
public World getWorld() {
|
|
|
|
return world;
|
|
|
|
}
|
|
|
|
|
2011-12-11 21:13:45 -08:00
|
|
|
/**
|
|
|
|
* Gets the chunk at the represented location
|
|
|
|
*
|
|
|
|
* @return Chunk at the represented location
|
|
|
|
*/
|
|
|
|
public Chunk getChunk() {
|
|
|
|
return world.getChunkAt(this);
|
|
|
|
}
|
|
|
|
|
2011-02-18 16:28:42 +00:00
|
|
|
/**
|
|
|
|
* Gets the block at the represented location
|
|
|
|
*
|
|
|
|
* @return Block at the represented location
|
|
|
|
*/
|
|
|
|
public Block getBlock() {
|
2011-02-18 16:34:49 +00:00
|
|
|
return world.getBlockAt(this);
|
2011-02-18 16:28:42 +00:00
|
|
|
}
|
|
|
|
|
2010-12-27 02:11:52 +00:00
|
|
|
/**
|
|
|
|
* Sets the x-coordinate of this location
|
|
|
|
*
|
|
|
|
* @param x X-coordinate
|
|
|
|
*/
|
|
|
|
public void setX(double x) {
|
|
|
|
this.x = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the x-coordinate of this location
|
|
|
|
*
|
|
|
|
* @return x-coordinate
|
|
|
|
*/
|
|
|
|
public double getX() {
|
|
|
|
return x;
|
|
|
|
}
|
2011-02-02 00:02:08 +01:00
|
|
|
|
2011-01-02 10:42:13 -08:00
|
|
|
/**
|
|
|
|
* Gets the floored value of the X component, indicating the block that
|
|
|
|
* this location is contained with.
|
2011-02-02 00:02:08 +01:00
|
|
|
*
|
2011-01-02 10:42:13 -08:00
|
|
|
* @return block X
|
|
|
|
*/
|
|
|
|
public int getBlockX() {
|
2011-02-25 09:51:52 +00:00
|
|
|
return locToBlock(x);
|
2011-01-02 10:42:13 -08:00
|
|
|
}
|
2010-12-27 02:11:52 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the y-coordinate of this location
|
|
|
|
*
|
|
|
|
* @param y y-coordinate
|
|
|
|
*/
|
|
|
|
public void setY(double y) {
|
|
|
|
this.y = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the y-coordinate of this location
|
|
|
|
*
|
|
|
|
* @return y-coordinate
|
|
|
|
*/
|
|
|
|
public double getY() {
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
2011-01-02 10:42:13 -08:00
|
|
|
/**
|
|
|
|
* Gets the floored value of the Y component, indicating the block that
|
|
|
|
* this location is contained with.
|
2011-02-02 00:02:08 +01:00
|
|
|
*
|
2011-01-02 10:42:13 -08:00
|
|
|
* @return block y
|
|
|
|
*/
|
|
|
|
public int getBlockY() {
|
2011-02-25 09:51:52 +00:00
|
|
|
return locToBlock(y);
|
2011-01-02 10:42:13 -08:00
|
|
|
}
|
|
|
|
|
2010-12-27 02:11:52 +00:00
|
|
|
/**
|
|
|
|
* Sets the z-coordinate of this location
|
|
|
|
*
|
|
|
|
* @param z z-coordinate
|
|
|
|
*/
|
|
|
|
public void setZ(double z) {
|
|
|
|
this.z = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the z-coordinate of this location
|
|
|
|
*
|
|
|
|
* @return z-coordinate
|
|
|
|
*/
|
|
|
|
public double getZ() {
|
|
|
|
return z;
|
|
|
|
}
|
|
|
|
|
2011-01-02 10:42:13 -08:00
|
|
|
/**
|
|
|
|
* Gets the floored value of the Z component, indicating the block that
|
|
|
|
* this location is contained with.
|
2011-02-02 00:02:08 +01:00
|
|
|
*
|
2011-01-02 10:42:13 -08:00
|
|
|
* @return block z
|
|
|
|
*/
|
|
|
|
public int getBlockZ() {
|
2011-02-25 09:51:52 +00:00
|
|
|
return locToBlock(z);
|
2011-01-02 10:42:13 -08:00
|
|
|
}
|
|
|
|
|
2010-12-28 16:59:25 +00:00
|
|
|
/**
|
|
|
|
* Sets the yaw of this location
|
|
|
|
*
|
|
|
|
* @param yaw New yaw
|
|
|
|
*/
|
|
|
|
public void setYaw(float yaw) {
|
|
|
|
this.yaw = yaw;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the yaw of this location
|
|
|
|
*
|
|
|
|
* @return Yaw
|
|
|
|
*/
|
|
|
|
public float getYaw() {
|
|
|
|
return yaw;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the pitch of this location
|
|
|
|
*
|
|
|
|
* @param pitch New pitch
|
|
|
|
*/
|
|
|
|
public void setPitch(float pitch) {
|
|
|
|
this.pitch = pitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the pitch of this location
|
|
|
|
*
|
|
|
|
* @return Pitch
|
|
|
|
*/
|
|
|
|
public float getPitch() {
|
|
|
|
return pitch;
|
|
|
|
}
|
|
|
|
|
2011-02-13 17:03:57 +00:00
|
|
|
/**
|
|
|
|
* Gets a Vector pointing in the direction that this Location is facing
|
|
|
|
*
|
|
|
|
* @return Vector
|
|
|
|
*/
|
|
|
|
public Vector getDirection() {
|
|
|
|
Vector vector = new Vector();
|
|
|
|
|
|
|
|
double rotX = this.getYaw();
|
|
|
|
double rotY = this.getPitch();
|
|
|
|
|
|
|
|
vector.setY(-Math.sin(Math.toRadians(rotY)));
|
|
|
|
|
|
|
|
double h = Math.cos(Math.toRadians(rotY));
|
2011-05-14 23:22:54 +02:00
|
|
|
|
|
|
|
vector.setX(-h * Math.sin(Math.toRadians(rotX)));
|
|
|
|
vector.setZ(h * Math.cos(Math.toRadians(rotX)));
|
2011-02-13 17:03:57 +00:00
|
|
|
|
|
|
|
return vector;
|
|
|
|
}
|
2011-06-12 01:18:17 +02:00
|
|
|
|
2011-06-09 23:50:10 -07:00
|
|
|
/**
|
|
|
|
* Adds the location by another.
|
|
|
|
*
|
|
|
|
* @see Vector
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param vec The other location
|
2011-06-09 23:50:10 -07:00
|
|
|
* @return the same location
|
|
|
|
* @throws IllegalArgumentException for differing worlds
|
|
|
|
*/
|
|
|
|
public Location add(Location vec) {
|
|
|
|
if (vec == null || vec.getWorld() != getWorld()) {
|
|
|
|
throw new IllegalArgumentException("Cannot add Locations of differing worlds");
|
|
|
|
}
|
2011-06-12 01:18:17 +02:00
|
|
|
|
2011-06-09 23:50:10 -07:00
|
|
|
x += vec.x;
|
|
|
|
y += vec.y;
|
|
|
|
z += vec.z;
|
|
|
|
return this;
|
|
|
|
}
|
2011-12-25 16:02:30 +01:00
|
|
|
|
|
|
|
/**
|
2011-10-11 02:04:40 +01:00
|
|
|
* Adds the location by a vector.
|
2011-12-25 16:02:30 +01:00
|
|
|
*
|
2011-10-11 02:04:40 +01:00
|
|
|
* @see Vector
|
|
|
|
* @param vec Vector to use
|
|
|
|
* @return the same location
|
|
|
|
*/
|
|
|
|
public Location add(Vector vec) {
|
|
|
|
this.x += vec.getX();
|
|
|
|
this.y += vec.getY();
|
|
|
|
this.z += vec.getZ();
|
|
|
|
return this;
|
|
|
|
}
|
2011-06-12 01:18:17 +02:00
|
|
|
|
2011-06-09 23:50:10 -07:00
|
|
|
/**
|
|
|
|
* Adds the location by another. Not world-aware.
|
|
|
|
*
|
|
|
|
* @see Vector
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param x X coordinate
|
|
|
|
* @param y Y coordinate
|
|
|
|
* @param z Z coordinate
|
2011-06-09 23:50:10 -07:00
|
|
|
* @return the same location
|
|
|
|
*/
|
|
|
|
public Location add(double x, double y, double z) {
|
|
|
|
this.x += x;
|
|
|
|
this.y += y;
|
|
|
|
this.z += z;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Subtracts the location by another.
|
|
|
|
*
|
|
|
|
* @see Vector
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param vec The other location
|
2011-06-09 23:50:10 -07:00
|
|
|
* @return the same location
|
|
|
|
* @throws IllegalArgumentException for differing worlds
|
|
|
|
*/
|
|
|
|
public Location subtract(Location vec) {
|
|
|
|
if (vec == null || vec.getWorld() != getWorld()) {
|
|
|
|
throw new IllegalArgumentException("Cannot add Locations of differing worlds");
|
|
|
|
}
|
2011-06-12 01:18:17 +02:00
|
|
|
|
2011-06-09 23:50:10 -07:00
|
|
|
x -= vec.x;
|
|
|
|
y -= vec.y;
|
|
|
|
z -= vec.z;
|
|
|
|
return this;
|
|
|
|
}
|
2011-12-25 16:02:30 +01:00
|
|
|
|
|
|
|
/**
|
2011-10-11 02:04:40 +01:00
|
|
|
* Subtracts the location by a vector.
|
2011-12-25 16:02:30 +01:00
|
|
|
*
|
2011-10-11 02:04:40 +01:00
|
|
|
* @see Vector
|
|
|
|
* @param vec The vector to use
|
|
|
|
* @return the same location
|
|
|
|
*/
|
|
|
|
public Location subtract(Vector vec) {
|
|
|
|
this.x -= vec.getX();
|
|
|
|
this.y -= vec.getY();
|
|
|
|
this.z -= vec.getZ();
|
|
|
|
return this;
|
|
|
|
}
|
2011-06-09 23:50:10 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Subtracts the location by another. Not world-aware and
|
|
|
|
* orientation independent.
|
|
|
|
*
|
|
|
|
* @see Vector
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param x X coordinate
|
|
|
|
* @param y Y coordinate
|
|
|
|
* @param z Z coordinate
|
2011-06-09 23:50:10 -07:00
|
|
|
* @return the same location
|
|
|
|
*/
|
|
|
|
public Location subtract(double x, double y, double z) {
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* @see Vector
|
|
|
|
* @return the magnitude
|
|
|
|
*/
|
|
|
|
public double length() {
|
|
|
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the magnitude of the location squared. Not world-aware and
|
|
|
|
* orientation independent.
|
|
|
|
*
|
|
|
|
* @see Vector
|
|
|
|
* @return the magnitude
|
|
|
|
*/
|
|
|
|
public double lengthSquared() {
|
|
|
|
return Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-12-25 16:02:30 +01:00
|
|
|
* Get the distance between this location and another. The value
|
2011-06-09 23:50:10 -07:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* @see Vector
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param o The other location
|
2011-06-09 23:50:10 -07:00
|
|
|
* @return the distance
|
|
|
|
* @throws IllegalArgumentException for differing worlds
|
|
|
|
*/
|
|
|
|
public double distance(Location o) {
|
2012-01-09 23:55:43 -05:00
|
|
|
return Math.sqrt(distanceSquared(o));
|
2011-06-09 23:50:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the squared distance between this location and another.
|
|
|
|
*
|
|
|
|
* @see Vector
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param o The other location
|
2011-06-09 23:50:10 -07:00
|
|
|
* @return the distance
|
|
|
|
* @throws IllegalArgumentException for differing worlds
|
|
|
|
*/
|
|
|
|
public double distanceSquared(Location o) {
|
2012-01-09 23:55:43 -05:00
|
|
|
if (o == null) {
|
2012-01-10 00:20:03 -05:00
|
|
|
throw new IllegalArgumentException("Cannot measure distance to a null location");
|
|
|
|
} else if (o.getWorld() == null || getWorld() == null) {
|
2012-01-09 23:55:43 -05:00
|
|
|
throw new IllegalArgumentException("Cannot measure distance to a null world");
|
2012-01-10 00:20:03 -05:00
|
|
|
} else if (o.getWorld() != getWorld()) {
|
2012-01-09 23:55:43 -05:00
|
|
|
throw new IllegalArgumentException("Cannot measure distance between " + getWorld().getName() + " and " + o.getWorld().getName());
|
2011-06-09 23:50:10 -07:00
|
|
|
}
|
2011-06-12 01:18:17 +02:00
|
|
|
|
2011-06-09 23:50:10 -07:00
|
|
|
return Math.pow(x - o.x, 2) + Math.pow(y - o.y, 2) + Math.pow(z - o.z, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Performs scalar multiplication, multiplying all components with a scalar.
|
|
|
|
* Not world-aware.
|
|
|
|
*
|
2011-09-25 02:56:40 +01:00
|
|
|
* @param m The factor
|
2011-06-09 23:50:10 -07:00
|
|
|
* @see Vector
|
|
|
|
* @return the same location
|
|
|
|
*/
|
|
|
|
public Location multiply(double m) {
|
|
|
|
x *= m;
|
|
|
|
y *= m;
|
|
|
|
z *= m;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Zero this location's components. Not world-aware.
|
|
|
|
*
|
|
|
|
* @see Vector
|
|
|
|
* @return the same location
|
|
|
|
*/
|
|
|
|
public Location zero() {
|
|
|
|
x = 0;
|
|
|
|
y = 0;
|
|
|
|
z = 0;
|
|
|
|
return this;
|
|
|
|
}
|
2011-02-13 17:03:57 +00:00
|
|
|
|
2010-12-27 02:11:52 +00:00
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
if (obj == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (getClass() != obj.getClass()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
final Location other = (Location) obj;
|
2011-05-14 23:22:54 +02:00
|
|
|
|
2010-12-27 02:11:52 +00:00
|
|
|
if (this.world != other.world && (this.world == null || !this.world.equals(other.world))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-12-28 16:59:25 +00:00
|
|
|
if (Float.floatToIntBits(this.pitch) != Float.floatToIntBits(other.pitch)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (Float.floatToIntBits(this.yaw) != Float.floatToIntBits(other.yaw)) {
|
|
|
|
return false;
|
|
|
|
}
|
2010-12-27 02:11:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
2010-12-28 16:59:25 +00:00
|
|
|
int hash = 3;
|
2011-05-14 23:22:54 +02:00
|
|
|
|
2010-12-28 16:59:25 +00:00
|
|
|
hash = 19 * hash + (this.world != null ? this.world.hashCode() : 0);
|
|
|
|
hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
|
|
|
|
hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
|
|
|
|
hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
|
|
|
|
hash = 19 * hash + Float.floatToIntBits(this.pitch);
|
|
|
|
hash = 19 * hash + Float.floatToIntBits(this.yaw);
|
2010-12-27 02:11:52 +00:00
|
|
|
return hash;
|
|
|
|
}
|
2010-12-28 16:59:25 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2011-09-18 11:48:00 +02:00
|
|
|
return "Location{" + "world=" + world + ",x=" + x + ",y=" + y + ",z=" + z + ",pitch=" + pitch + ",yaw=" + yaw + '}';
|
2010-12-28 16:59:25 +00:00
|
|
|
}
|
2011-02-02 00:02:08 +01:00
|
|
|
|
2011-02-19 22:47:23 +00:00
|
|
|
/**
|
|
|
|
* Constructs a new {@link Vector} based on this Location
|
|
|
|
*
|
|
|
|
* @return New Vector containing the coordinates represented by this Location
|
|
|
|
*/
|
2011-01-02 16:23:56 +08:00
|
|
|
public Vector toVector() {
|
|
|
|
return new Vector(x, y, z);
|
|
|
|
}
|
2010-12-29 03:19:12 +00:00
|
|
|
|
|
|
|
@Override
|
2011-01-08 12:39:54 -08:00
|
|
|
public Location clone() {
|
2011-03-05 12:27:51 +01:00
|
|
|
try {
|
2012-02-14 01:49:06 -06:00
|
|
|
return (Location) super.clone();
|
2011-03-05 12:27:51 +01:00
|
|
|
} catch (CloneNotSupportedException e) {
|
2012-02-14 01:49:06 -06:00
|
|
|
throw new Error(e);
|
2011-03-05 12:27:51 +01:00
|
|
|
}
|
2010-12-29 03:19:12 +00:00
|
|
|
}
|
2011-02-25 09:51:52 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Safely converts a double (location coordinate) to an int (block coordinate)
|
|
|
|
*
|
|
|
|
* @param loc Precise coordinate
|
|
|
|
* @return Block coordinate
|
|
|
|
*/
|
|
|
|
public static int locToBlock(double loc) {
|
2012-02-29 16:07:47 -06:00
|
|
|
return NumberConversions.floor(loc);
|
2011-02-25 09:51:52 +00:00
|
|
|
}
|
2010-12-27 02:11:52 +00:00
|
|
|
}
|