package net.aufdemrand.denizen.objects;
import net.aufdemrand.denizen.Settings;
import net.aufdemrand.denizen.nms.NMSHandler;
import net.aufdemrand.denizen.nms.interfaces.EntityHelper;
import net.aufdemrand.denizen.nms.util.PlayerProfile;
import net.aufdemrand.denizen.objects.notable.NotableManager;
import net.aufdemrand.denizen.tags.BukkitTagContext;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.PathFinder;
import net.aufdemrand.denizen.utilities.Utilities;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizen.utilities.entity.DenizenEntityType;
import net.aufdemrand.denizencore.objects.*;
import net.aufdemrand.denizencore.objects.notable.Notable;
import net.aufdemrand.denizencore.objects.notable.Note;
import net.aufdemrand.denizencore.objects.properties.Property;
import net.aufdemrand.denizencore.objects.properties.PropertyParser;
import net.aufdemrand.denizencore.tags.Attribute;
import net.aufdemrand.denizencore.tags.TagContext;
import net.aufdemrand.denizencore.tags.core.EscapeTags;
import net.aufdemrand.denizencore.utilities.CoreUtilities;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.*;
import org.bukkit.block.banner.PatternType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable;
import org.bukkit.material.MaterialData;
import org.bukkit.util.BlockIterator;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class dLocation extends org.bukkit.Location implements dObject, Notable, Adjustable {
// This pattern correctly reads both 0.9 and 0.8 notables
final static Pattern notablePattern =
Pattern.compile("(\\w+)[;,]((-?\\d+\\.?\\d*,){3,5}.+)",
Pattern.CASE_INSENSITIVE);
/////////////////////
// STATIC METHODS
/////////////////
public void makeUnique(String id) {
NotableManager.saveAs(this, id);
}
@Note("Locations")
public String getSaveObject() {
return (getBlockX() + 0.5)
+ "," + getBlockY()
+ "," + (getBlockZ() + 0.5)
+ "," + getPitch()
+ "," + getYaw()
+ "," + getWorld().getName();
}
public static String getSaved(dLocation location) {
for (dLocation saved : NotableManager.getAllType(dLocation.class)) {
if (saved.getBlockX() != location.getBlockX()) {
continue;
}
if (saved.getBlockY() != location.getBlockY()) {
continue;
}
if (saved.getBlockZ() != location.getBlockZ()) {
continue;
}
if (!saved.getWorld().getName().equals(location.getWorld().getName())) {
continue;
}
return NotableManager.getSavedId(saved);
}
return null;
}
public void forget() {
NotableManager.remove(this);
}
/*
* Called on server startup or /denizen reload locations. Should probably not be called manually.
*/
public static void _recallLocations() {
List<String> loclist = DenizenAPI.getCurrentInstance().getSaves().getStringList("dScript.Locations");
for (String location : loclist) {
Matcher m = notablePattern.matcher(location);
if (m.matches()) {
String id = m.group(1);
dLocation loc = valueOf(m.group(2));
NotableManager.saveAs(loc, id);
}
}
DenizenAPI.getCurrentInstance().getSaves().set("dScript.Locations", null);
}
//////////////////
// OBJECT FETCHER
////////////////
public static dLocation valueOf(String string) {
return valueOf(string, null);
}
/**
* Gets a Location Object from a string form of id,x,y,z,world
* or a dScript argument (location:)x,y,z,world. If including an Id,
* this location will persist and can be recalled at any time.
*
* @param string the string or dScript argument String
* @return a Location, or null if incorrectly formatted
*/
@Fetchable("l")
public static dLocation valueOf(String string, TagContext context) {
if (string == null) {
return null;
}
if (string.startsWith("l@")) {
string = string.substring(2);
}
if (NotableManager.isSaved(string) && NotableManager.isType(string, dLocation.class)) {
return (dLocation) NotableManager.getSavedObject(string);
}
////////
// Match location formats
// Split values
List<String> split = CoreUtilities.split(string, ',');
if (split.size() == 2)
// If 4 values, world-less 2D location format
// x,y
{
try {
return new dLocation(null,
Double.valueOf(split.get(0)),
Double.valueOf(split.get(1)));
}
catch (Exception e) {
if (context == null || context.debug) {
dB.log("Minor: valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
}
return null;
}
}
else if (split.size() == 3)
// If 3 values, either worldless location format
// x,y,z or 2D location format x,y,world
{
try {
World world = Bukkit.getWorld(split.get(2));
if (world != null) {
return new dLocation(world,
Double.valueOf(split.get(0)),
Double.valueOf(split.get(1)));
}
return new dLocation(null,
Double.valueOf(split.get(0)),
Double.valueOf(split.get(1)),
Double.valueOf(split.get(2)));
}
catch (Exception e) {
if (context == null || context.debug) {
dB.log("Minor: valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
}
return null;
}
}
else if (split.size() == 4)
// If 4 values, standard dScript location format
// x,y,z,world
{
try {
return new dLocation(Bukkit.getWorld(split.get(3)),
Double.valueOf(split.get(0)),
Double.valueOf(split.get(1)),
Double.valueOf(split.get(2)));
}
catch (Exception e) {
if (context == null || context.debug) {
dB.log("Minor: valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
}
return null;
}
}
else if (split.size() == 6)
// If 6 values, location with pitch/yaw
// x,y,z,yaw,pitch,world
{
try {
return new dLocation(Bukkit.getWorld(split.get(5)),
Double.valueOf(split.get(0)),
Double.valueOf(split.get(1)),
Double.valueOf(split.get(2)),
Float.valueOf(split.get(3)),
Float.valueOf(split.get(4)));
}
catch (Exception e) {
if (context == null || context.debug) {
dB.log("Minor: valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
}
return null;
}
}
if (context == null || context.debug) {
dB.log("Minor: valueOf dLocation returning null: " + string);
}
return null;
}
public static boolean matches(String string) {
if (string == null || string.length() == 0) {
return false;
}
if (string.startsWith("l@")) {
return true;
}
return dLocation.valueOf(string, new BukkitTagContext(null, null, false, null, false, null)) != null;
}
/////////////////////
// CONSTRUCTORS
//////////////////
private boolean is2D = false;
/**
* Turns a Bukkit Location into a Location, which has some helpful methods
* for working with dScript.
*
* @param location the Bukkit Location to reference
*/
public dLocation(Location location) {
// Just save the yaw and pitch as they are; don't check if they are
// higher than 0, because Minecraft yaws are weird and can have
// negative values
super(location.getWorld(), location.getX(), location.getY(), location.getZ(),
location.getYaw(), location.getPitch());
}
public dLocation(Vector vector) {
super(null, vector.getX(), vector.getY(), vector.getZ());
}
public dLocation(World world, double x, double y) {
this(world, x, y, 0);
this.is2D = true;
}
/**
* Turns a world and coordinates into a Location, which has some helpful methods
* for working with dScript.
*
* @param world the world in which the location resides
* @param x x-coordinate of the location
* @param y y-coordinate of the location
* @param z z-coordinate of the location
*/
public dLocation(World world, double x, double y, double z) {
super(world, x, y, z);
}
public dLocation(World world, double x, double y, double z, float yaw, float pitch) {
super(world, x, y, z, pitch, yaw);
}
/////////////////////
// INSTANCE FIELDS/METHODS
/////////////////
// A boolean that determines whether this location will identify
// as a notable or not
private boolean raw = false;
private void setRaw(boolean state) {
this.raw = state;
}
@Override
public void setPitch(float pitch) {
super.setPitch(pitch);
}
// TODO: Why does this and the above exist?
@Override
public void setYaw(float yaw) {
super.setYaw(yaw);
}
public boolean hasInventory() {
return getBlock().getState() instanceof InventoryHolder;
}
public Inventory getBukkitInventory() {
return hasInventory() ? ((InventoryHolder) getBlock().getState()).getInventory() : null;
}
public dInventory getInventory() {
return hasInventory() ? new dInventory(getBukkitInventory()) : null;
}
public BlockFace getSkullBlockFace(int rotation) {
switch (rotation) {
case 0:
return BlockFace.NORTH;
case 1:
return BlockFace.NORTH_NORTH_EAST;
case 2:
return BlockFace.NORTH_EAST;
case 3:
return BlockFace.EAST_NORTH_EAST;
case 4:
return BlockFace.EAST;
case 5:
return BlockFace.EAST_SOUTH_EAST;
case 6:
return BlockFace.SOUTH_EAST;
case 7:
return BlockFace.SOUTH_SOUTH_EAST;
case 8:
return BlockFace.SOUTH;
case 9:
return BlockFace.SOUTH_SOUTH_WEST;
case 10:
return BlockFace.SOUTH_WEST;
case 11:
return BlockFace.WEST_SOUTH_WEST;
case 12:
return BlockFace.WEST;
case 13:
return BlockFace.WEST_NORTH_WEST;
case 14:
return BlockFace.NORTH_WEST;
case 15:
return BlockFace.NORTH_NORTH_WEST;
default:
return null;
}
}
public byte getSkullRotation(BlockFace face) {
switch (face) {
case NORTH:
return 0;
case NORTH_NORTH_EAST:
return 1;
case NORTH_EAST:
return 2;
case EAST_NORTH_EAST:
return 3;
case EAST:
return 4;
case EAST_SOUTH_EAST:
return 5;
case SOUTH_EAST:
return 6;
case SOUTH_SOUTH_EAST:
return 7;
case SOUTH:
return 8;
case SOUTH_SOUTH_WEST:
return 9;
case SOUTH_WEST:
return 10;
case WEST_SOUTH_WEST:
return 11;
case WEST:
return 12;
case WEST_NORTH_WEST:
return 13;
case NORTH_WEST:
return 14;
case NORTH_NORTH_WEST:
return 15;
}
return -1;
}
public int compare(Location loc1, Location loc2) {
if (loc1 == null || loc2 == null || loc1.equals(loc2)) {
return 0;
}
else {
double dist = distanceSquared(loc1) - distanceSquared(loc2);
return dist == 0 ? 0 : (dist > 0 ? 1 : -1);
}
}
@Override
public int hashCode() {
return (int) (Math.floor(getX()) + Math.floor(getY()) + Math.floor(getZ()));
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof dLocation)) {
return false;
}
dLocation other = (dLocation) o;
if ((other.getWorld() == null && getWorld() != null)
|| (getWorld() == null && other.getWorld() != null)
|| (getWorld() != null && other.getWorld() != null
&& !getWorld().getName().equalsIgnoreCase(other.getWorld().getName()))) {
return false;
}
return Math.floor(getX()) == Math.floor(other.getX())
&& Math.floor(getY()) == Math.floor(other.getY())
&& Math.floor(getZ()) == Math.floor(other.getZ());
}
String prefix = "Location";
@Override
public String getObjectType() {
return "Location";
}
@Override
public String getPrefix() {
return prefix;
}
@Override
public dLocation setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
@Override
public String debug() {
return (isUnique() ? "<G>" + prefix + "='<A>" + identify() + "(<Y>" + identifyRaw() + "<A>)<G>' "
: "<G>" + prefix + "='<Y>" + identify() + "<G>' ");
}
@Override
public boolean isUnique() {
return getSaved(this) != null;
}
@Override
public String identify() {
if (!raw && isUnique()) {
return "l@" + getSaved(this);
}
else {
return identifyRaw();
}
}
@Override
public String identifySimple() {
if (isUnique()) {
return "l@" + getSaved(this);
}
else if (getWorld() == null) {
return "l@" + getBlockX() + "," + getBlockY() + (!is2D ? "," + getBlockZ() : "");
}
else {
return "l@" + getBlockX() + "," + getBlockY() + (!is2D ? "," + getBlockZ() : "")
+ "," + getWorld().getName();
}
}
public String identifyRaw() {
if (getYaw() != 0.0 || getPitch() != 0.0) {
return "l@" + getX() + "," + getY() + "," + getZ() + "," + getPitch() + "," + getYaw()
+ (getWorld() != null ? "," + getWorld().getName() : "");
}
else {
return "l@" + getX() + "," + getY() + (!is2D ? "," + getZ() : "")
+ (getWorld() != null ? "," + getWorld().getName() : "");
}
}
@Override
public String toString() {
return identify();
}
@Override
public String getAttribute(Attribute attribute) {
if (attribute == null) {
return null;
}
/////////////////////
// BLOCK ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <l@location.above>
// @returns dLocation
// @description
// Returns the location one block above this location.
// -->
if (attribute.startsWith("above")) {
return new dLocation(this.clone().add(0, 1, 0))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.below>
// @returns dLocation
// @description
// Returns the location one block below this location.
// -->
if (attribute.startsWith("below")) {
return new dLocation(this.clone().add(0, -1, 0))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.block>
// @returns dLocation
// @description
// Returns the location of the block this location is on,
// i.e. returns a location without decimals or direction.
// -->
if (attribute.startsWith("block")) {
return new dLocation(getWorld(), getBlockX(), getBlockY(), getBlockZ())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.center>
// @returns dLocation
// @description
// Returns the location at the center of the block this location is on.
// -->
if (attribute.startsWith("center")) {
return new dLocation(getWorld(), getBlockX() + 0.5, getBlockY() + 0.5, getBlockZ() + 0.5)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.highest>
// @returns dLocation
// @description
// Returns the location of the highest solid block at the location.
// -->
if (attribute.startsWith("highest")) {
return new dLocation(getWorld().getHighestBlockAt(this).getLocation().add(0, -1, 0))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.base_color>
// @returns Element
// @description
// Returns the base color of the banner at this location.
// For the list of possible colors, see <@link url http://bit.ly/1dydq12>.
// -->
if (attribute.startsWith("base_color")) {
DyeColor color = ((Banner) getBlock().getState()).getBaseColor();
return new Element(color != null ? color.name() : "BLACK").getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.has_inventory>
// @returns Element(Boolean)
// @description
// Returns whether the block at the location has an inventory.
// -->
if (attribute.startsWith("has_inventory")) {
return new Element(getBlock().getState() instanceof InventoryHolder).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.inventory>
// @returns dInventory
// @description
// Returns the dInventory of the block at the location. If the
// block is not a container, returns null.
// -->
if (attribute.startsWith("inventory")) {
dObject obj = Element.handleNull(identify() + ".inventory", getInventory(), "dInventory", attribute.hasAlternative());
return obj == null ? null : obj.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.material>
// @returns dMaterial
// @description
// Returns the material of the block at the location.
// -->
if (attribute.startsWith("material")) {
return dMaterial.getMaterialFrom(getBlock().getType(), getBlock().getData()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.patterns>
// @returns dList
// @group properties
// @mechanism dLocation.patterns
// @description
// Lists the patterns of the banner at this location in the form "li@COLOR/PATTERN|COLOR/PATTERN" etc.
// Available colors: black, blue, brown, cyan, gray, green, light_blue, lime, magenta, orange, pink
// purple, red, silver, white, and yellow.
// Available patterns: base, border, bricks, circle_middle, creeper, cross, curly_border, diagonal_left,
// diagonal_left_mirror, diagonal_right, diagonal_right_mirror, flower, gradient, gradient_up, half_horizontal,
// half_horizontal_mirror, half_vertical, half_vertical_mirror, mojang, rhombus_middle, skull,
// square_bottom_left, square_bottom_right, square_top_left, square_top_right, straight_cross, stripe_bottom,
// stripe_center, stripe_downleft, stripe_downright, stripe_left, stripe_middle, stripe_right, stripe_small,
// stripe_top, triangle_bottom, triangle_top, triangles_bottom, and triangles_top
// -->
if (attribute.startsWith("patterns")) {
dList list = new dList();
for (org.bukkit.block.banner.Pattern pattern : ((Banner) getBlock().getState()).getPatterns()) {
list.add(pattern.getColor().name() + "/" + pattern.getPattern().name());
}
return list.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.head_rotation>
// @returns Element(Number)
// @description
// Gets the rotation of the head at this location. Can be 1-16.
// @mechanism dLocation.head_rotation
// -->
if (attribute.startsWith("head_rotation")) {
return new Element(getSkullRotation(((Skull) getBlock().getState()).getRotation()) + 1)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.switched>
// @returns Element(Boolean)
// @description
// Returns whether the block at the location is considered to be switched on.
// (For buttons, levers, etc.)
// To change this, see <@link command Switch>
// -->
if (attribute.startsWith("switched")) {
Material type = getBlock().getType();
if (type == Material.IRON_DOOR_BLOCK
|| type == Material.WOODEN_DOOR
|| type == Material.DARK_OAK_DOOR
|| type == Material.BIRCH_DOOR
|| type == Material.ACACIA_DOOR
|| type == Material.JUNGLE_DOOR
|| type == Material.SPRUCE_DOOR) {
Location location = this;
int data = getBlock().getData();
if (data >= 8) {
location = clone().add(0, -1, 0);
}
return new Element((location.getBlock().getData() & 0x4) > 0).getAttribute(attribute.fulfill(1));
}
else if (type == Material.TRAP_DOOR
|| type == Material.IRON_TRAPDOOR) {
return new Element((getBlock().getData() & 0x4) > 0).getAttribute(attribute.fulfill(1));
}
else {
return new Element((getBlock().getData() & 0x8) > 0).getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.sign_contents>
// @returns dList
// @mechanism dLocation.sign_contents
// @description
// Returns a list of lines on a sign.
// -->
if (attribute.startsWith("sign_contents")) {
if (getBlock().getState() instanceof Sign) {
return new dList(Arrays.asList(((Sign) getBlock().getState()).getLines()))
.getAttribute(attribute.fulfill(1));
}
else {
return null;
}
}
// <--[tag]
// @attribute <l@location.spawner_type>
// @mechanism dLocation.spawner_type
// @returns dEntity
// @description
// Returns the type of entity spawned by a mob spawner.
// -->
if (attribute.startsWith("spawner_type")) {
if (getBlock().getState() instanceof CreatureSpawner) {
return new dEntity(DenizenEntityType.getByName(((CreatureSpawner) getBlock().getState())
.getSpawnedType().name())).getAttribute(attribute.fulfill(1));
}
else {
return null;
}
}
// <--[tag]
// @attribute <l@location.drops>
// @returns dList(dItem)
// @description
// Returns what items the block at the location would drop if broken naturally.
// -->
if (attribute.startsWith("drops")) {
Collection<ItemStack> its = getBlock().getDrops();
dList list = new dList();
for (ItemStack it: its) {
list.add(new dItem(it).identify());
}
return list.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.skull_type>
// @returns Element
// @description
// Returns the type of the skull.
// -->
if (attribute.startsWith("skull_type")) {
BlockState blockState = getBlock().getState();
if (blockState instanceof Skull) {
String t = ((Skull) blockState).getSkullType().name();
return new Element(t).getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.skull_name>
// @returns Element
// @mechanism dLocation.skull_skin
// @description
// Returns the name of the skin the skull is displaying.
// -->
if (attribute.startsWith("skull_name")) {
BlockState blockState = getBlock().getState();
if (blockState instanceof Skull) {
PlayerProfile profile = NMSHandler.getInstance().getBlockHelper().getPlayerProfile((Skull) blockState);
String n = profile.getName();
if (n == null) {
n = ((Skull) blockState).getOwningPlayer().getName();
}
return new Element(n).getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.skull_skin>
// @returns Element
// @mechanism dLocation.skull_skin
// @description
// Returns the skin the skull is displaying - just the name or UUID as text, not a player object.
// -->
if (attribute.startsWith("skull_skin")) {
BlockState blockState = getBlock().getState();
if (blockState instanceof Skull) {
PlayerProfile profile = NMSHandler.getInstance().getBlockHelper().getPlayerProfile((Skull) blockState);
String name = profile.getName();
UUID uuid = profile.getUniqueId();
String texture = profile.getTexture();
attribute = attribute.fulfill(1);
// <--[tag]
// @attribute <l@location.skull_skin.full>
// @returns Element|Element
// @mechanism dLocation.skull_skin
// @description
// Returns the skin the skull item is displaying - just the name or UUID as text, not a player object,
// along with the permanently cached texture property.
// -->
if (attribute.startsWith("full")) {
return new Element((uuid != null ? uuid : name != null ? name : null)
+ (texture != null ? "|" + texture : ""))
.getAttribute(attribute.fulfill(1));
}
return new Element(uuid != null ? uuid.toString() : name != null ? name : null).getAttribute(attribute);
}
else {
return null;
}
}
// <--[tag]
// @attribute <l@location.simple.formatted>
// @returns Element
// @description
// Returns the formatted simple version of the dLocation's block coordinates.
// In the format: X 'x', Y 'y', Z 'z', in world 'world'
// For example, X '1', Y '2', Z '3', in world 'world_nether'
// -->
if (attribute.startsWith("simple.formatted")) {
return new Element("X '" + getBlockX()
+ "', Y '" + getBlockY()
+ "', Z '" + getBlockZ()
+ "', in world '" + getWorld().getName() + "'").getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.simple>
// @returns Element
// @description
// Returns a simple version of the dLocation's block coordinates.
// In the format: x,y,z,world
// For example: 1,2,3,world_nether
// -->
if (attribute.startsWith("simple")) {
if (getWorld() == null) {
return new Element(getBlockX() + "," + getBlockY() + "," + getBlockZ())
.getAttribute(attribute.fulfill(1));
}
else {
return new Element(getBlockX() + "," + getBlockY() + "," + getBlockZ()
+ "," + getWorld().getName()).getAttribute(attribute.fulfill(1));
}
}
/////////////////////
// DIRECTION ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <l@location.precise_impact_normal[<range>]>
// @returns dLocation
// @description
// Returns the exact impact normal at the location this location is pointing at.
// Optionally, specify a maximum range to find the location from.
// -->
if (attribute.startsWith("precise_impact_normal")) {
int range = attribute.getIntContext(1);
if (range < 1) {
range = 200;
}
double xzLen = Math.cos((getPitch() % 360) * (Math.PI / 180));
double nx = xzLen * Math.sin(-getYaw() * (Math.PI / 180));
double ny = Math.sin(getPitch() * (Math.PI / 180));
double nz = xzLen * Math.cos(getYaw() * (Math.PI / 180));
Location location = NMSHandler.getInstance().getEntityHelper().getImpactNormal(this, new org.bukkit.util.Vector(nx, -ny, nz), range);
if (location != null) {
return new dLocation(location).getAttribute(attribute.fulfill(1));
}
else {
return null;
}
}
// <--[tag]
// @attribute <l@location.precise_cursor_on[<range>]>
// @returns dLocation
// @description
// Returns the exact location this location is pointing at.
// Optionally, specify a maximum range to find the location from.
// -->
if (attribute.startsWith("precise_cursor_on")) {
int range = attribute.getIntContext(1);
if (range < 1) {
range = 200;
}
double xzLen = Math.cos((getPitch() % 360) * (Math.PI / 180));
double nx = xzLen * Math.sin(-getYaw() * (Math.PI / 180));
double ny = Math.sin(getPitch() * (Math.PI / 180));
double nz = xzLen * Math.cos(getYaw() * (Math.PI / 180));
Location location = NMSHandler.getInstance().getEntityHelper().rayTrace(this, new org.bukkit.util.Vector(nx, -ny, nz), range);
if (location != null) {
return new dLocation(location).getAttribute(attribute.fulfill(1));
}
else {
return null;
}
}
// <--[tag]
// @attribute <l@location.points_between[<location>]>
// @returns dList(dLocation)
// @description
// Finds all locations between this location and another, separated by 1 block-width each.
// -->
if (attribute.startsWith("points_between")) {
dLocation target = dLocation.valueOf(attribute.getContext(1));
if (target == null) {
return null;
}
attribute = attribute.fulfill(1);
// <--[tag]
// @attribute <l@location.points_between[<location>].distance[<#.#>]>
// @returns dList(dLocation)
// @description
// Finds all locations between this location and another, separated by the specified distance each.
// -->
double rad = 1d;
if (attribute.startsWith("distance")) {
rad = attribute.getDoubleContext(1);
attribute = attribute.fulfill(1);
}
dList list = new dList();
org.bukkit.util.Vector rel = target.toVector().subtract(this.toVector());
double len = rel.length();
rel = rel.multiply(1d / len);
for (double i = 0d; i < len; i += rad) {
list.add(new dLocation(this.clone().add(rel.clone().multiply(i))).identify());
}
return list.getAttribute(attribute);
}
// <--[tag]
// @attribute <l@location.facing_blocks[<#>]>
// @returns dList(dLocation)
// @description
// Finds all block locations in the direction this location is facing,
// optionally with a custom range (default is 100).
// For example a location at 0,0,0 facing straight up
// will include 0,1,0 0,2,0 and so on.
// -->
if (attribute.startsWith("facing_blocks")) {
int range = attribute.getIntContext(1);
if (range < 1) {
range = 100;
}
dList list = new dList();
BlockIterator iterator = new BlockIterator(this, 0, range);
while (iterator.hasNext()) {
list.add(new dLocation(iterator.next().getLocation()).identify());
}
return list.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.line_of_sight[<location>]>
// @returns Element(Boolean)
// @description
// Returns whether the specified location is within this location's
// line of sight.
// -->
if (attribute.startsWith("line_of_sight") && attribute.hasContext(1)) {
dLocation location = dLocation.valueOf(attribute.getContext(1));
if (location != null) {
return new Element(NMSHandler.getInstance().getEntityHelper().canTrace(getWorld(), toVector(), location.toVector()))
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.direction.vector>
// @returns dLocation
// @description
// Returns the location's direction as a one-length vector.
// -->
if (attribute.startsWith("direction.vector")) {
double xzLen = Math.cos((getPitch() % 360) * (Math.PI / 180));
double nx = xzLen * Math.sin(-getYaw() * (Math.PI / 180));
double ny = Math.sin(getPitch() * (Math.PI / 180));
double nz = xzLen * Math.cos(getYaw() * (Math.PI / 180));
return new dLocation(getWorld(), nx, -ny, nz).getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.direction[<location>]>
// @returns Element
// @description
// Returns the compass direction between two locations.
// If no second location is specified, returns the direction of the location.
// Example returns include "north", "southwest", ...
// -->
if (attribute.startsWith("direction")) {
// Get the cardinal direction from this location to another
if (attribute.hasContext(1) && dLocation.matches(attribute.getContext(1))) {
// Subtract this location's vector from the other location's vector,
// not the other way around
dLocation target = dLocation.valueOf(attribute.getContext(1));
attribute = attribute.fulfill(1);
EntityHelper entityHelper = NMSHandler.getInstance().getEntityHelper();
// <--[tag]
// @attribute <l@location.direction[<location>].yaw>
// @returns Element(Decimal)
// @description
// Returns the yaw direction between two locations.
// -->
if (attribute.startsWith("yaw")) {
return new Element(entityHelper.normalizeYaw(entityHelper.getYaw
(target.toVector().subtract(this.toVector())
.normalize())))
.getAttribute(attribute.fulfill(1));
}
else {
return new Element(entityHelper.getCardinal(entityHelper.getYaw
(target.toVector().subtract(this.toVector())
.normalize())))
.getAttribute(attribute);
}
}
// Get a cardinal direction from this location's yaw
else {
return new Element(NMSHandler.getInstance().getEntityHelper().getCardinal(getYaw()))
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.face[<location>]>
// @returns dLocation
// @description
// Returns a location containing a yaw/pitch that point from the current location
// to the target location.
// -->
if (attribute.startsWith("face")
&& attribute.hasContext(1)) {
Location two = dLocation.valueOf(attribute.getContext(1));
return new dLocation(NMSHandler.getInstance().getEntityHelper().faceLocation(this, two))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.facing[<entity>/<location>]>
// @returns Element(Boolean)
// @description
// Returns whether the location's yaw is facing another
// entity or location.
// -->
if (attribute.startsWith("facing")) {
if (attribute.hasContext(1)) {
// The default number of degrees if there is no degrees attribute
int degrees = 45;
// The attribute to fulfill from
int attributePos = 1;
// <--[tag]
// @attribute <location.facing[<entity>/<location>].degrees[X]>
// @returns Element(Boolean)
// @description
// Returns whether the location's yaw is facing another
// entity or location, within a specified degree range.
// -->
if (attribute.getAttribute(2).startsWith("degrees") &&
attribute.hasContext(2) &&
aH.matchesInteger(attribute.getContext(2))) {
degrees = attribute.getIntContext(2);
attributePos++;
}
if (dLocation.matches(attribute.getContext(1))) {
return new Element(NMSHandler.getInstance().getEntityHelper().isFacingLocation
(this, dLocation.valueOf(attribute.getContext(1)), degrees))
.getAttribute(attribute.fulfill(attributePos));
}
else if (dEntity.matches(attribute.getContext(1))) {
return new Element(NMSHandler.getInstance().getEntityHelper().isFacingLocation
(this, dEntity.valueOf(attribute.getContext(1))
.getBukkitEntity().getLocation(), degrees))
.getAttribute(attribute.fulfill(attributePos));
}
}
}
// <--[tag]
// @attribute <l@location.pitch>
// @returns Element(Decimal)
// @description
// Returns the pitch of the object at the location.
// -->
if (attribute.startsWith("pitch")) {
return new Element(getPitch()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.with_pose[<entity>/<pitch>,<yaw>]>
// @returns dLocation
// @description
// Returns the location with pitch and yaw.
// -->
if (attribute.startsWith("with_pose")) {
String context = attribute.getContext(1);
Float pitch = 0f;
Float yaw = 0f;
if (dEntity.matches(context)) {
dEntity ent = dEntity.valueOf(context);
if (ent.isSpawned()) {
pitch = ent.getBukkitEntity().getLocation().getPitch();
yaw = ent.getBukkitEntity().getLocation().getYaw();
}
}
else if (context.split(",").length == 2) {
String[] split = context.split(",");
pitch = Float.valueOf(split[0]);
yaw = Float.valueOf(split[1]);
}
dLocation loc = dLocation.valueOf(identify());
loc.setPitch(pitch);
loc.setYaw(yaw);
return loc.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.yaw.simple>
// @returns Element
// @description
// Returns the yaw as 'North', 'South', 'East', or 'West'.
// -->
if (attribute.startsWith("yaw.simple")) {
float yaw = NMSHandler.getInstance().getEntityHelper().normalizeYaw(getYaw());
if (yaw < 45) {
return new Element("South")
.getAttribute(attribute.fulfill(2));
}
else if (yaw < 135) {
return new Element("West")
.getAttribute(attribute.fulfill(2));
}
else if (yaw < 225) {
return new Element("North")
.getAttribute(attribute.fulfill(2));
}
else if (yaw < 315) {
return new Element("East")
.getAttribute(attribute.fulfill(2));
}
else {
return new Element("South")
.getAttribute(attribute.fulfill(2));
}
}
// <--[tag]
// @attribute <l@location.yaw.raw>
// @returns Element(Decimal)
// @description
// Returns the raw yaw of the object at the location.
// -->
if (attribute.startsWith("yaw.raw")) {
return new Element(getYaw())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.yaw>
// @returns Element(Decimal)
// @description
// Returns the normalized yaw of the object at the location.
// -->
if (attribute.startsWith("yaw")) {
return new Element(NMSHandler.getInstance().getEntityHelper().normalizeYaw(getYaw()))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.rotate_around_x[<#.#>]>
// @returns dLocation
// @description
// Returns the location rotated around the x axis by a specified angle in radians.
// -->
if (attribute.startsWith("rotate_around_x") && attribute.hasContext(1)) {
double angle = attribute.getDoubleContext(1);
double cos = Math.cos(angle);
double sin = Math.sin(angle);
double y = (getY() * cos) - (getZ() * sin);
double z = (getY() * sin) + (getZ() * cos);
Location location = clone();
location.setY(y);
location.setZ(z);
return new dLocation(location).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.rotate_around_y[<#.#>]>
// @returns dLocation
// @description
// Returns the location rotated around the y axis by a specified angle in radians.
// -->
if (attribute.startsWith("rotate_around_y") && attribute.hasContext(1)) {
double angle = attribute.getDoubleContext(1);
double cos = Math.cos(angle);
double sin = Math.sin(angle);
double x = (getX() * cos) + (getZ() * sin);
double z = (getX() * -sin) + (getZ() * cos);
Location location = clone();
location.setX(x);
location.setZ(z);
return new dLocation(location).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.rotate_around_z[<#.#>]>
// @returns dLocation
// @description
// Returns the location rotated around the z axis by a specified angle in radians.
// -->
if (attribute.startsWith("rotate_around_z") && attribute.hasContext(1)) {
double angle = attribute.getDoubleContext(1);
double cos = Math.cos(angle);
double sin = Math.sin(angle);
double x = (getX() * cos) - (getY() * sin);
double y = (getZ() * sin) + (getY() * cos);
Location location = clone();
location.setX(x);
location.setY(y);
return new dLocation(location).getAttribute(attribute.fulfill(1));
}
/////////////////////
// ENTITY AND BLOCK LIST ATTRIBUTES
/////////////////
if (attribute.matches("find") || attribute.startsWith("nearest")) {
attribute.fulfill(1);
// <--[tag]
// @attribute <l@location.find.blocks[<block>|...].within[<#>]>
// @returns dList
// @description
// Returns a list of matching blocks within a radius.
// Note: current implementation measures the center of nearby block's distance from the exact given location.
// -->
if (attribute.startsWith("blocks")
&& attribute.getAttribute(2).startsWith("within")
&& attribute.hasContext(2)) {
ArrayList<dLocation> found = new ArrayList<dLocation>();
int radius = aH.matchesInteger(attribute.getContext(2)) ? attribute.getIntContext(2) : 10;
List<dMaterial> materials = new ArrayList<dMaterial>();
if (attribute.hasContext(1)) {
materials = dList.valueOf(attribute.getContext(1)).filter(dMaterial.class);
}
// Avoid NPE from invalid materials
if (materials == null) {
return null;
}
int max = Settings.blockTagsMaxBlocks();
int index = 0;
attribute.fulfill(2);
Location tstart = getBlock().getLocation();
double tstartY = tstart.getY();
fullloop:
for (int x = -(radius); x <= radius; x++) {
for (int y = -(radius); y <= radius; y++) {
double newY = y + tstartY;
if (newY < 0 || newY > 255) {
continue;
}
for (int z = -(radius); z <= radius; z++) {
index++;
if (index > max) {
break fullloop;
}
if (Utilities.checkLocation(this, tstart.clone().add(x + 0.5, y + 0.5, z + 0.5), radius)) {
if (!materials.isEmpty()) {
for (dMaterial material : materials) {
if (material.hasData() && material.getData() != 0) { // TODO: less arbitrary matching
if (material.matchesMaterialData(tstart.clone().add(x, y, z).getBlock().getState().getData())) {
found.add(new dLocation(tstart.clone().add(x, y, z)));
}
}
else if (material.getMaterial() == tstart.clone().add(x, y, z).getBlock().getType()) {
found.add(new dLocation(tstart.clone().add(x, y, z)));
}
}
}
else {
found.add(new dLocation(tstart.clone().add(x, y, z)));
}
}
}
}
}
Collections.sort(found, new Comparator<dLocation>() {
@Override
public int compare(dLocation loc1, dLocation loc2) {
return dLocation.this.compare(loc1, loc2);
}
});
return new dList(found).getAttribute(attribute);
}
// <--[tag]
// @attribute <l@location.find.surface_blocks[<block>|...].within[<#.#>]>
// @returns dList
// @description
// Returns a list of matching surface blocks within a radius.
// -->
else if (attribute.startsWith("surface_blocks")
&& attribute.getAttribute(2).startsWith("within")
&& attribute.hasContext(2)) {
ArrayList<dLocation> found = new ArrayList<dLocation>();
double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
List<dMaterial> materials = new ArrayList<dMaterial>();
if (attribute.hasContext(1)) {
materials = dList.valueOf(attribute.getContext(1)).filter(dMaterial.class);
}
// Avoid NPE from invalid materials
if (materials == null) {
return null;
}
int max = Settings.blockTagsMaxBlocks();
int index = 0;
attribute.fulfill(2);
Location loc = getBlock().getLocation().add(0.5f, 0.5f, 0.5f);
fullloop:
for (double x = -(radius); x <= radius; x++) {
for (double y = -(radius); y <= radius; y++) {
for (double z = -(radius); z <= radius; z++) {
index++;
if (index > max) {
break fullloop;
}
if (Utilities.checkLocation(loc, getBlock().getLocation().add(x + 0.5, y + 0.5, z + 0.5), radius)) {
Location l = getBlock().getLocation().clone().add(x, y, z);
if (!materials.isEmpty()) {
for (dMaterial material : materials) {
if (material.matchesMaterialData(getBlock()
.getLocation().clone().add(x, y, z).getBlock().getType().getNewData(getBlock()
.getLocation().clone().add(x, y, z).getBlock().getData()))) {
if (l.clone().add(0, 1, 0).getBlock().getType() == Material.AIR
&& l.clone().add(0, 2, 0).getBlock().getType() == Material.AIR
&& l.getBlock().getType() != Material.AIR) {
found.add(new dLocation(getBlock().getLocation().clone().add(x + 0.5, y, z + 0.5)));
}
}
}
}
else {
if (l.clone().add(0, 1, 0).getBlock().getType() == Material.AIR
&& l.clone().add(0, 2, 0).getBlock().getType() == Material.AIR
&& l.getBlock().getType() != Material.AIR) {
found.add(new dLocation(getBlock().getLocation().clone().add(x + 0.5, y, z + 0.5)));
}
}
}
}
}
}
Collections.sort(found, new Comparator<dLocation>() {
@Override
public int compare(dLocation loc1, dLocation loc2) {
return dLocation.this.compare(loc1, loc2);
}
});
return new dList(found).getAttribute(attribute);
}
// <--[tag]
// @attribute <l@location.find.players.within[<#.#>]>
// @returns dList
// @description
// Returns a list of players within a radius.
// -->
else if (attribute.startsWith("players")
&& attribute.getAttribute(2).startsWith("within")
&& attribute.hasContext(2)) {
ArrayList<dPlayer> found = new ArrayList<dPlayer>();
double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
attribute.fulfill(2);
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.isDead() && Utilities.checkLocation(this, player.getLocation(), radius)) {
found.add(new dPlayer(player));
}
}
Collections.sort(found, new Comparator<dPlayer>() {
@Override
public int compare(dPlayer pl1, dPlayer pl2) {
return dLocation.this.compare(pl1.getLocation(), pl2.getLocation());
}
});
return new dList(found).getAttribute(attribute);
}
// <--[tag]
// @attribute <l@location.find.npcs.within[<#.#>]>
// @returns dList
// @description
// Returns a list of NPCs within a radius.
// -->
else if (attribute.startsWith("npcs")
&& attribute.getAttribute(2).startsWith("within")
&& attribute.hasContext(2)) {
ArrayList<dNPC> found = new ArrayList<dNPC>();
double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
attribute.fulfill(2);
for (dNPC npc : DenizenAPI.getSpawnedNPCs()) {
if (Utilities.checkLocation(this.getBlock().getLocation(), npc.getLocation(), radius)) {
found.add(npc);
}
}
Collections.sort(found, new Comparator<dNPC>() {
@Override
public int compare(dNPC npc1, dNPC npc2) {
return dLocation.this.compare(npc1.getLocation(), npc2.getLocation());
}
});
return new dList(found).getAttribute(attribute);
}
// <--[tag]
// @attribute <l@location.find.entities[<entity>|...].within[<#.#>]>
// @returns dList
// @description
// Returns a list of entities within a radius, with an optional search parameter
// for the entity type.
// -->
else if (attribute.startsWith("entities")
&& attribute.getAttribute(2).startsWith("within")
&& attribute.hasContext(2)) {
dList ent_list = new dList();
if (attribute.hasContext(1)) {
ent_list = dList.valueOf(attribute.getContext(1));
}
ArrayList<dEntity> found = new ArrayList<dEntity>();
double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
attribute.fulfill(2);
for (Entity entity : getWorld().getEntities()) {
if (Utilities.checkLocation(this, entity.getLocation(), radius)) {
dEntity current = new dEntity(entity);
if (!ent_list.isEmpty()) {
for (String ent : ent_list) {
if (current.comparedTo(ent)) {
found.add(current);
break;
}
}
}
else {
found.add(current);
}
}
}
Collections.sort(found, new Comparator<dEntity>() {
@Override
public int compare(dEntity ent1, dEntity ent2) {
return dLocation.this.compare(ent1.getLocation(), ent2.getLocation());
}
});
return new dList(found).getAttribute(attribute);
}
// <--[tag]
// @attribute <l@location.find.living_entities.within[<#.#>]>
// @returns dList
// @description
// Returns a list of living entities within a radius.
// -->
else if (attribute.startsWith("living_entities")
&& attribute.getAttribute(2).startsWith("within")
&& attribute.hasContext(2)) {
ArrayList<dEntity> found = new ArrayList<dEntity>();
double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
attribute.fulfill(2);
for (Entity entity : getWorld().getEntities()) {
if (entity instanceof LivingEntity
&& Utilities.checkLocation(this, entity.getLocation(), radius)) {
found.add(new dEntity(entity));
}
}
Collections.sort(found, new Comparator<dEntity>() {
@Override
public int compare(dEntity ent1, dEntity ent2) {
return dLocation.this.compare(ent1.getLocation(), ent2.getLocation());
}
});
return new dList(found).getAttribute(attribute);
}
}
// <--[tag]
// @attribute <l@location.find_path[<location>]>
// @returns dList(dLocation)
// @description
// Returns a full list of points along the path from this location to the given location.
// Uses a max range of 100 blocks from the start.
// -->
if (attribute.startsWith("find_path")
&& attribute.hasContext(1)) {
dLocation two = dLocation.valueOf(attribute.getContext(1));
if (two == null) {
return null;
}
List<dLocation> locs = PathFinder.getPath(this, two);
dList list = new dList();
for (dLocation loc : locs) {
list.add(loc.identify());
}
return list.getAttribute(attribute.fulfill(1));
}
/////////////////////
// IDENTIFICATION ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <l@location.formatted.citizens>
// @returns Element
// @description
// Returns the location formatted for a Citizens command.
// In the format: x.x:y.y:z.z:world
// For example: 1.0:2.0:3.0:world_nether
// -->
if (attribute.startsWith("formatted.citizens")) {
return new Element(getX() + ":" + getY() + ":" + getZ() + ":" + getWorld().getName()).getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.formatted>
// @returns Element
// @description
// Returns the formatted version of the dLocation.
// In the format: X 'x.x', Y 'y.y', Z 'z.z', in world 'world'
// For example: X '1.0', Y '2.0', Z '3.0', in world 'world_nether'
// -->
if (attribute.startsWith("formatted")) {
return new Element("X '" + getX()
+ "', Y '" + getY()
+ "', Z '" + getZ()
+ "', in world '" + getWorld().getName() + "'").getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.chunk>
// @returns dChunk
// @description
// Returns the chunk that this location belongs to.
// -->
if (attribute.startsWith("chunk") ||
attribute.startsWith("get_chunk")) {
return new dChunk(this).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.raw>
// @returns dLocation
// @description
// Returns the raw representation of this location,
// ignoring any notables it might match.
// -->
if (attribute.startsWith("raw")) {
dLocation rawLocation = new dLocation(this);
rawLocation.setRaw(true);
return rawLocation.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.world>
// @returns dWorld
// @description
// Returns the world that the location is in.
// -->
if (attribute.startsWith("world")) {
return dWorld.mirrorBukkitWorld(getWorld())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.x>
// @returns Element(Decimal)
// @description
// Returns the X coordinate of the location.
// -->
if (attribute.startsWith("x")) {
return new Element(getX()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.y>
// @returns Element(Decimal)
// @description
// Returns the Y coordinate of the location.
// -->
if (attribute.startsWith("y")) {
return new Element(getY()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.z>
// @returns Element(Decimal)
// @description
// Returns the Z coordinate of the location.
// -->
if (attribute.startsWith("z")) {
return new Element(getZ()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.notable_name>
// @returns Element
// @description
// Gets the name of a Notable dLocation. If the location isn't noted,
// this is null.
// -->
if (attribute.startsWith("notable_name")) {
String notname = NotableManager.getSavedId(this);
if (notname == null) {
return null;
}
return new Element(notname).getAttribute(attribute.fulfill(1));
}
/////////////////////
// MATHEMATICAL ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <l@location.add[<location>]>
// @returns dLocation
// @description
// Returns the location with the specified coordinates added to it.
// -->
if (attribute.startsWith("add")
&& attribute.hasContext(1)) {
String[] ints = attribute.getContext(1).replace("l@", "").split(",", 4); // TODO: Just dLocation.valueOf?
if (ints.length >= 3) {
if ((aH.matchesDouble(ints[0]) || aH.matchesInteger(ints[0]))
&& (aH.matchesDouble(ints[1]) || aH.matchesInteger(ints[1]))
&& (aH.matchesDouble(ints[2]) || aH.matchesInteger(ints[2]))) {
return new dLocation(this.clone().add(Double.valueOf(ints[0]),
Double.valueOf(ints[1]),
Double.valueOf(ints[2]))).getAttribute(attribute.fulfill(1));
}
}
else if (dLocation.matches(attribute.getContext(1))) {
return new dLocation(this.clone().add(dLocation.valueOf(attribute.getContext(1))))
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.sub[<location>]>
// @returns dLocation
// @description
// Returns the location with the specified coordinates subtracted from it.
// -->
if (attribute.startsWith("sub")
&& attribute.hasContext(1)) {
String[] ints = attribute.getContext(1).replace("l@", "").split(",", 4); // TODO: Just dLocation.valueOf?
if (ints.length == 3 || ints.length == 4) {
if ((aH.matchesDouble(ints[0]) || aH.matchesInteger(ints[0]))
&& (aH.matchesDouble(ints[1]) || aH.matchesInteger(ints[1]))
&& (aH.matchesDouble(ints[2]) || aH.matchesInteger(ints[2]))) {
return new dLocation(this.clone().subtract(Double.valueOf(ints[0]),
Double.valueOf(ints[1]),
Double.valueOf(ints[2]))).getAttribute(attribute.fulfill(1));
}
}
else if (dLocation.matches(attribute.getContext(1))) {
return new dLocation(this.clone().subtract(dLocation.valueOf(attribute.getContext(1))))
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.mul[<length>]>
// @returns dLocation
// @description
// Returns the location multiplied by the specified length.
// -->
if (attribute.startsWith("mul") &&
attribute.hasContext(1)) {
return new dLocation(this.clone().multiply(Double.parseDouble(attribute.getContext(1))))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.div[<length>]>
// @returns dLocation
// @description
// Returns the location divided by the specified length.
// -->
if (attribute.startsWith("div") &&
attribute.hasContext(1)) {
return new dLocation(this.clone().multiply(1D / Double.parseDouble(attribute.getContext(1))))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.normalize>
// @returns dLocation
// @description
// Returns a 1-length vector in the same direction as this vector location.
// -->
if (attribute.startsWith("normalize")) {
double len = Math.sqrt(Math.pow(getX(), 2) + Math.pow(getY(), 2) + Math.pow(getZ(), 2));
if (len == 0) {
return this.getAttribute(attribute.fulfill(1));
}
else {
return new dLocation(this.clone().multiply(1D / len))
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.vector_length>
// @returns Element(Decimal)
// @description
// Returns the 3D length of the vector/location.
// -->
if (attribute.startsWith("vector_length")) {
return new Element(Math.sqrt(Math.pow(getX(), 2) + Math.pow(getY(), 2) + Math.pow(getZ(), 2)))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.distance_squared[<location>]>
// @returns Element(Decimal)
// @description
// Returns the distance between 2 locations, squared.
// -->
if (attribute.startsWith("distance_squared")
&& attribute.hasContext(1)) {
if (dLocation.matches(attribute.getContext(1))) {
dLocation toLocation = dLocation.valueOf(attribute.getContext(1));
if (!getWorld().getName().equalsIgnoreCase(toLocation.getWorld().getName())) {
if (!attribute.hasAlternative()) {
dB.echoError("Can't measure distance between two different worlds!");
}
return null;
}
return new Element(this.distanceSquared(toLocation))
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <l@location.distance[<location>]>
// @returns Element(Decimal)
// @description
// Returns the distance between 2 locations.
// -->
if (attribute.startsWith("distance")
&& attribute.hasContext(1)) {
if (dLocation.matches(attribute.getContext(1))) {
dLocation toLocation = dLocation.valueOf(attribute.getContext(1));
// <--[tag]
// @attribute <l@location.distance[<location>].horizontal>
// @returns Element(Decimal)
// @description
// Returns the horizontal distance between 2 locations.
// -->
if (attribute.getAttribute(2).startsWith("horizontal")) {
// <--[tag]
// @attribute <l@location.distance[<location>].horizontal.multiworld>
// @returns Element(Decimal)
// @description
// Returns the horizontal distance between 2 multiworld locations.
// -->
if (attribute.getAttribute(3).startsWith("multiworld")) {
return new Element(Math.sqrt(
Math.pow(this.getX() - toLocation.getX(), 2) +
Math.pow(this.getZ() - toLocation.getZ(), 2)))
.getAttribute(attribute.fulfill(3));
}
else if (this.getWorld() == toLocation.getWorld()) {
return new Element(Math.sqrt(
Math.pow(this.getX() - toLocation.getX(), 2) +
Math.pow(this.getZ() - toLocation.getZ(), 2)))
.getAttribute(attribute.fulfill(2));
}
}
// <--[tag]
// @attribute <l@location.distance[<location>].vertical>
// @returns Element(Decimal)
// @description
// Returns the vertical distance between 2 locations.
// -->
else if (attribute.getAttribute(2).startsWith("vertical")) {
// <--[tag]
// @attribute <l@location.distance[<location>].vertical.multiworld>
// @returns Element(Decimal)
// @description
// Returns the vertical distance between 2 multiworld locations.
// -->
if (attribute.getAttribute(3).startsWith("multiworld")) {
return new Element(Math.abs(this.getY() - toLocation.getY()))
.getAttribute(attribute.fulfill(3));
}
else if (this.getWorld() == toLocation.getWorld()) {
return new Element(Math.abs(this.getY() - toLocation.getY()))
.getAttribute(attribute.fulfill(2));
}
}
if (!getWorld().getName().equalsIgnoreCase(toLocation.getWorld().getName())) {
if (!attribute.hasAlternative()) {
dB.echoError("Can't measure distance between two different worlds!");
}
return null;
}
else {
return new Element(this.distance(toLocation))
.getAttribute(attribute.fulfill(1));
}
}
}
// <--[tag]
// @attribute <l@location.is_within[<cuboid>/<ellipsoid>]>
// @returns Element(Boolean)
// @description
// Returns whether the location is within the cuboid or ellipsoid.
// -->
if (attribute.startsWith("is_within")
&& attribute.hasContext(1)) {
if (dEllipsoid.matches(attribute.getContext(1))) {
dEllipsoid ellipsoid = dEllipsoid.valueOf(attribute.getContext(1));
if (ellipsoid != null) {
return new Element(ellipsoid.contains(this))
.getAttribute(attribute.fulfill(1));
}
}
else {
dCuboid cuboid = dCuboid.valueOf(attribute.getContext(1));
if (cuboid != null) {
return new Element(cuboid.isInsideCuboid(this))
.getAttribute(attribute.fulfill(1));
}
}
}
/////////////////////
// STATE ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <l@location.biome.formatted>
// @returns Element
// @description
// Returns the formatted biome name at the location.
// -->
if (attribute.startsWith("biome.formatted")) {
return new Element(CoreUtilities.toLowerCase(getBlock().getBiome().name()).replace('_', ' '))
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.biome>
// @mechanism dLocation.biome
// @returns dBiome
// @description
// Returns the biome at the location.
// -->
if (attribute.startsWith("biome")) {
return new dBiome(getBlock().getBiome())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.cuboids>
// @returns dList(dCuboid)
// @description
// Returns a dList of all notable dCuboids that include this location.
// -->
if (attribute.startsWith("cuboids")) {
List<dCuboid> cuboids = dCuboid.getNotableCuboidsContaining(this);
dList cuboid_list = new dList();
for (dCuboid cuboid : cuboids) {
cuboid_list.add(cuboid.identify());
}
return cuboid_list.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.ellipsoids>
// @returns dList(dCuboid)
// @description
// Returns a dList of all notable dEllipsoids that include this location.
// -->
if (attribute.startsWith("ellipsoids")) {
List<dEllipsoid> ellipsoids = dEllipsoid.getNotableEllipsoidsContaining(this);
dList ellipsoid_list = new dList();
for (dEllipsoid ellipsoid : ellipsoids) {
ellipsoid_list.add(ellipsoid.identify());
}
return ellipsoid_list.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.is_liquid>
// @returns Element(Boolean)
// @description
// Returns whether the block at the location is a liquid.
// -->
if (attribute.startsWith("is_liquid")) {
return new Element(getBlock().isLiquid()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.light.blocks>
// @returns Element(Number)
// @description
// Returns the amount of light from light blocks that is
// on the location.
// -->
if (attribute.startsWith("light.from_blocks") ||
attribute.startsWith("light.blocks")) {
return new Element(getBlock().getLightFromBlocks())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.light.sky>
// @returns Element(Number)
// @description
// Returns the amount of light from the sky that is
// on the location.
// -->
if (attribute.startsWith("light.from_sky") ||
attribute.startsWith("light.sky")) {
return new Element(getBlock().getLightFromSky())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <l@location.light>
// @returns Element(Number)
// @description
// Returns the total amount of light on the location.
// -->
if (attribute.startsWith("light")) {
return new Element(getBlock().getLightLevel())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.power>
// @returns Element(Number)
// @description
// Returns the current redstone power level of a block.
// -->
if (attribute.startsWith("power")) {
return new Element(getBlock().getBlockPower())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.type>
// @returns Element
// @description
// Always returns 'Location' for dLocation objects. All objects fetchable by the Object Fetcher will return the
// type of object that is fulfilling this attribute.
// -->
if (attribute.startsWith("type")) {
return new Element("Location").getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.command_block_name>
// @returns Element
// @mechanism command_block_name
// @description
// Returns the name a command block is set to.
// -->
if (attribute.startsWith("command_block_name")
&& getBlock().getType() == Material.COMMAND) {
return new Element(((CommandBlock) getBlock().getState()).getName())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.command_block>
// @returns Element
// @mechanism command_block
// @description
// Returns the command a command block is set to.
// -->
if (attribute.startsWith("command_block")
&& getBlock().getType() == Material.COMMAND) {
return new Element(((CommandBlock) getBlock().getState()).getCommand())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.furnace_burn_time>
// @returns Element(Number)
// @mechanism furnace_burn_time
// @description
// Returns the burn time a furnace has left.
// -->
if (attribute.startsWith("furnace_burn_time")) {
return new Element(((Furnace) getBlock().getState()).getBurnTime())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.furnace_cook_time>
// @returns Element(Number)
// @mechanism furnace_cook_time
// @description
// Returns the cook time a furnace has left.
// -->
if (attribute.startsWith("furnace_cook_time")) {
return new Element(((Furnace) getBlock().getState()).getCookTime())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <l@location.attached_to>
// @returns dLocation
// @description
// Returns the block this block is attached to.
// (Only if it is a lever or button!)
// -->
if (attribute.startsWith("attached_to")) {
BlockFace face = BlockFace.SELF;
MaterialData data = getBlock().getState().getData();
if (data instanceof Attachable) {
face = ((Attachable) data).getAttachedFace();
}
if (face != BlockFace.SELF) {
return new dLocation(getBlock().getRelative(face).getLocation()).getAttribute(attribute.fulfill(1));
}
}
// Iterate through this object's properties' attributes
for (Property property : PropertyParser.getProperties(this)) {
String returned = property.getAttribute(attribute);
if (returned != null) {
return returned;
}
}
return new Element(identify()).getAttribute(attribute);
}
public void applyProperty(Mechanism mechanism) {
dB.echoError("Cannot apply properties to a location!");
}
@Override
public void adjust(Mechanism mechanism) {
Element value = mechanism.getValue();
// <--[mechanism]
// @object dLocation
// @name block_type
// @input dMaterial
// @description
// Sets the type of the block.
// @tags
// <l@location.material>
// -->
if (mechanism.matches("block_type") && mechanism.requireObject(dMaterial.class)) {
dMaterial mat = value.asType(dMaterial.class);
byte data = mat.hasData() ? mat.getData() : 0;
getBlock().setTypeIdAndData(mat.getMaterial().getId(), data, false);
}
// <--[mechanism]
// @object dLocation
// @name biome
// @input dBiome
// @description
// Sets the biome of the block.
// @tags
// <l@location.biome>
// -->
if (mechanism.matches("biome") && mechanism.requireObject(dBiome.class)) {
value.asType(dBiome.class).getBiome().changeBlockBiome(this);
}
// <--[mechanism]
// @object dLocation
// @name spawner_type
// @input dEntity
// @description
// Sets the entity that a mob spawner will spawn.
// @tags
// <l@location.spawner_type>
// -->
if (mechanism.matches("spawner_type") && mechanism.requireObject(dEntity.class)
&& getBlock().getState() instanceof CreatureSpawner) {
((CreatureSpawner) getBlock().getState()).setSpawnedType(value.asType(dEntity.class).getBukkitEntityType());
}
// <--[mechanism]
// @object dLocation
// @name sign_contents
// @input dList
// @description
// Sets the contents of a sign block.
// Note that this takes an escaped list.
// See <@link language property escaping>.
// @tags
// <l@location.sign_contents>
// -->
if (mechanism.matches("sign_contents") && getBlock().getState() instanceof Sign) {
Sign state = (Sign) getBlock().getState();
for (int i = 0; i < 4; i++) {
state.setLine(i, "");
}
dList list = value.asType(dList.class);
if (list.size() > 4) {
dB.echoError("Sign can only hold four lines!");
}
else {
for (int i = 0; i < list.size(); i++) {
state.setLine(i, EscapeTags.unEscape(list.get(i)));
}
}
state.update();
}
// <--[mechanism]
// @object dLocation
// @name skull_skin
// @input Element
// @description
// Sets the skin of a skull block.
// Takes a username.
// @tags
// <l@location.skull_skin>
// -->
if (mechanism.matches("skull_skin")) {
final BlockState blockState = getBlock().getState();
if (blockState instanceof Skull) {
dList list = mechanism.getValue().asType(dList.class);
String idString = list.get(0);
PlayerProfile profile;
if (idString.contains("-")) {
UUID uuid = UUID.fromString(idString);
profile = new PlayerProfile(null, uuid, null);
} else {
profile = new PlayerProfile(idString, null, null);
}
profile = NMSHandler.getInstance().fillPlayerProfile(profile);
NMSHandler.getInstance().getBlockHelper().setPlayerProfile((Skull) blockState, profile);
}
}
// <--[mechanism]
// @object dLocation
// @name command_block_name
// @input Element
// @description
// Sets the name of a command block.
// @tags
// <l@location.command_block_name>
// -->
if (mechanism.matches("command_block_name")) {
if (getBlock().getType() == Material.COMMAND) {
CommandBlock block = ((CommandBlock) getBlock().getState());
block.setName(value.asString());
block.update();
}
}
// <--[mechanism]
// @object dLocation
// @name command_block
// @input Element
// @description
// Sets the command of a command block.
// @tags
// <l@location.command_block>
// -->
if (mechanism.matches("command_block")) {
if (getBlock().getType() == Material.COMMAND) {
CommandBlock block = ((CommandBlock) getBlock().getState());
block.setCommand(value.asString());
block.update();
}
}
// <--[mechanism]
// @object dLocation
// @name furnace_burn_time
// @input Element(Number)
// @description
// Sets the burn time for a furnace in ticks. Maximum is 32767.
// @tags
// <l@location.furnace_burn_time>
// -->
if (mechanism.matches("furnace_burn_time")) {
Material material = getBlock().getType();
if (material == Material.FURNACE || material == Material.BURNING_FURNACE) {
Furnace furnace = (Furnace) getBlock().getState();
furnace.setBurnTime((short) value.asInt());
}
}
// <--[mechanism]
// @object dLocation
// @name furnace_cook_time
// @input Element(Number)
// @description
// Sets the cook time for a furnace in ticks. Maximum is 32767.
// @tags
// <l@location.furnace_cook_time>
// -->
if (mechanism.matches("furnace_cook_time")) {
Material material = getBlock().getType();
if (material == Material.FURNACE || material == Material.BURNING_FURNACE) {
Furnace furnace = (Furnace) getBlock().getState();
furnace.setCookTime((short) value.asInt());
}
}
// <--[mechanism]
// @object dLocation
// @name base_color
// @input Element
// @description
// Changes the base color of the banner at this location.
// For the list of possible colors, see <@link url http://bit.ly/1dydq12>.
// @tags
// <l@location.base_color>
// -->
if (mechanism.matches("base_color")) {
Banner banner = (Banner) getBlock().getState();
banner.setBaseColor(DyeColor.valueOf(mechanism.getValue().asString().toUpperCase()));
banner.update();
}
// <--[mechanism]
// @object dLocation
// @name patterns
// @input dList
// @description
// Changes the patterns of the banner at this location. Input must be in the form
// "li@COLOR/PATTERN|COLOR/PATTERN" etc.
// For the list of possible colors, see <@link url http://bit.ly/1dydq12>.
// For the list of possible patterns, see <@link url http://bit.ly/1MqRn7T>.
// @tags
// <l@location.patterns>
// -->
if (mechanism.matches("patterns")) {
List<org.bukkit.block.banner.Pattern> patterns = new ArrayList<org.bukkit.block.banner.Pattern>();
dList list = mechanism.getValue().asType(dList.class);
List<String> split;
for (String string : list) {
try {
split = CoreUtilities.split(string, '/', 2);
patterns.add(new org.bukkit.block.banner.Pattern(DyeColor.valueOf(split.get(0).toUpperCase()),
PatternType.valueOf(split.get(1).toUpperCase())));
}
catch (Exception e) {
dB.echoError("Could not apply pattern to banner: " + string);
}
}
Banner banner = (Banner) getBlock().getState();
banner.setPatterns(patterns);
banner.update();
}
// <--[mechanism]
// @object dLocation
// @name head_rotation
// @input Element(Number)
// @description
// Sets the rotation of the head at this location. Must be an integer 1 to 16.
// @tags
// <l@location.head_rotation>
// -->
if (mechanism.matches("head_rotation") && mechanism.requireInteger()) {
((Skull) getBlock().getState()).setRotation(getSkullBlockFace(value.asInt() - 1));
}
// <--[mechanism]
// @object dLocation
// @name data
// @input Element(Number)
// @description
// Sets the data-value of a block.
// @tags
// <l@location.material.data>
// -->
if (mechanism.matches("data") && mechanism.hasValue()) {
getBlock().setData((byte) value.asInt());
}
if (!mechanism.fulfilled()) {
mechanism.reportInvalid();
}
}
}