package com.supaham.commons.bukkit.utils;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Preconditions;
import org.bukkit.Material;
import org.bukkit.TreeSpecies;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import java.util.EnumSet;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Utility methods for working with {@link Material} instances. This class contains methods such as
* {@link #isInteractableBlock(Material)}, {@link #isContainer(Material)}, and more.
*
* @since 0.1
*/
public final class MaterialUtils {
private static final Set<Material> interactableBlocks = EnumSet.noneOf(Material.class);
static {
interactableBlocks.clear();
interactableBlocks.add(Material.DISPENSER);
interactableBlocks.add(Material.NOTE_BLOCK);
interactableBlocks.add(Material.BED_BLOCK);
interactableBlocks.add(Material.TNT);
interactableBlocks.add(Material.CHEST);
interactableBlocks.add(Material.WORKBENCH);
interactableBlocks.add(Material.CROPS);
interactableBlocks.add(Material.SOIL);
interactableBlocks.add(Material.FURNACE);
interactableBlocks.add(Material.BURNING_FURNACE);
interactableBlocks.add(Material.WOODEN_DOOR);
interactableBlocks.add(Material.LEVER);
interactableBlocks.add(Material.IRON_DOOR_BLOCK);
interactableBlocks.add(Material.STONE_BUTTON);
interactableBlocks.add(Material.JUKEBOX);
interactableBlocks.add(Material.FENCE);
interactableBlocks.add(Material.SOUL_SAND);
interactableBlocks.add(Material.CAKE_BLOCK);
interactableBlocks.add(Material.DIODE_BLOCK_OFF);
interactableBlocks.add(Material.DIODE_BLOCK_ON);
interactableBlocks.add(Material.TRAP_DOOR);
interactableBlocks.add(Material.IRON_FENCE);
interactableBlocks.add(Material.FENCE_GATE);
interactableBlocks.add(Material.NETHER_FENCE);
interactableBlocks.add(Material.ENCHANTMENT_TABLE);
interactableBlocks.add(Material.BREWING_STAND);
interactableBlocks.add(Material.CAULDRON);
interactableBlocks.add(Material.ENDER_PORTAL_FRAME);
interactableBlocks.add(Material.DRAGON_EGG);
// TODO possibly interactable? interactableBlocks.add(Material.COCOA);
interactableBlocks.add(Material.ENDER_CHEST);
interactableBlocks.add(Material.COMMAND);
interactableBlocks.add(Material.BEACON);
interactableBlocks.add(Material.FLOWER_POT);
interactableBlocks.add(Material.CARROT);
interactableBlocks.add(Material.POTATO);
interactableBlocks.add(Material.WOOD_BUTTON);
interactableBlocks.add(Material.ANVIL);
interactableBlocks.add(Material.TRAPPED_CHEST);
interactableBlocks.add(Material.REDSTONE_COMPARATOR_OFF);
interactableBlocks.add(Material.REDSTONE_COMPARATOR_ON);
interactableBlocks.add(Material.HOPPER);
interactableBlocks.add(Material.DROPPER);
}
/**
* Checks whether a {@link Material} can be interacted with.
*
* @param material material to check
*
* @return true if the {@code material} can be interacted with
*/
public static boolean isInteractableBlock(Material material) {
return material != null && material.isBlock() && interactableBlocks.contains(material);
}
/**
* Checks whether a {@link Material} is a container. This doesn't necessarily mean it has a
* persistent inventory.
*
* @param material material to check
*
* @return whether the {@code material} is a container
*/
public static boolean isContainer(Material material) {
switch (material) {
case BEACON:
case BREWING_STAND:
case BURNING_FURNACE:
case CHEST:
case DISPENSER:
case DIODE:
case DIODE_BLOCK_OFF:
case DIODE_BLOCK_ON:
case DRAGON_EGG:
case DROPPER:
case ENCHANTMENT_TABLE:
case ENDER_CHEST:
case FURNACE:
case HOPPER:
case JUKEBOX:
case REDSTONE_COMPARATOR:
case REDSTONE_COMPARATOR_OFF:
case REDSTONE_COMPARATOR_ON:
case TRAPPED_CHEST:
case WORKBENCH:
case STORAGE_MINECART:
case POWERED_MINECART:
return true;
default:
return false;
}
}
/**
* Checks whether a {@link Material} is a sign.
*
* @param material material to test
*
* @return whether the {@code material} is a sign
*/
public static boolean isSign(Material material) {
return material == Material.SIGN
|| material == Material.SIGN_POST
|| material == Material.WALL_SIGN;
}
/**
* Checks whether a {@link Block} is the same as a {@link MaterialData}. If either the Block or
* the MaterialData have a data (damage/durability) value of -1, only the {@link Material} is
* tested.
*
* @param block block to check
* @param materialData {@link MaterialData} to test against
*
* @return whether the {@code block}'s data matches the {@code materialData}
*/
public static boolean same(Block block, MaterialData materialData) {
return same(block.getType(), block.getData(), materialData);
}
/**
* Checks whether an {@link ItemStack} is the same as a {@link MaterialData}. If either the
* ItemStack or the MaterialData have a data (damage/durability) value of -1, only the {@link
* Material} is tested.
*
* @param item item to check
* @param materialData {@link MaterialData} to test against
*
* @return whether the {@code item}'s data matches the {@code materialData}
*/
public static boolean same(ItemStack item, MaterialData materialData) {
return same(item.getData(), materialData);
}
/**
* Checks whether a {@link Material} and data value (byte) is the same as a {@link MaterialData}.
* This simply constructs a {@link MaterialData} out of the given data and calls {@link
* #same(MaterialData, MaterialData)}. If either the Block or the MaterialData have a data
* (damage/durability) value of -1, only the {@link Material} is tested.
*
* @param type type to check
* @param data data to check, set to -1 to ignore data checks
* @param materialData {@link MaterialData} to test against
*
* @return whether the {@code block}'s data matches the {@code materialData}
*/
public static boolean same(Material type, byte data, MaterialData materialData) {
return same(new MaterialData(type, data), materialData);
}
/**
* Checks whether a {@link MaterialData} is the same as another {@link MaterialData}. If either
* of
* the MaterialDatas have a data (damage/durability) value of -1, only the {@link Material} is
* tested.
*
* @param data1 first {@link MaterialData} to test
* @param data2 first {@link MaterialData} to test
*
* @return whether the {@code block}'s data matches the {@code materialData}
*/
public static boolean same(MaterialData data1, MaterialData data2) {
if (data1 == null) {
return data2 == null;
} else if (data2 == null) {
return false;
}
return data1.getItemType().equals(data2.getItemType())
&& ((data1.getData() == -1 || data2.getData() == -1)
|| data1.getData() == data2.getData());
}
/**
* Checks whether a {@link Material} is a gold tool.
*
* @param material material to check
*
* @return true if the {@code material} is a gold tool
*/
public static boolean isGoldTool(@Nonnull Material material) {
checkNotNull(material, "material cannot be null.");
switch (material) {
case GOLD_AXE:
case GOLD_SPADE:
case GOLD_SWORD:
case GOLD_PICKAXE:
return true;
default:
return false;
}
}
/**
* Returns a {@link MaterialData} with the type as the dropped item of the given material data.
*
* @param materialData material data to convert to item. This is returned if the type is not an
* item
*
* @return material data
*/
public static MaterialData toItem(@Nonnull MaterialData materialData) {
Material material = materialData.getItemType();
if (!material.isBlock()) {
return materialData;
}
byte data = 0;
byte mdata = materialData.getData();
switch (material) {
case STONE:
material = Material.COBBLESTONE;
break;
case GRASS:
case SOIL:
case MYCEL:
material = Material.DIRT;
break;
case GRAVEL:
material = Material.FLINT;
break;
case GOLD_ORE:
material = Material.GOLD_INGOT;
break;
case IRON_ORE:
material = Material.IRON_INGOT;
break;
case COAL_ORE:
material = Material.COAL;
break;
case LOG:
case LOG_2:
data = TreeSpecies.getByData((byte) (mdata & 0x3)).getData();
material = Material.WOOD;
break;
case LEAVES:
case LEAVES_2:
material = Material.SAPLING;
data = TreeSpecies.getByData((byte) (mdata & 0x3)).getData();
break;
case LAPIS_ORE:
material = Material.INK_SACK;
data = 4;
break;
case BED:
material = Material.BED;
break;
case PISTON_EXTENSION:
case PISTON_MOVING_PIECE:
material = Material.PISTON_BASE;
break;
case BOOKSHELF:
material = Material.BOOK;
break;
case REDSTONE_WIRE:
case REDSTONE_ORE:
case GLOWING_REDSTONE_ORE:
material = Material.REDSTONE;
break;
case DIAMOND_ORE:
material = Material.DIAMOND;
break;
case CROPS:
material = Material.SEEDS;
break;
case BURNING_FURNACE:
material = Material.FURNACE;
break;
case SIGN_POST:
case WALL_SIGN:
material = Material.SIGN;
break;
case WOODEN_DOOR:
material = Material.WOOD_DOOR;
break;
case REDSTONE_TORCH_ON:
material = Material.REDSTONE_TORCH_OFF;
break;
case CLAY:
material = Material.CLAY_BALL;
break;
case DIODE_BLOCK_ON:
material = Material.DIODE;
break;
case HUGE_MUSHROOM_1:
material = Material.RED_MUSHROOM;
break;
case HUGE_MUSHROOM_2:
material = Material.BROWN_MUSHROOM;
break;
case IRON_FENCE:
break;
case PUMPKIN_STEM:
material = Material.PUMPKIN_SEEDS;
break;
case MELON_STEM:
material = Material.MELON_SEEDS;
data = 0;
break;
case BREWING_STAND:
material = Material.BREWING_STAND_ITEM;
break;
case CAULDRON:
material = Material.CAULDRON_ITEM;
break;
case DRAGON_EGG:
break;
case REDSTONE_LAMP_ON:
material = Material.REDSTONE_LAMP_OFF;
break;
case WOOD_DOUBLE_STEP:
material = Material.WOOD_STEP;
break;
case COCOA:
material = Material.INK_SACK;
data = 3;
break;
case EMERALD_ORE:
material = Material.EMERALD;
break;
case TRIPWIRE_HOOK:
material = Material.EMERALD;
break;
case TRIPWIRE:
material = Material.STRING;
break;
case CARROT:
material = Material.CARROT_ITEM;
break;
case POTATO:
material = Material.POTATO_ITEM;
break;
case REDSTONE_COMPARATOR_ON:
material = Material.REDSTONE_COMPARATOR_OFF;
break;
case DAYLIGHT_DETECTOR_INVERTED:
material = Material.DAYLIGHT_DETECTOR;
break;
case QUARTZ_ORE:
material = Material.QUARTZ;
break;
case CARPET:
case DOUBLE_PLANT:
data = mdata;
break;
case STANDING_BANNER:
case WALL_BANNER:
material = Material.BANNER;
break;
case DOUBLE_STONE_SLAB2:
material = Material.STONE_SLAB2;
data = mdata;
break;
}
return new MaterialData(material, data);
}
/**
* Returns whether a {@link Material} is a block of leaves. Useful since Minecraft has multiple
* leaves blocks with different ids.
*
* @param material material to check, nullable
*
* @return whether the given material is leaves
*/
public static boolean isLeaves(@Nullable Material material) {
return material != null && (material == Material.LEAVES || material == Material.LEAVES_2);
}
/**
* Returns whether a {@link Material} matches any of the given others.
*
* @param material material to test
* @param any materials to check for
*
* @return whether {@code material} is of any of the materials in {@code any}
*/
public static boolean equals(@Nullable Material material, @Nonnull Material... any) {
Preconditions.checkNotNull(any, "array of materials cannot be null.");
for (Material anyMat : any) {
if (material == null && anyMat == Material.AIR) {
return true;
} else if (material == anyMat) {
return true;
}
}
return false;
}
private MaterialUtils() {}
}