/**
* BetonQuest - advanced quests for Bukkit
* Copyright (C) 2016 Jakub "Co0sh" Sapalski
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pl.betoncraft.betonquest.item;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.FireworkEffectMeta;
import org.bukkit.inventory.meta.FireworkMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionEffect;
import pl.betoncraft.betonquest.Instruction;
import pl.betoncraft.betonquest.InstructionParseException;
import pl.betoncraft.betonquest.ItemID;
import pl.betoncraft.betonquest.item.typehandler.BookHandler;
import pl.betoncraft.betonquest.item.typehandler.ColorHandler;
import pl.betoncraft.betonquest.item.typehandler.DataHandler;
import pl.betoncraft.betonquest.item.typehandler.EnchantmentsHandler;
import pl.betoncraft.betonquest.item.typehandler.FireworkHandler;
import pl.betoncraft.betonquest.item.typehandler.HeadOwnerHandler;
import pl.betoncraft.betonquest.item.typehandler.LoreHandler;
import pl.betoncraft.betonquest.item.typehandler.NameHandler;
import pl.betoncraft.betonquest.item.typehandler.PotionHandler;
import pl.betoncraft.betonquest.item.typehandler.UnbreakableHandler;
/**
* Represents an item handled by the configuration.
*
* @author Jakub Sapalski
*/
public class QuestItem {
private Material material = null;
private DataHandler data = new DataHandler();
private NameHandler name = new NameHandler();
private LoreHandler lore = new LoreHandler();
private EnchantmentsHandler enchants = new EnchantmentsHandler();
private UnbreakableHandler unbreakable = new UnbreakableHandler();
private PotionHandler potion = new PotionHandler();
private BookHandler book = new BookHandler();
private HeadOwnerHandler head = new HeadOwnerHandler();
private ColorHandler color = new ColorHandler();
private FireworkHandler firework = new FireworkHandler();
/**
* Legacy method for the updater, don't use for anything else.
*
* @deprecated Use a different constructor.
*
* @param material
* material name
* @param data
* data value
* @param enchants
* map of enchantments and their levels
* @param name
* name of the item
* @param lore
* list of lore lines
*/
public QuestItem(String material, int data, Map<String, Integer> enchants, String name, List<String> lore) {
this.material = Material.matchMaterial(material);
if (data >= 0) {
try {
this.data.set(String.valueOf(data));
} catch (InstructionParseException e) {}
}
if (enchants != null && !enchants.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (String key : enchants.keySet()) {
builder.append(key + ":" + enchants.get(key));
}
try {
this.enchants.set(builder.substring(0, builder.length() - 1));
} catch (InstructionParseException e) {}
}
if (name != null && !name.isEmpty()) {
try {
this.name.set(name);
} catch (InstructionParseException e) {}
}
if (lore != null && !lore.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (String line : lore) {
builder.append(line + ";");
}
try {
this.lore.set(builder.substring(0, builder.length() - 1));
} catch (InstructionParseException e) {}
}
}
/**
* Creates new instance of the quest item using the ID from items.yml file.
*
* @param itemID
* ID of the item
* @throws InstructionParseException
* when item parsing goes wrong
*/
public QuestItem(ItemID itemID) throws InstructionParseException {
this(itemID.generateInstruction());
}
/**
* Creates new instance of the quest item using the Instruction object.
*
* @param instruction
* Instruction object
* @throws InstructionParseException
* when item parsing goes wrong
*/
public QuestItem(Instruction instruction) throws InstructionParseException {
this(instruction.getInstruction());
}
/**
* Creates new instance of the quest item using the instruction string.
*
* @param instruction
* instruction String
* @throws InstructionParseException
* when item parsing goes wrong
*/
public QuestItem(String instruction) throws InstructionParseException {
if (instruction == null)
throw new NullPointerException("Item instruction is null");
String[] parts = instruction.split(" ");
if (parts.length < 1) {
throw new InstructionParseException("Not enough arguments");
}
material = Material.matchMaterial(parts[0]);
if (material == null) {
throw new InstructionParseException("Unknown item type: " + parts[0]);
}
for (String part : parts) {
if (part.startsWith("data:")) {
data.set(cut(part));
} else if (part.toLowerCase().startsWith("enchants:")) {
enchants.set(cut(part));
} else if (part.toLowerCase().equals("enchants-containing")) {
enchants.setNotExact();
} else if (part.toLowerCase().startsWith("name:")) {
name.set(cut(part));
} else if (part.toLowerCase().startsWith("lore:")) {
lore.set(cut(part));
} else if (part.toLowerCase().equals("lore-containing")) {
lore.setNotExact();
} else if (part.toLowerCase().startsWith("unbreakable:")) {
unbreakable.set(cut(part));
} else if (part.toLowerCase().equals("unbreakable")) {
unbreakable.set("true");
} else if (part.toLowerCase().startsWith("title:")) {
book.setTitle(cut(part));
} else if (part.toLowerCase().startsWith("author:")) {
book.setAuthor(cut(part));
} else if (part.toLowerCase().startsWith("text:")) {
book.setText(cut(part));
} else if (part.toLowerCase().startsWith("type:")) {
potion.setType(cut(part));
} else if (part.toLowerCase().equals("extended")) {
potion.setExtended("true");
} else if (part.toLowerCase().startsWith("extended:")) {
potion.setExtended(cut(part));
} else if (part.toLowerCase().equals("upgraded")) {
potion.setUpgraded("true");
} else if (part.toLowerCase().startsWith("upgraded:")) {
potion.setUpgraded(cut(part));
} else if (part.toLowerCase().startsWith("effects:")) {
potion.setCustom(cut(part));
} else if (part.toLowerCase().equals("effects-containing")) {
potion.setNotExact();
} else if (part.toLowerCase().startsWith("owner:")) {
head.set(cut(part));
} else if (part.toLowerCase().startsWith("color:")) {
color.set(cut(part));
} else if (part.toLowerCase().startsWith("firework:")) {
firework.setEffects(cut(part));
} else if (part.toLowerCase().startsWith("power:")) {
firework.setPower(cut(part));
} else if (part.toLowerCase().equals("firework-containing")) {
firework.setNotExact();
}
}
}
private static String cut(String uncut) {
return uncut.substring(uncut.indexOf(':') + 1);
}
@Override
public boolean equals(Object o) {
QuestItem item;
if (o instanceof QuestItem) {
item = (QuestItem) o;
} else {
return false;
}
if (item.material != material) {
return false;
}
if (!item.data.equals(data)) {
return false;
}
if (!item.unbreakable.equals(unbreakable)) {
return false;
}
if (!item.enchants.equals(enchants)) {
return false;
}
if (!item.lore.equals(lore)) {
return false;
}
if (!item.name.equals(name)) {
return false;
}
if (!item.potion.equals(potion)) {
return false;
}
if (!item.book.equals(book)) {
return false;
}
if (!item.head.equals(head)) {
return false;
}
if (!item.color.equals(color)) {
return false;
}
if (!item.firework.equals(firework)) {
return false;
}
return true;
}
/**
* Compares ItemStack to the quest item.
*
* @param item
* ItemStack to compare
* @return true if the item matches
*/
@SuppressWarnings("deprecation")
public boolean compare(ItemStack item) {
// basic item checks
if (item == null) {
return false;
}
if (item.getType() != material) {
return false;
}
// basic meta checks
ItemMeta meta = item.getItemMeta();
if (!data.check(item.getData().getData())) {
return false;
}
if (!name.check(meta.getDisplayName())) {
return false;
}
if (!lore.check(meta.getLore())) {
return false;
}
if (!unbreakable.check(meta.spigot().isUnbreakable())) {
return false;
}
// advanced meta checks
if (meta instanceof EnchantmentStorageMeta) {
EnchantmentStorageMeta enchantMeta = (EnchantmentStorageMeta) meta;
if (!enchants.check(enchantMeta.getStoredEnchants())) {
return false;
}
} else {
if (!enchants.check(item.getEnchantments())) {
return false;
}
}
if (meta instanceof PotionMeta) {
PotionMeta potionMeta = (PotionMeta) meta;
if (!potion.checkBase(potionMeta.getBasePotionData())) {
return false;
}
if (!potion.checkCustom(potionMeta.getCustomEffects())) {
return false;
}
}
if (meta instanceof BookMeta) {
BookMeta bookMeta = (BookMeta) item.getItemMeta();
if (!book.checkTitle(bookMeta.getTitle())) {
return false;
}
if (!book.checkAuthor(bookMeta.getAuthor())) {
return false;
}
if (!book.checkText(bookMeta.getPages())) {
return false;
}
}
if (meta instanceof SkullMeta) {
SkullMeta skullMeta = (SkullMeta) item.getItemMeta();
if (!head.check(skullMeta.getOwner())) {
return false;
}
}
if (meta instanceof LeatherArmorMeta) {
LeatherArmorMeta armorMeta = (LeatherArmorMeta) item.getItemMeta();
if (!color.check(armorMeta.getColor())) {
return false;
}
}
if (meta instanceof FireworkMeta) {
FireworkMeta fireworkMeta = (FireworkMeta) item.getItemMeta();
if (!firework.checkEffects(fireworkMeta.getEffects())) {
return false;
}
if (!firework.checkPower(fireworkMeta.getPower())) {
return false;
}
}
if (meta instanceof FireworkEffectMeta) {
FireworkEffectMeta fireworkMeta = (FireworkEffectMeta) item.getItemMeta();
if (!firework.checkSingleEffect(fireworkMeta.getEffect())) {
return false;
}
}
return true;
}
/**
* Generates this quest item as ItemStack with given amount.
*
* @param stackSize
* size of generated stack
* @return the ItemStack equal to this quest item
*/
@SuppressWarnings("deprecation")
public ItemStack generate(int stackSize) {
ItemStack item = new ItemStack(material, stackSize, data.get());
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(name.get());
meta.setLore(lore.get());
meta.spigot().setUnbreakable(unbreakable.get());
if (meta instanceof EnchantmentStorageMeta) {
EnchantmentStorageMeta enchantMeta = (EnchantmentStorageMeta) meta;
// why no bulk adding method?!
Map<Enchantment, Integer> map = enchants.get();
for (Entry<Enchantment, Integer> e : map.entrySet()) {
enchantMeta.addStoredEnchant(e.getKey(), e.getValue(), true);
}
} else {
Map<Enchantment, Integer> map = enchants.get();
for (Entry<Enchantment, Integer> e : map.entrySet()) {
meta.addEnchant(e.getKey(), e.getValue(), true);
}
}
if (meta instanceof PotionMeta) {
PotionMeta potionMeta = (PotionMeta) meta;
potionMeta.setBasePotionData(potion.getBase());
// this is getting ridiculous
for (PotionEffect effect : potion.getCustom()) {
potionMeta.addCustomEffect(effect, true);
}
}
if (meta instanceof BookMeta) {
BookMeta bookMeta = (BookMeta) meta;
bookMeta.setTitle(book.getTitle());
bookMeta.setAuthor(book.getAuthor());
bookMeta.setPages(book.getText());
}
if (meta instanceof SkullMeta) {
SkullMeta skullMeta = (SkullMeta) meta;
skullMeta.setOwner(head.get());
}
if (meta instanceof LeatherArmorMeta) {
LeatherArmorMeta armorMeta = (LeatherArmorMeta) meta;
armorMeta.setColor(color.get());
}
if (meta instanceof FireworkMeta) {
FireworkMeta fireworkMeta = (FireworkMeta) meta;
fireworkMeta.addEffects(firework.getEffects());
fireworkMeta.setPower(firework.getPower());
}
if (meta instanceof FireworkEffectMeta) {
FireworkEffectMeta fireworkMeta = (FireworkEffectMeta) meta;
List<FireworkEffect> list = firework.getEffects();
fireworkMeta.setEffect(list.isEmpty() ? null : list.get(0));
}
item.setItemMeta(meta);
return item;
}
public enum Existence {
REQUIRED, FORBIDDEN, WHATEVER
}
public enum Number {
EQUAL, MORE, LESS, WHATEVER
}
/**
* @return the material
*/
public Material getMaterial() {
return material;
}
/**
* @return the data value
*/
public short getData() {
return data.get();
}
/**
* @return the map of enchantments and their levels
*/
public Map<Enchantment, Integer> getEnchants() {
return enchants.get();
}
/**
* @return the display name or null if there is no name
*/
public String getName() {
return name.get();
}
/**
* @return the list of lore lines, can be empty
*/
public List<String> getLore() {
return lore.get();
}
/**
* @return the title of a book or null if it's not a book
*/
public String getTitle() {
return book.getTitle();
}
/**
* @return the author of a book or null if it's not a book
*/
public String getAuthor() {
return book.getAuthor();
}
/**
* @return the pages from the book or null if it's not a book
*/
public List<String> getText() {
return book.getText();
}
/**
* @return the list of custom effects of the potion
*/
public List<PotionEffect> getEffects() {
return potion.getCustom();
}
/**
* @return color of the leather armor
*/
public Color getColor() {
return color.get();
}
/**
* @return owner of the head
*/
public String getOwner() {
return head.get();
}
/**
* @return the base data of the potion
*/
public PotionData getBaseEffect() {
return potion.getBase();
}
/**
* @return if the item has "Unbreakable" tag
*/
public boolean isUnbreakable() {
return unbreakable.get();
}
/**
* @return the list of firework effects
*/
public List<FireworkEffect> getFireworkEffects() {
return firework.getEffects();
}
/**
* @return power of the firework
*/
public int getPower() {
return firework.getPower();
}
/**
* Converts ItemStack to string, which can be later parsed by QuestItem
*
* @param item
* ItemStack to convert
* @return converted string
*/
@SuppressWarnings("deprecation")
public static String itemToString(ItemStack item) {
String name = "";
String lore = "";
String enchants = "";
String title = "";
String text = "";
String author = "";
String effects = "";
String color = "";
String owner = "";
String firework = "";
ItemMeta meta = item.getItemMeta();
if (meta.hasDisplayName()) {
name = " name:" + meta.getDisplayName().replace(" ", "_");
}
if (meta.hasLore()) {
StringBuilder string = new StringBuilder();
for (String line : meta.getLore()) {
string.append(line + ";");
}
lore = " lore:" + string.substring(0, string.length() - 1).replace(" ", "_");
}
if (meta.hasEnchants()) {
StringBuilder string = new StringBuilder();
for (Enchantment enchant : meta.getEnchants().keySet()) {
string.append(enchant.getName() + ":" + meta.getEnchants().get(enchant) + ",");
}
enchants = " enchants:" + string.substring(0, string.length() - 1);
}
if (meta instanceof BookMeta) {
BookMeta bookMeta = (BookMeta) meta;
if (bookMeta.hasAuthor()) {
author = " author:" + bookMeta.getAuthor().replace(" ", "_");
}
if (bookMeta.hasTitle()) {
title = " title:" + bookMeta.getTitle().replace(" ", "_");
}
if (bookMeta.hasPages()) {
StringBuilder strBldr = new StringBuilder();
for (String page : bookMeta.getPages()) {
if (page.startsWith("\"") && page.endsWith("\"")) {
page = page.substring(1, page.length() - 1);
}
// this will remove black color code between lines
// Bukkit is adding it for some reason (probably to mess people's code)
strBldr.append(page.replace(" ", "_").replaceAll("(§0)?\\n(§0)?", "\\\\n") + "|");
}
text = " text:" + strBldr.substring(0, strBldr.length() - 1);
}
}
if (meta instanceof PotionMeta) {
PotionMeta potionMeta = (PotionMeta) meta;
PotionData pData = potionMeta.getBasePotionData();
effects = " type:" + pData.getType().toString() + (pData.isExtended() ? " extended" : "")
+ (pData.isUpgraded() ? " upgraded" : "");
if (potionMeta.hasCustomEffects()) {
StringBuilder string = new StringBuilder();
for (PotionEffect effect : potionMeta.getCustomEffects()) {
int power = effect.getAmplifier() + 1;
int duration = (effect.getDuration() - (effect.getDuration() % 20)) / 20;
string.append(effect.getType().getName() + ":" + power + ":" + duration + ",");
}
effects += " effects:" + string.substring(0, string.length() - 1);
}
}
if (meta instanceof LeatherArmorMeta) {
LeatherArmorMeta armorMeta = (LeatherArmorMeta) meta;
if (!armorMeta.getColor().equals(Bukkit.getServer().getItemFactory().getDefaultLeatherColor())) {
DyeColor c = DyeColor.getByColor(armorMeta.getColor());
color = " color:" + (c == null ? '#' + Integer.toHexString(armorMeta.getColor().asRGB()) : c.toString());
}
}
if (meta instanceof EnchantmentStorageMeta) {
EnchantmentStorageMeta storageMeta = (EnchantmentStorageMeta) meta;
if (storageMeta.hasStoredEnchants()) {
StringBuilder string = new StringBuilder();
for (Enchantment enchant : storageMeta.getStoredEnchants().keySet()) {
string.append(enchant.getName() + ":" + storageMeta.getStoredEnchants().get(enchant) + ",");
}
enchants = " enchants:" + string.substring(0, string.length() - 1);
}
}
if (meta instanceof SkullMeta) {
SkullMeta skullMeta = (SkullMeta) meta;
if (skullMeta.hasOwner()) {
owner = " owner:" + skullMeta.getOwner();
}
}
if (meta instanceof FireworkMeta) {
FireworkMeta fireworkMeta = (FireworkMeta) meta;
if (fireworkMeta.hasEffects()) {
StringBuilder builder = new StringBuilder();
builder.append(" firework:");
for (FireworkEffect effect : fireworkMeta.getEffects()) {
builder.append(effect.getType() + ":");
for (Color c : effect.getColors()) {
DyeColor dye = DyeColor.getByFireworkColor(c);
builder.append((dye != null ? dye : '#' + Integer.toHexString(c.asRGB())) + ";");
}
// remove last semicolon
builder.setLength(Math.max(builder.length() - 1, 0));
builder.append(":");
for (Color c : effect.getFadeColors()) {
DyeColor dye = DyeColor.getByFireworkColor(c);
builder.append((dye != null ? dye : '#' + Integer.toHexString(c.asRGB())) + ";");
}
builder.setLength(Math.max(builder.length() - 1, 0));
builder.append(":" + effect.hasTrail() + ":" + effect.hasFlicker() + ",");
}
builder.setLength(Math.max(builder.length() - 1, 0));
builder.append(" power:" + fireworkMeta.getPower());
firework = builder.toString();
}
}
if (meta instanceof FireworkEffectMeta) {
FireworkEffectMeta fireworkMeta = (FireworkEffectMeta) meta;
if (fireworkMeta.hasEffect()) {
FireworkEffect effect = fireworkMeta.getEffect();
StringBuilder builder = new StringBuilder();
builder.append(" firework:");
builder.append(effect.getType() + ":");
for (Color c : effect.getColors()) {
DyeColor dye = DyeColor.getByFireworkColor(c);
builder.append((dye != null ? dye : '#' + Integer.toHexString(c.asRGB())) + ";");
}
// remove last semicolon
builder.setLength(Math.max(builder.length() - 1, 0));
builder.append(":");
for (Color c : effect.getFadeColors()) {
DyeColor dye = DyeColor.getByFireworkColor(c);
builder.append((dye != null ? dye : '#' + Integer.toHexString(c.asRGB())) + ";");
}
builder.setLength(Math.max(builder.length() - 1, 0));
builder.append(":" + effect.hasTrail() + ":" + effect.hasFlicker());
}
}
// put it all together in a single string
return item.getType() + " data:" + item.getData().getData() + name + lore + enchants + title + author + text
+ effects + color + owner + firework;
}
}