use of org.bukkit.loot.Lootable in project Denizen-For-Bukkit by DenizenScript.
the class LocationTag method adjust.
@Override
public void adjust(Mechanism mechanism) {
// -->
if (mechanism.matches("block_facing") && mechanism.requireObject(LocationTag.class)) {
LocationTag faceVec = mechanism.valueAsType(LocationTag.class);
Block block = getBlock();
MaterialTag material = new MaterialTag(block);
if (!MaterialDirectional.describes(material)) {
mechanism.echoError("LocationTag.block_facing mechanism failed: block is not directional.");
return;
}
MaterialDirectional.getFrom(material).setFacing(Utilities.faceFor(faceVec.toVector()));
block.setBlockData(material.getModernData());
}
// -->
if (mechanism.matches("block_type") && mechanism.requireObject(MaterialTag.class)) {
MaterialTag mat = mechanism.valueAsType(MaterialTag.class);
getBlock().setBlockData(mat.getModernData(), false);
}
// -->
if (mechanism.matches("biome") && mechanism.requireObject(BiomeTag.class)) {
mechanism.valueAsType(BiomeTag.class).getBiome().setTo(getBlock());
}
// -->
if (mechanism.matches("spawner_custom_rules") && mechanism.requireObject(MapTag.class) && getBlockState() instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
MapTag map = mechanism.valueAsType(MapTag.class);
ObjectTag skyMin = map.getObject("sky_min"), skyMax = map.getObject("sky_max"), blockMin = map.getObject("block_min"), blockMax = map.getObject("block_max");
if (skyMin == null || skyMax == null || blockMin == null || blockMax == null) {
mechanism.echoError("Invalid spawner_custom_rules input, missing map keys.");
return;
}
NMSHandler.getBlockHelper().setSpawnerCustomRules(spawner, Integer.parseInt(skyMin.toString()), Integer.parseInt(skyMax.toString()), Integer.parseInt(blockMin.toString()), Integer.parseInt(blockMax.toString()));
spawner.update();
}
// -->
if (mechanism.matches("spawner_type") && mechanism.requireObject(EntityTag.class) && getBlockState() instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
NMSHandler.getBlockHelper().setSpawnerSpawnedType(spawner, mechanism.valueAsType(EntityTag.class));
spawner.update();
}
// -->
if (mechanism.matches("spawner_delay_data") && getBlockState() instanceof CreatureSpawner) {
ListTag list = mechanism.valueAsType(ListTag.class);
if (list.size() < 3) {
return;
}
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
spawner.setDelay(Integer.parseInt(list.get(0)));
int minDelay = Integer.parseInt(list.get(1));
int maxDelay = Integer.parseInt(list.get(2));
// or new min would be higher than the current max
if (minDelay > spawner.getMaxSpawnDelay()) {
spawner.setMaxSpawnDelay(maxDelay);
spawner.setMinSpawnDelay(minDelay);
} else {
spawner.setMinSpawnDelay(minDelay);
spawner.setMaxSpawnDelay(maxDelay);
}
spawner.update();
}
// -->
if (mechanism.matches("spawner_max_nearby_entities") && mechanism.requireInteger() && getBlockState() instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
spawner.setMaxNearbyEntities(mechanism.getValue().asInt());
spawner.update();
}
// -->
if (mechanism.matches("spawner_player_range") && mechanism.requireInteger() && getBlockState() instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
spawner.setRequiredPlayerRange(mechanism.getValue().asInt());
spawner.update();
}
// -->
if (mechanism.matches("spawner_range") && mechanism.requireInteger() && getBlockState() instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
spawner.setSpawnRange(mechanism.getValue().asInt());
spawner.update();
}
// -->
if (mechanism.matches("spawner_count") && mechanism.requireInteger() && getBlockState() instanceof CreatureSpawner) {
CreatureSpawner spawner = ((CreatureSpawner) getBlockState());
spawner.setSpawnCount(mechanism.getValue().asInt());
spawner.update();
}
// -->
if (mechanism.matches("lock") && getBlockState() instanceof Lockable) {
BlockState state = getBlockState();
((Lockable) state).setLock(mechanism.hasValue() ? mechanism.getValue().asString() : null);
state.update();
}
// -->
if (mechanism.matches("sign_contents") && getBlockState() instanceof Sign) {
Sign state = (Sign) getBlockState();
for (int i = 0; i < 4; i++) {
AdvancedTextImpl.instance.setSignLine(state, i, "");
}
ListTag list = mechanism.valueAsType(ListTag.class);
CoreUtilities.fixNewLinesToListSeparation(list);
if (list.size() > 4) {
Debug.echoError("Sign can only hold four lines!");
} else {
for (int i = 0; i < list.size(); i++) {
AdvancedTextImpl.instance.setSignLine(state, i, list.get(i));
}
}
state.update();
}
// -->
if (mechanism.matches("skull_skin")) {
final BlockState blockState = getBlockState();
Material material = getBlock().getType();
if (blockState instanceof Skull) {
ListTag list = mechanism.valueAsType(ListTag.class);
String idString = list.get(0);
String texture = null;
if (list.size() > 1) {
texture = list.get(1);
}
PlayerProfile profile;
if (idString.contains("-")) {
UUID uuid = UUID.fromString(idString);
String name = null;
if (list.size() > 2) {
name = list.get(2);
}
profile = new PlayerProfile(name, uuid, texture);
} else {
profile = new PlayerProfile(idString, null, texture);
}
profile = NMSHandler.getInstance().fillPlayerProfile(profile);
if (texture != null) {
// Ensure we didn't get overwritten
profile.setTexture(texture);
}
NMSHandler.getBlockHelper().setPlayerProfile((Skull) blockState, profile);
} else {
Debug.echoError("Unable to set skull_skin on block of type " + material.name() + " with state " + blockState.getClass().getCanonicalName());
}
}
// -->
if (mechanism.matches("hive_max_bees") && mechanism.requireInteger()) {
Beehive hive = (Beehive) getBlockState();
hive.setMaxEntities(mechanism.getValue().asInt());
hive.update();
}
// -->
if (mechanism.matches("release_bees")) {
Beehive hive = (Beehive) getBlockState();
hive.releaseEntities();
hive.update();
}
// -->
if (mechanism.matches("add_bee") && mechanism.requireObject(EntityTag.class)) {
Beehive hive = (Beehive) getBlockState();
hive.addEntity((Bee) mechanism.valueAsType(EntityTag.class).getBukkitEntity());
hive.update();
}
// -->
if (mechanism.matches("command_block_name")) {
if (getBlock().getState() instanceof CommandBlock) {
CommandBlock block = ((CommandBlock) getBlockState());
block.setName(mechanism.getValue().asString());
block.update();
}
}
// -->
if (mechanism.matches("command_block")) {
if (getBlock().getState() instanceof CommandBlock) {
CommandBlock block = ((CommandBlock) getBlockState());
block.setCommand(mechanism.getValue().asString());
block.update();
}
}
// -->
if (mechanism.matches("custom_name")) {
if (getBlockState() instanceof Nameable) {
String title = null;
if (mechanism.hasValue()) {
title = mechanism.getValue().asString();
}
BlockState state = getBlockState();
((Nameable) state).setCustomName(title);
state.update(true);
}
}
// -->
if (mechanism.matches("brewing_time")) {
if (getBlockState() instanceof BrewingStand) {
BrewingStand stand = (BrewingStand) getBlockState();
stand.setBrewingTime(mechanism.valueAsType(DurationTag.class).getTicksAsInt());
stand.update();
}
}
// -->
if (mechanism.matches("brewing_fuel_level")) {
if (getBlockState() instanceof BrewingStand) {
BrewingStand stand = (BrewingStand) getBlockState();
stand.setFuelLevel(mechanism.getValue().asInt());
stand.update();
}
}
// -->
if (mechanism.matches("furnace_burn_duration") && mechanism.requireObject(DurationTag.class)) {
if (getBlockState() instanceof Furnace) {
Furnace furnace = (Furnace) getBlockState();
furnace.setBurnTime((short) mechanism.valueAsType(DurationTag.class).getTicks());
furnace.update();
}
}
if (mechanism.matches("furnace_burn_time")) {
Deprecations.furnaceTimeTags.warn(mechanism.context);
if (getBlockState() instanceof Furnace) {
Furnace furnace = (Furnace) getBlockState();
furnace.setBurnTime((short) mechanism.getValue().asInt());
furnace.update();
}
}
// -->
if (mechanism.matches("furnace_cook_duration")) {
if (getBlockState() instanceof Furnace) {
Furnace furnace = (Furnace) getBlockState();
furnace.setCookTime((short) mechanism.valueAsType(DurationTag.class).getTicks());
furnace.update();
}
}
if (mechanism.matches("furnace_cook_time")) {
Deprecations.furnaceTimeTags.warn(mechanism.context);
if (getBlockState() instanceof Furnace) {
Furnace furnace = (Furnace) getBlockState();
furnace.setCookTime((short) mechanism.getValue().asInt());
furnace.update();
}
}
// -->
if (mechanism.matches("furnace_cook_duration_total")) {
if (getBlockState() instanceof Furnace) {
Furnace furnace = (Furnace) getBlockState();
furnace.setCookTimeTotal((short) mechanism.valueAsType(DurationTag.class).getTicks());
furnace.update();
}
}
if (mechanism.matches("furnace_cook_time_total")) {
Deprecations.furnaceTimeTags.warn(mechanism.context);
if (getBlockState() instanceof Furnace) {
Furnace furnace = (Furnace) getBlockState();
furnace.setCookTimeTotal((short) mechanism.getValue().asInt());
furnace.update();
}
}
// -->
if (mechanism.matches("patterns")) {
List<org.bukkit.block.banner.Pattern> patterns = new ArrayList<>();
ListTag list = mechanism.valueAsType(ListTag.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) {
Debug.echoError("Could not apply pattern to banner: " + string);
}
}
Banner banner = (Banner) getBlockState();
banner.setPatterns(patterns);
banner.update();
}
// -->
if (mechanism.matches("head_rotation") && mechanism.requireInteger()) {
Skull sk = (Skull) getBlockState();
sk.setRotation(getSkullBlockFace(mechanism.getValue().asInt() - 1));
sk.update();
}
// -->
if (mechanism.matches("generate_tree") && mechanism.requireEnum(TreeType.class)) {
boolean generated = getWorld().generateTree(this, TreeType.valueOf(mechanism.getValue().asString().toUpperCase()));
if (!generated) {
Debug.echoError("Could not generate tree at " + identifySimple() + ". Make sure this location can naturally generate a tree!");
}
}
// -->
if (mechanism.matches("beacon_primary_effect")) {
Beacon beacon = (Beacon) getBlockState();
beacon.setPrimaryEffect(PotionEffectType.getByName(mechanism.getValue().asString().toUpperCase()));
beacon.update();
}
// -->
if (mechanism.matches("beacon_secondary_effect")) {
Beacon beacon = (Beacon) getBlockState();
beacon.setSecondaryEffect(PotionEffectType.getByName(mechanism.getValue().asString().toUpperCase()));
beacon.update();
}
// -->
if (mechanism.matches("activate")) {
BlockState state = getBlockState();
if (state instanceof Dispenser) {
((Dispenser) state).dispense();
} else if (state instanceof Dropper) {
((Dropper) state).drop();
} else {
Debug.echoError("'activate' mechanism does not work for blocks of type: " + state.getType().name());
}
}
// -->
if (mechanism.matches("lectern_page") && mechanism.requireInteger()) {
BlockState state = getBlockState();
if (state instanceof Lectern) {
((Lectern) state).setPage(mechanism.getValue().asInt());
state.update();
} else {
Debug.echoError("'lectern_page' mechanism can only be called on a lectern block.");
}
}
// -->
if (mechanism.matches("clear_loot_table")) {
BlockState state = getBlockState();
if (state instanceof Lootable) {
((Lootable) state).setLootTable(null);
state.update();
} else {
Debug.echoError("'clear_loot_table' mechanism can only be called on a lootable block (like a chest).");
}
}
// -->
if (mechanism.matches("jukebox_record")) {
BlockState state = getBlockState();
if (state instanceof Jukebox) {
if (mechanism.hasValue() && mechanism.requireObject(ItemTag.class)) {
((Jukebox) state).setRecord(mechanism.valueAsType(ItemTag.class).getItemStack());
} else {
NMSHandler.getBlockHelper().makeBlockStateRaw(state);
((Jukebox) state).setRecord(null);
}
state.update();
} else {
Debug.echoError("'jukebox_record' mechanism can only be called on a jukebox block.");
}
}
// -->
if (mechanism.matches("jukebox_play") && mechanism.requireBoolean()) {
BlockState state = getBlockState();
if (state instanceof Jukebox) {
if (mechanism.getValue().asBoolean()) {
Material mat = ((Jukebox) state).getRecord().getType();
if (mat == Material.AIR) {
Debug.echoError("'jukebox_play' cannot play nothing.");
return;
}
((Jukebox) state).setPlaying(mat);
} else {
((Jukebox) state).stopPlaying();
}
state.update();
} else {
Debug.echoError("'jukebox_play' mechanism can only be called on a jukebox block.");
}
}
// -->
if (mechanism.matches("age") && mechanism.requireObject(DurationTag.class)) {
BlockState state = getBlockState();
if (state instanceof EndGateway) {
((EndGateway) state).setAge(mechanism.valueAsType(DurationTag.class).getTicks());
state.update();
} else {
Debug.echoError("'age' mechanism can only be called on end gateway blocks.");
}
}
// -->
if (mechanism.matches("is_exact_teleport") && mechanism.requireBoolean()) {
BlockState state = getBlockState();
if (state instanceof EndGateway) {
((EndGateway) state).setExactTeleport(mechanism.getValue().asBoolean());
state.update();
} else {
Debug.echoError("'is_exact_teleport' mechanism can only be called on end gateway blocks.");
}
}
// -->
if (mechanism.matches("exit_location") && mechanism.requireObject(LocationTag.class)) {
BlockState state = getBlockState();
if (state instanceof EndGateway) {
((EndGateway) state).setExitLocation(mechanism.valueAsType(LocationTag.class));
state.update();
} else {
Debug.echoError("'exit_location' mechanism can only be called on end gateway blocks.");
}
}
// -->
if (mechanism.matches("vanilla_tick")) {
NMSHandler.getBlockHelper().doRandomTick(this);
}
// -->
if (mechanism.matches("apply_bonemeal") && mechanism.requireEnum(BlockFace.class)) {
getBlock().applyBoneMeal(BlockFace.valueOf(mechanism.getValue().asString().toUpperCase()));
}
// -->
if (mechanism.matches("campfire_items") && mechanism.requireObject(ListTag.class)) {
BlockState state = getBlockState();
if (!(state instanceof Campfire)) {
Debug.echoError("'campfire_items' mechanism can only be called on campfire blocks.");
} else {
Campfire fire = (Campfire) state;
List<ItemTag> list = mechanism.valueAsType(ListTag.class).filter(ItemTag.class, mechanism.context);
for (int i = 0; i < list.size(); i++) {
if (i >= fire.getSize()) {
Debug.echoError("Cannot add item for index " + (i + 1) + " as the campfire can only hold " + fire.getSize() + " items.");
break;
}
fire.setItem(i, list.get(i).getItemStack());
}
fire.update();
}
}
// -->
if (mechanism.matches("ring_bell")) {
BlockState state = getBlockState();
if (!(state instanceof Bell)) {
Debug.echoError("'ring_bell' mechanism can only be called on Bell blocks.");
} else {
NMSHandler.getBlockHelper().ringBell((Bell) state);
}
}
// -->
if (mechanism.matches("sign_glowing") && mechanism.requireBoolean()) {
BlockState state = getBlockState();
if (!(state instanceof Sign)) {
Debug.echoError("'sign_glowing' mechanism can only be called on Sign blocks.");
} else {
Sign sign = (Sign) state;
sign.setGlowingText(mechanism.getValue().asBoolean());
sign.update();
}
}
// -->
if (mechanism.matches("sign_glow_color") && mechanism.requireEnum(DyeColor.class)) {
BlockState state = getBlockState();
if (!(state instanceof Sign)) {
Debug.echoError("'sign_glow_color' mechanism can only be called on Sign blocks.");
} else {
Sign sign = (Sign) state;
sign.setColor(mechanism.getValue().asEnum(DyeColor.class));
sign.update();
}
}
CoreUtilities.autoPropertyMechanism(this, mechanism);
}
use of org.bukkit.loot.Lootable in project Denizen-For-Bukkit by DenizenScript.
the class LocationTag method registerTags.
public static void registerTags() {
AbstractFlagTracker.registerFlagHandlers(tagProcessor);
// <--[tag]
// @attribute <LocationTag.block_facing>
// @returns LocationTag
// @mechanism LocationTag.block_facing
// @group world
// @description
// Returns the relative location vector of where this block is facing.
// Only works for block types that have directionality (such as signs, chests, stairs, etc.).
// This can return for example "1,0,0" to mean the block is facing towards the positive X axis.
// You can use <some_block_location.add[<some_block_location.block_facing>]> to get the block directly in front of this block (based on its facing direction).
// -->
tagProcessor.registerTag(LocationTag.class, "block_facing", (attribute, object) -> {
Block block = object.getBlockForTag(attribute);
MaterialTag material = new MaterialTag(block);
if (!MaterialDirectional.describes(material)) {
return null;
}
Vector vec = MaterialDirectional.getFrom(material).getDirectionVector();
if (vec == null) {
return null;
}
return new LocationTag(object.getWorld(), vec);
});
// <--[tag]
// @attribute <LocationTag.with_facing_direction>
// @returns LocationTag
// @group world
// @description
// Returns the location with its direction set to the block's facing direction.
// Only works for block types that have directionality (such as signs, chests, stairs, etc.).
// You can use <some_block_location.with_facing_direction.forward[1]> to get the block directly in front of this block (based on its facing direction).
// -->
tagProcessor.registerTag(LocationTag.class, "with_facing_direction", (attribute, object) -> {
Block block = object.getBlockForTag(attribute);
MaterialTag material = new MaterialTag(block);
if (!MaterialDirectional.describes(material)) {
return null;
}
Vector facing = MaterialDirectional.getFrom(material).getDirectionVector();
if (facing == null) {
return null;
}
LocationTag result = object.clone();
result.setDirection(facing);
return result;
});
// <--[tag]
// @attribute <LocationTag.above[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location above this location. Optionally specify a number of blocks to go up.
// This just moves straight along the Y axis, equivalent to <@link tag LocationTag.add> with input 0,1,0 (or the input value instead of '1').
// -->
tagProcessor.registerTag(LocationTag.class, "above", (attribute, object) -> {
return new LocationTag(object.clone().add(0, attribute.hasParam() ? attribute.getDoubleParam() : 1, 0));
});
// <--[tag]
// @attribute <LocationTag.below[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location below this location. Optionally specify a number of blocks to go down.
// This just moves straight along the Y axis, equivalent to <@link tag LocationTag.sub> with input 0,1,0 (or the input value instead of '1').
// -->
tagProcessor.registerTag(LocationTag.class, "below", (attribute, object) -> {
return new LocationTag(object.clone().subtract(0, attribute.hasParam() ? attribute.getDoubleParam() : 1, 0));
});
// <--[tag]
// @attribute <LocationTag.forward_flat[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location in front of this location based on yaw but not pitch. Optionally specify a number of blocks to go forward.
// -->
tagProcessor.registerTag(LocationTag.class, "forward_flat", (attribute, object) -> {
Location loc = object.clone();
loc.setPitch(0);
Vector vector = loc.getDirection().multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().add(vector));
});
// <--[tag]
// @attribute <LocationTag.backward_flat[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location behind this location based on yaw but not pitch. Optionally specify a number of blocks to go backward.
// This is equivalent to <@link tag LocationTag.forward_flat> in the opposite direction.
// -->
tagProcessor.registerTag(LocationTag.class, "backward_flat", (attribute, object) -> {
Location loc = object.clone();
loc.setPitch(0);
Vector vector = loc.getDirection().multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().subtract(vector));
});
// <--[tag]
// @attribute <LocationTag.forward[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location in front of this location based on pitch and yaw. Optionally specify a number of blocks to go forward.
// -->
tagProcessor.registerTag(LocationTag.class, "forward", (attribute, object) -> {
Vector vector = object.getDirection().multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().add(vector));
});
// <--[tag]
// @attribute <LocationTag.backward[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location behind this location based on pitch and yaw. Optionally specify a number of blocks to go backward.
// This is equivalent to <@link tag LocationTag.forward> in the opposite direction.
// -->
tagProcessor.registerTag(LocationTag.class, "backward", (attribute, object) -> {
Vector vector = object.getDirection().multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().subtract(vector));
});
// <--[tag]
// @attribute <LocationTag.left[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location to the left of this location based on pitch and yaw. Optionally specify a number of blocks to go left.
// This is equivalent to <@link tag LocationTag.forward> with a +90 degree rotation to the yaw and the pitch set to 0.
// -->
tagProcessor.registerTag(LocationTag.class, "left", (attribute, object) -> {
Location loc = object.clone();
loc.setPitch(0);
Vector vector = loc.getDirection().rotateAroundY(Math.PI / 2).multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().add(vector));
});
// <--[tag]
// @attribute <LocationTag.right[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location to the right of this location based on pitch and yaw. Optionally specify a number of blocks to go right.
// This is equivalent to <@link tag LocationTag.forward> with a -90 degree rotation to the yaw and the pitch set to 0.
// -->
tagProcessor.registerTag(LocationTag.class, "right", (attribute, object) -> {
Location loc = object.clone();
loc.setPitch(0);
Vector vector = loc.getDirection().rotateAroundY(Math.PI / 2).multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().subtract(vector));
});
// <--[tag]
// @attribute <LocationTag.up[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location above this location based on pitch and yaw. Optionally specify a number of blocks to go up.
// This is equivalent to <@link tag LocationTag.forward> with a +90 degree rotation to the pitch.
// To just get the location above this location, use <@link tag LocationTag.above> instead.
// -->
tagProcessor.registerTag(LocationTag.class, "up", (attribute, object) -> {
Location loc = object.clone();
loc.setPitch(loc.getPitch() - 90);
Vector vector = loc.getDirection().multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().add(vector));
});
// <--[tag]
// @attribute <LocationTag.down[(<#.#>)]>
// @returns LocationTag
// @group math
// @description
// Returns the location below this location based on pitch and yaw. Optionally specify a number of blocks to go down.
// This is equivalent to <@link tag LocationTag.forward> with a -90 degree rotation to the pitch.
// To just get the location above this location, use <@link tag LocationTag.below> instead.
// -->
tagProcessor.registerTag(LocationTag.class, "down", (attribute, object) -> {
Location loc = object.clone();
loc.setPitch(loc.getPitch() - 90);
Vector vector = loc.getDirection().multiply(attribute.hasParam() ? attribute.getDoubleParam() : 1);
return new LocationTag(object.clone().subtract(vector));
});
// <--[tag]
// @attribute <LocationTag.relative[<location>]>
// @returns LocationTag
// @group math
// @description
// Returns the location relative to this location. Input is a vector location of the form left,up,forward.
// For example, input -1,1,1 will return a location 1 block to the right, 1 block up, and 1 block forward.
// To just get the location relative to this without rotation math, use <@link tag LocationTag.add> instead.
// -->
tagProcessor.registerTag(LocationTag.class, "relative", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag offsetLoc = attribute.paramAsType(LocationTag.class);
if (offsetLoc == null) {
return null;
}
Location loc = object.clone();
Vector offset = loc.getDirection().multiply(offsetLoc.getZ());
loc.setPitch(loc.getPitch() - 90);
offset = offset.add(loc.getDirection().multiply(offsetLoc.getY()));
loc.setPitch(0);
offset = offset.add(loc.getDirection().rotateAroundY(Math.PI / 2).multiply(offsetLoc.getX()));
return new LocationTag(object.clone().add(offset));
});
// <--[tag]
// @attribute <LocationTag.block>
// @returns LocationTag
// @group math
// @description
// Returns the location of the block this location is on,
// i.e. returns a location without decimals or direction.
// Note that you almost never actually need this tag. This does not "get the block", this just rounds coordinates down.
// If you have this in a script, it is more likely to be a mistake than actually needed.
// Consider using <@link tag LocationTag.round_down> instead.
// -->
tagProcessor.registerTag(LocationTag.class, "block", (attribute, object) -> {
return new LocationTag(object.getWorld(), object.getBlockX(), object.getBlockY(), object.getBlockZ());
});
// <--[tag]
// @attribute <LocationTag.center>
// @returns LocationTag
// @group math
// @description
// Returns the location at the center of the block this location is on.
// -->
tagProcessor.registerTag(LocationTag.class, "center", (attribute, object) -> {
return new LocationTag(object.getWorld(), object.getBlockX() + 0.5, object.getBlockY() + 0.5, object.getBlockZ() + 0.5);
});
// <--[tag]
// @attribute <LocationTag.random_offset[<limit>]>
// @returns LocationTag
// @group math
// @description
// Returns a copy of this location, with the X/Y/Z offset by a random decimal value up to a given limit.
// The limit can either be an X,Y,Z location vector like [3,1,3] or a single value like [3] (which is equivalent to [3,3,3]).
// For example, for a location at 0,100,0, ".random_offset[1,2,3]" can return any decimal location within the cuboid from -1,98,-3 to 1,102,3.
// -->
tagProcessor.registerTag(LocationTag.class, "random_offset", (attribute, object) -> {
if (!attribute.hasParam()) {
attribute.echoError("LocationTag.random_offset[...] must have an input.");
return null;
}
Vector offsetLimit;
if (ArgumentHelper.matchesDouble(attribute.getParam())) {
double val = attribute.getDoubleParam();
offsetLimit = new Vector(val, val, val);
} else {
LocationTag val = attribute.paramAsType(LocationTag.class);
if (val == null) {
return null;
}
offsetLimit = val.toVector();
}
offsetLimit.setX(offsetLimit.getX() * (CoreUtilities.getRandom().nextDouble() * 2 - 1));
offsetLimit.setY(offsetLimit.getY() * (CoreUtilities.getRandom().nextDouble() * 2 - 1));
offsetLimit.setZ(offsetLimit.getZ() * (CoreUtilities.getRandom().nextDouble() * 2 - 1));
LocationTag output = object.clone();
output.add(offsetLimit);
return output;
});
// <--[tag]
// @attribute <LocationTag.highest>
// @returns LocationTag
// @group world
// @description
// Returns the location of the highest solid block at the location.
// -->
tagProcessor.registerTag(LocationTag.class, "highest", (attribute, object) -> {
Location result = object.getHighestBlockForTag(attribute);
return new LocationTag(result);
});
// <--[tag]
// @attribute <LocationTag.has_inventory>
// @returns ElementTag(Boolean)
// @group world
// @description
// Returns whether the block at the location has an inventory.
// -->
tagProcessor.registerTag(ElementTag.class, "has_inventory", (attribute, object) -> {
return new ElementTag(object.getBlockStateForTag(attribute) instanceof InventoryHolder);
});
// <--[tag]
// @attribute <LocationTag.inventory>
// @returns InventoryTag
// @group world
// @description
// Returns the InventoryTag of the block at the location. If the
// block is not a container, returns null.
// -->
tagProcessor.registerTag(InventoryTag.class, "inventory", (attribute, object) -> {
if (!object.isChunkLoadedSafe()) {
return null;
}
return ElementTag.handleNull(object.identify() + ".inventory", object.getInventory(), "InventoryTag", attribute.hasAlternative());
});
// <--[tag]
// @attribute <LocationTag.material>
// @returns MaterialTag
// @group world
// @description
// Returns the material of the block at the location.
// -->
tagProcessor.registerTag(MaterialTag.class, "material", (attribute, object) -> {
Block block = object.getBlockForTag(attribute);
if (block == null) {
return null;
}
return new MaterialTag(block);
});
// <--[tag]
// @attribute <LocationTag.patterns>
// @returns ListTag
// @mechanism LocationTag.patterns
// @group world
// @description
// Lists the patterns of the banner at this location in the form "COLOR/PATTERN|COLOR/PATTERN" etc.
// For the list of possible colors, see <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/DyeColor.html>.
// For the list of possible patterns, see <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/block/banner/PatternType.html>.
// -->
tagProcessor.registerTag(ListTag.class, "patterns", (attribute, object) -> {
ListTag list = new ListTag();
for (org.bukkit.block.banner.Pattern pattern : ((Banner) object.getBlockStateForTag(attribute)).getPatterns()) {
list.add(pattern.getColor().name() + "/" + pattern.getPattern().name());
}
return list;
});
// <--[tag]
// @attribute <LocationTag.head_rotation>
// @returns ElementTag(Number)
// @mechanism LocationTag.head_rotation
// @group world
// @description
// Gets the rotation of the head at this location. Can be 1-16.
// -->
tagProcessor.registerTag(ElementTag.class, "head_rotation", (attribute, object) -> {
return new ElementTag(object.getSkullRotation(((Skull) object.getBlockStateForTag(attribute)).getRotation()) + 1);
});
// <--[tag]
// @attribute <LocationTag.switched>
// @returns ElementTag(Boolean)
// @group world
// @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>
// -->
tagProcessor.registerTag(ElementTag.class, "switched", (attribute, object) -> {
return new ElementTag(SwitchCommand.switchState(object.getBlockForTag(attribute)));
});
// <--[tag]
// @attribute <LocationTag.sign_contents>
// @returns ListTag
// @mechanism LocationTag.sign_contents
// @group world
// @description
// Returns a list of lines on a sign.
// -->
tagProcessor.registerTag(ListTag.class, "sign_contents", (attribute, object) -> {
if (object.getBlockStateForTag(attribute) instanceof Sign) {
return new ListTag(Arrays.asList(AdvancedTextImpl.instance.getSignLines(((Sign) object.getBlockStateForTag(attribute)))));
} else {
return null;
}
});
// <--[tag]
// @attribute <LocationTag.spawner_type>
// @returns EntityTag
// @mechanism LocationTag.spawner_type
// @group world
// @description
// Returns the type of entity spawned by a mob spawner.
// -->
tagProcessor.registerTag(EntityTag.class, "spawner_type", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new EntityTag(DenizenEntityType.getByName(((CreatureSpawner) object.getBlockStateForTag(attribute)).getSpawnedType().name()));
});
// <--[tag]
// @attribute <LocationTag.spawner_display_entity>
// @returns EntityTag
// @group world
// @description
// Returns the full "display entity" for the spawner. This can contain more data than just a type.
// -->
tagProcessor.registerTag(EntityTag.class, "spawner_display_entity", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return NMSHandler.getEntityHelper().getMobSpawnerDisplayEntity(((CreatureSpawner) object.getBlockStateForTag(attribute))).describe(attribute.context);
});
// <--[tag]
// @attribute <LocationTag.spawner_spawn_delay>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_delay_data
// @group world
// @description
// Returns the current spawn delay for the spawner.
// This changes over time between <@link tag LocationTag.spawner_minimum_spawn_delay> and <@link tag LocationTag.spawner_maximum_spawn_delay>.
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_spawn_delay", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getDelay());
});
// <--[tag]
// @attribute <LocationTag.spawner_minimum_spawn_delay>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_delay_data
// @group world
// @description
// Returns the minimum spawn delay for the mob spawner.
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_minimum_spawn_delay", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getMinSpawnDelay());
});
// <--[tag]
// @attribute <LocationTag.spawner_maximum_spawn_delay>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_delay_data
// @group world
// @description
// Returns the maximum spawn delay for the mob spawner.
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_maximum_spawn_delay", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getMaxSpawnDelay());
});
// <--[tag]
// @attribute <LocationTag.spawner_player_range>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_player_range
// @group world
// @description
// Returns the maximum player range for the spawner (ie how close a player must be for this spawner to be active).
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_player_range", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getRequiredPlayerRange());
});
// <--[tag]
// @attribute <LocationTag.spawner_range>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_range
// @group world
// @description
// Returns the spawn range for the spawner (the radius mobs will spawn in).
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_range", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getSpawnRange());
});
// <--[tag]
// @attribute <LocationTag.spawner_max_nearby_entities>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_max_nearby_entities
// @group world
// @description
// Returns the maximum nearby entities for the spawner (the radius mobs will spawn in).
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_max_nearby_entities", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getMaxNearbyEntities());
});
// <--[tag]
// @attribute <LocationTag.spawner_count>
// @returns ElementTag(Number)
// @mechanism LocationTag.spawner_count
// @group world
// @description
// Returns the spawn count for the spawner.
// -->
tagProcessor.registerTag(ElementTag.class, "spawner_count", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CreatureSpawner)) {
return null;
}
return new ElementTag(((CreatureSpawner) object.getBlockStateForTag(attribute)).getSpawnCount());
});
// <--[tag]
// @attribute <LocationTag.lock>
// @returns ElementTag
// @mechanism LocationTag.lock
// @group world
// @description
// Returns the password to a locked container.
// -->
tagProcessor.registerTag(ElementTag.class, "lock", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof Lockable)) {
return null;
}
Lockable lock = (Lockable) object.getBlockStateForTag(attribute);
return new ElementTag(lock.isLocked() ? lock.getLock() : null);
});
// <--[tag]
// @attribute <LocationTag.is_locked>
// @returns ElementTag(Boolean)
// @mechanism LocationTag.lock
// @group world
// @description
// Returns whether the container is locked.
// -->
tagProcessor.registerTag(ElementTag.class, "is_locked", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof Lockable)) {
return null;
}
return new ElementTag(((Lockable) object.getBlockStateForTag(attribute)).isLocked());
});
// <--[tag]
// @attribute <LocationTag.is_lockable>
// @returns ElementTag(Boolean)
// @mechanism LocationTag.lock
// @group world
// @description
// Returns whether the container is lockable.
// -->
tagProcessor.registerTag(ElementTag.class, "is_lockable", (attribute, object) -> {
return new ElementTag(object.getBlockStateForTag(attribute) instanceof Lockable);
});
// <--[tag]
// @attribute <LocationTag.drops[(<item>)]>
// @returns ListTag(ItemTag)
// @group world
// @description
// Returns what items the block at the location would drop if broken naturally.
// Optionally specifier a breaker item.
// Not guaranteed to contain exactly correct or contain all possible drops (for things like plants that drop only when grown, ores that drop random amounts, etc).
// -->
tagProcessor.registerTag(ListTag.class, "drops", (attribute, object) -> {
ItemStack inputItem = null;
if (attribute.hasParam()) {
inputItem = attribute.paramAsType(ItemTag.class).getItemStack();
}
ListTag list = new ListTag();
for (ItemStack it : object.getDropsForTag(attribute, inputItem)) {
list.addObject(new ItemTag(it));
}
return list;
});
// <--[tag]
// @attribute <LocationTag.xp_drop[(<item>)]>
// @returns ElementTag(Number)
// @group world
// @description
// Returns how much experience, if any, the block at the location would drop if broken naturally.
// Returns 0 if a block wouldn't drop xp.
// Optionally specifier a breaker item.
// Not guaranteed to contain exactly the amount that actual drops if then broken later, as the value is usually randomized.
// -->
tagProcessor.registerTag(ElementTag.class, "xp_drop", (attribute, object) -> {
ItemStack inputItem = new ItemStack(Material.AIR);
if (attribute.hasParam()) {
inputItem = attribute.paramAsType(ItemTag.class).getItemStack();
}
return new ElementTag(object.getExpDropForTag(attribute, inputItem));
});
// <--[tag]
// @attribute <LocationTag.hive_bee_count>
// @returns ElementTag(Number)
// @group world
// @description
// Returns the number of bees inside a hive.
// -->
tagProcessor.registerTag(ElementTag.class, "hive_bee_count", (attribute, object) -> {
return new ElementTag(((Beehive) object.getBlockStateForTag(attribute)).getEntityCount());
});
// <--[tag]
// @attribute <LocationTag.hive_max_bees>
// @returns ElementTag(Number)
// @mechanism LocationTag.hive_max_bees
// @group world
// @description
// Returns the maximum number of bees allowed inside a hive.
// -->
tagProcessor.registerTag(ElementTag.class, "hive_max_bees", (attribute, object) -> {
return new ElementTag(((Beehive) object.getBlockStateForTag(attribute)).getMaxEntities());
});
// <--[tag]
// @attribute <LocationTag.skull_type>
// @returns ElementTag
// @group world
// @description
// Returns the type of the skull.
// -->
tagProcessor.registerTag(ElementTag.class, "skull_type", (attribute, object) -> {
BlockState blockState = object.getBlockStateForTag(attribute);
if (blockState instanceof Skull) {
String t = ((Skull) blockState).getSkullType().name();
return new ElementTag(t);
}
return null;
});
// <--[tag]
// @attribute <LocationTag.skull_name>
// @returns ElementTag
// @mechanism LocationTag.skull_skin
// @group world
// @description
// Returns the name of the skin the skull is displaying.
// -->
tagProcessor.registerTag(ElementTag.class, "skull_name", (attribute, object) -> {
BlockState blockState = object.getBlockStateForTag(attribute);
if (blockState instanceof Skull) {
PlayerProfile profile = NMSHandler.getBlockHelper().getPlayerProfile((Skull) blockState);
if (profile == null) {
return null;
}
String n = profile.getName();
if (n == null) {
n = ((Skull) blockState).getOwningPlayer().getName();
}
return new ElementTag(n);
}
return null;
});
// <--[tag]
// @attribute <LocationTag.skull_skin>
// @returns ElementTag
// @mechanism LocationTag.skull_skin
// @group world
// @description
// Returns the skin the skull is displaying - just the name or UUID as text, not a player object.
// -->
tagProcessor.registerTag(ElementTag.class, "skull_skin", (attribute, object) -> {
BlockState blockState = object.getBlockStateForTag(attribute);
if (blockState instanceof Skull) {
PlayerProfile profile = NMSHandler.getBlockHelper().getPlayerProfile((Skull) blockState);
if (profile == null) {
return null;
}
String name = profile.getName();
UUID uuid = profile.getUniqueId();
String texture = profile.getTexture();
// -->
if (attribute.startsWith("full", 2)) {
attribute.fulfill(1);
return new ElementTag((uuid != null ? uuid : name) + (texture != null ? "|" + texture : ""));
}
return new ElementTag(uuid != null ? uuid.toString() : name);
} else {
return null;
}
});
// <--[tag]
// @attribute <LocationTag.round>
// @returns LocationTag
// @group math
// @description
// Returns a rounded version of the LocationTag's coordinates.
// That is, each component (X, Y, Z, Yaw, Pitch) is rounded
// (eg, 0.1 becomes 0.0, 0.5 becomes 1.0, 0.9 becomes 1.0).
// This is NOT equivalent to the block coordinates. For that, use <@link tag LocationTag.round_down>.
// -->
tagProcessor.registerTag(LocationTag.class, "round", (attribute, object) -> {
LocationTag result = object.clone();
result.setX(Math.round(result.getX()));
result.setY(Math.round(result.getY()));
result.setZ(Math.round(result.getZ()));
result.setYaw(Math.round(result.getYaw()));
result.setPitch(Math.round(result.getPitch()));
return result;
});
// <--[tag]
// @attribute <LocationTag.round_up>
// @returns LocationTag
// @group math
// @description
// Returns a rounded-upward version of the LocationTag's coordinates.
// That is, each component (X, Y, Z, Yaw, Pitch) is rounded upward
// (eg, 0.1 becomes 1.0, 0.5 becomes 1.0, 0.9 becomes 1.0).
// -->
tagProcessor.registerTag(LocationTag.class, "round_up", (attribute, object) -> {
LocationTag result = object.clone();
result.setX(Math.ceil(result.getX()));
result.setY(Math.ceil(result.getY()));
result.setZ(Math.ceil(result.getZ()));
result.setYaw((float) Math.ceil((result.getYaw())));
result.setPitch((float) Math.ceil(result.getPitch()));
return result;
});
// <--[tag]
// @attribute <LocationTag.round_down>
// @returns LocationTag
// @group math
// @description
// Returns a rounded-downward version of the LocationTag's coordinates.
// That is, each component (X, Y, Z, Yaw, Pitch) is rounded downward
// (eg, 0.1 becomes 0.0, 0.5 becomes 0.0, 0.9 becomes 0.0).
// This is equivalent to the block coordinates of the location.
// -->
tagProcessor.registerTag(LocationTag.class, "round_down", (attribute, object) -> {
LocationTag result = object.clone();
result.setX(Math.floor(result.getX()));
result.setY(Math.floor(result.getY()));
result.setZ(Math.floor(result.getZ()));
result.setYaw((float) Math.floor((result.getYaw())));
result.setPitch((float) Math.floor(result.getPitch()));
return result;
});
// <--[tag]
// @attribute <LocationTag.round_to[<#>]>
// @returns LocationTag
// @group math
// @description
// Returns a rounded-to-precision version of the LocationTag's coordinates.
// That is, each component (X, Y, Z, Yaw, Pitch) is rounded to the specified decimal place
// (eg, 0.12345 .round_to[3] returns "0.123").
// -->
tagProcessor.registerTag(LocationTag.class, "round_to", (attribute, object) -> {
if (!attribute.hasParam()) {
attribute.echoError("The tag LocationTag.round_to[...] must have a value.");
return null;
}
LocationTag result = object.clone();
int ten = (int) Math.pow(10, attribute.getIntParam());
result.setX(((double) Math.round(result.getX() * ten)) / ten);
result.setY(((double) Math.round(result.getY() * ten)) / ten);
result.setZ(((double) Math.round(result.getZ() * ten)) / ten);
result.setYaw(((float) Math.round(result.getYaw() * ten)) / ten);
result.setPitch(((float) Math.round(result.getPitch() * ten)) / ten);
return result;
});
// <--[tag]
// @attribute <LocationTag.round_to_precision[<#.#>]>
// @returns LocationTag
// @group math
// @description
// Returns a rounded-to-precision version of the LocationTag's coordinates.
// That is, each component (X, Y, Z, Yaw, Pitch) is rounded to the specified precision value
// (0.12345 .round_to_precision[0.005] returns "0.125").
// -->
tagProcessor.registerTag(LocationTag.class, "round_to_precision", (attribute, object) -> {
if (!attribute.hasParam()) {
attribute.echoError("The tag LocationTag.round_to_precision[...] must have a value.");
return null;
}
LocationTag result = object.clone();
float precision = 1f / (float) attribute.getDoubleParam();
result.setX(((double) Math.round(result.getX() * precision)) / precision);
result.setY(((double) Math.round(result.getY() * precision)) / precision);
result.setZ(((double) Math.round(result.getZ() * precision)) / precision);
result.setYaw(((float) Math.round(result.getYaw() * precision)) / precision);
result.setPitch(((float) Math.round(result.getPitch() * precision)) / precision);
return result;
});
// <--[tag]
// @attribute <LocationTag.simple>
// @returns ElementTag
// @group identity
// @description
// Returns a simple version of the LocationTag's block coordinates.
// In the format: x,y,z,world
// For example: 1,2,3,world_nether
// -->
tagProcessor.registerTag(ElementTag.class, "simple", (attribute, object) -> {
// -->
if (attribute.startsWith("formatted", 2)) {
attribute.fulfill(1);
return new ElementTag("X '" + object.getBlockX() + "', Y '" + object.getBlockY() + "', Z '" + object.getBlockZ() + "', in world '" + object.getWorldName() + "'");
}
if (object.getWorldName() == null) {
return new ElementTag(object.getBlockX() + "," + object.getBlockY() + "," + object.getBlockZ());
} else {
return new ElementTag(object.getBlockX() + "," + object.getBlockY() + "," + object.getBlockZ() + "," + object.getWorldName());
}
});
// <--[tag]
// @attribute <LocationTag.precise_impact_normal[(<range>)]>
// @returns LocationTag
// @group world
// @description
// Returns the exact impact normal at the location this location is pointing at.
// In minecraft, the impact normal is generally the side of the block that the location is facing.
// Optionally, specify a maximum range to find the location from (defaults to 200).
// -->
tagProcessor.registerTag(LocationTag.class, "precise_impact_normal", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 200;
}
RayTraceResult traced = object.getWorld().rayTraceBlocks(object, object.getDirection(), range);
if (traced != null && traced.getHitBlockFace() != null) {
return new LocationTag(traced.getHitBlockFace().getDirection());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.precise_cursor_on_block[(<range>)]>
// @returns LocationTag
// @group world
// @description
// Returns the block location this location is pointing at.
// Optionally, specify a maximum range to find the location from (defaults to 200).
// -->
tagProcessor.registerTag(LocationTag.class, "precise_cursor_on_block", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 200;
}
RayTraceResult traced = object.getWorld().rayTraceBlocks(object, object.getDirection(), range);
if (traced != null && traced.getHitBlock() != null) {
return new LocationTag(traced.getHitBlock().getLocation());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.precise_cursor_on[(<range>)]>
// @returns LocationTag
// @group world
// @description
// Returns the exact location this location is pointing at.
// Optionally, specify a maximum range to find the location from (defaults to 200).
// -->
tagProcessor.registerTag(LocationTag.class, "precise_cursor_on", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 200;
}
RayTraceResult traced = object.getWorld().rayTraceBlocks(object, object.getDirection(), range);
if (traced != null && traced.getHitBlock() != null) {
return new LocationTag(traced.getHitBlock().getWorld(), traced.getHitPosition());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.precise_target_list[<range>]>
// @returns ListTag(EntityTag)
// @group world
// @description
// Returns a list of all entities this location is pointing directly at (using precise ray trace logic), up to a given range limit.
// -->
tagProcessor.registerTag(ListTag.class, "precise_target_list", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double range = attribute.getDoubleParam();
HashSet<UUID> hitIDs = new HashSet<>();
ListTag result = new ListTag();
Vector direction = object.getDirection();
World world = object.getWorld();
while (true) {
RayTraceResult hit = world.rayTrace(object, direction, range, FluidCollisionMode.NEVER, true, 0, (e) -> !hitIDs.contains(e.getUniqueId()));
if (hit == null || hit.getHitEntity() == null) {
return result;
}
hitIDs.add(hit.getHitEntity().getUniqueId());
result.addObject(new EntityTag(hit.getHitEntity()));
}
});
// <--[tag]
// @attribute <LocationTag.precise_target[(<range>)]>
// @returns EntityTag
// @group world
// @description
// Returns the entity this location is pointing at, using precise ray trace logic.
// Optionally, specify a maximum range to find the entity from (defaults to 100).
// -->
tagProcessor.registerTag(EntityTag.class, "precise_target", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 100;
}
RayTraceResult result;
// -->
if (attribute.startsWith("type", 2) && attribute.hasContext(2)) {
attribute.fulfill(1);
Set<EntityType> types = new HashSet<>();
for (String str : attribute.paramAsType(ListTag.class)) {
types.add(EntityTag.valueOf(str, attribute.context).getBukkitEntityType());
}
result = object.getWorld().rayTrace(object, object.getDirection(), range, FluidCollisionMode.NEVER, true, 0, (e) -> types.contains(e.getType()));
} else {
result = object.getWorld().rayTrace(object, object.getDirection(), range, FluidCollisionMode.NEVER, true, 0, null);
}
if (result != null && result.getHitEntity() != null) {
return new EntityTag(result.getHitEntity());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.precise_target_position[(<range>)]>
// @returns LocationTag
// @group world
// @description
// Returns the precise location this location is pointing at, when tracing against entities.
// Optionally, specify a maximum range to find the entity from (defaults to 100).
// -->
tagProcessor.registerTag(LocationTag.class, "precise_target_position", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 100;
}
RayTraceResult result;
// -->
if (attribute.startsWith("type", 2) && attribute.hasContext(2)) {
attribute.fulfill(1);
Set<EntityType> types = new HashSet<>();
for (String str : attribute.paramAsType(ListTag.class)) {
types.add(EntityTag.valueOf(str, attribute.context).getBukkitEntityType());
}
result = object.getWorld().rayTrace(object, object.getDirection(), range, FluidCollisionMode.NEVER, true, 0, (e) -> types.contains(e.getType()));
} else {
result = object.getWorld().rayTrace(object, object.getDirection(), range, FluidCollisionMode.NEVER, true, 0, null);
}
if (result != null) {
return new LocationTag(object.getWorld(), result.getHitPosition());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.points_between[<location>]>
// @returns ListTag(LocationTag)
// @group math
// @description
// Finds all locations between this location and another, separated by 1 block-width each.
// -->
tagProcessor.registerTag(ListTag.class, "points_between", (attribute, object) -> {
LocationTag target = attribute.paramAsType(LocationTag.class);
if (target == null) {
return null;
}
// <--[tag]
// @attribute <LocationTag.points_between[<location>].distance[<#.#>]>
// @returns ListTag(LocationTag)
// @group math
// @description
// Finds all locations between this location and another, separated by the specified distance each.
// -->
double rad = 1d;
if (attribute.startsWith("distance", 2)) {
rad = attribute.getDoubleContext(2);
attribute.fulfill(1);
}
ListTag list = new ListTag();
org.bukkit.util.Vector rel = target.toVector().subtract(object.toVector());
double len = rel.length();
if (len < 0.0001) {
return new ListTag();
}
rel = rel.multiply(1d / len);
for (double i = 0d; i <= len; i += rad) {
list.addObject(new LocationTag(object.clone().add(rel.clone().multiply(i))));
}
return list;
});
// <--[tag]
// @attribute <LocationTag.facing_blocks[(<#>)]>
// @returns ListTag(LocationTag)
// @group world
// @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.
// This is an imperfect block line tracer.
// -->
tagProcessor.registerTag(ListTag.class, "facing_blocks", (attribute, object) -> {
int range = attribute.getIntParam();
if (range < 1) {
range = 100;
}
ListTag list = new ListTag();
try {
NMSHandler.getChunkHelper().changeChunkServerThread(object.getWorld());
BlockIterator iterator = new BlockIterator(object, 0, range);
while (iterator.hasNext()) {
list.addObject(new LocationTag(iterator.next().getLocation()));
}
} finally {
NMSHandler.getChunkHelper().restoreServerThread(object.getWorld());
}
return list;
});
// <--[tag]
// @attribute <LocationTag.line_of_sight[<location>]>
// @returns ElementTag(Boolean)
// @group math
// @description
// Returns whether the specified location is within this location's line of sight.
// -->
tagProcessor.registerTag(ElementTag.class, "line_of_sight", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag location = attribute.paramAsType(LocationTag.class);
if (location != null) {
try {
NMSHandler.getChunkHelper().changeChunkServerThread(object.getWorld());
return new ElementTag(NMSHandler.getEntityHelper().canTrace(object.getWorld(), object.toVector(), location.toVector()));
} finally {
NMSHandler.getChunkHelper().restoreServerThread(object.getWorld());
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.direction[(<location>)]>
// @returns ElementTag
// @group math
// @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", ...
// -->
tagProcessor.registerTag(ObjectTag.class, "direction", (attribute, object) -> {
// -->
if (attribute.startsWith("vector", 2)) {
attribute.fulfill(1);
return new LocationTag(object.getWorld(), object.getDirection());
}
// Get the cardinal direction from this location to another
if (attribute.hasParam() && LocationTag.matches(attribute.getParam())) {
// Subtract this location's vector from the other location's vector,
// not the other way around
LocationTag target = attribute.paramAsType(LocationTag.class);
EntityHelper entityHelper = NMSHandler.getEntityHelper();
// -->
if (attribute.startsWith("yaw", 2)) {
attribute.fulfill(1);
return new ElementTag(EntityHelper.normalizeYaw(entityHelper.getYaw(target.toVector().subtract(object.toVector()).normalize())));
} else {
return new ElementTag(entityHelper.getCardinal(entityHelper.getYaw(target.toVector().subtract(object.toVector()).normalize())));
}
} else // Get a cardinal direction from this location's yaw
{
return new ElementTag(NMSHandler.getEntityHelper().getCardinal(object.getYaw()));
}
});
// <--[tag]
// @attribute <LocationTag.rotate_yaw[<#.#>]>
// @returns LocationTag
// @group math
// @description
// Returns the location with the yaw rotated the specified amount (eg 180 to face the location backwards).
// -->
tagProcessor.registerTag(LocationTag.class, "rotate_yaw", (attribute, object) -> {
LocationTag loc = LocationTag.valueOf(object.identify(), attribute.context).clone();
loc.setYaw(loc.getYaw() + (float) attribute.getDoubleParam());
return loc;
});
// <--[tag]
// @attribute <LocationTag.rotate_pitch[<#.#>]>
// @returns LocationTag
// @group math
// @description
// Returns the location with the pitch rotated the specified amount. Note that this is capped to +/- 90.
// -->
tagProcessor.registerTag(LocationTag.class, "rotate_pitch", (attribute, object) -> {
LocationTag loc = LocationTag.valueOf(object.identify(), attribute.context).clone();
loc.setPitch(Math.max(-90, Math.min(90, loc.getPitch() + (float) attribute.getDoubleParam())));
return loc;
});
// <--[tag]
// @attribute <LocationTag.face[<location>]>
// @returns LocationTag
// @group math
// @description
// Returns a location containing a yaw/pitch that point from the current location
// to the target location.
// -->
tagProcessor.registerTag(LocationTag.class, "face", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
Location two = attribute.paramAsType(LocationTag.class);
return new LocationTag(NMSHandler.getEntityHelper().faceLocation(object, two));
});
// <--[tag]
// @attribute <LocationTag.facing[<entity>/<location>]>
// @returns ElementTag(Boolean)
// @group math
// @description
// Returns whether the location's yaw is facing another entity or location, within a limit of 45 degrees of yaw.
// -->
tagProcessor.registerTag(ElementTag.class, "facing", (attribute, object) -> {
if (attribute.hasParam()) {
// The default number of degrees if there is no degrees attribute
int degrees = 45;
LocationTag facingLoc;
if (LocationTag.matches(attribute.getParam())) {
facingLoc = attribute.paramAsType(LocationTag.class);
} else if (EntityTag.matches(attribute.getParam())) {
facingLoc = attribute.paramAsType(EntityTag.class).getLocation();
} else {
if (!attribute.hasAlternative()) {
Debug.echoError("Tag location.facing[...] was given an invalid facing target.");
}
return null;
}
// -->
if (attribute.startsWith("degrees", 2) && attribute.hasContext(2)) {
String context = attribute.getContext(2);
attribute.fulfill(1);
if (context.contains(",")) {
String yaw = context.substring(0, context.indexOf(','));
String pitch = context.substring(context.indexOf(',') + 1);
degrees = Integer.parseInt(yaw);
int pitchDegrees = Integer.parseInt(pitch);
return new ElementTag(NMSHandler.getEntityHelper().isFacingLocation(object, facingLoc, degrees, pitchDegrees));
} else {
degrees = Integer.parseInt(context);
}
}
return new ElementTag(NMSHandler.getEntityHelper().isFacingLocation(object, facingLoc, degrees));
}
return null;
});
// <--[tag]
// @attribute <LocationTag.pitch>
// @returns ElementTag(Decimal)
// @group identity
// @description
// Returns the pitch of the object at the location.
// -->
tagProcessor.registerTag(ElementTag.class, "pitch", (attribute, object) -> {
return new ElementTag(object.getPitch());
});
// <--[tag]
// @attribute <LocationTag.with_pose[<entity>/<pitch>,<yaw>]>
// @returns LocationTag
// @group identity
// @description
// Returns the location with pitch and yaw.
// -->
tagProcessor.registerTag(LocationTag.class, "with_pose", (attribute, object) -> {
String context = attribute.getParam();
float pitch = 0f;
float yaw = 0f;
if (EntityTag.matches(context)) {
EntityTag ent = EntityTag.valueOf(context, attribute.context);
if (ent.isSpawnedOrValidForTag()) {
pitch = ent.getBukkitEntity().getLocation().getPitch();
yaw = ent.getBukkitEntity().getLocation().getYaw();
}
} else if (context.split(",").length == 2) {
String[] split = context.split(",");
pitch = Float.parseFloat(split[0]);
yaw = Float.parseFloat(split[1]);
}
LocationTag loc = object.clone();
loc.setPitch(pitch);
loc.setYaw(yaw);
return loc;
});
// <--[tag]
// @attribute <LocationTag.yaw>
// @returns ElementTag(Decimal)
// @group identity
// @description
// Returns the normalized yaw of the object at the location.
// -->
tagProcessor.registerTag(ElementTag.class, "yaw", (attribute, object) -> {
// -->
if (attribute.startsWith("simple", 2)) {
attribute.fulfill(1);
float yaw = EntityHelper.normalizeYaw(object.getYaw());
if (yaw < 45) {
return new ElementTag("South");
} else if (yaw < 135) {
return new ElementTag("West");
} else if (yaw < 225) {
return new ElementTag("North");
} else if (yaw < 315) {
return new ElementTag("East");
} else {
return new ElementTag("South");
}
}
// -->
if (attribute.startsWith("raw", 2)) {
attribute.fulfill(1);
return new ElementTag(object.getYaw());
}
return new ElementTag(EntityHelper.normalizeYaw(object.getYaw()));
});
// <--[tag]
// @attribute <LocationTag.rotate_around_x[<#.#>]>
// @returns LocationTag
// @group math
// @description
// Returns the location-vector rotated around the x axis by a specified angle in radians.
// Generally used in a format like <player.location.add[<location[0,1,0].rotate_around_x[<[some_angle]>]>]>.
// -->
tagProcessor.registerTag(LocationTag.class, "rotate_around_x", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double[] values = getRotatedAroundX(attribute.getDoubleParam(), object.getY(), object.getZ());
Location location = object.clone();
location.setY(values[0]);
location.setZ(values[1]);
return new LocationTag(location);
});
// <--[tag]
// @attribute <LocationTag.rotate_around_y[<#.#>]>
// @returns LocationTag
// @group math
// @description
// Returns the location-vector rotated around the y axis by a specified angle in radians.
// Generally used in a format like <player.location.add[<location[1,0,0].rotate_around_y[<[some_angle]>]>]>.
// -->
tagProcessor.registerTag(LocationTag.class, "rotate_around_y", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double[] values = getRotatedAroundY(attribute.getDoubleParam(), object.getX(), object.getZ());
Location location = object.clone();
location.setX(values[0]);
location.setZ(values[1]);
return new LocationTag(location);
});
// <--[tag]
// @attribute <LocationTag.rotate_around_z[<#.#>]>
// @returns LocationTag
// @group math
// @description
// Returns the location-vector rotated around the z axis by a specified angle in radians.
// Generally used in a format like <player.location.add[<location[1,0,0].rotate_around_z[<[some_angle]>]>]>.
// -->
tagProcessor.registerTag(LocationTag.class, "rotate_around_z", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double[] values = getRotatedAroundZ(attribute.getDoubleParam(), object.getX(), object.getY());
Location location = object.clone();
location.setX(values[0]);
location.setY(values[1]);
return new LocationTag(location);
});
// <--[tag]
// @attribute <LocationTag.points_around_x[radius=<#.#>;points=<#>]>
// @returns ListTag(LocationTag)
// @group math
// @description
// Returns a list of points in a circle around a location's x axis with the specified radius and number of points.
// For example: <player.location.points_around_x[radius=10;points=16]>
// -->
tagProcessor.registerTag(ListTag.class, "points_around_x", (attribute, object) -> {
double[] values = parsePointsAroundArgs(attribute);
if (values == null) {
return null;
}
double angle = 2 * Math.PI / values[1];
ListTag points = new ListTag();
for (int i = 0; i < values[1]; i++) {
double[] result = getRotatedAroundX(angle * i, values[0], 0);
points.addObject(object.clone().add(0, result[0], result[1]));
}
return points;
});
// <--[tag]
// @attribute <LocationTag.points_around_y[radius=<#.#>;points=<#>]>
// @returns ListTag(LocationTag)
// @group math
// @description
// Returns a list of points in a circle around a location's y axis with the specified radius and number of points.
// For example: <player.location.points_around_y[radius=10;points=16]>
// -->
tagProcessor.registerTag(ListTag.class, "points_around_y", (attribute, object) -> {
double[] values = parsePointsAroundArgs(attribute);
if (values == null) {
return null;
}
double angle = 2 * Math.PI / values[1];
ListTag points = new ListTag();
for (int i = 0; i < values[1]; i++) {
double[] result = getRotatedAroundY(angle * i, values[0], 0);
points.addObject(object.clone().add(result[0], 0, result[1]));
}
return points;
});
// <--[tag]
// @attribute <LocationTag.points_around_z[radius=<#.#>;points=<#>]>
// @returns ListTag(LocationTag)
// @group math
// @description
// Returns a list of points in a circle around a location's z axis with the specified radius and number of points.
// For example: <player.location.points_around_z[radius=10;points=16]>
// -->
tagProcessor.registerTag(ListTag.class, "points_around_z", (attribute, object) -> {
double[] values = parsePointsAroundArgs(attribute);
if (values == null) {
return null;
}
double angle = 2 * Math.PI / values[1];
ListTag points = new ListTag();
for (int i = 0; i < values[1]; i++) {
double[] result = getRotatedAroundZ(angle * i, 0, values[0]);
points.addObject(object.clone().add(result[0], result[1], 0));
}
return points;
});
// <--[tag]
// @attribute <LocationTag.flood_fill[<limit>]>
// @returns ListTag(LocationTag)
// @group world
// @description
// Returns the set of all blocks, starting at the given location,
// that can be directly reached in a way that only travels through blocks of the same type as the starting block.
// For example, if starting at an air block inside an enclosed building, this will return all air blocks inside the building (but none outside, and no non-air blocks).
// As another example, if starting on a block of iron_ore in the ground, this will find all other blocks of iron ore that are part of the same vein.
// This will not travel diagonally, only the 6 cardinal directions (N/E/S/W/Up/Down).
// As this is potentially infinite should there be any opening however small, a limit must be given.
// The limit value can be: a CuboidTag, an EllipsoidTag, or an ElementTag(Decimal) to use as a radius.
// Note that the returned list will not be in any particular order.
// -->
tagProcessor.registerTag(ListTag.class, "flood_fill", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
AreaContainmentObject area = CuboidTag.valueOf(attribute.getParam(), CoreUtilities.noDebugContext);
if (area == null) {
area = EllipsoidTag.valueOf(attribute.getParam(), CoreUtilities.noDebugContext);
}
if (area == null) {
double radius = attribute.getDoubleParam();
if (radius <= 0) {
return null;
}
area = new EllipsoidTag(object.clone(), new LocationTag(object.getWorld(), radius, radius, radius));
}
FloodFiller flooder = new FloodFiller();
NMSHandler.getChunkHelper().changeChunkServerThread(object.getWorld());
try {
if (object.getWorld() == null) {
attribute.echoError("LocationTag trying to read block, but cannot because no world is specified.");
return null;
}
if (!object.isChunkLoaded()) {
attribute.echoError("LocationTag trying to read block, but cannot because the chunk is unloaded. Use the 'chunkload' command to ensure the chunk is loaded.");
return null;
}
// -->
if (attribute.startsWith("types", 2) && attribute.hasContext(2)) {
flooder.matcher = attribute.getContext(2);
attribute.fulfill(1);
} else {
flooder.requiredMaterial = object.getBlock().getType();
}
flooder.run(object, area);
} finally {
NMSHandler.getChunkHelper().restoreServerThread(object.getWorld());
}
return new ListTag((Collection<LocationTag>) flooder.result);
});
// <--[tag]
// @attribute <LocationTag.find_nearest_biome[<biome>]>
// @returns LocationTag
// @group finding
// @description
// Returns the location of the nearest block of the given biome type (or null).
// Warning: may be extremely slow to process. Use with caution.
// -->
tagProcessor.registerTag(LocationTag.class, "find_nearest_biome", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
BiomeTag biome = attribute.paramAsType(BiomeTag.class);
if (biome == null) {
attribute.echoError("Invalid biome input.");
return null;
}
Location result = NMSHandler.getWorldHelper().getNearestBiomeLocation(object, biome);
if (result == null) {
return null;
}
return new LocationTag(result);
});
// <--[tag]
// @attribute <LocationTag.find_blocks_flagged[<flag_name>].within[<#>]>
// @returns ListTag(LocationTag)
// @group finding
// @description
// Returns a list of blocks that have the specified flag within a radius.
// Note: current implementation measures the center of nearby block's distance from the exact given location.
// Result list is sorted by closeness (1 = closest, 2 = next closest, ... last = farthest).
// Searches the internal flag lists, rather than through all possible blocks.
// -->
tagProcessor.registerTag(ListTag.class, "find_blocks_flagged", (attribute, object) -> {
if (!attribute.hasParam() || !attribute.startsWith("within", 2) || !attribute.hasContext(2)) {
attribute.echoError("find_blocks_flagged[...].within[...] tag malformed.");
return null;
}
String flagName = CoreUtilities.toLowerCase(attribute.getParam());
attribute.fulfill(1);
double radius = attribute.getDoubleParam();
if (!object.isChunkLoadedSafe()) {
attribute.echoError("LocationTag trying to read block, but cannot because the chunk is unloaded. Use the 'chunkload' command to ensure the chunk is loaded.");
return null;
}
double minPossibleX = object.getX() - radius;
double minPossibleZ = object.getZ() - radius;
double maxPossibleX = object.getX() + radius;
double maxPossibleZ = object.getZ() + radius;
int minChunkX = (int) Math.floor(minPossibleX / 16);
int minChunkZ = (int) Math.floor(minPossibleZ / 16);
int maxChunkX = (int) Math.ceil(maxPossibleX / 16);
int maxChunkZ = (int) Math.ceil(maxPossibleZ / 16);
ChunkTag testChunk = new ChunkTag(object);
final ArrayList<LocationTag> found = new ArrayList<>();
for (int x = minChunkX; x <= maxChunkX; x++) {
testChunk.chunkX = x;
for (int z = minChunkZ; z <= maxChunkZ; z++) {
testChunk.chunkZ = z;
testChunk.cachedChunk = null;
if (testChunk.isLoadedSafe()) {
LocationFlagSearchHelper.getFlaggedLocations(testChunk.getChunkForTag(attribute), flagName, (loc) -> {
loc.setX(loc.getX() + 0.5);
loc.setY(loc.getY() + 0.5);
loc.setZ(loc.getZ() + 0.5);
if (Utilities.checkLocation(object, loc, radius)) {
found.add(new LocationTag(loc));
}
});
}
}
}
found.sort(object::compare);
return new ListTag(found);
});
// <--[tag]
// @attribute <LocationTag.find_entities[(<matcher>)].within[<#.#>]>
// @returns ListTag(EntityTag)
// @group finding
// @description
// Returns a list of entities within a radius, with an optional search parameter for the entity type.
// Result list is sorted by closeness (1 = closest, 2 = next closest, ... last = farthest).
// -->
tagProcessor.registerTag(ListTag.class, "find_entities", (attribute, object) -> {
String matcher = attribute.hasParam() ? attribute.getParam() : null;
if (!attribute.startsWith("within", 2) || !attribute.hasContext(2)) {
return null;
}
double radius = attribute.getDoubleContext(2);
attribute.fulfill(1);
ListTag found = new ListTag();
BoundingBox box = BoundingBox.of(object, radius, radius, radius);
for (Entity entity : new WorldTag(object.getWorld()).getPossibleEntitiesForBoundary(box)) {
if (Utilities.checkLocationWithBoundingBox(object, entity, radius)) {
EntityTag current = new EntityTag(entity);
if (matcher == null || BukkitScriptEvent.tryEntity(current, matcher)) {
found.addObject(current.getDenizenObject());
}
}
}
found.objectForms.sort((ent1, ent2) -> object.compare(((EntityFormObject) ent1).getLocation(), ((EntityFormObject) ent2).getLocation()));
return found;
});
// <--[tag]
// @attribute <LocationTag.find_blocks[(<matcher>)].within[<#.#>]>
// @returns ListTag(LocationTag)
// @group finding
// @description
// Returns a list of blocks within a radius, with an optional search parameter for the block material.
// Note: current implementation measures the center of nearby block's distance from the exact given location.
// Result list is sorted by closeness (1 = closest, 2 = next closest, ... last = farthest).
// -->
tagProcessor.registerTag(ListTag.class, "find_blocks", (attribute, object) -> {
String matcher = attribute.hasParam() ? attribute.getParam() : null;
if (!attribute.startsWith("within", 2) || !attribute.hasContext(2)) {
return null;
}
double radius = attribute.getDoubleContext(2);
attribute.fulfill(1);
ListTag found = new ListTag();
int max = Settings.blockTagsMaxBlocks();
int index = 0;
Location tstart = object.getBlockLocation();
double tstartY = tstart.getY();
int radiusInt = (int) radius;
fullloop: for (int y = -radiusInt; y <= radiusInt; y++) {
double newY = y + tstartY;
if (!Utilities.isLocationYSafe(newY, object.getWorld())) {
continue;
}
for (int x = -radiusInt; x <= radiusInt; x++) {
for (int z = -radiusInt; z <= radiusInt; z++) {
index++;
if (index > max) {
break fullloop;
}
if (Utilities.checkLocation(object, tstart.clone().add(x + 0.5, y + 0.5, z + 0.5), radius)) {
if (matcher == null || BukkitScriptEvent.tryMaterial(new LocationTag(tstart.clone().add(x, y, z)).getBlockTypeForTag(attribute), matcher)) {
found.addObject(new LocationTag(tstart.clone().add(x, y, z)));
}
}
}
}
}
found.objectForms.sort((loc1, loc2) -> object.compare((LocationTag) loc1, (LocationTag) loc2));
return found;
});
// <--[tag]
// @attribute <LocationTag.find_spawnable_blocks_within[<#.#>]>
// @returns ListTag(LocationTag)
// @group finding
// @description
// Returns a list of blocks within a radius, that are safe for spawning, with the same logic as <@link tag LocationTag.is_spawnable>.
// Note: current implementation measures the center of nearby block's distance from the exact given location.
// Result list is sorted by closeness (1 = closest, 2 = next closest, ... last = farthest).
// -->
tagProcessor.registerTag(ListTag.class, "find_spawnable_blocks_within", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double radius = attribute.getDoubleParam();
ListTag found = new ListTag();
int max = Settings.blockTagsMaxBlocks();
int index = 0;
Location tstart = object.getBlockLocation();
double tstartY = tstart.getY();
int radiusInt = (int) radius;
fullloop: for (int y = -radiusInt; y <= radiusInt; y++) {
double newY = y + tstartY;
if (!Utilities.isLocationYSafe(newY, object.getWorld())) {
continue;
}
for (int x = -radiusInt; x <= radiusInt; x++) {
for (int z = -radiusInt; z <= radiusInt; z++) {
index++;
if (index > max) {
break fullloop;
}
Location loc = tstart.clone().add(x + 0.5, y + 0.5, z + 0.5);
if (Utilities.checkLocation(object, loc, radius) && SpawnableHelper.isSpawnable(loc)) {
found.addObject(new LocationTag(loc.add(0, -0.5, 0)));
}
}
}
}
found.objectForms.sort((loc1, loc2) -> object.compare((LocationTag) loc1, (LocationTag) loc2));
return found;
});
// <--[tag]
// @attribute <LocationTag.find_players_within[<#.#>]>
// @returns ListTag(PlayerTag)
// @group finding
// @description
// Returns a list of players within a radius.
// Result list is sorted by closeness (1 = closest, 2 = next closest, ... last = farthest).
// -->
tagProcessor.registerTag(ListTag.class, "find_players_within", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double radius = attribute.getDoubleParam();
ArrayList<PlayerTag> found = new ArrayList<>();
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.isDead() && Utilities.checkLocationWithBoundingBox(object, player, radius)) {
found.add(new PlayerTag(player));
}
}
found.sort((pl1, pl2) -> object.compare(pl1.getLocation(), pl2.getLocation()));
return new ListTag(found);
});
// <--[tag]
// @attribute <LocationTag.find_npcs_within[<#.#>]>
// @returns ListTag(NPCTag)
// @group finding
// @description
// Returns a list of NPCs within a radius.
// Result list is sorted by closeness (1 = closest, 2 = next closest, ... last = farthest).
// -->
tagProcessor.registerTag(ListTag.class, "find_npcs_within", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
double radius = attribute.getDoubleParam();
ArrayList<NPCTag> found = new ArrayList<>();
for (NPC npc : CitizensAPI.getNPCRegistry()) {
if (npc.isSpawned() && Utilities.checkLocationWithBoundingBox(object, npc.getEntity(), radius)) {
found.add(new NPCTag(npc));
}
}
found.sort((npc1, npc2) -> object.compare(npc1.getLocation(), npc2.getLocation()));
return new ListTag(found);
});
tagProcessor.registerTag(ObjectTag.class, "find", (attribute, object) -> {
if (!attribute.startsWith("within", 3) || !attribute.hasContext(3)) {
return null;
}
double radius = attribute.getDoubleContext(3);
if (attribute.startsWith("blocks", 2)) {
Deprecations.locationFindEntities.warn(attribute.context);
ArrayList<LocationTag> found = new ArrayList<>();
List<MaterialTag> materials = new ArrayList<>();
if (attribute.hasContext(2)) {
materials = attribute.contextAsType(2, ListTag.class).filter(MaterialTag.class, attribute.context);
}
// Avoid NPE from invalid materials
if (materials == null) {
return null;
}
int max = Settings.blockTagsMaxBlocks();
int index = 0;
attribute.fulfill(2);
Location tstart = object.getBlockLocation();
double tstartY = tstart.getY();
int radiusInt = (int) radius;
fullloop: for (int x = -radiusInt; x <= radiusInt; x++) {
for (int y = -radiusInt; y <= radiusInt; y++) {
double newY = y + tstartY;
if (!Utilities.isLocationYSafe(newY, object.getWorld())) {
continue;
}
for (int z = -radiusInt; z <= radiusInt; z++) {
index++;
if (index > max) {
break fullloop;
}
if (Utilities.checkLocation(object, tstart.clone().add(x + 0.5, y + 0.5, z + 0.5), radius)) {
if (!materials.isEmpty()) {
for (MaterialTag material : materials) {
if (material.getMaterial() == new LocationTag(tstart.clone().add(x, y, z)).getBlockTypeForTag(attribute)) {
found.add(new LocationTag(tstart.clone().add(x, y, z)));
}
}
} else {
found.add(new LocationTag(tstart.clone().add(x, y, z)));
}
}
}
}
}
found.sort(object::compare);
return new ListTag(found);
} else // -->
if (attribute.startsWith("surface_blocks", 2)) {
ArrayList<LocationTag> found = new ArrayList<>();
List<MaterialTag> materials = new ArrayList<>();
if (attribute.hasContext(2)) {
materials = attribute.contextAsType(2, ListTag.class).filter(MaterialTag.class, attribute.context);
}
// Avoid NPE from invalid materials
if (materials == null) {
return null;
}
int max = Settings.blockTagsMaxBlocks();
int index = 0;
attribute.fulfill(2);
Location blockLoc = object.getBlockLocation();
Location loc = blockLoc.clone().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, blockLoc.clone().add(x + 0.5, y + 0.5, z + 0.5), radius)) {
LocationTag l = new LocationTag(blockLoc.clone().add(x, y, z));
if (!materials.isEmpty()) {
for (MaterialTag material : materials) {
if (material.getMaterial() == l.getBlockTypeForTag(attribute)) {
if (new LocationTag(l.clone().add(0, 1, 0)).getBlockTypeForTag(attribute) == Material.AIR && new LocationTag(l.clone().add(0, 2, 0)).getBlockTypeForTag(attribute) == Material.AIR && l.getBlockTypeForTag(attribute) != Material.AIR) {
found.add(new LocationTag(blockLoc.clone().add(x + 0.5, y, z + 0.5)));
}
}
}
} else {
if (new LocationTag(l.clone().add(0, 1, 0)).getBlockTypeForTag(attribute) == Material.AIR && new LocationTag(l.clone().add(0, 2, 0)).getBlockTypeForTag(attribute) == Material.AIR && l.getBlockTypeForTag(attribute) != Material.AIR) {
found.add(new LocationTag(blockLoc.clone().add(x + 0.5, y, z + 0.5)));
}
}
}
}
}
}
found.sort(object::compare);
return new ListTag(found);
} else if (attribute.startsWith("players", 2)) {
Deprecations.locationFindEntities.warn(attribute.context);
ArrayList<PlayerTag> found = new ArrayList<>();
attribute.fulfill(2);
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.isDead() && Utilities.checkLocationWithBoundingBox(object, player, radius)) {
found.add(new PlayerTag(player));
}
}
found.sort((pl1, pl2) -> object.compare(pl1.getLocation(), pl2.getLocation()));
return new ListTag(found);
} else if (attribute.startsWith("npcs", 2)) {
Deprecations.locationFindEntities.warn(attribute.context);
ArrayList<NPCTag> found = new ArrayList<>();
attribute.fulfill(2);
for (NPC npc : CitizensAPI.getNPCRegistry()) {
if (npc.isSpawned() && Utilities.checkLocationWithBoundingBox(object, npc.getEntity(), radius)) {
found.add(new NPCTag(npc));
}
}
found.sort((npc1, npc2) -> object.compare(npc1.getLocation(), npc2.getLocation()));
return new ListTag(found);
} else if (attribute.startsWith("entities", 2)) {
Deprecations.locationFindEntities.warn(attribute.context);
ListTag ent_list = attribute.hasContext(2) ? attribute.contextAsType(2, ListTag.class) : null;
ListTag found = new ListTag();
attribute.fulfill(2);
for (Entity entity : new WorldTag(object.getWorld()).getEntitiesForTag()) {
if (Utilities.checkLocationWithBoundingBox(object, entity, radius)) {
EntityTag current = new EntityTag(entity);
if (ent_list != null) {
for (String ent : ent_list) {
if (current.comparedTo(ent)) {
found.addObject(current.getDenizenObject());
break;
}
}
} else {
found.addObject(current.getDenizenObject());
}
}
}
found.objectForms.sort((ent1, ent2) -> object.compare(((EntityFormObject) ent1).getLocation(), ((EntityFormObject) ent2).getLocation()));
return new ListTag(found.objectForms);
} else // -->
if (attribute.startsWith("living_entities", 2)) {
ListTag found = new ListTag();
attribute.fulfill(2);
BoundingBox box = BoundingBox.of(object, radius, radius, radius);
for (Entity entity : new WorldTag(object.getWorld()).getPossibleEntitiesForBoundary(box)) {
if (entity instanceof LivingEntity && Utilities.checkLocationWithBoundingBox(object, entity, radius)) {
found.addObject(new EntityTag(entity).getDenizenObject());
}
}
found.objectForms.sort((ent1, ent2) -> object.compare(((EntityFormObject) ent1).getLocation(), ((EntityFormObject) ent2).getLocation()));
return new ListTag(found.objectForms);
} else // -->
if (attribute.startsWith("structure", 2) && attribute.hasContext(2)) {
String typeName = attribute.getContext(2);
StructureType type = StructureType.getStructureTypes().get(typeName);
if (type == null) {
attribute.echoError("Invalid structure type '" + typeName + "'.");
return null;
}
attribute.fulfill(2);
Location result = object.getWorld().locateNearestStructure(object, type, (int) radius, false);
if (result == null) {
return null;
}
return new LocationTag(result);
} else // -->
if (attribute.startsWith("unexplored_structure", 2) && attribute.hasContext(2)) {
String typeName = attribute.getContext(2);
StructureType type = StructureType.getStructureTypes().get(typeName);
if (type == null) {
attribute.echoError("Invalid structure type '" + typeName + "'.");
return null;
}
attribute.fulfill(2);
Location result = object.getWorld().locateNearestStructure(object, type, (int) radius, true);
if (result == null) {
return null;
}
return new LocationTag(result);
}
return null;
});
// <--[tag]
// @attribute <LocationTag.find_path[<location>]>
// @returns ListTag(LocationTag)
// @group finding
// @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.
// -->
tagProcessor.registerTag(ListTag.class, "find_path", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag two = attribute.paramAsType(LocationTag.class);
if (two == null) {
return null;
}
List<LocationTag> locs = PathFinder.getPath(object, two);
ListTag list = new ListTag();
for (LocationTag loc : locs) {
list.addObject(loc);
}
return list;
});
// <--[tag]
// @attribute <LocationTag.formatted>
// @returns ElementTag
// @group identity
// @description
// Returns the formatted version of the LocationTag.
// 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'
// -->
tagProcessor.registerTag(ElementTag.class, "formatted", (attribute, object) -> {
// -->
if (attribute.startsWith("citizens", 2)) {
attribute.fulfill(1);
return new ElementTag(object.getX() + ":" + object.getY() + ":" + object.getZ() + ":" + object.getWorldName());
}
return new ElementTag("X '" + object.getX() + "', Y '" + object.getY() + "', Z '" + object.getZ() + "', in world '" + object.getWorldName() + "'");
});
// <--[tag]
// @attribute <LocationTag.chunk>
// @returns ChunkTag
// @group identity
// @description
// Returns the chunk that this location belongs to.
// -->
tagProcessor.registerTag(ChunkTag.class, "chunk", (attribute, object) -> {
return new ChunkTag(object);
}, "get_chunk");
// <--[tag]
// @attribute <LocationTag.raw>
// @returns ElementTag
// @group identity
// @description
// Returns the raw representation of this location, without any note name.
// -->
tagProcessor.registerTag(ElementTag.class, "raw", (attribute, object) -> {
return new ElementTag(object.identifyRaw());
});
// <--[tag]
// @attribute <LocationTag.world>
// @returns WorldTag
// @group identity
// @description
// Returns the world that the location is in.
// -->
tagProcessor.registerTag(WorldTag.class, "world", (attribute, object) -> {
return WorldTag.mirrorBukkitWorld(object.getWorld());
});
// <--[tag]
// @attribute <LocationTag.x>
// @returns ElementTag(Decimal)
// @group identity
// @description
// Returns the X coordinate of the location.
// -->
tagProcessor.registerTag(ElementTag.class, "x", (attribute, object) -> {
return new ElementTag(object.getX());
});
// <--[tag]
// @attribute <LocationTag.y>
// @returns ElementTag(Decimal)
// @group identity
// @description
// Returns the Y coordinate of the location.
// -->
tagProcessor.registerTag(ElementTag.class, "y", (attribute, object) -> {
return new ElementTag(object.getY());
});
// <--[tag]
// @attribute <LocationTag.z>
// @returns ElementTag(Decimal)
// @group identity
// @description
// Returns the Z coordinate of the location.
// -->
tagProcessor.registerTag(ElementTag.class, "z", (attribute, object) -> {
return new ElementTag(object.getZ());
});
// <--[tag]
// @attribute <LocationTag.xyz>
// @returns ElementTag
// @group identity
// @description
// Returns the location in "x,y,z" format.
// For example: 1,2,3
// World, yaw, and pitch will be excluded from this output.
// -->
tagProcessor.registerTag(ElementTag.class, "xyz", (attribute, object) -> {
return new ElementTag(CoreUtilities.doubleToString(object.getX()) + "," + CoreUtilities.doubleToString(object.getY()) + "," + CoreUtilities.doubleToString(object.getZ()));
});
// <--[tag]
// @attribute <LocationTag.with_x[<number>]>
// @returns LocationTag
// @group identity
// @description
// Returns a copy of the location with a changed X value.
// -->
tagProcessor.registerTag(LocationTag.class, "with_x", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag output = object.clone();
output.setX(attribute.getDoubleParam());
return output;
});
// <--[tag]
// @attribute <LocationTag.with_y[<number>]>
// @returns LocationTag
// @group identity
// @description
// Returns a copy of the location with a changed Y value.
// -->
tagProcessor.registerTag(LocationTag.class, "with_y", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag output = object.clone();
output.setY(attribute.getDoubleParam());
return output;
});
// <--[tag]
// @attribute <LocationTag.with_z[<number>]>
// @returns LocationTag
// @group identity
// @description
// Returns a copy of the location with a changed Z value.
// -->
tagProcessor.registerTag(LocationTag.class, "with_z", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag output = object.clone();
output.setZ(attribute.getDoubleParam());
return output;
});
// <--[tag]
// @attribute <LocationTag.with_yaw[<number>]>
// @returns LocationTag
// @group identity
// @description
// Returns a copy of the location with a changed yaw value.
// -->
tagProcessor.registerTag(LocationTag.class, "with_yaw", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag output = object.clone();
output.setYaw((float) attribute.getDoubleParam());
return output;
});
// <--[tag]
// @attribute <LocationTag.with_pitch[<number>]>
// @returns LocationTag
// @group identity
// @description
// Returns a copy of the location with a changed pitch value.
// -->
tagProcessor.registerTag(LocationTag.class, "with_pitch", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag output = object.clone();
output.setPitch((float) attribute.getDoubleParam());
return output;
});
// <--[tag]
// @attribute <LocationTag.with_world[<world>]>
// @returns LocationTag
// @group identity
// @description
// Returns a copy of the location with a changed world value.
// -->
tagProcessor.registerTag(LocationTag.class, "with_world", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
LocationTag output = object.clone();
WorldTag world = attribute.paramAsType(WorldTag.class);
output.setWorld(world.getWorld());
return output;
});
// <--[tag]
// @attribute <LocationTag.note_name>
// @returns ElementTag
// @group identity
// @description
// Gets the name of a noted LocationTag. If the location isn't noted, this is null.
// -->
tagProcessor.registerTag(ElementTag.class, "note_name", (attribute, object) -> {
String noteName = NoteManager.getSavedId((object));
if (noteName == null) {
return null;
}
return new ElementTag(noteName);
}, "notable_name");
// <--[tag]
// @attribute <LocationTag.add[<location>]>
// @returns LocationTag
// @group math
// @description
// Returns the location with the specified coordinates added to it.
// -->
tagProcessor.registerTag(LocationTag.class, "add", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
// TODO: Just LocationTag.valueOf?
String[] ints = attribute.getParam().replace("l@", "").split(",", 4);
if (ints.length >= 3) {
if (ArgumentHelper.matchesDouble(ints[0]) && ArgumentHelper.matchesDouble(ints[1]) && ArgumentHelper.matchesDouble(ints[2])) {
return new LocationTag(object.clone().add(Double.valueOf(ints[0]), Double.valueOf(ints[1]), Double.valueOf(ints[2])));
}
}
if (LocationTag.matches(attribute.getParam())) {
return object.clone().add(attribute.paramAsType(LocationTag.class));
}
return null;
});
// <--[tag]
// @attribute <LocationTag.sub[<location>]>
// @returns LocationTag
// @group math
// @description
// Returns the location with the specified coordinates subtracted from it.
// -->
tagProcessor.registerTag(LocationTag.class, "sub", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
// TODO: Just LocationTag.valueOf?
String[] ints = attribute.getParam().replace("l@", "").split(",", 4);
if (ints.length == 3 || ints.length == 4) {
if (ArgumentHelper.matchesDouble(ints[0]) && ArgumentHelper.matchesDouble(ints[1]) && ArgumentHelper.matchesDouble(ints[2])) {
return new LocationTag(object.clone().subtract(Double.valueOf(ints[0]), Double.valueOf(ints[1]), Double.valueOf(ints[2])));
}
}
if (LocationTag.matches(attribute.getParam())) {
return new LocationTag(object.clone().subtract(attribute.paramAsType(LocationTag.class)));
}
return null;
});
// <--[tag]
// @attribute <LocationTag.mul[<length>]>
// @returns LocationTag
// @group math
// @description
// Returns the location multiplied by the specified length.
// -->
tagProcessor.registerTag(LocationTag.class, "mul", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
return new LocationTag(object.clone().multiply(Double.parseDouble(attribute.getParam())));
});
// <--[tag]
// @attribute <LocationTag.div[<length>]>
// @returns LocationTag
// @group math
// @description
// Returns the location divided by the specified length.
// -->
tagProcessor.registerTag(LocationTag.class, "div", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
return new LocationTag(object.clone().multiply(1D / Double.parseDouble(attribute.getParam())));
});
// <--[tag]
// @attribute <LocationTag.normalize>
// @returns LocationTag
// @group math
// @description
// Returns a 1-length vector in the same direction as this vector location.
// -->
tagProcessor.registerTag(LocationTag.class, "normalize", (attribute, object) -> {
double len = Math.sqrt(Math.pow(object.getX(), 2) + Math.pow(object.getY(), 2) + Math.pow(object.getZ(), 2));
if (len == 0) {
len = 1;
}
return new LocationTag(object.clone().multiply(1D / len));
});
// <--[tag]
// @attribute <LocationTag.vector_length>
// @returns ElementTag(Decimal)
// @synonyms LocationTag.magnitude
// @group math
// @description
// Returns the 3D length of the vector/location.
// -->
tagProcessor.registerTag(ElementTag.class, "vector_length", (attribute, object) -> {
return new ElementTag(Math.sqrt(Math.pow(object.getX(), 2) + Math.pow(object.getY(), 2) + Math.pow(object.getZ(), 2)));
});
// <--[tag]
// @attribute <LocationTag.vector_to_face>
// @returns ElementTag
// @description
// Returns the name of the BlockFace represented by a normal vector.
// Result can be any of the following:
// NORTH, EAST, SOUTH, WEST, UP, DOWN, NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST,
// WEST_NORTH_WEST, NORTH_NORTH_WEST, NORTH_NORTH_EAST, EAST_NORTH_EAST, EAST_SOUTH_EAST,
// SOUTH_SOUTH_EAST, SOUTH_SOUTH_WEST, WEST_SOUTH_WEST, SELF
// -->
tagProcessor.registerTag(ElementTag.class, "vector_to_face", (attribute, object) -> {
BlockFace face = Utilities.faceFor(object.toVector());
if (face != null) {
return new ElementTag(face.name());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.distance_squared[<location>]>
// @returns ElementTag(Decimal)
// @group math
// @description
// Returns the distance between 2 locations, squared.
// -->
tagProcessor.registerTag(ElementTag.class, "distance_squared", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
if (LocationTag.matches(attribute.getParam())) {
LocationTag toLocation = attribute.paramAsType(LocationTag.class);
if (!object.getWorldName().equalsIgnoreCase(toLocation.getWorldName())) {
if (!attribute.hasAlternative()) {
Debug.echoError("Can't measure distance between two different worlds!");
}
return null;
}
return new ElementTag(object.distanceSquared(toLocation));
}
return null;
});
// <--[tag]
// @attribute <LocationTag.distance[<location>]>
// @returns ElementTag(Decimal)
// @group math
// @description
// Returns the distance between 2 locations.
// -->
tagProcessor.registerTag(ElementTag.class, "distance", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
if (LocationTag.matches(attribute.getParam())) {
LocationTag toLocation = attribute.paramAsType(LocationTag.class);
// -->
if (attribute.startsWith("horizontal", 2)) {
// -->
if (attribute.startsWith("multiworld", 3)) {
attribute.fulfill(2);
return new ElementTag(Math.sqrt(Math.pow(object.getX() - toLocation.getX(), 2) + Math.pow(object.getZ() - toLocation.getZ(), 2)));
}
attribute.fulfill(1);
if (object.getWorldName().equalsIgnoreCase(toLocation.getWorldName())) {
return new ElementTag(Math.sqrt(Math.pow(object.getX() - toLocation.getX(), 2) + Math.pow(object.getZ() - toLocation.getZ(), 2)));
}
} else // -->
if (attribute.startsWith("vertical", 2)) {
// -->
if (attribute.startsWith("multiworld", 3)) {
attribute.fulfill(2);
return new ElementTag(Math.abs(object.getY() - toLocation.getY()));
}
attribute.fulfill(1);
if (object.getWorldName().equalsIgnoreCase(toLocation.getWorldName())) {
return new ElementTag(Math.abs(object.getY() - toLocation.getY()));
}
}
if (!object.getWorldName().equalsIgnoreCase(toLocation.getWorldName())) {
if (!attribute.hasAlternative()) {
Debug.echoError("Can't measure distance between two different worlds!");
}
return null;
} else {
return new ElementTag(object.distance(toLocation));
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.is_within_border>
// @returns ElementTag(Boolean)
// @group world
// @description
// Returns whether the location is within the world border.
// -->
tagProcessor.registerTag(ElementTag.class, "is_within_border", (attribute, object) -> {
return new ElementTag(object.getWorld().getWorldBorder().isInside(object));
});
// <--[tag]
// @attribute <LocationTag.is_within[<area>]>
// @returns ElementTag(Boolean)
// @group areas
// @description
// Returns whether the location is within the specified area (cuboid, ellipsoid, polygon, ...).
// -->
tagProcessor.registerTag(ElementTag.class, "is_within", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
if (EllipsoidTag.matches(attribute.getParam())) {
EllipsoidTag ellipsoid = attribute.paramAsType(EllipsoidTag.class);
if (ellipsoid != null) {
return new ElementTag(ellipsoid.contains(object));
}
} else if (PolygonTag.matches(attribute.getParam())) {
PolygonTag polygon = attribute.paramAsType(PolygonTag.class);
if (polygon != null) {
return new ElementTag(polygon.doesContainLocation(object));
}
} else {
CuboidTag cuboid = attribute.paramAsType(CuboidTag.class);
if (cuboid != null) {
return new ElementTag(cuboid.isInsideCuboid(object));
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.to_ellipsoid[<size>]>
// @returns EllipsoidTag
// @group areas
// @description
// Returns an ellipsoid centered at this location with the specified size.
// Size input is a vector of x,y,z size.
// -->
tagProcessor.registerTag(EllipsoidTag.class, "to_ellipsoid", (attribute, object) -> {
if (!attribute.hasParam()) {
attribute.echoError("to_ellipsoid[...] tag must have input.");
return null;
}
return new EllipsoidTag(object.clone(), attribute.getParamObject().asType(LocationTag.class, attribute.context));
});
// <--[tag]
// @attribute <LocationTag.to_cuboid[<location>]>
// @returns CuboidTag
// @group areas
// @description
// Returns a cuboid from this location to the specified location.
// -->
tagProcessor.registerTag(CuboidTag.class, "to_cuboid", (attribute, object) -> {
if (!attribute.hasParam()) {
attribute.echoError("to_cuboid[...] tag must have input.");
return null;
}
return new CuboidTag(object.clone(), attribute.getParamObject().asType(LocationTag.class, attribute.context));
});
// <--[tag]
// @attribute <LocationTag.biome>
// @mechanism LocationTag.biome
// @returns BiomeTag
// @group world
// @description
// Returns the biome at the location.
// -->
tagProcessor.registerTag(ObjectTag.class, "biome", (attribute, object) -> {
if (attribute.startsWith("formatted", 2)) {
Deprecations.locationBiomeFormattedTag.warn(attribute.context);
attribute.fulfill(1);
return new ElementTag(CoreUtilities.toLowerCase(object.getBiomeForTag(attribute).getName()).replace('_', ' '));
}
return new BiomeTag(object.getBiomeForTag(attribute));
});
// <--[tag]
// @attribute <LocationTag.cuboids>
// @returns ListTag(CuboidTag)
// @group areas
// @description
// Returns a ListTag of all noted CuboidTags that include this location.
// -->
tagProcessor.registerTag(ListTag.class, "cuboids", (attribute, object) -> {
List<CuboidTag> cuboids = CuboidTag.getNotableCuboidsContaining(object);
ListTag cuboid_list = new ListTag();
for (CuboidTag cuboid : cuboids) {
cuboid_list.addObject(cuboid);
}
return cuboid_list;
});
// <--[tag]
// @attribute <LocationTag.ellipsoids>
// @returns ListTag(EllipsoidTag)
// @group areas
// @description
// Returns a ListTag of all noted EllipsoidTags that include this location.
// -->
tagProcessor.registerTag(ListTag.class, "ellipsoids", (attribute, object) -> {
List<EllipsoidTag> ellipsoids = EllipsoidTag.getNotableEllipsoidsContaining(object);
ListTag ellipsoid_list = new ListTag();
for (EllipsoidTag ellipsoid : ellipsoids) {
ellipsoid_list.addObject(ellipsoid);
}
return ellipsoid_list;
});
// <--[tag]
// @attribute <LocationTag.polygons>
// @returns ListTag(PolygonTag)
// @group areas
// @description
// Returns a ListTag of all noted PolygonTags that include this location.
// -->
tagProcessor.registerTag(ListTag.class, "polygons", (attribute, object) -> {
List<PolygonTag> polygons = PolygonTag.getNotedPolygonsContaining(object);
ListTag polygon_list = new ListTag();
for (PolygonTag polygon : polygons) {
polygon_list.addObject(polygon);
}
return polygon_list;
});
// <--[tag]
// @attribute <LocationTag.is_liquid>
// @returns ElementTag(Boolean)
// @group world
// @description
// Returns whether the block at the location is a liquid.
// -->
tagProcessor.registerTag(ElementTag.class, "is_liquid", (attribute, object) -> {
Block b = object.getBlockForTag(attribute);
if (b != null) {
try {
NMSHandler.getChunkHelper().changeChunkServerThread(object.getWorld());
return new ElementTag(b.isLiquid());
} finally {
NMSHandler.getChunkHelper().restoreServerThread(object.getWorld());
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.light>
// @returns ElementTag(Number)
// @group world
// @description
// Returns the total amount of light on the location.
// -->
tagProcessor.registerTag(ElementTag.class, "light", (attribute, object) -> {
Block b = object.getBlockForTag(attribute);
if (b != null) {
try {
NMSHandler.getChunkHelper().changeChunkServerThread(object.getWorld());
// -->
if (attribute.startsWith("blocks", 2)) {
attribute.fulfill(1);
return new ElementTag(object.getBlockForTag(attribute).getLightFromBlocks());
}
// -->
if (attribute.startsWith("sky", 2)) {
attribute.fulfill(1);
return new ElementTag(object.getBlockForTag(attribute).getLightFromSky());
}
return new ElementTag(object.getBlockForTag(attribute).getLightLevel());
} finally {
NMSHandler.getChunkHelper().restoreServerThread(object.getWorld());
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.power>
// @returns ElementTag(Number)
// @group world
// @description
// Returns the current redstone power level of a block.
// -->
tagProcessor.registerTag(ElementTag.class, "power", (attribute, object) -> {
Block b = object.getBlockForTag(attribute);
if (b != null) {
try {
NMSHandler.getChunkHelper().changeChunkServerThread(object.getWorld());
return new ElementTag(object.getBlockForTag(attribute).getBlockPower());
} finally {
NMSHandler.getChunkHelper().restoreServerThread(object.getWorld());
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.lectern_page>
// @returns ElementTag(Number)
// @mechanism LocationTag.lectern_page
// @group world
// @description
// Returns the current page on display in the book on this Lectern block.
// -->
tagProcessor.registerTag(ElementTag.class, "lectern_page", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (state instanceof Lectern) {
return new ElementTag(((Lectern) state).getPage());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.has_loot_table>
// @returns ElementTag(Boolean)
// @mechanism LocationTag.clear_loot_table
// @group world
// @description
// Returns an element indicating whether the chest at this location has a loot-table set.
// -->
tagProcessor.registerTag(ElementTag.class, "has_loot_table", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (state instanceof Lootable) {
return new ElementTag(((Lootable) state).getLootTable() != null);
}
return null;
});
// <--[tag]
// @attribute <LocationTag.loot_table_id>
// @returns ElementTag
// @mechanism LocationTag.clear_loot_table
// @group world
// @description
// Returns an element indicating the minecraft key for the loot-table for the chest at this location (if any).
// -->
tagProcessor.registerTag(ElementTag.class, "loot_table_id", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (state instanceof Lootable) {
LootTable table = ((Lootable) state).getLootTable();
if (table != null) {
return new ElementTag(table.getKey().toString());
}
}
return null;
});
// <--[tag]
// @attribute <LocationTag.tree_distance>
// @returns ElementTag(Number)
// @group world
// @description
// Returns a number of how many blocks away from a connected tree leaves are.
// Defaults to 7 if not connected to a tree.
// -->
tagProcessor.registerTag(ElementTag.class, "tree_distance", (attribute, object) -> {
MaterialTag material = new MaterialTag(object.getBlockForTag(attribute));
if (MaterialPersistent.describes(material)) {
return new ElementTag(MaterialPersistent.getFrom(material).getDistance());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.command_block_name>
// @returns ElementTag
// @mechanism LocationTag.command_block_name
// @group world
// @description
// Returns the name a command block is set to.
// -->
tagProcessor.registerTag(ElementTag.class, "command_block_name", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CommandBlock)) {
return null;
}
return new ElementTag(((CommandBlock) object.getBlockStateForTag(attribute)).getName());
});
// <--[tag]
// @attribute <LocationTag.command_block>
// @returns ElementTag
// @mechanism LocationTag.command_block
// @group world
// @description
// Returns the command a command block is set to.
// -->
tagProcessor.registerTag(ElementTag.class, "command_block", (attribute, object) -> {
if (!(object.getBlockStateForTag(attribute) instanceof CommandBlock)) {
return null;
}
return new ElementTag(((CommandBlock) object.getBlockStateForTag(attribute)).getCommand());
});
// <--[tag]
// @attribute <LocationTag.brewing_time>
// @returns DurationTag
// @mechanism LocationTag.brewing_time
// @group world
// @description
// Returns the brewing time a brewing stand has left.
// -->
tagProcessor.registerTag(DurationTag.class, "brewing_time", (attribute, object) -> {
return new DurationTag((long) ((BrewingStand) object.getBlockStateForTag(attribute)).getBrewingTime());
});
// <--[tag]
// @attribute <LocationTag.brewing_fuel_level>
// @returns ElementTag(Number)
// @mechanism LocationTag.brewing_fuel_level
// @group world
// @description
// Returns the level of fuel a brewing stand has. Each unit of fuel can power one brewing operation.
// -->
tagProcessor.registerTag(ElementTag.class, "brewing_fuel_level", (attribute, object) -> {
return new ElementTag(((BrewingStand) object.getBlockStateForTag(attribute)).getFuelLevel());
});
// <--[tag]
// @attribute <LocationTag.furnace_burn_duration>
// @returns DurationTag
// @mechanism LocationTag.furnace_burn_duration
// @group world
// @description
// Returns the burn time a furnace has left.
// -->
tagProcessor.registerTag(DurationTag.class, "furnace_burn_duration", (attribute, object) -> {
return new DurationTag((long) ((Furnace) object.getBlockStateForTag(attribute)).getBurnTime());
});
tagProcessor.registerTag(ElementTag.class, "furnace_burn_time", (attribute, object) -> {
Deprecations.furnaceTimeTags.warn(attribute.context);
return new ElementTag(((Furnace) object.getBlockStateForTag(attribute)).getBurnTime());
});
// <--[tag]
// @attribute <LocationTag.furnace_cook_duration>
// @returns DurationTag
// @mechanism LocationTag.furnace_cook_duration
// @group world
// @description
// Returns the cook time a furnace has been cooking its current item for.
// -->
tagProcessor.registerTag(DurationTag.class, "furnace_cook_duration", (attribute, object) -> {
return new DurationTag((long) ((Furnace) object.getBlockStateForTag(attribute)).getCookTime());
});
tagProcessor.registerTag(ElementTag.class, "furnace_cook_time", (attribute, object) -> {
Deprecations.furnaceTimeTags.warn(attribute.context);
return new ElementTag(((Furnace) object.getBlockStateForTag(attribute)).getCookTime());
});
// <--[tag]
// @attribute <LocationTag.furnace_cook_duration_total>
// @returns DurationTag
// @mechanism LocationTag.furnace_cook_duration_total
// @group world
// @description
// Returns the total cook time a furnace has left.
// -->
tagProcessor.registerTag(DurationTag.class, "furnace_cook_duration_total", (attribute, object) -> {
return new DurationTag((long) ((Furnace) object.getBlockStateForTag(attribute)).getCookTimeTotal());
});
tagProcessor.registerTag(ElementTag.class, "furnace_cook_time_total", (attribute, object) -> {
Deprecations.furnaceTimeTags.warn(attribute.context);
return new ElementTag(((Furnace) object.getBlockStateForTag(attribute)).getCookTimeTotal());
});
// <--[tag]
// @attribute <LocationTag.beacon_tier>
// @returns ElementTag(Number)
// @group world
// @description
// Returns the tier level of a beacon pyramid (0-4).
// -->
tagProcessor.registerTag(ElementTag.class, "beacon_tier", (attribute, object) -> {
return new ElementTag(((Beacon) object.getBlockStateForTag(attribute)).getTier());
});
// <--[tag]
// @attribute <LocationTag.beacon_primary_effect>
// @returns ElementTag
// @mechanism LocationTag.beacon_primary_effect
// @group world
// @description
// Returns the primary effect of the beacon. The return is simply a potion effect type name.
// -->
tagProcessor.registerTag(ElementTag.class, "beacon_primary_effect", (attribute, object) -> {
PotionEffect effect = ((Beacon) object.getBlockStateForTag(attribute)).getPrimaryEffect();
if (effect == null) {
return null;
}
return new ElementTag(effect.getType().getName());
});
// <--[tag]
// @attribute <LocationTag.beacon_secondary_effect>
// @returns ElementTag
// @mechanism LocationTag.beacon_secondary_effect
// @group world
// @description
// Returns the secondary effect of the beacon. The return is simply a potion effect type name.
// -->
tagProcessor.registerTag(ElementTag.class, "beacon_secondary_effect", (attribute, object) -> {
PotionEffect effect = ((Beacon) object.getBlockStateForTag(attribute)).getSecondaryEffect();
if (effect == null) {
return null;
}
return new ElementTag(effect.getType().getName());
});
// <--[tag]
// @attribute <LocationTag.attached_to>
// @returns LocationTag
// @group world
// @description
// Returns the block this block is attached to.
// (For buttons, levers, signs, torches, etc).
// -->
tagProcessor.registerTag(LocationTag.class, "attached_to", (attribute, object) -> {
BlockFace face = BlockFace.SELF;
MaterialTag material = new MaterialTag(object.getBlockForTag(attribute));
if (material.getMaterial() == Material.TORCH || material.getMaterial() == Material.REDSTONE_TORCH || material.getMaterial() == Material.SOUL_TORCH) {
face = BlockFace.DOWN;
} else if (material.getMaterial() == Material.WALL_TORCH || material.getMaterial() == Material.REDSTONE_WALL_TORCH || material.getMaterial() == Material.SOUL_WALL_TORCH) {
face = ((Directional) material.getModernData()).getFacing().getOppositeFace();
} else if (MaterialSwitchFace.describes(material)) {
face = MaterialSwitchFace.getFrom(material).getAttachedTo();
} else if (material.hasModernData() && material.getModernData() instanceof org.bukkit.block.data.type.WallSign) {
face = ((org.bukkit.block.data.type.WallSign) material.getModernData()).getFacing().getOppositeFace();
} else {
MaterialData data = object.getBlockStateForTag(attribute).getData();
if (data instanceof Attachable) {
face = ((Attachable) data).getAttachedFace();
}
}
if (face != BlockFace.SELF) {
return new LocationTag(object.getBlockForTag(attribute).getRelative(face).getLocation());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.other_block>
// @returns LocationTag
// @group world
// @description
// If the location is part of a double-block structure (double chests, double plants, doors, beds, etc),
// returns the location of the other block in the double-block structure.
// -->
tagProcessor.registerTag(LocationTag.class, "other_block", (attribute, object) -> {
Block b = object.getBlockForTag(attribute);
MaterialTag material = new MaterialTag(b);
if (MaterialHalf.describes(material)) {
Vector vec = MaterialHalf.getFrom(material).getRelativeBlockVector();
if (vec != null) {
return new LocationTag(object.clone().add(vec));
}
}
if (!attribute.hasAlternative()) {
Debug.echoError("Block of type " + object.getBlockTypeForTag(attribute).name() + " isn't supported by other_block.");
}
return null;
});
// <--[tag]
// @attribute <LocationTag.custom_name>
// @returns ElementTag
// @mechanism LocationTag.custom_name
// @group world
// @description
// Returns the custom name of this block.
// Only works for nameable blocks, such as chests and dispensers.
// -->
tagProcessor.registerTag(ElementTag.class, "custom_name", (attribute, object) -> {
if (object.getBlockStateForTag(attribute) instanceof Nameable) {
return new ElementTag(((Nameable) object.getBlockStateForTag(attribute)).getCustomName());
}
return null;
});
// <--[tag]
// @attribute <LocationTag.local_difficulty>
// @returns ElementTag(Decimal)
// @group world
// @description
// Returns the local difficulty (damage scaler) at the location.
// This is based internally on multiple factors, including <@link tag ChunkTag.inhabited_time> and <@link tag WorldTag.difficulty>.
// -->
tagProcessor.registerTag(ElementTag.class, "local_difficulty", (attribute, object) -> {
return new ElementTag(NMSHandler.getWorldHelper().getLocalDifficulty(object));
});
// <--[tag]
// @attribute <LocationTag.jukebox_record>
// @returns ItemTag
// @mechanism LocationTag.jukebox_record
// @group world
// @description
// Returns the record item currently inside the jukebox.
// If there's no record, will return air.
// -->
tagProcessor.registerTag(ItemTag.class, "jukebox_record", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof Jukebox)) {
attribute.echoError("'jukebox_record' tag is only valid for jukebox blocks.");
return null;
}
return new ItemTag(((Jukebox) state).getRecord());
});
// <--[tag]
// @attribute <LocationTag.jukebox_is_playing>
// @returns ElementTag
// @mechanism LocationTag.jukebox_play
// @group world
// @description
// Returns whether the jukebox is currently playing a song.
// -->
tagProcessor.registerTag(ElementTag.class, "jukebox_is_playing", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof Jukebox)) {
attribute.echoError("'jukebox_is_playing' tag is only valid for jukebox blocks.");
return null;
}
return new ElementTag(((Jukebox) state).isPlaying());
});
// <--[tag]
// @attribute <LocationTag.age>
// @returns DurationTag
// @mechanism LocationTag.age
// @group world
// @description
// Returns the age of an end gateway.
// -->
tagProcessor.registerTag(DurationTag.class, "age", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof EndGateway)) {
attribute.echoError("'age' tag is only valid for end_gateway blocks.");
return null;
}
return new DurationTag(((EndGateway) state).getAge());
});
// <--[tag]
// @attribute <LocationTag.is_exact_teleport>
// @returns ElementTag(Boolean)
// @mechanism LocationTag.is_exact_teleport
// @group world
// @description
// Returns whether an end gateway is 'exact teleport' - if false, the destination will be randomly chosen *near* the destination.
// -->
tagProcessor.registerTag(ElementTag.class, "is_exact_teleport", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof EndGateway)) {
attribute.echoError("'is_exact_teleport' tag is only valid for end_gateway blocks.");
return null;
}
return new ElementTag(((EndGateway) state).isExactTeleport());
});
// <--[tag]
// @attribute <LocationTag.exit_location>
// @returns LocationTag
// @mechanism LocationTag.exit_location
// @group world
// @description
// Returns the exit location of an end gateway block.
// -->
tagProcessor.registerTag(LocationTag.class, "exit_location", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof EndGateway)) {
attribute.echoError("'exit_location' tag is only valid for end_gateway blocks.");
return null;
}
Location loc = ((EndGateway) state).getExitLocation();
if (loc == null) {
return null;
}
return new LocationTag(loc);
});
// <--[tag]
// @attribute <LocationTag.is_in[<matcher>]>
// @returns ElementTag(Boolean)
// @group areas
// @description
// Returns whether the location is in an area, using the same logic as an event "in" switch.
// Invalid input may produce odd error messages, as this is passed through the event system as a fake event.
// -->
tagProcessor.registerTag(ElementTag.class, "is_in", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
return new ElementTag(BukkitScriptEvent.inCheckInternal(attribute.context, "is_in tag", object, attribute.getParam(), "is_in tag", "is_in tag"));
});
// <--[tag]
// @attribute <LocationTag.campfire_items>
// @returns ListTag(ItemTag)
// @mechanism LocationTag.campfire_items
// @group world
// @description
// Returns a list of items currently in this campfire.
// This list has air items in empty slots, and is always sized exactly the same as the number of spaces a campfire has.
// (A standard campfire has exactly 4 slots).
// -->
tagProcessor.registerTag(ListTag.class, "campfire_items", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof Campfire)) {
return null;
}
Campfire fire = (Campfire) state;
ListTag output = new ListTag();
for (int i = 0; i < fire.getSize(); i++) {
output.addObject(new ItemTag(fire.getItem(i)));
}
return output;
});
// <--[tag]
// @attribute <LocationTag.is_spawnable>
// @returns ElementTag(Boolean)
// @group world
// @description
// Returns whether the location is safe to spawn at, for a player or player-like entity.
// Specifically this verifies that:
// - The block above this location is air.
// - The block at this location is non-solid.
// - The block below this location is solid.
// - All relevant blocks are not dangerous (like fire, lava, etc.), or unstable/small/awkward (like fences, doors, etc.) or otherwise likely to go wrong (like pressure plates).
// -->
tagProcessor.registerTag(ElementTag.class, "is_spawnable", (attribute, object) -> {
return new ElementTag(SpawnableHelper.isSpawnable(object));
});
// <--[tag]
// @attribute <LocationTag.sign_glowing>
// @returns ElementTag(Boolean)
// @mechanism LocationTag.sign_glowing
// @group world
// @description
// Returns whether the location is a Sign block that is glowing.
// -->
tagProcessor.registerTag(ElementTag.class, "sign_glowing", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof Sign)) {
attribute.echoError("Location is not a valid Sign block.");
return null;
}
return new ElementTag(((Sign) state).isGlowingText());
});
// <--[tag]
// @attribute <LocationTag.sign_glow_color>
// @returns ElementTag
// @mechanism LocationTag.sign_glow_color
// @group world
// @description
// Returns the name of the glow-color of the sign at the location.
// See also <@link tag LocationTag.sign_glowing>
// -->
tagProcessor.registerTag(ElementTag.class, "sign_glow_color", (attribute, object) -> {
BlockState state = object.getBlockStateForTag(attribute);
if (!(state instanceof Sign)) {
attribute.echoError("Location is not a valid Sign block.");
return null;
}
return new ElementTag(((Sign) state).getColor().name());
});
}
use of org.bukkit.loot.Lootable in project Denizen-For-Bukkit by DenizenScript.
the class EntityTag method registerTags.
public static void registerTags() {
AbstractFlagTracker.registerFlagHandlers(tagProcessor);
PropertyParser.registerPropertyTagHandlers(EntityTag.class, tagProcessor);
// ///////////////////
// UNSPAWNED ATTRIBUTES
// ///////////////
// <--[tag]
// @attribute <EntityTag.entity_type>
// @returns ElementTag
// @group data
// @description
// Returns the type of the entity.
// -->
tagProcessor.registerTag(ElementTag.class, "entity_type", (attribute, object) -> {
return new ElementTag(object.entity_type.getName());
});
// <--[tag]
// @attribute <EntityTag.translated_name>
// @returns ElementTag
// @description
// Returns the localized name of the entity.
// Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
// -->
tagProcessor.registerTag(ElementTag.class, "translated_name", (attribute, object) -> {
String key = object.getEntityType().getBukkitEntityType().getKey().getKey();
return new ElementTag(ChatColor.COLOR_CHAR + "[translate=entity.minecraft." + key + "]");
});
// <--[tag]
// @attribute <EntityTag.is_spawned>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity is spawned.
// -->
tagProcessor.registerTag(ElementTag.class, "is_spawned", (attribute, object) -> {
return new ElementTag(object.isSpawned());
});
// <--[tag]
// @attribute <EntityTag.eid>
// @returns ElementTag(Number)
// @group data
// @description
// Returns the entity's temporary server entity ID.
// -->
registerSpawnedOnlyTag(ElementTag.class, "eid", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().getEntityId());
});
// <--[tag]
// @attribute <EntityTag.uuid>
// @returns ElementTag
// @group data
// @description
// Returns the permanent unique ID of the entity.
// Works with offline players.
// -->
tagProcessor.registerTag(ElementTag.class, "uuid", (attribute, object) -> {
return new ElementTag(object.getUUID().toString());
});
// <--[tag]
// @attribute <EntityTag.script>
// @returns ScriptTag
// @group data
// @description
// Returns the entity script that spawned this entity, if any.
// -->
tagProcessor.registerTag(ScriptTag.class, "script", (attribute, object) -> {
if (object.entityScript == null) {
return null;
}
ScriptTag tag = new ScriptTag(object.entityScript);
if (tag.isValid()) {
return tag;
}
return null;
});
// <--[tag]
// @attribute <EntityTag.scriptname>
// @returns ElementTag
// @deprecated use ".script.name" instead.
// @group data
// @description
// Use ".script.name" instead.
// -->
tagProcessor.registerTag(ElementTag.class, "scriptname", (attribute, object) -> {
Deprecations.hasScriptTags.warn(attribute.context);
if (object.entityScript == null) {
return null;
}
return new ElementTag(object.entityScript);
});
// ///////////////////
// IDENTIFICATION ATTRIBUTES
// ///////////////
registerSpawnedOnlyTag(ObjectTag.class, "custom_id", (attribute, object) -> {
Deprecations.entityCustomIdTag.warn(attribute.context);
if (CustomNBT.hasCustomNBT(object.getLivingEntity(), "denizen-script-id")) {
return new ScriptTag(CustomNBT.getCustomNBT(object.getLivingEntity(), "denizen-script-id"));
} else {
return new ElementTag(object.getBukkitEntity().getType().name());
}
});
// <--[tag]
// @attribute <EntityTag.name>
// @returns ElementTag
// @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.
// -->
registerSpawnedOnlyTag(ElementTag.class, "name", (attribute, object) -> {
return new ElementTag(object.getName(), true);
});
// ///////////////////
// INVENTORY ATTRIBUTES
// ///////////////
// <--[tag]
// @attribute <EntityTag.saddle>
// @returns ItemTag
// @group inventory
// @description
// If the entity is a horse or pig, returns the saddle as a ItemTag, or air if none.
// -->
registerSpawnedOnlyTag(ItemTag.class, "saddle", (attribute, object) -> {
if (object.getLivingEntity() instanceof AbstractHorse) {
return new ItemTag(((AbstractHorse) object.getLivingEntity()).getInventory().getSaddle());
} else if (object.getLivingEntity() instanceof Steerable) {
return new ItemTag(((Steerable) object.getLivingEntity()).hasSaddle() ? Material.SADDLE : Material.AIR);
}
return null;
});
// <--[tag]
// @attribute <EntityTag.horse_armor>
// @returns ItemTag
// @group inventory
// @description
// If the entity is a horse, returns the item equipped as the horses armor, or air if none.
// -->
registerSpawnedOnlyTag(ItemTag.class, "horse_armor", (attribute, object) -> {
if (object.getLivingEntity() instanceof Horse) {
return new ItemTag(((Horse) object.getLivingEntity()).getInventory().getArmor());
}
return null;
}, "horse_armour");
// <--[tag]
// @attribute <EntityTag.has_saddle>
// @returns ElementTag(Boolean)
// @group inventory
// @description
// If the entity is a pig or horse, returns whether it has a saddle equipped.
// -->
registerSpawnedOnlyTag(ElementTag.class, "has_saddle", (attribute, object) -> {
if (object.getLivingEntity() instanceof AbstractHorse) {
return new ElementTag(((AbstractHorse) object.getLivingEntity()).getInventory().getSaddle().getType() == Material.SADDLE);
} else if (object.getLivingEntity() instanceof Steerable) {
return new ElementTag(((Steerable) object.getLivingEntity()).hasSaddle());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.is_trading>
// @returns ElementTag(Boolean)
// @description
// Returns whether the villager entity is trading.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_trading", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Merchant) {
return new ElementTag(((Merchant) object.getBukkitEntity()).isTrading());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.trading_with>
// @returns PlayerTag
// @description
// Returns the player who is trading with the villager entity, or null if it is not trading.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "trading_with", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Merchant && ((Merchant) object.getBukkitEntity()).getTrader() != null) {
return new EntityTag(((Merchant) object.getBukkitEntity()).getTrader()).getDenizenObject();
}
return null;
});
// ///////////////////
// LOCATION ATTRIBUTES
// ///////////////
// <--[tag]
// @attribute <EntityTag.map_trace>
// @returns LocationTag
// @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.
// -->
registerSpawnedOnlyTag(LocationTag.class, "map_trace", (attribute, object) -> {
EntityHelper.MapTraceResult mtr = NMSHandler.getEntityHelper().mapTrace(object.getLivingEntity(), 200);
if (mtr != null) {
double x = 0;
double y;
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 LocationTag(null, Math.round(x), Math.round(y));
}
return null;
});
// <--[tag]
// @attribute <EntityTag.can_see[<entity>]>
// @returns ElementTag(Boolean)
// @group location
// @description
// Returns whether the entity can see the specified other entity (has an uninterrupted line-of-sight).
// -->
registerSpawnedOnlyTag(ElementTag.class, "can_see", (attribute, object) -> {
if (object.isLivingEntity() && attribute.hasParam() && EntityTag.matches(attribute.getParam())) {
EntityTag toEntity = attribute.paramAsType(EntityTag.class);
if (toEntity != null && toEntity.isSpawnedOrValidForTag()) {
return new ElementTag(object.getLivingEntity().hasLineOfSight(toEntity.getBukkitEntity()));
}
}
return null;
});
// <--[tag]
// @attribute <EntityTag.eye_location>
// @returns LocationTag
// @group location
// @description
// Returns the location of the entity's eyes.
// -->
registerSpawnedOnlyTag(LocationTag.class, "eye_location", (attribute, object) -> {
return new LocationTag(object.getEyeLocation());
});
// <--[tag]
// @attribute <EntityTag.eye_height>
// @returns ElementTag(Number)
// @group location
// @description
// Returns the height of the entity's eyes above its location.
// -->
registerSpawnedOnlyTag(ElementTag.class, "eye_height", (attribute, object) -> {
if (object.isLivingEntity()) {
return new ElementTag(object.getLivingEntity().getEyeHeight());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.cursor_on_solid[(<range>)]>
// @returns LocationTag
// @group location
// @description
// Returns the location of the solid block the entity is looking at.
// Optionally, specify a maximum range to find the location from (defaults to 200).
// This uses logic equivalent to <@link tag LocationTag.precise_cursor_on_block[(range)]>.
// Note that this will return null if there is no solid block in range.
// This only uses solid blocks, ie it ignores passable blocks like tall-grass. Use <@link tag EntityTag.cursor_on> to include passable blocks.
// -->
registerSpawnedOnlyTag(LocationTag.class, "cursor_on_solid", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 200;
}
RayTraceResult traced = object.getWorld().rayTraceBlocks(object.getEyeLocation(), object.getEyeLocation().getDirection(), range, FluidCollisionMode.NEVER, true);
if (traced != null && traced.getHitBlock() != null) {
return new LocationTag(traced.getHitBlock().getLocation());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.cursor_on[(<range>)]>
// @returns LocationTag
// @group location
// @description
// Returns the location of the block the entity is looking at.
// Optionally, specify a maximum range to find the location from (defaults to 200).
// This uses logic equivalent to <@link tag LocationTag.precise_cursor_on_block[(range)]>.
// Note that this will return null if there is no block in range.
// This uses all blocks, ie it includes passable blocks like tall-grass and water. Use <@link tag EntityTag.cursor_on_solid> to exclude passable blocks.
// -->
registerSpawnedOnlyTag(LocationTag.class, "cursor_on", (attribute, object) -> {
double range = attribute.getDoubleParam();
if (range <= 0) {
range = 200;
}
RayTraceResult traced = object.getWorld().rayTraceBlocks(object.getEyeLocation(), object.getEyeLocation().getDirection(), range, FluidCollisionMode.ALWAYS, false);
if (traced != null && traced.getHitBlock() != null) {
return new LocationTag(traced.getHitBlock().getLocation());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.location>
// @returns LocationTag
// @group location
// @description
// Returns the location of the entity.
// For living entities, this is at the center of their feet.
// For eye location, use <@link tag EntityTag.eye_location>
// Works with offline players.
// -->
registerSpawnedOnlyTag(LocationTag.class, "location", (attribute, object) -> {
return object.doLocationTag(attribute);
});
// <--[tag]
// @attribute <EntityTag.standing_on>
// @returns LocationTag
// @group location
// @description
// Returns the location of the block the entity is standing on top of (if on the ground, returns null if in the air).
// -->
registerSpawnedOnlyTag(LocationTag.class, "standing_on", (attribute, object) -> {
if (!object.getBukkitEntity().isOnGround()) {
return null;
}
Location loc = object.getBukkitEntity().getLocation().clone().subtract(0, 0.05f, 0);
return new LocationTag(loc.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
});
// <--[tag]
// @attribute <EntityTag.body_yaw>
// @returns ElementTag(Decimal)
// @group location
// @description
// Returns the entity's body yaw (separate from head yaw).
// -->
registerSpawnedOnlyTag(ElementTag.class, "body_yaw", (attribute, object) -> {
return new ElementTag(NMSHandler.getEntityHelper().getBaseYaw(object.getBukkitEntity()));
});
// <--[tag]
// @attribute <EntityTag.velocity>
// @returns LocationTag
// @group location
// @mechanism EntityTag.velocity
// @description
// Returns the movement velocity of the entity.
// Note: Does not accurately calculate player clientside movement velocity.
// -->
registerSpawnedOnlyTag(LocationTag.class, "velocity", (attribute, object) -> {
return new LocationTag(object.getBukkitEntity().getVelocity().toLocation(object.getBukkitEntity().getWorld()));
});
// <--[tag]
// @attribute <EntityTag.world>
// @returns WorldTag
// @group location
// @description
// Returns the world the entity is in. Works with offline players.
// -->
registerSpawnedOnlyTag(WorldTag.class, "world", (attribute, object) -> {
return new WorldTag(object.getBukkitEntity().getWorld());
});
// ///////////////////
// STATE ATTRIBUTES
// ///////////////
// <--[tag]
// @attribute <EntityTag.can_pickup_items>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.can_pickup_items
// @group attributes
// @description
// Returns whether the entity can pick up items.
// -->
registerSpawnedOnlyTag(ElementTag.class, "can_pickup_items", (attribute, object) -> {
if (object.isLivingEntity()) {
return new ElementTag(object.getLivingEntity().getCanPickupItems());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.fallingblock_material>
// @returns MaterialTag
// @mechanism EntityTag.fallingblock_type
// @group attributes
// @description
// Returns the material of a fallingblock-type entity.
// -->
registerSpawnedOnlyTag(MaterialTag.class, "fallingblock_material", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FallingBlock)) {
return null;
}
return new MaterialTag(((FallingBlock) object.getBukkitEntity()).getBlockData());
});
// <--[tag]
// @attribute <EntityTag.fall_distance>
// @returns ElementTag(Decimal)
// @mechanism EntityTag.fall_distance
// @group attributes
// @description
// Returns how far the entity has fallen.
// -->
registerSpawnedOnlyTag(ElementTag.class, "fall_distance", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().getFallDistance());
});
// <--[tag]
// @attribute <EntityTag.fire_time>
// @returns DurationTag
// @mechanism EntityTag.fire_time
// @group attributes
// @description
// Returns the duration for which the entity will remain on fire
// -->
registerSpawnedOnlyTag(DurationTag.class, "fire_time", (attribute, object) -> {
return new DurationTag(object.getBukkitEntity().getFireTicks() / 20);
});
// <--[tag]
// @attribute <EntityTag.on_fire>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether the entity is currently ablaze or not.
// -->
registerSpawnedOnlyTag(ElementTag.class, "on_fire", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().getFireTicks() > 0);
});
// <--[tag]
// @attribute <EntityTag.leash_holder>
// @returns EntityTag
// @mechanism EntityTag.leash_holder
// @group attributes
// @description
// Returns the leash holder of entity.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "leash_holder", (attribute, object) -> {
if (object.isLivingEntity() && object.getLivingEntity().isLeashed()) {
return new EntityTag(object.getLivingEntity().getLeashHolder()).getDenizenObject();
}
return null;
}, "get_leash_holder");
// <--[tag]
// @attribute <EntityTag.passengers>
// @returns ListTag(EntityTag)
// @mechanism EntityTag.passengers
// @group attributes
// @description
// Returns a list of the entity's passengers, if any.
// -->
registerSpawnedOnlyTag(ListTag.class, "passengers", (attribute, object) -> {
ArrayList<EntityTag> passengers = new ArrayList<>();
for (Entity ent : object.getBukkitEntity().getPassengers()) {
passengers.add(new EntityTag(ent));
}
return new ListTag(passengers);
}, "get_passengers");
// <--[tag]
// @attribute <EntityTag.passenger>
// @returns EntityTag
// @mechanism EntityTag.passenger
// @group attributes
// @description
// Returns the entity's passenger, if any.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "passenger", (attribute, object) -> {
if (!object.getBukkitEntity().isEmpty()) {
return new EntityTag(object.getBukkitEntity().getPassenger()).getDenizenObject();
}
return null;
}, "get_passenger");
// <--[tag]
// @attribute <EntityTag.shooter>
// @returns EntityTag
// @group attributes
// @mechanism EntityTag.shooter
// @synonyms EntityTag.arrow_firer,EntityTag.fishhook_shooter,EntityTag.snowball_thrower
// @description
// Returns the projectile's shooter, if any.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "shooter", (attribute, object) -> {
EntityTag shooter = object.getShooter();
if (shooter == null) {
return null;
}
return shooter.getDenizenObject();
}, "get_shooter");
// <--[tag]
// @attribute <EntityTag.left_shoulder>
// @returns EntityTag
// @mechanism EntityTag.left_shoulder
// @description
// Returns the entity on the entity's left shoulder.
// Only applies to player-typed entities.
// NOTE: The returned entity will not be spawned within the world,
// so most operations are invalid unless the entity is first spawned in.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "left_shoulder", (attribute, object) -> {
if (!(object.getLivingEntity() instanceof HumanEntity)) {
return null;
}
Entity e = ((HumanEntity) object.getLivingEntity()).getShoulderEntityLeft();
if (e == null) {
return null;
}
return new EntityTag(e).getDenizenObject();
});
// <--[tag]
// @attribute <EntityTag.right_shoulder>
// @returns EntityTag
// @mechanism EntityTag.right_shoulder
// @description
// Returns the entity on the entity's right shoulder.
// Only applies to player-typed entities.
// NOTE: The returned entity will not be spawned within the world,
// so most operations are invalid unless the entity is first spawned in.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "right_shoulder", (attribute, object) -> {
if (!(object.getLivingEntity() instanceof HumanEntity)) {
return null;
}
Entity e = ((HumanEntity) object.getLivingEntity()).getShoulderEntityRight();
if (e == null) {
return null;
}
return new EntityTag(e).getDenizenObject();
});
// <--[tag]
// @attribute <EntityTag.vehicle>
// @returns EntityTag
// @group attributes
// @description
// If the entity is in a vehicle, returns the vehicle as a EntityTag.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "vehicle", (attribute, object) -> {
if (object.getBukkitEntity().isInsideVehicle()) {
return new EntityTag(object.getBukkitEntity().getVehicle()).getDenizenObject();
}
return null;
}, "get_vehicle");
// <--[tag]
// @attribute <EntityTag.can_breed>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.can_breed
// @group attributes
// @description
// Returns whether the animal entity is capable of mating with another of its kind.
// -->
registerSpawnedOnlyTag(ElementTag.class, "can_breed", (attribute, object) -> {
if (!(object.getLivingEntity() instanceof Breedable)) {
return new ElementTag(false);
}
return new ElementTag(((Breedable) object.getLivingEntity()).canBreed());
});
// <--[tag]
// @attribute <EntityTag.breeding>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.breed
// @group attributes
// @description
// Returns whether the animal entity is trying to with another of its kind.
// -->
registerSpawnedOnlyTag(ElementTag.class, "breeding", (attribute, object) -> {
if (!(object.getLivingEntity() instanceof Animals)) {
return null;
}
return new ElementTag(((Animals) object.getLivingEntity()).getLoveModeTicks() > 0);
}, "is_breeding");
// <--[tag]
// @attribute <EntityTag.has_passenger>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.passenger
// @group attributes
// @description
// Returns whether the entity has a passenger.
// -->
registerSpawnedOnlyTag(ElementTag.class, "has_passenger", (attribute, object) -> {
return new ElementTag(!object.getBukkitEntity().isEmpty());
});
// <--[tag]
// @attribute <EntityTag.is_empty>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether the entity does not have a passenger.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_empty", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().isEmpty());
}, "empty");
// <--[tag]
// @attribute <EntityTag.is_inside_vehicle>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether the entity is inside a vehicle.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_inside_vehicle", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().isInsideVehicle());
}, "inside_vehicle");
// <--[tag]
// @attribute <EntityTag.is_leashed>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether the entity is leashed.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_leashed", (attribute, object) -> {
return new ElementTag(object.isLivingEntity() && object.getLivingEntity().isLeashed());
}, "leashed");
// <--[tag]
// @attribute <EntityTag.is_sheared>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether a sheep is sheared.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_sheared", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Sheep)) {
return null;
}
return new ElementTag(((Sheep) object.getBukkitEntity()).isSheared());
});
// <--[tag]
// @attribute <EntityTag.is_on_ground>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether the entity is supported by a block.
// This can be inaccurate for players.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_on_ground", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().isOnGround());
}, "on_ground");
// <--[tag]
// @attribute <EntityTag.is_persistent>
// @returns ElementTag(Boolean)
// @group attributes
// @mechanism EntityTag.persistent
// @description
// Returns whether the entity will not be removed completely when far away from players.
// In other words: whether the entity should be saved to file when chunks unload (otherwise, the entity is gone entirely if despawned for any reason).
// -->
// <--[tag]
// @attribute <EntityTag.persistent>
// @returns ElementTag(Boolean)
// @group attributes
// @mechanism EntityTag.persistent
// @deprecated use 'is_persistent'
// @description
// Outdated form of <@link tag EntityTag.is_persistent>
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_persistent", (attribute, object) -> {
return new ElementTag(object.isLivingEntity() && !object.getLivingEntity().getRemoveWhenFarAway());
}, "persistent");
// <--[tag]
// @attribute <EntityTag.is_collidable>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.collidable
// @group attributes
// @description
// Returns whether the entity is collidable.
// Returns the persistent collidable value for NPCs.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_collidable", (attribute, object) -> {
if (object.isCitizensNPC()) {
return new ElementTag(object.getDenizenNPC().getCitizen().data().get(NPC.COLLIDABLE_METADATA, true));
}
return new ElementTag(object.getLivingEntity().isCollidable());
});
// <--[tag]
// @attribute <EntityTag.is_sleeping>
// @returns ElementTag(Boolean)
// @description
// Returns whether the player, NPC, or villager is currently sleeping.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_sleeping", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Player) {
return new ElementTag(((Player) object.getBukkitEntity()).isSleeping());
} else if (object.getBukkitEntity() instanceof Villager) {
return new ElementTag(((Villager) object.getBukkitEntity()).isSleeping());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.killer>
// @returns PlayerTag
// @group attributes
// @description
// Returns the player that last killed the entity.
// -->
registerSpawnedOnlyTag(PlayerTag.class, "killer", (attribute, object) -> {
return getPlayerFrom(object.getLivingEntity().getKiller());
});
registerSpawnedOnlyTag(ObjectTag.class, "last_damage", (attribute, object) -> {
// -->
if (attribute.startsWith("amount", 2)) {
attribute.fulfill(1);
return new ElementTag(object.getLivingEntity().getLastDamage());
}
// -->
if (attribute.startsWith("cause", 2)) {
attribute.fulfill(1);
if (object.getBukkitEntity().getLastDamageCause() == null) {
return null;
}
return new ElementTag(object.getBukkitEntity().getLastDamageCause().getCause().name());
}
// -->
if (attribute.startsWith("duration", 2)) {
attribute.fulfill(1);
return new DurationTag((long) object.getLivingEntity().getNoDamageTicks());
}
// -->
if (attribute.startsWith("max_duration", 2)) {
attribute.fulfill(1);
return new DurationTag((long) object.getLivingEntity().getMaximumNoDamageTicks());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.absorption_health>
// @returns ElementTag(Decimal)
// @mechanism EntityTag.absorption_health
// @description
// Returns the living entity's absorption health.
// -->
registerSpawnedOnlyTag(ElementTag.class, "absorption_health", (attribute, object) -> {
return new ElementTag(NMSHandler.getEntityHelper().getAbsorption(object.getLivingEntity()));
});
// <--[tag]
// @attribute <EntityTag.max_oxygen>
// @returns DurationTag
// @group attributes
// @description
// Returns the maximum duration of oxygen the entity can have.
// Works with offline players.
// -->
registerSpawnedOnlyTag(DurationTag.class, "max_oxygen", (attribute, object) -> {
return new DurationTag((long) object.getLivingEntity().getMaximumAir());
});
// <--[tag]
// @attribute <EntityTag.oxygen>
// @returns DurationTag
// @mechanism EntityTag.oxygen
// @group attributes
// @description
// Returns the duration of oxygen the entity has left.
// Works with offline players.
// -->
registerSpawnedOnlyTag(DurationTag.class, "oxygen", (attribute, object) -> {
if (attribute.startsWith("max", 2)) {
Deprecations.entityMaxOxygenTag.warn(attribute.context);
attribute.fulfill(1);
return new DurationTag((long) object.getLivingEntity().getMaximumAir());
}
return new DurationTag((long) object.getLivingEntity().getRemainingAir());
});
registerSpawnedOnlyTag(ElementTag.class, "remove_when_far", (attribute, object) -> {
Deprecations.entityRemoveWhenFar.warn(attribute.context);
return new ElementTag(object.getLivingEntity().getRemoveWhenFarAway());
});
// <--[tag]
// @attribute <EntityTag.target>
// @returns EntityTag
// @group attributes
// @description
// Returns the target entity of the creature or shulker_bullet, if any.
// This is the entity that a hostile mob is currently trying to attack.
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "target", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Creature) {
Entity target = ((Creature) object.getLivingEntity()).getTarget();
if (target != null) {
return new EntityTag(target).getDenizenObject();
}
} else if (object.getBukkitEntity() instanceof ShulkerBullet) {
Entity target = ((ShulkerBullet) object.getLivingEntity()).getTarget();
if (target != null) {
return new EntityTag(target).getDenizenObject();
}
}
return null;
});
// <--[tag]
// @attribute <EntityTag.precise_target[(<range>)]>
// @returns EntityTag
// @description
// Returns the entity this entity is looking at, using precise ray trace logic.
// Optionally, specify a maximum range to find the entity from (defaults to 200).
// -->
registerSpawnedOnlyTag(EntityFormObject.class, "precise_target", (attribute, object) -> {
int range = attribute.getIntParam();
if (range < 1) {
range = 200;
}
Predicate<Entity> requirement;
// -->
if (attribute.startsWith("type", 2) && attribute.hasContext(2)) {
attribute.fulfill(1);
String matcher = attribute.getParam();
requirement = (e) -> !e.equals(object.getBukkitEntity()) && BukkitScriptEvent.tryEntity(new EntityTag(e), matcher);
} else {
requirement = (e) -> !e.equals(object.getBukkitEntity());
}
RayTraceResult result = object.getWorld().rayTrace(object.getEyeLocation(), object.getEyeLocation().getDirection(), range, FluidCollisionMode.NEVER, true, 0, requirement);
if (result != null && result.getHitEntity() != null) {
return new EntityTag(result.getHitEntity()).getDenizenObject();
}
return null;
});
// <--[tag]
// @attribute <EntityTag.precise_target_position[(<range>)]>
// @returns LocationTag
// @description
// Returns the location this entity is looking at, using precise ray trace (against entities) logic.
// Optionally, specify a maximum range to find the target from (defaults to 200).
// -->
registerSpawnedOnlyTag(LocationTag.class, "precise_target_position", (attribute, object) -> {
int range = attribute.getIntParam();
if (range < 1) {
range = 200;
}
Predicate<Entity> requirement;
// -->
if (attribute.startsWith("type", 2) && attribute.hasContext(2)) {
attribute.fulfill(1);
String matcher = attribute.getParam();
requirement = (e) -> !e.equals(object.getBukkitEntity()) && BukkitScriptEvent.tryEntity(new EntityTag(e), matcher);
} else {
requirement = (e) -> !e.equals(object.getBukkitEntity());
}
RayTraceResult result = object.getWorld().rayTrace(object.getEyeLocation(), object.getEyeLocation().getDirection(), range, FluidCollisionMode.NEVER, true, 0, requirement);
if (result != null) {
return new LocationTag(object.getWorld(), result.getHitPosition());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.time_lived>
// @returns DurationTag
// @mechanism EntityTag.time_lived
// @group attributes
// @description
// Returns how long the entity has lived.
// -->
registerSpawnedOnlyTag(DurationTag.class, "time_lived", (attribute, object) -> {
return new DurationTag(object.getBukkitEntity().getTicksLived() / 20);
});
// <--[tag]
// @attribute <EntityTag.pickup_delay>
// @returns DurationTag
// @mechanism EntityTag.pickup_delay
// @group attributes
// @description
// Returns how long before the item-type entity can be picked up by a player.
// -->
registerSpawnedOnlyTag(DurationTag.class, "pickup_delay", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Item)) {
return null;
}
return new DurationTag(((Item) object.getBukkitEntity()).getPickupDelay() * 20);
}, "pickupdelay");
// <--[tag]
// @attribute <EntityTag.is_in_block>
// @returns ElementTag(Boolean)
// @group attributes
// @description
// Returns whether or not the arrow/trident entity is in a block.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_in_block", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Arrow) {
return new ElementTag(((Arrow) object.getBukkitEntity()).isInBlock());
}
return null;
});
// <--[tag]
// @attribute <EntityTag.attached_block>
// @returns LocationTag
// @group attributes
// @description
// Returns the location of the block that the arrow/trident or hanging entity is attached to.
// -->
registerSpawnedOnlyTag(LocationTag.class, "attached_block", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Arrow) {
Block attachedBlock = ((Arrow) object.getBukkitEntity()).getAttachedBlock();
if (attachedBlock != null) {
return new LocationTag(attachedBlock.getLocation());
}
} else if (object.getBukkitEntity() instanceof Hanging) {
Vector dir = ((Hanging) object.getBukkitEntity()).getAttachedFace().getDirection();
return new LocationTag(object.getLocation().clone().add(dir.multiply(0.5))).getBlockLocation();
}
return null;
});
// <--[tag]
// @attribute <EntityTag.gliding>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.gliding
// @group attributes
// @description
// Returns whether this entity is gliding.
// -->
registerSpawnedOnlyTag(ElementTag.class, "gliding", (attribute, object) -> {
return new ElementTag(object.getLivingEntity().isGliding());
});
// <--[tag]
// @attribute <EntityTag.swimming>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.swimming
// @group attributes
// @description
// Returns whether this entity is swimming.
// -->
registerSpawnedOnlyTag(ElementTag.class, "swimming", (attribute, object) -> {
return new ElementTag(object.getLivingEntity().isSwimming());
});
// <--[tag]
// @attribute <EntityTag.visual_pose>
// @returns ElementTag
// @group attributes
// @description
// Returns the name of the entity's current visual pose.
// See <@link url https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/Pose.html>
// -->
registerSpawnedOnlyTag(ElementTag.class, "visual_pose", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().getPose().name());
});
// <--[tag]
// @attribute <EntityTag.glowing>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.glowing
// @group attributes
// @description
// Returns whether this entity is glowing.
// -->
registerSpawnedOnlyTag(ElementTag.class, "glowing", (attribute, object) -> {
return new ElementTag(object.getBukkitEntity().isGlowing());
});
// ///////////////////
// TYPE ATTRIBUTES
// ///////////////
// <--[tag]
// @attribute <EntityTag.is_living>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity type is a living-type entity (eg a cow or a player or anything else that lives, as specifically opposed to non-living entities like paintings, etc).
// Not to be confused with the idea of being alive - see <@link tag EntityTag.is_spawned>.
// -->
tagProcessor.registerTag(ElementTag.class, "is_living", (attribute, object) -> {
return new ElementTag(object.isLivingEntityType());
});
// <--[tag]
// @attribute <EntityTag.is_monster>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity type is a hostile monster.
// -->
tagProcessor.registerTag(ElementTag.class, "is_monster", (attribute, object) -> {
return new ElementTag(object.isMonsterType());
});
// <--[tag]
// @attribute <EntityTag.is_mob>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity type is a mob (Not a player or NPC).
// -->
tagProcessor.registerTag(ElementTag.class, "is_mob", (attribute, object) -> {
return new ElementTag(object.isMobType());
});
// <--[tag]
// @attribute <EntityTag.is_npc>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity is a Citizens NPC.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_npc", (attribute, object) -> {
return new ElementTag(object.isCitizensNPC());
});
// <--[tag]
// @attribute <EntityTag.is_player>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity is a player.
// Works with offline players.
// -->
tagProcessor.registerTag(ElementTag.class, "is_player", (attribute, object) -> {
return new ElementTag(object.isPlayer());
});
// <--[tag]
// @attribute <EntityTag.is_projectile>
// @returns ElementTag(Boolean)
// @group data
// @description
// Returns whether the entity type is a projectile.
// -->
tagProcessor.registerTag(ElementTag.class, "is_projectile", (attribute, object) -> {
if (object.getBukkitEntity() == null && object.entity_type != null) {
return new ElementTag(Projectile.class.isAssignableFrom(object.entity_type.getBukkitEntityType().getEntityClass()));
}
return new ElementTag(object.isProjectile());
});
// ///////////////////
// PROPERTY ATTRIBUTES
// ///////////////
// <--[tag]
// @attribute <EntityTag.tameable>
// @returns ElementTag(Boolean)
// @group properties
// @description
// Returns whether the entity is tameable.
// If this returns true, it will enable access to:
// <@link mechanism EntityTag.tame>, <@link mechanism EntityTag.owner>,
// <@link tag EntityTag.is_tamed>, and <@link tag EntityTag.owner>
// -->
registerSpawnedOnlyTag(ElementTag.class, "tameable", (attribute, object) -> {
return new ElementTag(EntityTame.describes(object));
}, "is_tameable");
// <--[tag]
// @attribute <EntityTag.ageable>
// @returns ElementTag(Boolean)
// @group properties
// @description
// Returns whether the entity is ageable.
// If this returns true, it will enable access to:
// <@link mechanism EntityTag.age>, <@link mechanism EntityTag.age_lock>,
// <@link tag EntityTag.is_baby>, <@link tag EntityTag.age>,
// and <@link tag EntityTag.is_age_locked>
// -->
registerSpawnedOnlyTag(ElementTag.class, "ageable", (attribute, object) -> {
return new ElementTag(EntityAge.describes(object));
}, "is_ageable");
// <--[tag]
// @attribute <EntityTag.colorable>
// @returns ElementTag(Boolean)
// @group properties
// @description
// Returns whether the entity can be colored.
// If this returns true, it will enable access to:
// <@link mechanism EntityTag.color> and <@link tag EntityTag.color>
// -->
registerSpawnedOnlyTag(ElementTag.class, "colorable", (attribute, object) -> {
return new ElementTag(EntityColor.describes(object));
}, "is_colorable");
// <--[tag]
// @attribute <EntityTag.experience>
// @returns ElementTag(Number)
// @mechanism EntityTag.experience
// @group properties
// @description
// Returns the experience value of this experience orb entity.
// -->
registerSpawnedOnlyTag(ElementTag.class, "experience", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof ExperienceOrb)) {
return null;
}
return new ElementTag(((ExperienceOrb) object.getBukkitEntity()).getExperience());
});
// <--[tag]
// @attribute <EntityTag.fuse_ticks>
// @returns ElementTag(Number)
// @mechanism EntityTag.fuse_ticks
// @group properties
// @description
// Returns the number of ticks until the explosion of the primed TNT.
// -->
registerSpawnedOnlyTag(ElementTag.class, "fuse_ticks", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof TNTPrimed)) {
return null;
}
return new ElementTag(((TNTPrimed) object.getBukkitEntity()).getFuseTicks());
});
// <--[tag]
// @attribute <EntityTag.dragon_phase>
// @returns ElementTag
// @mechanism EntityTag.dragon_phase
// @group properties
// @description
// Returns the phase an EnderDragon is currently in.
// Valid phases: <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/EnderDragon.Phase.html>
// -->
registerSpawnedOnlyTag(ElementTag.class, "dragon_phase", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof EnderDragon)) {
return null;
}
return new ElementTag(((EnderDragon) object.getLivingEntity()).getPhase().name());
});
// <--[tag]
// @attribute <EntityTag.weapon_damage[(<entity>)]>
// @returns ElementTag(Number)
// @group properties
// @description
// Returns the amount of damage the entity will do based on its held item.
// Optionally, specify a target entity to test how much damage will be done to that specific target
// (modified based on enchantments and that entity's armor/status/etc).
// Note that the result will not always be completely exact, as it doesn't take into account some specific factors
// (eg sweeping vs single-hit, etc).
// -->
registerSpawnedOnlyTag(ElementTag.class, "weapon_damage", (attribute, object) -> {
Entity target = null;
if (attribute.hasParam()) {
target = attribute.paramAsType(EntityTag.class).getBukkitEntity();
}
return new ElementTag(NMSHandler.getEntityHelper().getDamageTo(object.getLivingEntity(), target));
});
// <--[tag]
// @attribute <EntityTag.skin_layers>
// @returns ListTag
// @mechanism EntityTag.skin_layers
// @description
// Returns the skin layers currently visible on a player-type entity.
// Output is a list of values from the set of:
// CAPE, HAT, JACKET, LEFT_PANTS, LEFT_SLEEVE, RIGHT_PANTS, or RIGHT_SLEEVE.
// -->
registerSpawnedOnlyTag(ListTag.class, "skin_layers", (attribute, object) -> {
byte flags = NMSHandler.getPlayerHelper().getSkinLayers((Player) object.getBukkitEntity());
ListTag result = new ListTag();
for (PlayerHelper.SkinLayer layer : PlayerHelper.SkinLayer.values()) {
if ((flags & layer.flag) != 0) {
result.add(layer.name());
}
}
return result;
});
// <--[tag]
// @attribute <EntityTag.is_disguised[(<player>)]>
// @returns ElementTag(Boolean)
// @group properties
// @description
// Returns whether the entity is currently disguised, either globally (if no context input given), or to the specified player.
// Relates to <@link command disguise>.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_disguised", (attribute, object) -> {
HashMap<UUID, DisguiseCommand.TrackedDisguise> map = DisguiseCommand.disguises.get(object.getUUID());
if (map == null) {
return new ElementTag(false);
}
if (attribute.hasParam()) {
PlayerTag player = attribute.paramAsType(PlayerTag.class);
if (player == null) {
attribute.echoError("Invalid player for is_disguised tag.");
return null;
}
return new ElementTag(map.containsKey(player.getUUID()) || map.containsKey(null));
} else {
return new ElementTag(map.containsKey(null));
}
});
// <--[tag]
// @attribute <EntityTag.disguised_type[(<player>)]>
// @returns EntityTag
// @group properties
// @description
// Returns the entity type the entity is disguised as, either globally (if no context input given), or to the specified player.
// Relates to <@link command disguise>.
// -->
registerSpawnedOnlyTag(EntityTag.class, "disguised_type", (attribute, object) -> {
HashMap<UUID, DisguiseCommand.TrackedDisguise> map = DisguiseCommand.disguises.get(object.getUUID());
if (map == null) {
return null;
}
DisguiseCommand.TrackedDisguise disguise;
if (attribute.hasParam()) {
PlayerTag player = attribute.paramAsType(PlayerTag.class);
if (player == null) {
attribute.echoError("Invalid player for is_disguised tag.");
return null;
}
disguise = map.get(player.getUUID());
if (disguise == null) {
disguise = map.get(null);
}
} else {
disguise = map.get(null);
}
if (disguise == null) {
return null;
}
return disguise.as.duplicate();
});
// <--[tag]
// @attribute <EntityTag.disguise_to_others[(<player>)]>
// @returns EntityTag
// @group properties
// @description
// Returns the fake entity used to disguise the entity in other's views, either globally (if no context input given), or to the specified player.
// Relates to <@link command disguise>.
// -->
registerSpawnedOnlyTag(EntityTag.class, "disguise_to_others", (attribute, object) -> {
HashMap<UUID, DisguiseCommand.TrackedDisguise> map = DisguiseCommand.disguises.get(object.getUUID());
if (map == null) {
return null;
}
DisguiseCommand.TrackedDisguise disguise;
if (attribute.hasParam()) {
PlayerTag player = attribute.paramAsType(PlayerTag.class);
if (player == null) {
attribute.echoError("Invalid player for is_disguised tag.");
return null;
}
disguise = map.get(player.getUUID());
if (disguise == null) {
disguise = map.get(null);
}
} else {
disguise = map.get(null);
}
if (disguise == null) {
return null;
}
if (disguise.toOthers == null) {
return null;
}
return disguise.toOthers.entity;
});
// <--[tag]
// @attribute <EntityTag.describe>
// @returns EntityTag
// @group properties
// @description
// Returns the entity's full description, including all properties.
// -->
tagProcessor.registerTag(EntityTag.class, "describe", (attribute, object) -> {
return object.describe(attribute.context);
});
// <--[tag]
// @attribute <EntityTag.advanced_matches[<matcher>]>
// @returns ElementTag(Boolean)
// @group element checking
// @description
// Returns whether the entity matches some matcher text, using the system behind <@link language Advanced Script Event Matching>.
// -->
tagProcessor.registerTag(ElementTag.class, "advanced_matches", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
return new ElementTag(BukkitScriptEvent.tryEntity(object, attribute.getParam()));
});
// <--[tag]
// @attribute <EntityTag.has_equipped[<item-matcher>]>
// @returns ElementTag(Boolean)
// @group element checking
// @description
// Returns whether the entity has any armor equipment item that matches the given item matcher, using the system behind <@link language Advanced Script Event Matching>.
// For example, has_equipped[diamond_*] will return true if the entity is wearing at least one piece of diamond armor.
// -->
registerSpawnedOnlyTag(ElementTag.class, "has_equipped", (attribute, object) -> {
if (!attribute.hasParam()) {
return null;
}
if (!object.isLivingEntity()) {
return null;
}
String matcher = attribute.getParam();
for (ItemStack item : object.getLivingEntity().getEquipment().getArmorContents()) {
if (BukkitScriptEvent.tryItem(new ItemTag(item), matcher)) {
return new ElementTag(true);
}
}
return new ElementTag(false);
});
// <--[tag]
// @attribute <EntityTag.loot_table_id>
// @returns ElementTag
// @description
// Returns an element indicating the minecraft key for the loot-table for the entity (if any).
// -->
registerSpawnedOnlyTag(ElementTag.class, "loot_table_id", (attribute, object) -> {
if (object.getBukkitEntity() instanceof Lootable) {
LootTable table = ((Lootable) object.getBukkitEntity()).getLootTable();
if (table != null) {
return new ElementTag(table.getKey().toString());
}
}
return null;
});
// <--[tag]
// @attribute <EntityTag.fish_hook_state>
// @returns ElementTag
// @description
// Returns the current state of the fish hook, as any of: UNHOOKED, HOOKED_ENTITY, BOBBING (unhooked means the fishing hook is in the air or on ground).
// -->
registerSpawnedOnlyTag(ElementTag.class, "fish_hook_state", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_state is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).getState().name());
});
// <--[tag]
// @attribute <EntityTag.fish_hook_lure_time>
// @returns DurationTag
// @mechanism EntityTag.fish_hook_lure_time
// @description
// Returns the remaining time before this fish hook will lure a fish.
// -->
registerSpawnedOnlyTag(DurationTag.class, "fish_hook_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) NMSHandler.getFishingHelper().getLureTime((FishHook) object.getBukkitEntity()));
});
// <--[tag]
// @attribute <EntityTag.fish_hook_min_lure_time>
// @returns DurationTag
// @mechanism EntityTag.fish_hook_min_lure_time
// @description
// Returns the minimum possible time before this fish hook can lure a fish.
// -->
registerSpawnedOnlyTag(DurationTag.class, "fish_hook_min_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_min_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) ((FishHook) object.getBukkitEntity()).getMinWaitTime());
});
// <--[tag]
// @attribute <EntityTag.fish_hook_max_lure_time>
// @returns DurationTag
// @mechanism EntityTag.fish_hook_max_lure_time
// @description
// Returns the maximum possible time before this fish hook will lure a fish.
// -->
registerSpawnedOnlyTag(DurationTag.class, "fish_hook_max_lure_time", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_max_lure_time is only valid for fish hooks.");
return null;
}
return new DurationTag((long) ((FishHook) object.getBukkitEntity()).getMaxWaitTime());
});
// <--[tag]
// @attribute <EntityTag.fish_hook_hooked_entity>
// @returns EntityTag
// @mechanism EntityTag.fish_hook_hooked_entity
// @description
// Returns the entity this fish hook is attached to.
// -->
registerSpawnedOnlyTag(EntityTag.class, "fish_hook_hooked_entity", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_hooked_entity is only valid for fish hooks.");
return null;
}
Entity entity = ((FishHook) object.getBukkitEntity()).getHookedEntity();
return entity != null ? new EntityTag(entity) : null;
});
// <--[tag]
// @attribute <EntityTag.fish_hook_apply_lure>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.fish_hook_apply_lure
// @description
// Returns whether this fish hook should respect the lure enchantment.
// Every level of lure enchantment reduces lure time by 5 seconds.
// -->
registerSpawnedOnlyTag(ElementTag.class, "fish_hook_apply_lure", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_apply_lure is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).getApplyLure());
});
// <--[tag]
// @attribute <EntityTag.fish_hook_in_open_water>
// @returns ElementTag(Boolean)
// @description
// Returns whether this fish hook is in open water. Fish hooks in open water can catch treasure.
// See <@link url https://minecraft.fandom.com/wiki/Fishing> for more info.
// -->
registerSpawnedOnlyTag(ElementTag.class, "fish_hook_in_open_water", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof FishHook)) {
attribute.echoError("EntityTag.fish_hook_in_open_water is only valid for fish hooks.");
return null;
}
return new ElementTag(((FishHook) object.getBukkitEntity()).isInOpenWater());
});
// <--[tag]
// @attribute <EntityTag.attached_entities[(<player>)]>
// @returns ListTag(EntityTag)
// @description
// Returns the entities attached to this entity by <@link command attach>.
// Optionally, specify a player. If specified, will return entities attached visible to that player. If not specified, returns entities globally attached.
// -->
registerSpawnedOnlyTag(ListTag.class, "attached_entities", (attribute, object) -> {
PlayerTag player = attribute.hasParam() ? attribute.paramAsType(PlayerTag.class) : null;
EntityAttachmentHelper.EntityAttachedToMap data = EntityAttachmentHelper.toEntityToData.get(object.getUUID());
ListTag result = new ListTag();
if (data == null) {
return result;
}
for (EntityAttachmentHelper.PlayerAttachMap map : data.attachedToMap.values()) {
if (player == null || map.getAttachment(player.getUUID()) != null) {
result.addObject(map.attached);
}
}
return result;
});
// <--[tag]
// @attribute <EntityTag.attached_to[(<player>)]>
// @returns EntityTag
// @description
// Returns the entity that this entity was attached to by <@link command attach>.
// Optionally, specify a player. If specified, will return entity attachment visible to that player. If not specified, returns any entity global attachment.
// -->
registerSpawnedOnlyTag(EntityTag.class, "attached_to", (attribute, object) -> {
PlayerTag player = attribute.hasParam() ? attribute.paramAsType(PlayerTag.class) : null;
EntityAttachmentHelper.PlayerAttachMap data = EntityAttachmentHelper.attachedEntityToData.get(object.getUUID());
if (data == null) {
return null;
}
EntityAttachmentHelper.AttachmentData attached = data.getAttachment(player == null ? null : player.getUUID());
if (attached == null) {
return null;
}
return attached.to;
});
// <--[tag]
// @attribute <EntityTag.attached_offset[(<player>)]>
// @returns LocationTag
// @description
// Returns the offset of an attachment for this entity to another that was attached by <@link command attach>.
// Optionally, specify a player. If specified, will return entity attachment visible to that player. If not specified, returns any entity global attachment.
// -->
registerSpawnedOnlyTag(LocationTag.class, "attached_offset", (attribute, object) -> {
PlayerTag player = attribute.hasParam() ? attribute.paramAsType(PlayerTag.class) : null;
EntityAttachmentHelper.PlayerAttachMap data = EntityAttachmentHelper.attachedEntityToData.get(object.getUUID());
if (data == null) {
return null;
}
EntityAttachmentHelper.AttachmentData attached = data.getAttachment(player == null ? null : player.getUUID());
if (attached == null) {
return null;
}
return attached.positionalOffset == null ? null : new LocationTag(attached.positionalOffset);
});
// <--[tag]
// @attribute <EntityTag.attack_cooldown_duration>
// @returns DurationTag
// @mechanism EntityTag.attack_cooldown
// @description
// Returns the amount of time that passed since the start of the attack cooldown.
// -->
registerSpawnedOnlyTag(DurationTag.class, "attack_cooldown_duration", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Player)) {
attribute.echoError("Only player-type entities can have attack_cooldowns!");
return null;
}
return new DurationTag((long) NMSHandler.getPlayerHelper().ticksPassedDuringCooldown((Player) object.getLivingEntity()));
});
// <--[tag]
// @attribute <EntityTag.attack_cooldown_max_duration>
// @returns DurationTag
// @mechanism EntityTag.attack_cooldown
// @description
// Returns the maximum amount of time that can pass before the player's main hand has returned
// to its original place after the cooldown has ended.
// NOTE: This is slightly inaccurate and may not necessarily match with the actual attack
// cooldown progress.
// -->
registerSpawnedOnlyTag(DurationTag.class, "attack_cooldown_max_duration", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Player)) {
attribute.echoError("Only player-type entities can have attack_cooldowns!");
return null;
}
return new DurationTag((long) NMSHandler.getPlayerHelper().getMaxAttackCooldownTicks((Player) object.getLivingEntity()));
});
// <--[tag]
// @attribute <EntityTag.attack_cooldown_percent>
// @returns ElementTag(Decimal)
// @mechanism EntityTag.attack_cooldown_percent
// @description
// Returns the progress of the attack cooldown. 0 means that the attack cooldown has just
// started, while 100 means that the attack cooldown has finished.
// NOTE: This may not match exactly with the clientside attack cooldown indicator.
// -->
registerSpawnedOnlyTag(ElementTag.class, "attack_cooldown_percent", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof Player)) {
attribute.echoError("Only player-type entities can have attack_cooldowns!");
return null;
}
return new ElementTag(NMSHandler.getPlayerHelper().getAttackCooldownPercent((Player) object.getLivingEntity()) * 100);
});
// <--[tag]
// @attribute <EntityTag.is_hand_raised>
// @returns ElementTag(Boolean)
// @mechanism EntityTag.attack_cooldown_percent
// @description
// Returns whether the player's hand is currently raised. Valid for players and player-type NPCs.
// A player's hand is raised when they are blocking with a shield, aiming a crossbow, looking through a spyglass, etc.
// -->
registerSpawnedOnlyTag(ElementTag.class, "is_hand_raised", (attribute, object) -> {
if (!(object.getBukkitEntity() instanceof HumanEntity)) {
attribute.echoError("Only player-type entities can have is_hand_raised!");
return null;
}
return new ElementTag(((HumanEntity) object.getLivingEntity()).isHandRaised());
});
}
Aggregations