package net.aufdemrand.denizen.objects;
import net.aufdemrand.denizen.flags.FlagManager;
import net.aufdemrand.denizen.nms.NMSHandler;
import net.aufdemrand.denizen.nms.NMSVersion;
import net.aufdemrand.denizen.nms.interfaces.EntityHelper;
import net.aufdemrand.denizen.nms.interfaces.FakePlayer;
import net.aufdemrand.denizen.objects.properties.entity.EntityAge;
import net.aufdemrand.denizen.objects.properties.entity.EntityColor;
import net.aufdemrand.denizen.objects.properties.entity.EntityTame;
import net.aufdemrand.denizen.scripts.containers.core.EntityScriptContainer;
import net.aufdemrand.denizen.scripts.containers.core.EntityScriptHelper;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizencore.utilities.debugging.dB;
import net.aufdemrand.denizen.utilities.depends.Depends;
import net.aufdemrand.denizen.utilities.entity.DenizenEntityType;
import net.aufdemrand.denizen.utilities.nbt.CustomNBT;
import net.aufdemrand.denizencore.objects.*;
import net.aufdemrand.denizencore.objects.properties.Property;
import net.aufdemrand.denizencore.objects.properties.PropertyParser;
import net.aufdemrand.denizencore.scripts.ScriptRegistry;
import net.aufdemrand.denizencore.tags.Attribute;
import net.aufdemrand.denizencore.tags.TagContext;
import net.aufdemrand.denizencore.utilities.CoreUtilities;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.EntityEffect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.*;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class dEntity implements dObject, Adjustable {
/////////////////////
// STATIC METHODS
/////////////////
private static final Map<UUID, Entity> rememberedEntities = new HashMap<UUID, Entity>();
public static void rememberEntity(Entity entity) {
if (entity == null) {
return;
}
rememberedEntities.put(entity.getUniqueId(), entity);
}
public static void forgetEntity(Entity entity) {
if (entity == null) {
return;
}
rememberedEntities.remove(entity.getUniqueId());
}
public static boolean isNPC(Entity entity) {
return entity != null && entity.hasMetadata("NPC") && entity.getMetadata("NPC").get(0).asBoolean();
}
public static boolean isCitizensNPC(Entity entity) {
return entity != null && Depends.citizens != null && CitizensAPI.getNPCRegistry().isNPC(entity);
}
public static dNPC getNPCFrom(Entity entity) {
if (isCitizensNPC(entity)) {
return dNPC.fromEntity(entity);
}
else {
return null;
}
}
public static boolean isPlayer(Entity entity) {
return entity != null && entity instanceof Player && !isNPC(entity);
}
public static dPlayer getPlayerFrom(Entity entity) {
if (isPlayer(entity)) {
return dPlayer.mirrorBukkitPlayer((Player) entity);
}
else {
return null;
}
}
public dItem getItemInHand() {
if (isLivingEntity() && getLivingEntity().getEquipment() != null) {
ItemStack its = getLivingEntity().getEquipment().getItemInHand();
if (its == null) {
return null;
}
return new dItem(its.clone());
}
return null;
}
//////////////////
// OBJECT FETCHER
////////////////
public static dEntity valueOf(String string) {
return valueOf(string, null);
}
/**
* Gets a dEntity Object from a string form. </br>
* </br>
* Unique dEntities: </br>
* n@13 will return the entity object of NPC 13 </br>
* e@5884 will return the entity object for the entity with the entityid of 5884 </br>
* e@jimmys_pet will return the saved entity object for the id 'jimmys pet' </br>
* p@aufdemrand will return the entity object for aufdemrand </br>
* </br>
* New dEntities: </br>
* zombie will return an unspawned Zombie dEntity </br>
* super_creeper will return an unspawned custom 'Super_Creeper' dEntity </br>
*
* @param string the string or dScript argument String
* @return a dEntity, or null
*/
@Fetchable("e")
public static dEntity valueOf(String string, TagContext context) {
if (string == null) {
return null;
}
Matcher m;
///////
// Handle objects with properties through the object fetcher
m = ObjectFetcher.DESCRIBED_PATTERN.matcher(string);
if (m.matches()) {
return ObjectFetcher.getObjectFrom(dEntity.class, string, context);
}
// Choose a random entity type if "RANDOM" is used
if (string.equalsIgnoreCase("RANDOM")) {
EntityType randomType = null;
// When selecting a random entity type, ignore invalid or inappropriate ones
while (randomType == null ||
randomType.name().matches("^(COMPLEX_PART|DROPPED_ITEM|ENDER_CRYSTAL" +
"|ENDER_DRAGON|FISHING_HOOK|ITEM_FRAME|LEASH_HITCH|LIGHTNING" +
"|PAINTING|PLAYER|UNKNOWN|WEATHER|WITHER|WITHER_SKULL)$")) {
randomType = EntityType.values()[CoreUtilities.getRandom().nextInt(EntityType.values().length)];
}
return new dEntity(DenizenEntityType.getByName(randomType.name()), "RANDOM");
}
///////
// Match @object format
// Make sure string matches what this interpreter can accept.
m = entity_by_id.matcher(string);
if (m.matches()) {
String entityGroup = m.group(1).toUpperCase();
// NPC entity
if (entityGroup.matches("N@")) {
dNPC npc = dNPC.valueOf(string);
if (npc != null) {
return new dEntity(npc);
}
else {
dB.echoError("NPC '" + string
+ "' does not exist!");
}
}
// Player entity
else if (entityGroup.matches("P@")) {
LivingEntity returnable = dPlayer.valueOf(m.group(2)).getPlayerEntity();
if (returnable != null) {
return new dEntity(returnable);
}
else if (context == null || context.debug) {
dB.echoError("Invalid Player! '" + m.group(2)
+ "' could not be found. Has the player logged off?");
}
}
// Assume entity
else {
try {
UUID entityID = UUID.fromString(m.group(2));
Entity entity = getEntityForID(entityID);
if (entity != null) {
return new dEntity(entity);
}
return null;
}
catch (Exception ex) {
ex.getCause(); // DO NOTHING
}
// else if (isSaved(m.group(2)))
// return getSaved(m.group(2));
}
}
string = string.replace("e@", "");
////////
// Match Custom Entity
if (ScriptRegistry.containsScript(string, EntityScriptContainer.class)) {
// Construct a new custom unspawned entity from script
return ScriptRegistry.getScriptContainerAs(string, EntityScriptContainer.class).getEntityFrom();
}
////////
// Match Entity_Type
m = entity_with_data.matcher(string);
if (m.matches()) {
String data1 = null;
String data2 = null;
if (m.group(2) != null) {
data1 = m.group(2);
}
if (m.group(3) != null) {
data2 = m.group(3);
}
// Handle custom DenizenEntityTypes
if (DenizenEntityType.isRegistered(m.group(1))) {
return new dEntity(DenizenEntityType.getByName(m.group(1)), data1, data2);
}
}
if (context == null || context.debug) {
dB.log("valueOf dEntity returning null: " + string);
}
return null;
}
public static Entity getEntityForID(UUID id) {
if (rememberedEntities.containsKey(id)) {
return rememberedEntities.get(id);
}
for (World world : Bukkit.getWorlds()) {
Entity entity = NMSHandler.getInstance().getEntityHelper().getEntity(world, id);
if (entity != null) {
return entity;
}
}
return null;
}
final static Pattern entity_by_id = Pattern.compile("(n@|e@|p@)(.+)",
Pattern.CASE_INSENSITIVE);
final static Pattern entity_with_data = Pattern.compile("(\\w+),?(\\w+)?,?(\\w+)?",
Pattern.CASE_INSENSITIVE);
public static boolean matches(String arg) {
// Accept anything that starts with a valid entity object identifier.
Matcher m;
m = entity_by_id.matcher(arg);
if (m.matches()) {
return true;
}
// No longer picky about e@.. let's remove it from the arg
arg = arg.replace("e@", "").toUpperCase();
// Allow 'random'
if (arg.equals("RANDOM")) {
return true;
}
// Allow any entity script
if (ScriptRegistry.containsScript(arg, EntityScriptContainer.class)) {
return true;
}
// Use regex to make some matcher groups
m = entity_with_data.matcher(arg);
if (m.matches()) {
// Check first word with a valid entity_type (other groups are datas used in constructors)
if (DenizenEntityType.isRegistered(m.group(1))) {
return true;
}
}
// No luck otherwise!
return false;
}
/////////////////////
// CONSTRUCTORS
//////////////////
public dEntity(Entity entity) {
if (entity != null) {
this.entity = entity;
entityScript = EntityScriptHelper.getEntityScript(entity);
this.uuid = entity.getUniqueId();
this.entity_type = DenizenEntityType.getByEntity(entity);
if (isCitizensNPC(entity)) {
this.npc = getNPCFrom(entity);
}
}
else {
dB.echoError("Entity referenced is null!");
}
}
@Deprecated
public dEntity(EntityType entityType) {
if (entityType != null) {
this.entity = null;
this.entity_type = DenizenEntityType.getByName(entityType.name());
}
else {
dB.echoError("Entity_type referenced is null!");
}
}
@Deprecated
public dEntity(EntityType entityType, ArrayList<Mechanism> mechanisms) {
this(entityType);
this.mechanisms = mechanisms;
}
@Deprecated
public dEntity(EntityType entityType, String data1) {
if (entityType != null) {
this.entity = null;
this.entity_type = DenizenEntityType.getByName(entityType.name());
this.data1 = data1;
}
else {
dB.echoError("Entity_type referenced is null!");
}
}
@Deprecated
public dEntity(EntityType entityType, String data1, String data2) {
if (entityType != null) {
this.entity = null;
this.entity_type = DenizenEntityType.getByName(entityType.name());
this.data1 = data1;
this.data2 = data2;
}
else {
dB.echoError("Entity_type referenced is null!");
}
}
public dEntity(DenizenEntityType entityType) {
if (entityType != null) {
this.entity = null;
this.entity_type = entityType;
}
else {
dB.echoError("DenizenEntityType referenced is null!");
}
}
public dEntity(DenizenEntityType entityType, ArrayList<Mechanism> mechanisms) {
this(entityType);
this.mechanisms = mechanisms;
}
public dEntity(DenizenEntityType entityType, String data1) {
if (entityType != null) {
this.entity = null;
this.entity_type = entityType;
this.data1 = data1;
}
else {
dB.echoError("DenizenEntityType referenced is null!");
}
}
public dEntity(DenizenEntityType entityType, String data1, String data2) {
if (entityType != null) {
this.entity = null;
this.entity_type = entityType;
this.data1 = data1;
this.data2 = data2;
}
else {
dB.echoError("DenizenEntityType referenced is null!");
}
}
public dEntity(dNPC npc) {
if (Depends.citizens == null) {
return;
}
if (npc != null) {
this.npc = npc;
if (npc.isSpawned()) {
this.entity = npc.getEntity();
this.entity_type = DenizenEntityType.getByName(npc.getEntityType().name());
this.uuid = entity.getUniqueId();
}
}
else {
dB.echoError("NPC referenced is null!");
}
}
/////////////////////
// INSTANCE FIELDS/METHODS
/////////////////
private Entity entity = null;
private DenizenEntityType entity_type = null;
private String data1 = null;
private String data2 = null;
private DespawnedEntity despawned_entity = null;
private dNPC npc = null;
private UUID uuid = null;
private String entityScript = null;
public DenizenEntityType getEntityType() {
return entity_type;
}
public EntityType getBukkitEntityType() {
return entity_type.getBukkitEntityType();
}
public void setEntityScript(String entityScript) {
this.entityScript = entityScript;
}
public String getEntityScript() {
return entityScript;
}
/**
* Returns the unique UUID of this entity
*
* @return The UUID
*/
public UUID getUUID() {
return uuid;
}
public String getSaveName() {
String baseID = uuid.toString().toUpperCase().replace("-", "");
return baseID.substring(0, 2) + "." + baseID;
}
/**
* Get the dObject that most accurately describes this entity,
* useful for automatically saving dEntities to contexts as
* dNPCs and dPlayers
*
* @return The dObject
*/
public dObject getDenizenObject() {
if (entity == null) {
return null;
}
if (isCitizensNPC()) {
return getDenizenNPC();
}
else if (isPlayer()) {
return new dPlayer(getPlayer());
}
else {
return this;
}
}
/**
* Get the Bukkit entity corresponding to this dEntity
*
* @return the underlying Bukkit entity
*/
public Entity getBukkitEntity() {
return entity;
}
/**
* Get the living entity corresponding to this dEntity
*
* @return The living entity
*/
public LivingEntity getLivingEntity() {
if (entity instanceof LivingEntity) {
return (LivingEntity) entity;
}
else {
return null;
}
}
/**
* Check whether this dEntity is a living entity
*
* @return true or false
*/
public boolean isLivingEntity() {
return (entity instanceof LivingEntity);
}
public boolean hasInventory() {
return getBukkitEntity() instanceof InventoryHolder || isCitizensNPC();
}
/**
* Get the dNPC corresponding to this dEntity
*
* @return The dNPC
*/
public dNPC getDenizenNPC() {
if (npc != null) {
return npc;
}
else {
return getNPCFrom(entity);
}
}
/**
* Check whether this dEntity is an NPC
*
* @return true or false
*/
public boolean isNPC() {
return npc != null || isNPC(entity);
}
public boolean isCitizensNPC() {
return npc != null || isCitizensNPC(entity);
}
/**
* Get the Player corresponding to this dEntity
*
* @return The Player
*/
public Player getPlayer() {
if (isPlayer()) {
return (Player) entity;
}
else {
return null;
}
}
/**
* Get the dPlayer corresponding to this dEntity
*
* @return The dPlayer
*/
public dPlayer getDenizenPlayer() {
if (isPlayer()) {
return new dPlayer(getPlayer());
}
else {
return null;
}
}
/**
* Check whether this dEntity is a Player
*
* @return true or false
*/
public boolean isPlayer() {
return entity instanceof Player && !isNPC();
}
/**
* Get this dEntity as a Projectile
*
* @return The Projectile
*/
public Projectile getProjectile() {
return (Projectile) entity;
}
/**
* Check whether this dEntity is a Projectile
*
* @return true or false
*/
public boolean isProjectile() {
return entity instanceof Projectile;
}
/**
* Get this Projectile entity's shooter
*
* @return A dEntity of the shooter
*/
public dEntity getShooter() {
if (hasShooter()) {
return new dEntity((LivingEntity) getProjectile().getShooter());
}
else {
return null;
}
}
/**
* Set this Projectile entity's shooter
*/
public void setShooter(dEntity shooter) {
if (isProjectile() && shooter.isLivingEntity()) {
getProjectile().setShooter(shooter.getLivingEntity());
}
}
/**
* Check whether this entity has a shooter.
*
* @return true or false
*/
public boolean hasShooter() {
return isProjectile() && getProjectile().getShooter() != null && getProjectile().getShooter() instanceof LivingEntity;
// TODO: Handle other shooter source thingy types
}
public Inventory getBukkitInventory() {
if (hasInventory()) {
if (!isCitizensNPC()) {
return ((InventoryHolder) getBukkitEntity()).getInventory();
}
}
return null;
}
/**
* Returns this entity's dInventory.
*
* @return the entity's dInventory
*/
public dInventory getInventory() {
return hasInventory() ? isCitizensNPC() ? getDenizenNPC().getDenizenInventory()
: new dInventory(getBukkitInventory()) : null;
}
public String getName() {
if (isCitizensNPC()) {
return getDenizenNPC().getCitizen().getName();
}
if (entity instanceof FakePlayer) {
return ((FakePlayer) entity).getFullName();
}
if (entity instanceof Player) {
return entity.getName();
}
String customName = entity.getCustomName();
if (customName != null) {
return customName;
}
return entity_type.getName();
}
/**
* Returns this entity's equipment
*
* @return the entity's equipment
*/
public dList getEquipment() {
ItemStack[] equipment = getLivingEntity().getEquipment().getArmorContents();
dList equipmentList = new dList();
for (ItemStack item : equipment) {
equipmentList.add(new dItem(item).identify());
}
return equipmentList;
}
/**
* Whether this entity identifies as a generic
* entity type, for instance "e@cow", instead of
* a spawned entity
*
* @return true or false
*/
public boolean isGeneric() {
return !isUnique();
}
/**
* Get the location of this entity
*
* @return The Location
*/
public dLocation getLocation() {
if (isUnique() && entity != null) {
return new dLocation(entity.getLocation());
}
return null;
}
/**
* Get the eye location of this entity
*
* @return The location
*/
public dLocation getEyeLocation() {
if (isPlayer()) {
return new dLocation(getPlayer().getEyeLocation());
}
else if (!isGeneric() && isLivingEntity()) {
return new dLocation(getLivingEntity().getEyeLocation());
}
else if (!isGeneric()) {
return new dLocation(getBukkitEntity().getLocation());
}
return null;
}
/**
* Gets the velocity of this entity
*
* @return The velocity's vector
*/
public Vector getVelocity() {
if (!isGeneric()) {
return entity.getVelocity();
}
return null;
}
/**
* Sets the velocity of this entity
*/
public void setVelocity(Vector vector) {
if (!isGeneric()) {
if (entity instanceof WitherSkull) {
((WitherSkull) entity).setDirection(vector);
}
else {
entity.setVelocity(vector);
}
}
}
/**
* Gets the world of this entity
*
* @return The entity's world
*/
public World getWorld() {
if (!isGeneric()) {
return entity.getWorld();
}
return null;
}
public void spawnAt(Location location) {
// If the entity is already spawned, teleport it.
if (isCitizensNPC()) {
if (getDenizenNPC().getCitizen().isSpawned()) {
getDenizenNPC().getCitizen().teleport(location, TeleportCause.PLUGIN);
}
else {
getDenizenNPC().getCitizen().spawn(location);
entity = getDenizenNPC().getCitizen().getEntity();
uuid = getDenizenNPC().getCitizen().getEntity().getUniqueId();
}
}
else if (entity != null && isUnique()) {
entity.teleport(location);
}
else {
if (entity_type != null) {
if (despawned_entity != null) {
// If entity had a custom_script, use the script to rebuild the base entity.
if (despawned_entity.custom_script != null) {
// TODO: Build entity from custom script
}
// Else, use the entity_type specified/remembered
else {
entity = entity_type.spawnNewEntity(location, mechanisms);
}
getLivingEntity().teleport(location);
getLivingEntity().getEquipment().setArmorContents(despawned_entity.equipment);
getLivingEntity().setHealth(despawned_entity.health);
despawned_entity = null;
}
else {
org.bukkit.entity.Entity ent = null;
if (entity_type.getName().equals("PLAYER")) {
if (Depends.citizens == null) {
dB.echoError("Cannot spawn entity of type PLAYER!");
return;
}
else {
dNPC npc = new dNPC(net.citizensnpcs.api.CitizensAPI.getNPCRegistry().createNPC(EntityType.PLAYER, data1));
npc.getCitizen().spawn(location);
entity = npc.getEntity();
uuid = entity.getUniqueId();
}
}
else if (entity_type.getName().equals("FALLING_BLOCK")) {
Material material = null;
if (data1 != null && dMaterial.matches(data1)) {
material = dMaterial.valueOf(data1).getMaterial();
// If we did not get a block with "RANDOM", or we got
// air or portals, keep trying
while (data1.equalsIgnoreCase("RANDOM") &&
((!material.isBlock()) ||
material == Material.AIR ||
material == Material.PORTAL ||
material == Material.ENDER_PORTAL)) {
material = dMaterial.valueOf(data1).getMaterial();
}
}
// If material is null or not a block, default to SAND
if (material == null || (!material.isBlock())) {
material = Material.SAND;
}
byte materialData = 0;
// Get special data value from data2 if it is a valid integer
if (data2 != null && aH.matchesInteger(data2)) {
materialData = (byte) aH.getIntegerFrom(data2);
}
// This is currently the only way to spawn a falling block
ent = location.getWorld().spawnFallingBlock(location, material, materialData);
entity = ent;
uuid = entity.getUniqueId();
}
else {
ent = entity_type.spawnNewEntity(location, mechanisms);
entity = ent;
if (entity == null) {
if (dB.verbose) {
dB.echoError("Failed to spawn entity of type " + entity_type.getName());
}
return;
}
uuid = entity.getUniqueId();
if (entityScript != null) {
EntityScriptHelper.setEntityScript(entity, entityScript);
}
if (entity_type.getName().equals("PIG_ZOMBIE")) {
// Give pig zombies golden swords by default, unless data2 specifies
// a different weapon
if (!dItem.matches(data1)) {
data1 = "gold_sword";
}
((PigZombie) entity).getEquipment()
.setItemInHand(dItem.valueOf(data1).getItemStack());
}
else if (entity_type.getName().equals("SKELETON")) {
// Give skeletons bows by default, unless data2 specifies
// a different weapon
if (!dItem.matches(data2)) {
data2 = "bow";
}
((Skeleton) entity).getEquipment()
.setItemInHand(dItem.valueOf(data2).getItemStack());
}
// If there is some special subtype data associated with this dEntity,
// use the setSubtype method to set it in a clean, object-oriented
// way that uses reflection
//
// Otherwise, just use entity-specific methods manually
if (data1 != null) {
// TODO: Discourage usage of + delete the below (Use properties instead!)
// TODO: Remove in 1.0
try {
// Allow creepers to be powered - replaced by EntityPowered
if (ent instanceof Creeper && data1.equalsIgnoreCase("POWERED")) {
((Creeper) entity).setPowered(true);
}
// Allow setting of blocks held by endermen - replaced by EntityItem
else if (ent instanceof Enderman && dMaterial.matches(data1)) {
((Enderman) entity).setCarriedMaterial(dMaterial.valueOf(data1).getMaterialData());
}
// Allow setting of horse variants and colors - replaced by EntityColor
else if (ent instanceof Horse) {
setSubtype("org.bukkit.entity.Horse", "org.bukkit.entity.Horse$Variant", "setVariant", data1);
if (data2 != null) {
setSubtype("org.bukkit.entity.Horse", "org.bukkit.entity.Horse$Color", "setColor", data2);
}
}
// Allow setting of ocelot types - replaced by EntityColor
else if (ent instanceof Ocelot) {
setSubtype("org.bukkit.entity.Ocelot", "org.bukkit.entity.Ocelot$Type", "setCatType", data1);
}
// Allow setting of sheep colors - replaced by EntityColor
else if (ent instanceof Sheep) {
setSubtype("org.bukkit.entity.Sheep", "org.bukkit.DyeColor", "setColor", data1);
}
// Allow setting of skeleton types - replaced by EntitySkeleton
else if (ent instanceof Skeleton) {
setSubtype("org.bukkit.entity.Skeleton", "org.bukkit.entity.Skeleton$SkeletonType", "setSkeletonType", data1);
}
// Allow setting of slime sizes - replaced by EntitySize
else if (ent instanceof Slime && aH.matchesInteger(data1)) {
((Slime) entity).setSize(aH.getIntegerFrom(data1));
}
// Allow setting of villager professions - replaced by EntityProfession
else if (ent instanceof Villager) {
setSubtype("org.bukkit.entity.Villager", "org.bukkit.entity.Villager$Profession", "setProfession", data1);
}
}
catch (Exception e) {
dB.echoError("Error setting custom entity data.");
dB.echoError(e);
}
}
}
}
}
else {
dB.echoError("Cannot spawn a null dEntity!");
}
if (!isUnique()) {
dB.echoError("Error spawning entity - bad entity type, blocked by another plugin, or tried to spawn in an unloaded chunk?");
return;
}
for (Mechanism mechanism : mechanisms) {
adjust(mechanism);
}
mechanisms.clear();
}
}
public void despawn() {
despawned_entity = new DespawnedEntity(this);
getLivingEntity().remove();
}
public void respawn() {
if (despawned_entity != null) {
spawnAt(despawned_entity.location);
}
else if (entity == null) {
dB.echoError("Cannot respawn a null dEntity!");
}
}
public boolean isSpawned() {
return entity != null && isValid();
}
public boolean isValid() {
return entity != null && entity.isValid();
}
public void remove() {
EntityScriptHelper.unlinkEntity(entity);
entity.remove();
}
public void teleport(Location location) {
if (isCitizensNPC()) {
getDenizenNPC().getCitizen().teleport(location, TeleportCause.PLUGIN);
}
else {
entity.teleport(location);
}
}
/**
* Make this entity target another living entity, attempting both
* old entity AI and new entity AI targeting methods
*
* @param target The LivingEntity target
*/
public void target(LivingEntity target) {
if (!isSpawned() || !(entity instanceof Creature)) {
dB.echoError(identify() + " is not a valid creature entity!");
return;
}
NMSHandler.getInstance().getEntityHelper().setTarget((Creature) entity, target);
}
/**
* Set the subtype of this entity by using the chosen method and Enum from
* this Bukkit entity's class and:
* 1) using a random subtype if value is "RANDOM"
* 2) looping through the entity's subtypes until one matches the value string
* <p/>
* Example: setSubtype("org.bukkit.entity.Ocelot", "org.bukkit.entity.Ocelot$Type", "setCatType", "SIAMESE_CAT");
*
* @param entityName The name of the entity's class.
* @param typeName The name of the entity class' Enum with subtypes.
* @param method The name of the method used to set the subtype of this entity.
* @param value The value of the subtype.
*/
public void setSubtype(String entityName, String typeName, String method, String value)
throws Exception {
Class<?> entityClass = Class.forName(entityName);
Class<?> typeClass = Class.forName(typeName);
Object[] types = typeClass.getEnumConstants();
if (value.equalsIgnoreCase("RANDOM")) {
entityClass.getMethod(method, typeClass).invoke(entity, types[CoreUtilities.getRandom().nextInt(types.length)]);
}
else {
for (Object type : types) {
if (type.toString().equalsIgnoreCase(value)) {
entityClass.getMethod(method, typeClass).invoke(entity, type);
break;
}
}
}
}
public void setEntity(Entity entity) {
this.entity = entity;
}
// Used to store some information about a livingEntity while it's despawned
private class DespawnedEntity {
Double health = null;
Location location = null;
ItemStack[] equipment = null;
String custom_script = null;
public DespawnedEntity(dEntity entity) {
if (entity != null) {
// Save some important info to rebuild the entity
health = entity.getLivingEntity().getHealth();
location = entity.getLivingEntity().getLocation();
equipment = entity.getLivingEntity().getEquipment().getArmorContents();
if (CustomNBT.hasCustomNBT(entity.getLivingEntity(), "denizen-script-id")) {
custom_script = CustomNBT.getCustomNBT(entity.getLivingEntity(), "denizen-script-id");
}
}
}
}
public int comparesTo(dEntity entity) {
// Never matches a null
if (entity == null) {
return 0;
}
// If provided is unique, and both are the same unique entity, return 1.
if (entity.isUnique() && entity.identify().equals(identify())) {
return 1;
}
// If provided isn't unique...
if (!entity.isUnique()) {
// Return 1 if this object isn't unique either, but matches
if (!isUnique() && entity.identify().equals(identify())) {
return 1;
}
// Return 1 if the provided object isn't unique, but whose entity_type
// matches this object, even if this object is unique.
if (entity_type == entity.entity_type) {
return 1;
}
}
return 0;
}
public boolean comparedTo(String compare) {
compare = CoreUtilities.toLowerCase(compare);
if (compare.equals("entity")) {
return true;
}
else if (compare.equals("player")) {
return isPlayer();
}
else if (compare.equals("npc")) {
return isCitizensNPC() || isNPC();
}
else if (getEntityScript() != null && compare.equals(CoreUtilities.toLowerCase(getEntityScript()))) {
return true;
}
else if (compare.equals(getEntityType().getLowercaseName())) {
return true;
}
return false;
}
/////////////////////
// dObject Methods
///////////////////
private String prefix = "Entity";
@Override
public String getObjectType() {
return "Entity";
}
@Override
public String getPrefix() {
return prefix;
}
@Override
public dEntity setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
@Override
public String debug() {
return "<G>" + prefix + "='<Y>" + identify() + "<G>' ";
}
@Override
public String identify() {
// Check if entity is an NPC
if (npc != null) {
return "n@" + npc.getId();
}
// Check if entity is a Player or is spawned
if (entity != null) {
if (isPlayer()) {
return "p@" + getPlayer().getUniqueId();
}
// TODO:
// Check if entity is a 'notable entity'
// if (isSaved(this))
// return "e@" + getSaved(this);
else if (isSpawned() || rememberedEntities.containsKey(entity.getUniqueId())) {
return "e@" + entity.getUniqueId().toString();
}
}
// Try to identify as an entity script
if (entityScript != null) {
return "e@" + entityScript;
}
// Check if an entity_type is available
if (entity_type != null) {
// Build the pseudo-property-string, if any
StringBuilder properties = new StringBuilder();
for (Mechanism mechanism : mechanisms) {
properties.append(mechanism.getName()).append("=").append(mechanism.getValue().asString().replace(';', (char) 0x2011)).append(";");
}
String propertyOutput = "";
if (properties.length() > 0) {
propertyOutput = "[" + properties.substring(0, properties.length() - 1) + "]";
}
return "e@" + entity_type.getLowercaseName() + propertyOutput;
}
return "null";
}
@Override
public String identifySimple() {
// Check if entity is an NPC
if (npc != null && npc.isValid()) {
return "n@" + npc.getId();
}
if (isPlayer()) {
return "p@" + getPlayer().getName();
}
// Try to identify as an entity script
if (entityScript != null) {
return "e@" + entityScript;
}
// Check if an entity_type is available
if (entity_type != null) {
return "e@" + entity_type.getLowercaseName();
}
return "null";
}
public String identifyType() {
if (isCitizensNPC()) {
return "npc";
}
else if (isPlayer()) {
return "player";
}
else {
return "e@" + entity_type.getName();
}
}
public String identifySimpleType() {
if (isCitizensNPC()) {
return "npc";
}
else if (isPlayer()) {
return "player";
}
else {
return entity_type.getLowercaseName();
}
}
@Override
public String toString() {
return identify();
}
@Override
public boolean isUnique() {
return isPlayer() || isCitizensNPC() || isSpawned()
|| (entity != null && rememberedEntities.containsKey(entity.getUniqueId())); // || isSaved()
}
public boolean matchesEntity(String ent) {
if (ent.equalsIgnoreCase("entity")) {
return true;
}
if (ent.equalsIgnoreCase("npc")) {
return this.isCitizensNPC();
}
if (ent.equalsIgnoreCase("player")) {
return this.isPlayer();
}
if (ent.equalsIgnoreCase("vehicle")) {
return entity instanceof Vehicle;
}
if (ent.equalsIgnoreCase("projectile")) {
return entity instanceof Projectile;
}
if (ent.equalsIgnoreCase("hanging")) {
return entity instanceof Hanging;
}
if (ent.equalsIgnoreCase(getName())) {
return true;
}
if (ent.equalsIgnoreCase(entity_type.getLowercaseName())) {
return true;
}
if (entity != null && getEntityScript() != null) {
return ent.equalsIgnoreCase(getEntityScript());
}
if (uuid != null && uuid.toString().equals(ent)) {
return true;
}
return false;
}
@Override
public String getAttribute(Attribute attribute) {
if (attribute == null) {
return null;
}
if (entity == null && entity_type == null) {
if (npc != null) {
return new Element(identify()).getAttribute(attribute);
}
dB.echoError("dEntity has returned null.");
return null;
}
/////////////////////
// DEBUG ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.debug.log>
// @returns Element(Boolean)
// @group debug
// @description
// Debugs the entity in the log and returns true.
// -->
if (attribute.startsWith("debug.log")) {
dB.log(debug());
return new Element(Boolean.TRUE.toString())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <e@entity.debug.no_color>
// @returns Element
// @group debug
// @description
// Returns the entity's debug with no color.
// -->
if (attribute.startsWith("debug.no_color")) {
return new Element(ChatColor.stripColor(debug()))
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <e@entity.debug>
// @returns Element
// @group debug
// @description
// Returns the entity's debug.
// -->
if (attribute.startsWith("debug")) {
return new Element(debug())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.prefix>
// @returns Element
// @group debug
// @description
// Returns the prefix of the entity.
// -->
if (attribute.startsWith("prefix")) {
return new Element(prefix)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.type>
// @returns Element
// @description
// Always returns 'Entity' for dEntity 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("Entity").getAttribute(attribute.fulfill(1));
}
/////////////////////
// UNSPAWNED ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.entity_type>
// @returns Element
// @group data
// @description
// Returns the type of the entity.
// -->
if (attribute.startsWith("entity_type")) {
return new Element(entity_type.getName()).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_spawned>
// @returns Element(Boolean)
// @group data
// @description
// Returns whether the entity is spawned.
// -->
if (attribute.startsWith("is_spawned")) {
return new Element(isSpawned())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.eid>
// @returns Element(Number)
// @group data
// @description
// Returns the entity's temporary server entity ID.
// -->
if (attribute.startsWith("eid")) {
return new Element(entity.getEntityId())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.uuid>
// @returns Element
// @group data
// @description
// Returns the permanent unique ID of the entity.
// Works with offline players.
// -->
if (attribute.startsWith("uuid")) {
return new Element(getUUID().toString())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.scriptname>
// @returns Element(Boolean)
// @group data
// @description
// Returns the name of the entity script that spawned this entity, if any.
// -->
if (attribute.startsWith("script")) {
// TODO: Maybe return legit null?
return new Element(entityScript == null ? "null" : entityScript)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.has_flag[<flag_name>]>
// @returns Element(Boolean)
// @description
// Returns true if the entity has the specified flag, otherwise returns false.
// -->
if (attribute.startsWith("has_flag")) {
String flag_name;
if (attribute.hasContext(1)) {
flag_name = attribute.getContext(1);
}
else {
return null;
}
if (isPlayer() || isCitizensNPC()) {
dB.echoError("Reading flag for PLAYER or NPC as if it were an ENTITY!");
return null;
}
return new Element(FlagManager.entityHasFlag(this, flag_name)).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.flag[<flag_name>]>
// @returns Flag dList
// @description
// Returns the specified flag from the entity.
// -->
if (attribute.startsWith("flag")) {
String flag_name;
if (attribute.hasContext(1)) {
flag_name = attribute.getContext(1);
}
else {
return null;
}
if (isPlayer() || isCitizensNPC()) {
dB.echoError("Reading flag for PLAYER or NPC as if it were an ENTITY!");
return null;
}
if (attribute.getAttribute(2).equalsIgnoreCase("is_expired")
|| attribute.startsWith("isexpired")) {
return new Element(!FlagManager.entityHasFlag(this, flag_name))
.getAttribute(attribute.fulfill(2));
}
if (attribute.getAttribute(2).equalsIgnoreCase("size") && !FlagManager.entityHasFlag(this, flag_name)) {
return new Element(0).getAttribute(attribute.fulfill(2));
}
if (FlagManager.entityHasFlag(this, flag_name)) {
FlagManager.Flag flag = DenizenAPI.getCurrentInstance().flagManager().getEntityFlag(this, flag_name);
return new dList(flag.toString(), true, flag.values()).getAttribute(attribute.fulfill(1));
}
return new Element(identify()).getAttribute(attribute);
}
// <--[tag]
// @attribute <e@entity.list_flags[(regex:)<search>]>
// @returns dList
// @description
// Returns a list of an entity's flag names, with an optional search for
// names containing a certain pattern.
// -->
if (attribute.startsWith("list_flags")) {
dList allFlags = new dList(DenizenAPI.getCurrentInstance().flagManager().listEntityFlags(this));
dList searchFlags = null;
if (!allFlags.isEmpty() && attribute.hasContext(1)) {
searchFlags = new dList();
String search = attribute.getContext(1);
if (search.startsWith("regex:")) {
try {
Pattern pattern = Pattern.compile(search.substring(6), Pattern.CASE_INSENSITIVE);
for (String flag : allFlags) {
if (pattern.matcher(flag).matches()) {
searchFlags.add(flag);
}
}
}
catch (Exception e) {
dB.echoError(e);
}
}
else {
search = CoreUtilities.toLowerCase(search);
for (String flag : allFlags) {
if (CoreUtilities.toLowerCase(flag).contains(search)) {
searchFlags.add(flag);
}
}
}
}
return searchFlags == null ? allFlags.getAttribute(attribute.fulfill(1))
: searchFlags.getAttribute(attribute.fulfill(1));
}
if (entity == null) {
return new Element(identify()).getAttribute(attribute);
}
// Only spawned entities past this point!
/////////////////////
// IDENTIFICATION ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.custom_id>
// @returns dScript/Element
// @group data
// @description
// If the entity has a script ID, returns the dScript of that ID.
// Otherwise, returns the name of the entity type.
// -->
if (attribute.startsWith("custom_id")) {
if (CustomNBT.hasCustomNBT(getLivingEntity(), "denizen-script-id")) {
return new dScript(CustomNBT.getCustomNBT(getLivingEntity(), "denizen-script-id"))
.getAttribute(attribute.fulfill(1));
}
else {
return new Element(entity.getType().name())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.name>
// @returns Element
// @group data
// @description
// Returns the name of the entity.
// This can be a player name, an NPC name, a custom_name, or the entity type.
// Works with offline players.
// -->
if (attribute.startsWith("name")) {
return new Element(getName()).getAttribute(attribute.fulfill(1));
}
/////////////////////
// INVENTORY ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.saddle>
// @returns dItem
// @group inventory
// @description
// If the entity is a horse or pig, returns the saddle as a dItem, or i@air if none.
// -->
if (attribute.startsWith("saddle")) {
if (getLivingEntity().getType() == EntityType.HORSE) {
return new dItem(((Horse) getLivingEntity()).getInventory().getSaddle())
.getAttribute(attribute.fulfill(1));
}
else if (getLivingEntity().getType() == EntityType.PIG) {
return new dItem(((Pig) getLivingEntity()).hasSaddle() ? Material.SADDLE : Material.AIR)
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.horse_armor>
// @returns dItem
// @group inventory
// @description
// If the entity is a horse, returns the item equipped as the horses armor, or i@air if none.
// -->
if (attribute.startsWith("horse_armor") || attribute.startsWith("horse_armour")) {
if (getLivingEntity().getType() == EntityType.HORSE) {
return new dItem(((Horse) getLivingEntity()).getInventory().getArmor())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.has_saddle>
// @returns Element(Boolean)
// @group inventory
// @description
// If the entity s a pig or horse, returns whether it has a saddle equipped.
// -->
if (attribute.startsWith("has_saddle")) {
if (getLivingEntity().getType() == EntityType.HORSE) {
return new Element(((Horse) getLivingEntity()).getInventory().getSaddle().getType() == Material.SADDLE)
.getAttribute(attribute.fulfill(1));
}
else if (getLivingEntity().getType() == EntityType.PIG) {
return new Element(((Pig) getLivingEntity()).hasSaddle())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.item_in_hand>
// @returns dItem
// @group inventory
// @description
// Returns the item the entity is holding, or i@air if none.
// -->
if (attribute.startsWith("item_in_hand") ||
attribute.startsWith("iteminhand")) {
return new dItem(NMSHandler.getInstance().getEntityHelper().getItemInHand(getLivingEntity()))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.item_in_offhand>
// @returns dItem
// @group inventory
// @description
// Returns the item the entity is holding in their off hand, or i@air if none.
// -->
if (attribute.startsWith("item_in_offhand") ||
attribute.startsWith("iteminoffhand")) {
return new dItem(NMSHandler.getInstance().getEntityHelper().getItemInOffHand(getLivingEntity()))
.getAttribute(attribute.fulfill(1));
}
/////////////////////
// LOCATION ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.map_trace>
// @returns dLocation
// @group location
// @description
// Returns a 2D location indicating where on the map the entity's looking at.
// Each coordinate is in the range of 0 to 128.
// -->
if (attribute.startsWith("map_trace")) {
EntityHelper.MapTraceResult mtr = NMSHandler.getInstance().getEntityHelper().mapTrace(getLivingEntity(), 200);
if (mtr != null) {
double x = 0;
double y = 0;
double basex = mtr.hitLocation.getX() - Math.floor(mtr.hitLocation.getX());
double basey = mtr.hitLocation.getY() - Math.floor(mtr.hitLocation.getY());
double basez = mtr.hitLocation.getZ() - Math.floor(mtr.hitLocation.getZ());
if (mtr.angle == BlockFace.NORTH) {
x = 128f - (basex * 128f);
}
else if (mtr.angle == BlockFace.SOUTH) {
x = basex * 128f;
}
else if (mtr.angle == BlockFace.WEST) {
x = basez * 128f;
}
else if (mtr.angle == BlockFace.EAST) {
x = 128f - (basez * 128f);
}
y = 128f - (basey * 128f);
return new dLocation(null, Math.round(x), Math.round(y)).getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.can_see[<entity>]>
// @returns Element(Boolean)
// @group location
// @description
// Returns whether the entity can see the specified other entity.
// -->
if (attribute.startsWith("can_see")) {
if (isLivingEntity() && attribute.hasContext(1) && dEntity.matches(attribute.getContext(1))) {
dEntity toEntity = dEntity.valueOf(attribute.getContext(1));
if (toEntity != null && toEntity.isSpawned()) {
return new Element(getLivingEntity().hasLineOfSight(toEntity.getBukkitEntity())).getAttribute(attribute.fulfill(1));
}
}
}
// <--[tag]
// @attribute <e@entity.eye_location>
// @returns dLocation
// @group location
// @description
// Returns the location of the entity's eyes.
// -->
if (attribute.startsWith("eye_location")) {
return new dLocation(getEyeLocation())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.eye_height>
// @returns Element(Boolean)
// @group location
// @description
// Returns the height of the entity's eyes above its location.
// -->
if (attribute.startsWith("eye_height")) {
if (isLivingEntity()) {
return new Element(getLivingEntity().getEyeHeight())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.location.cursor_on[<range>]>
// @returns dLocation
// @group location
// @description
// Returns the location of the block the entity is looking at.
// Optionally, specify a maximum range to find the location from.
// -->
// <--[tag]
// @attribute <e@entity.location.cursor_on[<range>].ignore[<material>|...]>
// @returns dLocation
// @group location
// @description
// Returns the location of the block the entity is looking at, ignoring
// the specified materials along the way. Note that air is always an
// ignored material.
// Optionally, specify a maximum range to find the location from.
// -->
if (attribute.startsWith("location.cursor_on")) {
int range = attribute.getIntContext(2);
if (range < 1) {
range = 50;
}
Set<Material> set = new HashSet<Material>();
set.add(Material.AIR);
attribute = attribute.fulfill(2);
if (attribute.startsWith("ignore") && attribute.hasContext(1)) {
List<dMaterial> ignoreList = dList.valueOf(attribute.getContext(1)).filter(dMaterial.class);
for (dMaterial material : ignoreList) {
set.add(material.getMaterial());
}
attribute = attribute.fulfill(1);
}
return new dLocation(getLivingEntity().getTargetBlock(set, range).getLocation()).getAttribute(attribute);
}
// <--[tag]
// @attribute <e@entity.location.standing_on>
// @returns dLocation
// @group location
// @description
// Returns the location of what the entity is standing on.
// Works with offline players.
// -->
if (attribute.startsWith("location.standing_on")) {
return new dLocation(entity.getLocation().clone().add(0, -0.5f, 0))
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <e@entity.location>
// @returns dLocation
// @group location
// @description
// Returns the location of the entity.
// Works with offline players.
// -->
if (attribute.startsWith("location")) {
return new dLocation(entity.getLocation())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.velocity>
// @returns dLocation
// @group location
// @description
// Returns the movement velocity of the entity.
// Note: Does not accurately calculate player clientside movement velocity.
// -->
if (attribute.startsWith("velocity")) {
return new dLocation(entity.getVelocity().toLocation(entity.getWorld()))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.world>
// @returns dWorld
// @group location
// @description
// Returns the world the entity is in.
// -->
if (attribute.startsWith("world")) {
return new dWorld(entity.getWorld())
.getAttribute(attribute.fulfill(1));
}
/////////////////////
// STATE ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.can_pickup_items>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity can pick up items.
// -->
if (attribute.startsWith("can_pickup_items")) {
if (isLivingEntity()) {
return new Element(getLivingEntity().getCanPickupItems())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.fallingblock_material>
// @returns dMaterial
// @group attributes
// @description
// Returns the material of a fallingblock-type entity.
// -->
if (attribute.startsWith("fallingblock_material")) {
return dMaterial.getMaterialFrom(((FallingBlock) entity).getMaterial())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.fall_distance>
// @returns Element(Decimal)
// @group attributes
// @description
// Returns how far the entity has fallen.
// -->
if (attribute.startsWith("fall_distance")) {
return new Element(entity.getFallDistance())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.fire_time>
// @returns Duration
// @group attributes
// @description
// Returns the duration for which the entity will remain on fire
// -->
if (attribute.startsWith("fire_time")) {
return new Duration(entity.getFireTicks() / 20)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.on_fire>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns if the entity is currently ablaze or not.
// -->
if (attribute.startsWith("on_fire")) {
return new Element(entity.getFireTicks() > 0).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.leash_holder>
// @returns dEntity
// @group attributes
// @description
// Returns the leash holder of entity.
// -->
if (attribute.startsWith("leash_holder") || attribute.startsWith("get_leash_holder")) {
if (isLivingEntity() && getLivingEntity().isLeashed()) {
return new dEntity(getLivingEntity().getLeashHolder())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.passenger>
// @returns dEntity
// @group attributes
// @description
// Returns the entity's passenger, if any.
// -->
if (attribute.startsWith("passenger") || attribute.startsWith("get_passenger")) {
if (!entity.isEmpty()) {
return new dEntity(entity.getPassenger())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.shooter>
// @returns dEntity
// @group attributes
// @Mechanism dEntity.shooter
// @description
// Returns the entity's shooter, if any.
// -->
if (attribute.startsWith("shooter") ||
attribute.startsWith("get_shooter")) {
if (isProjectile() && hasShooter()) {
return getShooter().getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.vehicle>
// @returns dEntity
// @group attributes
// @description
// If the entity is in a vehicle, returns the vehicle as a dEntity.
// -->
if (attribute.startsWith("vehicle") || attribute.startsWith("get_vehicle")) {
if (entity.isInsideVehicle()) {
return new dEntity(entity.getVehicle())
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.has_effect[<effect>]>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity has a specified effect.
// If no effect is specified, returns whether the entity has any effect.
// -->
if (attribute.startsWith("has_effect")) {
boolean returnElement = false;
if (attribute.hasContext(1)) {
PotionEffectType effectType = PotionEffectType.getByName(attribute.getContext(1));
for (org.bukkit.potion.PotionEffect effect : getLivingEntity().getActivePotionEffects()) {
if (effect.getType().equals(effectType)) {
returnElement = true;
}
}
}
else if (!getLivingEntity().getActivePotionEffects().isEmpty()) {
returnElement = true;
}
return new Element(returnElement).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.can_breed>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the animal entity is capable of mating with another of its kind.
// -->
if (attribute.startsWith("can_breed")) {
return new Element(((Ageable) getLivingEntity()).canBreed())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.breeding>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the animal entity is trying to with another of its kind.
// -->
if (attribute.startsWith("breeding") || attribute.startsWith("is_breeding")) {
return new Element(NMSHandler.getInstance().getEntityHelper().isBreeding((Animals) getLivingEntity()))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.has_passenger>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity has a passenger.
// -->
if (attribute.startsWith("has_passenger")) {
return new Element(!entity.isEmpty())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_empty>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity does not have a passenger.
// -->
if (attribute.startsWith("empty") || attribute.startsWith("is_empty")) {
return new Element(entity.isEmpty())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_inside_vehicle>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity is inside a vehicle.
// -->
if (attribute.startsWith("inside_vehicle") || attribute.startsWith("is_inside_vehicle")) {
return new Element(entity.isInsideVehicle())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_leashed>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity is leashed.
// -->
if (attribute.startsWith("leashed") || attribute.startsWith("is_leashed")) {
if (isLivingEntity()) {
return new Element(getLivingEntity().isLeashed())
.getAttribute(attribute.fulfill(1));
}
else {
return Element.FALSE
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.is_on_ground>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity is supported by a block.
// -->
if (attribute.startsWith("on_ground") || attribute.startsWith("is_on_ground")) {
return new Element(entity.isOnGround())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_persistent>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns whether the entity will not be removed completely when far away from players.
// -->
if (attribute.startsWith("persistent") || attribute.startsWith("is_persistent")) {
if (isLivingEntity()) {
return new Element(!getLivingEntity().getRemoveWhenFarAway())
.getAttribute(attribute.fulfill(1));
}
else {
return Element.FALSE
.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.killer>
// @returns dPlayer
// @group attributes
// @description
// Returns the player that last killed the entity.
// -->
if (attribute.startsWith("killer")) {
return getPlayerFrom(getLivingEntity().getKiller())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.last_damage.amount>
// @returns Element(Decimal)
// @group attributes
// @description
// Returns the amount of the last damage taken by the entity.
// -->
if (attribute.startsWith("last_damage.amount")) {
return new Element(getLivingEntity().getLastDamage())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <e@entity.last_damage.cause>
// @returns Element
// @group attributes
// @description
// Returns the cause of the last damage taken by the entity.
// -->
if (attribute.startsWith("last_damage.cause")
&& entity.getLastDamageCause() != null) {
return new Element(entity.getLastDamageCause().getCause().name())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <e@entity.last_damage.duration>
// @returns Duration
// @group attributes
// @description
// Returns the duration of the last damage taken by the entity.
// -->
if (attribute.startsWith("last_damage.duration")) {
return new Duration((long) getLivingEntity().getNoDamageTicks())
.getAttribute(attribute.fulfill(2));
}
// <--[tag]
// @attribute <e@entity.oxygen.max>
// @returns Duration
// @group attributes
// @description
// Returns the maximum duration of oxygen the entity can have.
// Works with offline players.
// -->
if (attribute.startsWith("oxygen.max")) {
return new Duration((long) getLivingEntity().getMaximumAir())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.oxygen>
// @returns Duration
// @group attributes
// @description
// Returns the duration of oxygen the entity has left.
// Works with offline players.
// -->
if (attribute.startsWith("oxygen")) {
return new Duration((long) getLivingEntity().getRemainingAir())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.remove_when_far>
// @returns Element(Boolean)
// @group attributes
// @description
// Returns if the entity despawns when away from players.
// -->
if (attribute.startsWith("remove_when_far")) {
return new Element(getLivingEntity().getRemoveWhenFarAway())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.target>
// @returns dEntity
// @group attributes
// @description
// Returns the target entity of the creature, if any.
// Note: use <n@npc.navigator.target_entity> for NPC's.
// -->
if (attribute.startsWith("target")) {
if (getBukkitEntity() instanceof Creature) {
Entity target = ((Creature) getLivingEntity()).getTarget();
if (target != null) {
return new dEntity(target).getAttribute(attribute.fulfill(1));
}
}
return null;
}
// <--[tag]
// @attribute <e@entity.time_lived>
// @returns Duration
// @group attributes
// @description
// Returns how long the entity has lived.
// -->
if (attribute.startsWith("time_lived")) {
return new Duration(entity.getTicksLived() / 20)
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.pickup_delay>
// @returns Duration
// @group attributes
// @description
// Returns how long the entity has lived.
// -->
if ((attribute.startsWith("pickup_delay") || attribute.startsWith("pickupdelay"))
&& getBukkitEntity() instanceof Item) {
return new Duration(((Item) getBukkitEntity()).getPickupDelay() * 20).getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.gliding>
// @returns Element(Boolean)
// @mechanism dEntity.gliding
// @group attributes
// @description
// Returns whether this entity is gliding.
// -->
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_9_R2) && attribute.startsWith("gliding")) {
return new Element(getLivingEntity().isGliding())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.glowing>
// @returns Element(Boolean)
// @mechanism dEntity.glowing
// @group attributes
// @description
// Returns whether this entity is glowing.
// -->
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_9_R2) && attribute.startsWith("glowing")) {
return new Element(getBukkitEntity().isGlowing())
.getAttribute(attribute.fulfill(1));
}
/////////////////////
// TYPE ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.is_living>
// @returns Element(Boolean)
// @group data
// @description
// Returns whether the entity is a living entity.
// -->
if (attribute.startsWith("is_living")) {
return new Element(isLivingEntity())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_mob>
// @returns Element(Boolean)
// @group data
// @description
// Returns whether the entity is a mob (Not a player or NPC).
// -->
if (attribute.startsWith("is_mob")) {
if (!isPlayer() && !isNPC()) {
return Element.TRUE.getAttribute(attribute.fulfill(1));
}
else {
return Element.FALSE.getAttribute(attribute.fulfill(1));
}
}
// <--[tag]
// @attribute <e@entity.is_npc>
// @returns Element(Boolean)
// @group data
// @description
// Returns whether the entity is a Citizens NPC.
// -->
if (attribute.startsWith("is_npc")) {
return new Element(isCitizensNPC())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_player>
// @returns Element(Boolean)
// @group data
// @description
// Returns whether the entity is a player.
// Works with offline players.
// -->
if (attribute.startsWith("is_player")) {
return new Element(isPlayer())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.is_projectile>
// @returns Element(Boolean)
// @group data
// @description
// Returns whether the entity is a projectile.
// -->
if (attribute.startsWith("is_projectile")) {
return new Element(isProjectile())
.getAttribute(attribute.fulfill(1));
}
/////////////////////
// PROPERTY ATTRIBUTES
/////////////////
// <--[tag]
// @attribute <e@entity.tameable>
// @returns Element(Boolean)
// @group properties
// @description
// Returns whether the entity is tameable.
// If this returns true, it will enable access to:
// <@link mechanism dEntity.tame>, <@link mechanism dEntity.owner>,
// <@link tag e@entity.is_tamed>, and <@link tag e@entity.get_owner>
// -->
if (attribute.startsWith("tameable") || attribute.startsWith("is_tameable")) {
return new Element(EntityTame.describes(this))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.ageable>
// @returns Element(Boolean)
// @group properties
// @description
// Returns whether the entity is ageable.
// If this returns true, it will enable access to:
// <@link mechanism dEntity.age>, <@link mechanism dEntity.age_lock>,
// <@link tag e@entity.is_baby>, <@link tag e@entity.age>,
// and <@link tag e@entity.is_age_locked>
// -->
if (attribute.startsWith("ageable") || attribute.startsWith("is_ageable")) {
return new Element(EntityAge.describes(this))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.colorable>
// @returns Element(Boolean)
// @group properties
// @description
// Returns whether the entity can be colored.
// If this returns true, it will enable access to:
// <@link mechanism dEntity.color> and <@link tag e@entity.color>
// -->
if (attribute.startsWith("colorable") || attribute.startsWith("is_colorable")) {
return new Element(EntityColor.describes(this))
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.experience>
// @returns Element(Number)
// @group properties
// @description
// Returns the experience value of this experience orb entity.
// -->
if (attribute.startsWith("experience") && getBukkitEntity() instanceof ExperienceOrb) {
return new Element(((ExperienceOrb) getBukkitEntity()).getExperience())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.dragon_phase>
// @returns Element(Number)
// @group properties
// @description
// Returns the phase an EnderDragon is currently in.
// Valid phases: CIRCLING, STRAFING, FLY_TO_PORTAL, LAND_ON_PORTAL, LEAVE_PORTAL,
// BREATH_ATTACK, SEARCH_FOR_BREATH_ATTACK_TARGET, ROAR_BEFORE_ATTACK,
// CHARGE_PLAYER, DYING, HOVER.
// -->
if (attribute.startsWith("dragon_phase") && getBukkitEntity() instanceof EnderDragon) {
return new Element(((EnderDragon) getLivingEntity()).getPhase().name())
.getAttribute(attribute.fulfill(1));
}
// <--[tag]
// @attribute <e@entity.describe>
// @returns Element(Boolean)
// @group properties
// @description
// Returns the entity's full description, including all properties.
// -->
if (attribute.startsWith("describe")) {
String escript = getEntityScript();
return new Element("e@" + (escript != null && escript.length() > 0 ? escript : getEntityType().getLowercaseName())
+ PropertyParser.getPropertiesString(this))
.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);
}
private ArrayList<Mechanism> mechanisms = new ArrayList<Mechanism>();
public ArrayList<Mechanism> getWaitingMechanisms() {
return mechanisms;
}
public void applyProperty(Mechanism mechanism) {
if (isGeneric()) {
mechanisms.add(mechanism);
}
else if (rememberedEntities.containsKey(entity.getUniqueId())) {
adjust(mechanism);
}
else {
dB.echoError("Cannot apply properties to an already-spawned entity!");
}
}
@Override
public void adjust(Mechanism mechanism) {
if (isGeneric()) {
mechanisms.add(mechanism);
return;
}
Element value = mechanism.getValue();
// <--[mechanism]
// @object dEntity
// @name item_in_hand
// @input dItem
// @description
// Sets the item in the entity's hand.
// The entity must be living.
// @tags
// <e@entity.item_in_hand>
// -->
if (mechanism.matches("item_in_hand")) {
NMSHandler.getInstance().getEntityHelper().setItemInHand(getLivingEntity(), value.asType(dItem.class).getItemStack());
}
// <--[mechanism]
// @object dEntity
// @name item_in_offhand
// @input dItem
// @description
// Sets the item in the entity's offhand.
// The entity must be living.
// @tags
// <e@entity.item_in_offhand>
// -->
if (mechanism.matches("item_in_offhand")) {
NMSHandler.getInstance().getEntityHelper().setItemInOffHand(getLivingEntity(), value.asType(dItem.class).getItemStack());
}
// <--[mechanism]
// @object dEntity
// @name shooter
// @input dEntity
// @description
// Sets the entity's shooter.
// The entity must be a projectile.
// @tags
// <e@entity.shooter>
// -->
if (mechanism.matches("shooter")) {
setShooter(value.asType(dEntity.class));
}
// <--[mechanism]
// @object dEntity
// @name can_pickup_items
// @input Element(Boolean)
// @description
// Sets whether the entity can pick up items.
// The entity must be living.
// @tags
// <e@entity.can_pickup_items>
// -->
if (mechanism.matches("can_pickup_items") && mechanism.requireBoolean()) {
getLivingEntity().setCanPickupItems(value.asBoolean());
}
// <--[mechanism]
// @object dEntity
// @name fall_distance
// @input Element(Float)
// @description
// Sets the fall distance.
// @tags
// <e@entity.fall_distance>
// -->
if (mechanism.matches("fall_distance") && mechanism.requireFloat()) {
entity.setFallDistance(value.asFloat());
}
// <--[mechanism]
// @object dEntity
// @name fire_time
// @input Duration
// @description
// Sets the entity's current fire time (time before the entity stops being on fire).
// @tags
// <e@entity.fire_time>
// -->
if (mechanism.matches("fire_time") && mechanism.requireObject(Duration.class)) {
entity.setFireTicks(value.asType(Duration.class).getTicksAsInt());
}
// <--[mechanism]
// @object dEntity
// @name leash_holder
// @input dEntity
// @description
// Sets the entity holding this entity by leash.
// The entity must be living.
// @tags
// <e@entity.leashed>
// <e@entity.leash_holder>
// -->
if (mechanism.matches("leash_holder") && mechanism.requireObject(dEntity.class)) {
getLivingEntity().setLeashHolder(value.asType(dEntity.class).getBukkitEntity());
}
// <--[mechanism]
// @object dEntity
// @name can_breed
// @input Element(Boolean)
// @description
// Sets whether the entity is capable of mating with another of its kind.
// The entity must be living and 'ageable'.
// @tags
// <e@entity.can_breed>
// -->
if (mechanism.matches("can_breed") && mechanism.requireBoolean()) {
((Ageable) getLivingEntity()).setBreed(true);
}
// <--[mechanism]
// @object dEntity
// @name breed
// @input Element(Boolean)
// @description
// Sets whether the entity is trying to mate with another of its kind.
// The entity must be living and an animal.
// @tags
// <e@entity.can_breed>
// -->
if (mechanism.matches("breed") && mechanism.requireBoolean()) {
NMSHandler.getInstance().getEntityHelper().setBreeding((Animals) getLivingEntity(), value.asBoolean());
}
// <--[mechanism]
// @object dEntity
// @name passenger
// @input dEntity
// @description
// Sets the passenger of this entity.
// @tags
// <e@entity.passenger>
// <e@entity.empty>
// -->
if (mechanism.matches("passenger") && mechanism.requireObject(dEntity.class)) {
entity.setPassenger(value.asType(dEntity.class).getBukkitEntity());
}
// <--[mechanism]
// @object dEntity
// @name time_lived
// @input Duration
// @description
// Sets the amount of time this entity has lived for.
// @tags
// <e@entity.time_lived>
// -->
if (mechanism.matches("time_lived") && mechanism.requireObject(Duration.class)) {
entity.setTicksLived(value.asType(Duration.class).getTicksAsInt());
}
// <--[mechanism]
// @object dEntity
// @name remaining_air
// @input Element(Number)
// @description
// Sets how much air the entity has remaining before it drowns.
// The entity must be living.
// @tags
// <e@entity.oxygen>
// <e@entity.oxygen.max>
// -->
if (mechanism.matches("remaining_air") && mechanism.requireInteger()) {
getLivingEntity().setRemainingAir(value.asInt());
}
// <--[mechanism]
// @object dEntity
// @name remove_effects
// @input None
// @description
// Removes all potion effects from the entity.
// The entity must be living.
// @tags
// <e@entity.has_effect[<effect>]>
// -->
if (mechanism.matches("remove_effects")) {
for (PotionEffect potionEffect : this.getLivingEntity().getActivePotionEffects()) {
getLivingEntity().removePotionEffect(potionEffect.getType());
}
}
// <--[mechanism]
// @object dEntity
// @name remove_when_far_away
// @input Element(Boolean)
// @description
// Sets whether the entity should be removed entirely when despawned.
// The entity must be living.
// @tags
// <e@entity.remove_when_far>
// -->
if (mechanism.matches("remove_when_far_away") && mechanism.requireBoolean()) {
getLivingEntity().setRemoveWhenFarAway(value.asBoolean());
}
// <--[mechanism]
// @object dEntity
// @name velocity
// @input dLocation
// @description
// Sets the entity's movement velocity.
// @tags
// <e@entity.velocity>
// -->
if (mechanism.matches("velocity") && mechanism.requireObject(dLocation.class)) {
setVelocity(value.asType(dLocation.class).toVector());
}
// <--[mechanism]
// @object dEntity
// @name move
// @input dLocation
// @description
// Forces an entity to move in the direction of the velocity specified.
// -->
if (mechanism.matches("move") && mechanism.requireObject(dLocation.class)) {
NMSHandler.getInstance().getEntityHelper().move(getBukkitEntity(), value.asType(dLocation.class).toVector());
}
// <--[mechanism]
// @object dEntity
// @name interact_with
// @input dLocation
// @description
// Makes a player-type entity interact with a block.
// @tags
// None
// -->
if (mechanism.matches("interact_with") && mechanism.requireObject(dLocation.class)) {
dLocation interactLocation = value.asType(dLocation.class);
NMSHandler.getInstance().getEntityHelper().forceInteraction(getPlayer(), interactLocation);
}
// <--[mechanism]
// @object dEntity
// @name play_death
// @input None
// @description
// Animates the entity dying.
// @tags
// None
// -->
if (mechanism.matches("play_death")) {
getLivingEntity().playEffect(EntityEffect.DEATH);
}
// <--[mechanism]
// @object dEntity
// @name pickup_delay
// @input Duration
// @description
// Sets the pickup delay of this Item Entity.
// @tags
// <e@entity.pickup_delay>
// -->
if ((mechanism.matches("pickup_delay") || mechanism.matches("pickupdelay")) &&
getBukkitEntity() instanceof Item && mechanism.requireObject(Duration.class)) {
((Item) getBukkitEntity()).setPickupDelay(value.asType(Duration.class).getTicksAsInt());
}
// <--[mechanism]
// @object dEntity
// @name gliding
// @input Element(Boolean)
// @description
// Sets whether this entity is gliding.
// @tags
// <e@entity.gliding>
// -->
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_9_R2) && mechanism.matches("gliding") && mechanism.requireBoolean()) {
getLivingEntity().setGliding(value.asBoolean());
}
// <--[mechanism]
// @object dEntity
// @name glowing
// @input Element(Boolean)
// @description
// Sets whether this entity is glowing.
// @tags
// <e@entity.glowing>
// -->
if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_9_R2) && mechanism.matches("glowing") && mechanism.requireBoolean()) {
getBukkitEntity().setGlowing(value.asBoolean());
if (Depends.citizens != null && CitizensAPI.getNPCRegistry().isNPC(getLivingEntity())) {
CitizensAPI.getNPCRegistry().getNPC(getLivingEntity()).data().setPersistent(NPC.GLOWING_METADATA, value.asBoolean());
}
}
// <--[mechanism]
// @object dEntity
// @name dragon_phase
// @input Element
// @description
// Sets an EnderDragon's combat phase.
// @tags
// <e@entity.dragon_phase>
// -->
if (mechanism.matches("dragon_phase")) {
EnderDragon ed = (EnderDragon) getLivingEntity();
ed.setPhase(EnderDragon.Phase.valueOf(value.asString().toUpperCase()));
}
// <--[mechanism]
// @object dEntity
// @name experience
// @input Element(Number)
// @description
// Sets the experience value of this experience orb entity.
// @tags
// <e@entity.experience>
// -->
if (mechanism.matches("experience") && getBukkitEntity() instanceof ExperienceOrb && mechanism.requireInteger()) {
((ExperienceOrb) getBukkitEntity()).setExperience(value.asInt());
}
// Iterate through this object's properties' mechanisms
for (Property property : PropertyParser.getProperties(this)) {
property.adjust(mechanism);
if (mechanism.fulfilled()) {
break;
}
}
if (!mechanism.fulfilled()) {
mechanism.reportInvalid();
}
}
}