diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/MMOItemPlayerIngredient.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/MMOItemPlayerIngredient.java index f858ca51..d0521010 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/MMOItemPlayerIngredient.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/ingredient/inventory/MMOItemPlayerIngredient.java @@ -5,7 +5,7 @@ import io.lumine.mythic.lib.api.item.NBTItem; public class MMOItemPlayerIngredient extends PlayerIngredient { - /* + /** * No need to save as MMOItemTemplate or Type instances. Just need the string, * because if they don't exist the recipe ingredients won't load anyways. * And it's better for performance yes diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java index b14a37ba..e15a6676 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CraftingRecipe.java @@ -46,33 +46,53 @@ public class CraftingRecipe extends Recipe { } @Override - public void whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) { + public boolean whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station) { if (!data.isOnline()) - return; + return false; - if (!hasOption(RecipeOption.SILENT_CRAFT)) - data.getPlayer().playSound(data.getPlayer().getLocation(), station.getSound(), 1, 1); /* - * If the recipe is instant, take the ingredients off and directly add - * the output to the player's inventory + * If the recipe is instant, take the ingredients off + * and directly add the output to the player's inventory */ if (isInstant()) { - PlayerUseCraftingStationEvent event = new PlayerUseCraftingStationEvent(data, station, recipe, - PlayerUseCraftingStationEvent.StationAction.INSTANT_RECIPE); + + ItemStack result = hasOption(RecipeOption.OUTPUT_ITEM) ? getOutput().generate(data.getRPG()) : null; + PlayerUseCraftingStationEvent event = new PlayerUseCraftingStationEvent(data, station, recipe, result); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) - return; + return false; - if (hasOption(RecipeOption.OUTPUT_ITEM)) - new SmartGive(data.getPlayer()).give(getOutput().generate(data.getRPG())); + if (result != null) + new SmartGive(data.getPlayer()).give(result); recipe.getRecipe().getTriggers().forEach(trigger -> trigger.whenCrafting(data)); + // Play sound + if (!hasOption(RecipeOption.SILENT_CRAFT)) + data.getPlayer().playSound(data.getPlayer().getLocation(), station.getSound(), 1, 1); + + // Recipe was successfully used + return true; + /* * If the recipe is not instant, add the item to the crafting queue */ - } else + } else { + + PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(data, station, recipe); + Bukkit.getPluginManager().callEvent(called); + if (called.isCancelled()) + return false; + + // Play sound + if (!hasOption(RecipeOption.SILENT_CRAFT)) + data.getPlayer().playSound(data.getPlayer().getLocation(), station.getSound(), 1, 1); + data.getCrafting().getQueue(station).add(this); + + // Recipe was successfully used + return true; + } } @Override diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/Recipe.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/Recipe.java index 9977a48c..5b044777 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/Recipe.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/Recipe.java @@ -144,13 +144,17 @@ public abstract class Recipe { * Called when all the recipe conditions are to true and when the player * eventually starts crafting OR when the player claims the item in the * crafting queue once the delay is over. + *

+ * This however checks for the bukkit event which can be cancelled; if + * this method returns false the ingredients shall not be consumed. * * @param data Player crafting the item * @param inv The player's ingredients * @param recipe The recipe used to craft the item * @param station The station used to craft the item + * @return If the crafting recipe was successfully used */ - public abstract void whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station); + public abstract boolean whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station); /** * Applies extra conditions when a player has just clicked on a recipe item diff --git a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/UpgradingRecipe.java b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/UpgradingRecipe.java index cb95fbdf..6573e945 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/UpgradingRecipe.java +++ b/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/UpgradingRecipe.java @@ -9,11 +9,13 @@ import net.Indyuce.mmoitems.api.crafting.ingredient.CheckedIngredient; import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient; import net.Indyuce.mmoitems.api.crafting.ingredient.MMOItemIngredient; import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory; +import net.Indyuce.mmoitems.api.event.PlayerUseCraftingStationEvent; import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem; import net.Indyuce.mmoitems.api.item.util.ConfigItems; import net.Indyuce.mmoitems.api.player.PlayerData; import net.Indyuce.mmoitems.api.util.message.Message; import net.Indyuce.mmoitems.stat.data.UpgradeData; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Sound; import org.bukkit.configuration.ConfigurationSection; @@ -40,17 +42,29 @@ public class UpgradingRecipe extends Recipe { } @Override - public void whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe castRecipe, CraftingStation station) { + public boolean whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe castRecipe, CraftingStation station) { + if (!data.isOnline()) + return false; + CheckedUpgradingRecipe recipe = (CheckedUpgradingRecipe) castRecipe; + PlayerUseCraftingStationEvent event = new PlayerUseCraftingStationEvent(data, station, recipe); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) + return false; + + // Update item recipe.getUpgradeData().upgrade(recipe.getMMOItem()); recipe.getUpgraded().setItemMeta(recipe.getMMOItem().newBuilder().build().getItemMeta()); castRecipe.getRecipe().getTriggers().forEach(trigger -> trigger.whenCrafting(data)); - if (!data.isOnline()) - return; - Message.UPGRADE_SUCCESS.format(ChatColor.YELLOW, "#item#", MMOUtils.getDisplayName(recipe.getUpgraded())).send(data.getPlayer()); - data.getPlayer().playSound(data.getPlayer().getLocation(), station.getSound(), 1, 1); + + // Play sound + if (!hasOption(RecipeOption.SILENT_CRAFT)) + data.getPlayer().playSound(data.getPlayer().getLocation(), station.getSound(), 1, 1); + + // Recipe used successfully + return true; } @Override diff --git a/src/main/java/net/Indyuce/mmoitems/api/event/PlayerUseCraftingStationEvent.java b/src/main/java/net/Indyuce/mmoitems/api/event/PlayerUseCraftingStationEvent.java index 4c6bc464..abe36104 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/event/PlayerUseCraftingStationEvent.java +++ b/src/main/java/net/Indyuce/mmoitems/api/event/PlayerUseCraftingStationEvent.java @@ -1,52 +1,94 @@ package net.Indyuce.mmoitems.api.event; import net.Indyuce.mmoitems.api.crafting.CraftingStation; +import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe; import net.Indyuce.mmoitems.api.crafting.recipe.CraftingRecipe; import net.Indyuce.mmoitems.api.crafting.recipe.Recipe; -import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe; +import net.Indyuce.mmoitems.api.crafting.recipe.UpgradingRecipe; import net.Indyuce.mmoitems.api.player.PlayerData; import org.apache.commons.lang.Validate; import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; public class PlayerUseCraftingStationEvent extends PlayerDataEvent { - private final Recipe recipe; - private final CheckedRecipe recipeInfo; private final CraftingStation station; + private final Recipe recipe; private final StationAction action; + @Nullable + private final CheckedRecipe recipeInfo; + @Nullable + private final ItemStack result; + private static final HandlerList handlers = new HandlerList(); /** - * Called when a player directly interacts with a recipe in the crafting - * station GUI. The recipe is either instant and the item is given - * instaneously, or the item is sent in the crafting queue - * + * Called when interacting with a non instant recipe. The item + * is therefore sent to the crafting queue. + * * @param playerData The player interacting with the crafting station * @param station The crafting station being used * @param recipeInfo The recipe being used to craft the item */ - public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, CheckedRecipe recipeInfo, StationAction action) { - this(playerData, station, recipeInfo, recipeInfo.getRecipe(), action); + public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, CheckedRecipe recipeInfo) { + this(playerData, station, recipeInfo, recipeInfo.getRecipe(), null, StationAction.INTERACT_WITH_RECIPE); } /** - * Called when a player claims an item from the crafting queue. - * + * Called when interacting with an upgrading recipe + * + * @param playerData The player interacting with the crafting station + * @param station The crafting station being used + * @param recipeInfo The recipe being used to craft the item + */ + public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, UpgradingRecipe.CheckedUpgradingRecipe recipeInfo) { + this(playerData, station, recipeInfo, recipeInfo.getRecipe(), null, StationAction.UPGRADE_RECIPE); + } + + /** + * Called when interacting with an instant recipe + * + * @param playerData The player interacting with the crafting station + * @param station The crafting station being used + * @param recipeInfo The recipe being used to craft the item + * @param result The item given to the player + */ + public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, CheckedRecipe recipeInfo, ItemStack result) { + this(playerData, station, recipeInfo, recipeInfo.getRecipe(), result, StationAction.INSTANT_RECIPE); + } + + /** + * Called when a player claims an item from the crafting queue + * + * @param playerData The player interacting with the crafting station + * @param station The crafting station being used + * @param recipe The recipe being used to craft the item + * @param result The item given to the player + */ + public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, Recipe recipe, ItemStack result) { + this(playerData, station, null, recipe, result, StationAction.CRAFTING_QUEUE); + } + + /** + * Called when a player removes an item from the queue + * * @param playerData The player interacting with the crafting station * @param station The crafting station being used * @param recipe The recipe being used to craft the item */ - public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, Recipe recipe, StationAction action) { - this(playerData, station, null, recipe, action); + public PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, Recipe recipe) { + this(playerData, station, null, recipe, null, StationAction.CANCEL_QUEUE); } - private PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, CheckedRecipe recipeInfo, Recipe recipe, - StationAction action) { + private PlayerUseCraftingStationEvent(PlayerData playerData, CraftingStation station, CheckedRecipe recipeInfo, Recipe recipe, ItemStack result, StationAction action) { super(playerData); - this.recipeInfo = recipeInfo; - this.recipe = recipe; this.station = station; + this.recipe = recipe; + this.recipeInfo = recipeInfo; + this.result = result; this.action = action; } @@ -54,20 +96,33 @@ public class PlayerUseCraftingStationEvent extends PlayerDataEvent { return station; } - /** - * @return The corresponding recipe info IF AND ONLY IF the player is - * interacting with a recipe. This method cannot be used when a - * player claims an item from the crafting queue. - */ - public CheckedRecipe getRecipeInfo() { - Validate.notNull(recipeInfo, "No recipe info is provided when a player claims an item in the crafting queue"); - return recipeInfo; - } - public boolean hasRecipeInfo() { return recipeInfo != null; } + /** + * @return The corresponding recipe info if the current + * interaction is either INTERACT_WITH_RECIPE or INSTANT_RECIPE + */ + public CheckedRecipe getRecipeInfo() { + Validate.notNull(action == StationAction.INSTANT_RECIPE || action == StationAction.INTERACT_WITH_RECIPE, "No recipe info is provided with " + action.name()); + return recipeInfo; + } + + public boolean hasResult() { + return result != null; + } + + /** + * @return The result item if the current interaction + * is either INSTANT_RECIPE or CRAFTING_QUEUE + */ + public ItemStack getResult() { + Validate.notNull(action == StationAction.INSTANT_RECIPE || action == StationAction.CRAFTING_QUEUE, "No result item is provided with " + action.name()); + return result; + } + + public Recipe getRecipe() { return recipe; } @@ -76,6 +131,10 @@ public class PlayerUseCraftingStationEvent extends PlayerDataEvent { return action; } + /** + * @return If the recipe used is an instantaneous recipe + * @deprecated Check if the interaction type is INSTANT_RECIPE instead + */ @Deprecated public boolean isInstant() { return recipe instanceof CraftingRecipe && ((CraftingRecipe) recipe).isInstant(); @@ -91,6 +150,11 @@ public class PlayerUseCraftingStationEvent extends PlayerDataEvent { public enum StationAction { + /** + * Called when a player uses an upgrading recipe. Upgrading recipes are always instantaneous + */ + UPGRADE_RECIPE, + /** * Called when a player places an item in the crafting queue when the * recipe is not instantaneous diff --git a/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java b/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java index 1fb5e914..799606af 100644 --- a/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java +++ b/src/main/java/net/Indyuce/mmoitems/gui/CraftingStationView.java @@ -181,39 +181,45 @@ public class CraftingStationView extends PluginInventory { * to the player and remove the recipe from the queue */ if (recipeInfo.isReady()) { - PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(playerData, station, recipe, - PlayerUseCraftingStationEvent.StationAction.CRAFTING_QUEUE); + ItemStack result = recipe.hasOption(Recipe.RecipeOption.OUTPUT_ITEM) ? recipe.getOutput().generate(playerData.getRPG()) : null; + + PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(playerData, station, recipe, result); Bukkit.getPluginManager().callEvent(called); if (called.isCancelled()) return; + // Remove from crafting queue playerData.getCrafting().getQueue(station).remove(recipeInfo); + // Apply triggers recipe.getTriggers().forEach(trigger -> trigger.whenCrafting(playerData)); - ItemStack craftedItem = recipe.getOutput().generate(playerData.getRPG()); - - CustomSoundListener.stationCrafting(craftedItem, player); - + // Play sounds + CustomSoundListener.stationCrafting(result, player); if (!recipe.hasOption(Recipe.RecipeOption.SILENT_CRAFT)) player.playSound(player.getLocation(), station.getSound(), 1, 1); - if (recipe.hasOption(Recipe.RecipeOption.OUTPUT_ITEM)) - new SmartGive(player).give(craftedItem); + if (result != null) + new SmartGive(player).give(result); /* * If the recipe is not ready, cancel the recipe and give the * ingredients back to the player */ } else { - PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(playerData, station, recipe, - PlayerUseCraftingStationEvent.StationAction.CANCEL_QUEUE); + PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(playerData, station, recipe); Bukkit.getPluginManager().callEvent(called); if (called.isCancelled()) return; + // Remove from crafting queue playerData.getCrafting().getQueue(station).remove(recipeInfo); - player.playSound(player.getLocation(), station.getSound(), 1, 1); + + // Play sounds + if (!recipe.hasOption(Recipe.RecipeOption.SILENT_CRAFT)) + player.playSound(player.getLocation(), station.getSound(), 1, 1); + + // Give ingredients back for (Ingredient ingredient : recipeInfo.getRecipe().getIngredients()) new SmartGive(player).give(ingredient.generateItemStack(playerData.getRPG())); } @@ -241,17 +247,12 @@ public class CraftingStationView extends PluginInventory { return; } - PlayerUseCraftingStationEvent called = new PlayerUseCraftingStationEvent(playerData, station, recipe, - PlayerUseCraftingStationEvent.StationAction.INTERACT_WITH_RECIPE); - Bukkit.getPluginManager().callEvent(called); - if (called.isCancelled()) - return; + if (recipe.getRecipe().whenUsed(playerData, ingredients, recipe, station)) { + recipe.getIngredients().forEach(ingredient -> ingredient.takeAway()); + recipe.getConditions().forEach(condition -> condition.getCondition().whenCrafting(playerData)); - recipe.getRecipe().whenUsed(playerData, ingredients, recipe, station); - recipe.getIngredients().forEach(ingredient -> ingredient.takeAway()); - recipe.getConditions().forEach(condition -> condition.getCondition().whenCrafting(playerData)); - - updateData(); + updateData(); + } } private CheckedRecipe getRecipe(String id) { diff --git a/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java b/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java index 139eeec8..c634bd3d 100644 --- a/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java +++ b/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java @@ -78,9 +78,9 @@ public class PlayerListener implements Listener { } /* - * prevent players from dropping items which are bound to them with a - * soulbound. items are cached inside a map waiting for the player to - * respawn. if he does not respawn the items are dropped on the ground, this + * Prevent players from dropping items which are bound to them with a + * soulbound. Items are cached inside a map waiting for the player to + * respawn. If he does not respawn the items are dropped on the ground, this * way there don't get lost */ @EventHandler(priority = EventPriority.HIGH)