package tc.oc.commons.bukkit.item; import java.util.Collections; import java.util.UUID; import javax.annotation.Nullable; import org.bukkit.Bukkit; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.Skin; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.material.Dye; import org.bukkit.material.MaterialData; import org.bukkit.material.Wool; import tc.oc.commons.core.ListUtils; import static com.google.common.base.Preconditions.checkNotNull; /** * A nice way to build {@link ItemStack}s * * TODO: attributes, canPlaceOn, canDestroy, potion effects, etc. */ public class ItemBuilder<S extends ItemBuilder<?>> { private final ItemStack stack; private @Nullable ItemMeta meta; public ItemBuilder() { this(new ItemStack(Material.AIR)); } public ItemBuilder(ItemStack stack) { this.stack = stack; } // Convenient generic alias for this @SuppressWarnings("unchecked") protected S self() { return (S) this; } public ItemStack get() { if(meta != null) { stack.setItemMeta(meta); } return stack; } public ItemStack copy() { return get().clone(); } protected void createMeta() { // Bukkit refuses to create meta for air final Material material = stack.getType() == Material.AIR ? Material.STONE : stack.getType(); if(meta != null) { // Ensure existing meta is the correct type meta = Bukkit.getItemFactory().asMetaFor(meta, material); } else { meta = stack.getItemMeta(); if(meta == null) { meta = Bukkit.getItemFactory().getItemMeta(material); } } checkNotNull(meta); } protected ItemMeta meta() { if(meta == null) { createMeta(); } return meta; } protected <T extends ItemMeta> T meta(Class<T> type) { final ItemMeta meta = meta(); if(!type.isInstance(meta)) { throw new IllegalArgumentException("Item of type " + stack.getType().name() + " cannot have metadata of type " + type.getName()); } return type.cast(meta); } public S material(Material material) { stack.setType(material); if(meta != null) { // Convert meta if needed createMeta(); } return self(); } public S material(MaterialData material) { material(material.getItemType()); stack.setData(material); stack.setDurability(material.getData()); return self(); } public S amount(int amount) { stack.setAmount(amount); return self(); } public S durability(int durability) { stack.setDurability((short) durability); return self(); } public S name(String name) { meta().setDisplayName(name); return self(); } /** * Append a line of lore */ public S lore(String lore) { meta().setLore(meta().hasLore() ? ListUtils.append(meta().getLore(), lore) : Collections.singletonList(lore)); return self(); } public S flags(ItemFlag...flags) { meta().addItemFlags(flags); return self(); } public S unbreakable(boolean unbreakable) { meta().setUnbreakable(unbreakable); return self(); } public S enchant(Enchantment enchantment, int level) { meta().addEnchant(enchantment, level, true); return self(); } public S color(DyeColor color) { final Material type = stack.getType(); switch(type) { case INK_SACK: stack.setData(new Dye(color)); break; case WOOL: stack.setData(new Wool(color)); break; default: // Assume a colored block // TODO verify this, support other things e.g. banners stack.setData(new MaterialData(type, color.getWoolData())); break; } // I'm not clear why Bukkit doesn't do this, but it needs to be done. // Of course, if we ever support non-block items with this method, // this gets a bit more complicated. stack.setDurability(stack.getData().getData()); return self(); } public S skin(String name, UUID uuid, Skin skin) { meta(SkullMeta.class).setOwner(name, uuid, skin); return self(); } public S shareable(boolean yes) { new BooleanItemTag("prevent-sharing", false).set(stack, !yes); return self(); } public S locked(boolean yes) { new BooleanItemTag("locked", false).set(stack, !yes); return self(); } }