/*
* Copyright or © or Copr. Amaury Carrade (2014 - 2016)
*
* http://amaury.carrade.eu
*
* This software is governed by the CeCILL-B license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL-B
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-B license and that you accept its terms.
*/
package eu.carrade.amaury.UHCReloaded.recipes;
import eu.carrade.amaury.UHCReloaded.UHCReloaded;
import eu.carrade.amaury.UHCReloaded.UHConfig;
import fr.zcraft.zlib.components.gui.GuiUtils;
import fr.zcraft.zlib.components.i18n.I;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.SkullType;
import org.bukkit.inventory.CraftingInventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.ArrayList;
public class RecipesManager
{
private UHCReloaded p = null;
private Material compassCentralIngredient = null;
private int compassRecipeType = -1;
public static final String RECIPE_COMPASS = "compass";
public static final String RECIPE_GLISTERING_MELON = "glistering";
public static final String RECIPE_ENCHANTED_GOLDEN_APPLE = "EGA";
public static final int COMPASS_DISABLED = 0;
public static final int COMPASS_EASY = 1;
public static final int COMPASS_MEDIUM = 2;
public static final int COMPASS_HARD = 3;
private String lastFailedRecipe = null;
public RecipesManager(UHCReloaded plugin)
{
this.p = plugin;
}
/**
* Registers the recipes needed, following the configuration.
*/
public void registerRecipes()
{
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.DO.get())
{
p.getServer().addRecipe(getGoldenHeadHumanRecipe());
}
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.DO.get())
{
p.getServer().addRecipe(getGoldenHeadMonsterRecipe());
}
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.ADD_LORE.get() || UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.ADD_LORE.get())
{
p.getServer().addRecipe(getLoreRemoverNormalRecipe());
p.getServer().addRecipe(getLoreRemoverNotchRecipe());
}
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_MELON_WITH_GOLD_BLOCK.get())
{
p.getServer().addRecipe(getGoldenMelonRecipe());
}
// Because the compass recipe is "semi-shapeless" (the central part is fixed, but the
// loots can be placed into any configuration), there isn't a registered recipe for it
// (I don't want to register 16 recipes for each difficulty).
// Instead, using the inventoryClickEvent/inventoryDragEvent, we checks manually if the
// recipe is valid.
switch (this.getCompassRecipeType())
{
case COMPASS_EASY:
compassCentralIngredient = Material.REDSTONE;
break;
case COMPASS_MEDIUM:
compassCentralIngredient = Material.ENDER_PEARL;
break;
case COMPASS_HARD:
compassCentralIngredient = Material.EYE_OF_ENDER;
break;
case COMPASS_DISABLED:
break;
}
}
/**
* Checks if a recipe is allowed.
*
* @param recipe
* @return True if the recipe is allowed, false else.
*/
public boolean isRecipeAllowed(Recipe recipe)
{
// Vanilla compass recipe is disabled if the special compass is used.
if (UHConfig.GAMEPLAY_CHANGES.COMPASS.ENABLED.get() && RecipeUtil.areSimilar(recipe, getVanillaCompassRecipe()))
{
this.lastFailedRecipe = RECIPE_COMPASS;
return false;
}
// Vanilla golden melon recipe is disabled if the craft with a gold block is enabled.
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_MELON_WITH_GOLD_BLOCK.get() && RecipeUtil.areSimilar(recipe, getVanillaGoldenMelonRecipe()))
{
this.lastFailedRecipe = RECIPE_GLISTERING_MELON;
return false;
}
// If enchanted golden apples are disabled...
// The same technique does not work, this is a workaround
if (UHConfig.GAMEPLAY_CHANGES.GOLDEN_APPLE.DISABLE_NOTCH_APPLES.get())
{
if (recipe.getResult().getType() == Material.GOLDEN_APPLE)
{
for (ItemStack item : RecipeUtil.getListOfIngredients(recipe))
{
if (item.getType() == Material.GOLD_BLOCK)
{
// There is a gold block in a recipe for a golden apple - NOPE NOPE NOPE
this.lastFailedRecipe = RECIPE_ENCHANTED_GOLDEN_APPLE;
return false;
}
}
}
}
// The recipe is allowed.
return true;
}
/**
* Checks if the recipe is a valid compass recipe.
* <p>
* A valid compass recipe is a recipe with:
* <ul>
* <li>
* in the center, the valid ingredient for the current compass craft
* (redstone, ender pearl or eye of ender);
* </li>
* <li>
* four iron ingots placed like the vanilla compass recipe;
* </li>
* <li>
* in the four corners, a bone, a rotten flesh, a spider eye and a gunpowder,
* placed in any shape.
* </li>
* </ul>
* <p>
* Executed in the {@code onInventoryClick} and {@code onInventoryDrag} events, to allow this to be recognized even if
* the recipe is not registered.
*
* @param matrix The content of the crafting inventory.
* @return true if the recipe is an alternate recipe for the compass.
*/
public boolean isValidCompassRecipe(ItemStack[] matrix)
{
if (matrix.length <= 5)
{
return false; // Small crafting grid
}
if (this.getCompassRecipeType() == COMPASS_DISABLED)
{
return false;
}
// 1: check of the static part (central ingredient + iron)
Material iron1 = matrix[1].getType();
Material iron2 = matrix[3].getType();
Material iron3 = matrix[5].getType();
Material iron4 = matrix[7].getType();
Material centralIngredient = matrix[4].getType();
if (!(iron1.equals(Material.IRON_INGOT)
&& iron2.equals(Material.IRON_INGOT)
&& iron3.equals(Material.IRON_INGOT)
&& iron4.equals(Material.IRON_INGOT)
&& centralIngredient.equals(compassCentralIngredient)))
{
return false;
}
// 2: check of the dynamic part (loots)
ArrayList<Material> corners = new ArrayList<>();
corners.add(matrix[0].getType());
corners.add(matrix[2].getType());
corners.add(matrix[6].getType());
corners.add(matrix[8].getType());
return corners.contains(Material.BONE)
&& corners.contains(Material.ROTTEN_FLESH)
&& corners.contains(Material.SPIDER_EYE)
&& corners.contains(Material.SULPHUR);
}
/**
* Adds the lore to the golden apples, if needed.
*
* @param recipe The recipe to change.
* @param inventory The crafting inventory (used to access the skull owner)
* @return The modified result (ItemStack) if a change was needed. Null if no change is needed.
*/
public ItemStack addLore(Recipe recipe, CraftingInventory inventory)
{
if ((UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.DO.get() || UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.DO.get())
&& (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.ADD_LORE.get() || UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.ADD_LORE.get())
&& (RecipeUtil.areSimilar(recipe, getGoldenHeadHumanRecipe()) || RecipeUtil.areSimilar(recipe, getGoldenHeadMonsterRecipe())))
{
ItemStack result = recipe.getResult();
ItemMeta meta = result.getItemMeta();
// Lookup for the head in the recipe
String name = "";
Boolean wither = true;
for (ItemStack item : inventory.getContents())
{
if (item.getType() == Material.SKULL_ITEM && item.getDurability() == (short) SkullType.PLAYER.ordinal())
{ // An human head
SkullMeta sm = (SkullMeta) item.getItemMeta();
if (sm.hasOwner())
{ // An human head
name = sm.getOwner();
wither = false;
}
break;
}
}
if ((wither && UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.ADD_LORE.get())
|| (!wither && UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.ADD_LORE.get()))
{
if (wither)
{
/// Golden head lore for withers
meta.setLore(GuiUtils.generateLore(I.t("Made from the fallen head of a malignant monster")));
}
else
{
/// Golden head lore for players. {0} = player name.
meta.setLore(GuiUtils.generateLore(I.t("Made from the fallen head of {0}", name)));
}
}
result.setItemMeta(meta);
return result;
}
return null;
}
/**
* Changes the name of the result item of the anti-lore recipe,
* to keep the same name than the original.
*
* @param recipe The recipe.
* @param inventory The crafting inventory. Used to get the name of the item placed in the inventory grid.
* @return The ItemStack if a change was needed; null else.
*/
public ItemStack keepNameOnLoreRemover(Recipe recipe, CraftingInventory inventory)
{
if ((UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.ADD_LORE.get() || UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.ADD_LORE.get())
&& (RecipeUtil.areSimilar(recipe, getLoreRemoverNormalRecipe()) || RecipeUtil.areSimilar(recipe, getLoreRemoverNotchRecipe())))
{
ItemStack original = null;
for (int slot = 0; slot <= 9; slot++)
{
original = inventory.getMatrix()[slot];
if (original.getType() != Material.AIR)
{
break; // Found (because there is only one item in the craft).
}
}
ItemMeta metaOriginal = original.getItemMeta();
if (metaOriginal != null && metaOriginal.hasDisplayName())
{
ItemStack result = recipe.getResult();
ItemMeta metaResult = result.getItemMeta();
metaResult.setDisplayName(metaOriginal.getDisplayName());
result.setItemMeta(metaResult);
return result;
}
return null;
}
return null;
}
/**
* Returns the current compass recipe.
*
* @return {@link RecipesManager#COMPASS_DISABLED}, {@link RecipesManager#COMPASS_EASY},
* {@link RecipesManager#COMPASS_MEDIUM} or {@link RecipesManager#COMPASS_HARD}.
*/
public int getCompassRecipeType()
{
if (compassRecipeType != -1)
{
return compassRecipeType;
}
if (UHConfig.GAMEPLAY_CHANGES.COMPASS.ENABLED.get())
{
switch (UHConfig.GAMEPLAY_CHANGES.COMPASS.RECIPE.get().toLowerCase())
{
case "easy":
compassRecipeType = COMPASS_EASY;
break;
case "hard":
compassRecipeType = COMPASS_HARD;
break;
default:
compassRecipeType = COMPASS_MEDIUM;
break;
}
}
else
{
compassRecipeType = COMPASS_DISABLED;
}
return compassRecipeType;
}
/**
* Returns the recipe that transforms 8 gold ingots and 1 human head into
* a golden apple.
*
* @return The shaped recipe.
*/
public ShapedRecipe getGoldenHeadHumanRecipe()
{
short damage = 0;
/// Item name of a golden head (from a player)
String name = I.tc("player_head", "{aqua}Golden head");
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.CRAFT_NOTCH_APPLE.get())
{
damage = 1;
/// Item name of an enchanted golden head (from a player)
name = I.tc("player_head", "{lightpurple}Golden head");
}
ItemStack goldenAppleStack = new ItemStack(Material.GOLDEN_APPLE, UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_HUMAN.NUMBER_CRAFTED.get(), damage);
ItemMeta goldenAppleMeta = goldenAppleStack.getItemMeta();
goldenAppleMeta.setDisplayName(ChatColor.RESET + name);
goldenAppleStack.setItemMeta(goldenAppleMeta);
ShapedRecipe goldenAppleFromHeadRecipe = new ShapedRecipe(goldenAppleStack);
goldenAppleFromHeadRecipe.shape("GGG", "GHG", "GGG");
goldenAppleFromHeadRecipe.setIngredient('G', Material.GOLD_INGOT);
goldenAppleFromHeadRecipe.setIngredient('H', Material.SKULL_ITEM, SkullType.PLAYER.ordinal()); // TODO: deprecated, but no alternative found...
return goldenAppleFromHeadRecipe;
}
/**
* Returns the recipe that transforms 8 gold ingots and 1 wither head into
* a golden apple.
*
* @return The shaped recipe.
*/
public ShapedRecipe getGoldenHeadMonsterRecipe()
{
short damage = 0;
/// Item name of a golden head (from a monster)
String name = I.tc("monster_head", "{aqua}Golden head");
if (UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.CRAFT_NOTCH_APPLE.get())
{
damage = 1;
/// Item name of an enchanted golden head (from a monster)
name = I.tc("monster_head", "{lightpurple}Golden head");
}
ItemStack goldenAppleStack = new ItemStack(Material.GOLDEN_APPLE, UHConfig.GAMEPLAY_CHANGES.CRAFT_GOLDEN_APPLE_FROM_HEAD.FROM_WITHER.NUMBER_CRAFTED.get(), damage);
ItemMeta goldenAppleMeta = goldenAppleStack.getItemMeta();
goldenAppleMeta.setDisplayName(ChatColor.RESET + name);
goldenAppleStack.setItemMeta(goldenAppleMeta);
ShapedRecipe goldenAppleFromWitherHeadRecipe = new ShapedRecipe(goldenAppleStack);
goldenAppleFromWitherHeadRecipe.shape("GGG", "GHG", "GGG");
goldenAppleFromWitherHeadRecipe.setIngredient('G', Material.GOLD_INGOT);
goldenAppleFromWitherHeadRecipe.setIngredient('H', Material.SKULL_ITEM, SkullType.WITHER.ordinal()); // TODO: deprecated, but no alternative found...
return goldenAppleFromWitherHeadRecipe;
}
/**
* Returns the recipe that transforms one golden apple into one golden apple.
* Used to remove the lore, so two apples from a different head are stackable.
*
* @return The shapeless recipe.
*/
public ShapelessRecipe getLoreRemoverNormalRecipe()
{
ShapelessRecipe goldenAppleLoreRemoverRecipe = new ShapelessRecipe(new ItemStack(Material.GOLDEN_APPLE, 1, (short) 0));
goldenAppleLoreRemoverRecipe.addIngredient(Material.GOLDEN_APPLE);
return goldenAppleLoreRemoverRecipe;
}
/**
* Returns the recipe that transforms one enchanted golden apple into one enchanted
* golden apple.
* Used to remove the lore, so two apples from a different head are stackable.
*
* @return The shapeless recipe.
*/
public ShapelessRecipe getLoreRemoverNotchRecipe()
{
ShapelessRecipe goldenAppleLoreRemoverNotchRecipe = new ShapelessRecipe(new ItemStack(Material.GOLDEN_APPLE, 1, (short) 1));
goldenAppleLoreRemoverNotchRecipe.addIngredient(Material.GOLDEN_APPLE, 1);
return goldenAppleLoreRemoverNotchRecipe;
}
/**
* Returns the recipe that transforms one melon and one gold block into a golden melon.
*
* @return The shapeless recipe.
*/
public ShapelessRecipe getGoldenMelonRecipe()
{
ShapelessRecipe goldenMelonRecipe = new ShapelessRecipe(new ItemStack(Material.SPECKLED_MELON));
goldenMelonRecipe.addIngredient(1, Material.GOLD_BLOCK);
goldenMelonRecipe.addIngredient(1, Material.MELON);
return goldenMelonRecipe;
}
/**
* Returns the vanilla recipe for the compass.
*
* @return The shaped recipe.
*/
public ShapedRecipe getVanillaCompassRecipe()
{
ShapedRecipe vanillaCompassRecipe = new ShapedRecipe(new ItemStack(Material.COMPASS));
vanillaCompassRecipe.shape(" I ", "IRI", " I ");
vanillaCompassRecipe.setIngredient('I', Material.IRON_INGOT);
vanillaCompassRecipe.setIngredient('R', Material.REDSTONE);
return vanillaCompassRecipe;
}
/**
* Returns the vanilla recipe for the golden melon.
*
* @return The shaped recipe.
*/
public ShapedRecipe getVanillaGoldenMelonRecipe()
{
ShapedRecipe vanillaGoldenMelonRecipe = new ShapedRecipe(new ItemStack(Material.SPECKLED_MELON));
vanillaGoldenMelonRecipe.shape("GGG", "GMG", "GGG");
vanillaGoldenMelonRecipe.setIngredient('G', Material.GOLD_NUGGET);
vanillaGoldenMelonRecipe.setIngredient('M', Material.MELON);
return vanillaGoldenMelonRecipe;
}
/**
* Returns the last failed recipe.
*
* Use {@link RecipesManager#RECIPE_COMPASS}, {@link RecipesManager#RECIPE_GLISTERING_MELON} and
* {@link RecipesManager#RECIPE_ENCHANTED_GOLDEN_APPLE} to get the type of the failed recipe.
*
* @return the lastFailedRecipe
*/
public String getLastFailedRecipe()
{
return lastFailedRecipe;
}
}