!Rewrote crafting station player ingredients

This commit is contained in:
Indyuce 2021-07-09 15:05:52 +02:00
parent 2ecaf4572c
commit 2f73a572ea
27 changed files with 761 additions and 576 deletions

View File

@ -3,6 +3,7 @@ package net.Indyuce.mmoitems.api.crafting;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.util.PostLoadObject;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.CraftingRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.Recipe;

View File

@ -1,19 +1,14 @@
package net.Indyuce.mmoitems.api.crafting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.configuration.ConfigurationSection;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.crafting.CraftingStatus.CraftingQueue.CraftingInfo;
import net.Indyuce.mmoitems.api.crafting.recipe.CraftingRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.Recipe;
import net.Indyuce.mmoitems.api.player.PlayerData;
import org.bukkit.configuration.ConfigurationSection;
import java.util.*;
import java.util.logging.Level;
public class CraftingStatus {
@ -170,103 +165,4 @@ public class CraftingStatus {
}
}
}
/*
* instant craft: the progress bar is displayed on the subtitle and players
* must stay around their initial position or the craft will be cancelled
*/
// private InstantCraftingInfo instant;
//
// public InstantCraftingInfo getInstant() {
// return instant;
// }
//
// public
// public class InstantCraftingInfo extends CraftingInfo {
// private final PlayerDataManager data;
// private boolean timedOut;
//
// public InstantCraftingInfo(PlayerDataManager data, Recipe recipe) {
// super(recipe);
// this.data = data;
// }
//
// public void start() {
// data.getPlayer().closeInventory();
//
// new BukkitRunnable() {
// int t = 0;
// final String format =
// Message.CRAFTING_SUBTITLE.formatRaw(ChatColor.GREEN);
// final Location loc = data.getPlayer().getLocation().clone();
//
// public void run() {
// t++;
//
// if (data.getPlayer() == null || !data.getPlayer().isOnline() ||
// data.getPlayer().isDead() ||
// !data.getPlayer().getWorld().equals(loc.getWorld()) ||
// loc.distanceSquared(data.getPlayer().getLocation()) > 15) {
// timedOut = true;
// cancel();
// return;
// }
//
// if ((double) t / 10 > recipe.getRecipe().getCraftingTime()) {
// timedOut = true;
//
// recipe = recipe.getRecipe().getRecipeInfo(data, new
// IngredientInventory(data.getPlayer()));
// if (!recipe.areConditionsMet()) {
// Message.CONDITIONS_NOT_MET.format(ChatColor.RED).send(data.getPlayer());
// data.getPlayer().playSound(data.getPlayer().getLocation(),
// Sound.ENTITY_VILLAGER_NO, 1, 1);
// return;
// }
//
// if (!recipe.allIngredientsHad()) {
// Message.NOT_ENOUGH_MATERIALS.format(ChatColor.RED).send(data.getPlayer());
// data.getPlayer().playSound(data.getPlayer().getLocation(),
// Sound.ENTITY_VILLAGER_NO, 1, 1);
// return;
// }
//
// craft();
//
// cancel();
// return;
// }
//
// double left = recipe.getRecipe().getCraftingTime() - (double) t / 10;
// double r = (double) t / 10 / recipe.getRecipe().getCraftingTime();
// data.getPlayer().sendTitle("", format.replace("#left#",
// formatDelay(left)).replace("#bar#", MMOUtils.getProgressBar(r, 10,
// AltChar.square)), 0, 20, 10);
// }
// }.runTaskTimer(MMOItems.plugin, 0, 2);
// }
//
// public void craft() {
// timedOut = true;
//
// for (CheckedIngredient ingredient : recipe.getIngredients())
// ingredient.getPlayerIngredient().reduceItem(ingredient.getIngredient().getAmount(),
// ingredient.getIngredient());
//
// data.getPlayer().playSound(data.getPlayer().getLocation(),
// Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
// for (ItemStack drop :
// data.getPlayer().getInventory().addItem(recipe.getRecipe().getOutput().generate()).values())
// data.getPlayer().getWorld().dropItem(data.getPlayer().getLocation(),
// drop);
//
// updateData();
// open();
// }
//
// public boolean isTimedOut() {
// return timedOut || (System.currentTimeMillis() - started >
// recipe.getRecipe().getCraftingTime());
// }
// }
}

View File

@ -1,193 +0,0 @@
package net.Indyuce.mmoitems.api.crafting;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.util.ui.QuickNumberRange;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.manager.CraftingManager.IngredientType;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class IngredientInventory {
/*
* ingredients in the map are not stored using class instances as keys but
* using a unique string which is an unique ingredient identifier. allows
* for faster map check ups and fixes the >64 amount and ingredient
* splitting glitches
*/
private final Map<String, PlayerIngredient> ingredients = new HashMap<>();
public IngredientInventory(Player player) { this(player.getInventory()); }
public IngredientInventory(Inventory inv) {
loop: for (ItemStack item : inv.getContents())
if (item != null && item.getType() != Material.AIR) {
NBTItem nbt = MythicLib.plugin.getVersion().getWrapper().getNBTItem(item);
for (IngredientType ingredient : MMOItems.plugin.getCrafting().getIngredients()) {
if (ingredient.check(nbt)) {
addIngredient(nbt, ingredient);
continue loop;
}
}
}
}
public void addIngredient(NBTItem item, IngredientType ingredient) {
String key = ingredient.getId() + ":" + ingredient.readKey(item);
if (ingredients.containsKey(key))
/*
* add to current ingredient a specific amount.
*/
ingredients.get(key).add(item.getItem());
else
/*
* load an item stack and turn it into an ingredient which makes
* checking ingredients later much faster.
*/
ingredients.put(key, new PlayerIngredient(item.getItem()));
}
@Nullable
public PlayerIngredient getIngredient(@NotNull Ingredient ingredient, @NotNull IngredientLookupMode lookupMode) {
String key = ingredient.getKey();
// Find level
QuickNumberRange lvl = null;
int dsh = key.indexOf('-');
if (dsh > 0) {
// Get lvl
String itemCrop = key.substring(dsh + 1);
String itemLevel = itemCrop.substring(0, itemCrop.indexOf('_'));
lvl = QuickNumberRange.getFromString(itemLevel);
key = key.substring(0, dsh) + key.substring(dsh + 1 + itemLevel.length()); }
// Remove lvl
//ING//MMOItems.log("\u00a7a>\u00a78>\u00a77 Reading ingredient\u00a7a " + key + "\u00a77 (of level \u00a7a" + lvl + "\u00a77)");
for (String invKey : ingredients.keySet()) {
int dash = invKey.indexOf('-');
Integer itemLvl = null;
String ingredientKey = invKey;
if (dash > 0) {
// Get lvl
String itemCrop = invKey.substring(dash + 1);
String itemLevel = itemCrop.substring(0, itemCrop.indexOf('_'));
itemLvl = SilentNumbers.IntegerParse(itemLevel);
ingredientKey = invKey.substring(0, dash) + invKey.substring(dash + 1 + itemLevel.length()); }
// Compare removing level
//ING//MMOItems.log(" \u00a7a>\u00a77 Comparing to \u00a7b" + invKey + "\u00a77 (\u00a73" + ingredientKey + "\u00a77)");
if (ingredientKey.equals(key)) {
// Get level
boolean levelMet = true;
if (lookupMode != IngredientLookupMode.IGNORE_ITEM_LEVEL && lvl != null) {
// Parse
if (itemLvl == null) { itemLvl = 0; }
levelMet = lvl.inRange(itemLvl);
//ING//MMOItems.log(" \u00a7a>\u00a77 Was level \u00a7e" + invKey + "\u00a77 (\u00a76" + levelMet + "\u00a77)");
}
if (levelMet) { return ingredients.get(invKey); }
}
}
return null;
}
@Deprecated
public boolean hasIngredient(Ingredient ingredient) {
PlayerIngredient found = getIngredient(ingredient, IngredientLookupMode.IGNORE_ITEM_LEVEL);
return found != null && found.getAmount() >= ingredient.getAmount();
}
public static class PlayerIngredient {
/*
* stores items which correspond to a specific ingredient. when the
* ingredient is taken off the player inventory, the itemstack amounts
* get lowered. these POINT towards the player inventory itemStacks.
*/
private final List<ItemStack> items = new ArrayList<>();
public PlayerIngredient(ItemStack item) {
items.add(item);
}
public int getAmount() {
int t = 0;
for (ItemStack item : items)
t += item.getAmount();
return t;
}
public void add(ItemStack item) {
items.add(item);
}
// used for upgrading recipes
public ItemStack getFirstItem() {
return items.get(0);
}
/*
* algorythm which takes away a certain amount of items. used to consume
* ingredients when using recipes
*/
public void reduceItem(int amount) {
Iterator<ItemStack> iterator = items.iterator();
while (iterator.hasNext() && amount > 0) {
ItemStack item = iterator.next();
// remove itemStack from list if amount <= 0
if (item.getAmount() < 1) {
iterator.remove();
continue;
}
// amount of items it can take from this particular ItemStack
int take = Math.min(item.getAmount(), amount);
amount -= take;
item.setAmount(item.getAmount() - take);
}
}
}
/*
* could use a boolean because there are only two states possible, but makes
* things clearer.
*/
public enum IngredientLookupMode {
/*
* item level must be ignored when the player is using an upgrading
* recipe, otherwise recipe cannot identify right item
*/
IGNORE_ITEM_LEVEL,
/*
* scans ingredient inventory considering item levels
*/
BASIC
}
}

View File

@ -0,0 +1,47 @@
package net.Indyuce.mmoitems.api.crafting;
import io.lumine.mythic.lib.api.MMOLineConfig;
import java.util.function.Function;
/**
* Used to handle both ingredients and conditions because they share the same
* properties:
* - a way to display them in the recipe item lore.
* - some string identifier
* - a function which takes as input a line config and returns a loaded ingredient/condition
*
* @param <C> Either Condition or Ingredient
*/
public class LoadedCraftingObject<C> {
private final String id;
private final Function<MMOLineConfig, C> function;
private ConditionalDisplay display;
public LoadedCraftingObject(String id, Function<MMOLineConfig, C> function, ConditionalDisplay display) {
this.id = id;
this.function = function;
this.display = display;
}
public String getId() {
return id;
}
public void setDisplay(ConditionalDisplay display) {
this.display = display;
}
public boolean hasDisplay() {
return display != null;
}
public ConditionalDisplay getDisplay() {
return display;
}
public C load(MMOLineConfig config) {
return function.apply(config);
}
}

View File

@ -0,0 +1,31 @@
package net.Indyuce.mmoitems.api.crafting.condition;
public class CheckedCondition {
private final Condition condition;
private final boolean met;
/**
* Instanciated everytime a condition needs to be evaluated for a player
* when evaluating a CheckedRecipe (when a player is opening a crafting
* station)
*
* @param condition Condition being evaluated
* @param met If the condition is met or not
*/
public CheckedCondition(Condition condition, boolean met) {
this.condition = condition;
this.met = met;
}
public boolean isMet() {
return met;
}
public Condition getCondition() {
return condition;
}
public String format() {
return condition.formatDisplay(isMet() ? condition.getDisplay().getPositive() : condition.getDisplay().getNegative());
}
}

View File

@ -56,34 +56,4 @@ public abstract class Condition {
public CheckedCondition evaluateCondition(PlayerData data) {
return new CheckedCondition(this, isMet(data));
}
public static class CheckedCondition {
private final Condition condition;
private final boolean met;
/**
* Instanciated everytime a condition needs to be evaluated for a player
* when evaluating a CheckedRecipe (when a player is opening a crafting
* station)
*
* @param condition Condition being evaluated
* @param met If the condition is met or not
*/
public CheckedCondition(Condition condition, boolean met) {
this.condition = condition;
this.met = met;
}
public boolean isMet() {
return met;
}
public Condition getCondition() {
return condition;
}
public String format() {
return condition.formatDisplay(isMet() ? condition.getDisplay().getPositive() : condition.getDisplay().getNegative());
}
}
}

View File

@ -0,0 +1,95 @@
package net.Indyuce.mmoitems.api.crafting.ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.PlayerIngredient;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.Set;
public class CheckedIngredient {
@NotNull
private final Ingredient ingredient;
@Nullable
private final Set<PlayerIngredient> found;
private final boolean isHad;
/**
* Instantiated everytime an ingredient is evaluated for a player when a
* CheckedRecipe is being created (when a player is opening a crafting
* station). This helps greatly reducing ingredient checkups by caching
* the items the plugin will need to take off the player's ingredient
*
* @param ingredient The ingredient being evaluated
* @param found The corresponding ingredient found in the player's
* ingredient
*/
public CheckedIngredient(@NotNull Ingredient ingredient, @Nullable Set<PlayerIngredient> found) {
this.ingredient = ingredient;
this.found = found;
this.isHad = getTotalAmount() >= ingredient.getAmount();
}
/**
* @return If the player has enough of the specific item or not
*/
public boolean isHad() {
return isHad;
}
public int getTotalAmount() {
int total = 0;
for (PlayerIngredient ing : this.found)
total += ing.getAmount();
return total;
}
/**
* Takes off the required amount of ingredients from a player's inventory.
*/
public void takeAway() {
reduceItem(ingredient.getAmount());
}
/**
* Takes off a specific amount of ingredients from a player's inventory.
*
* @param amount Amount to take off the player's inventory.
* It most likely matches ingredient.getAmount()
*/
public void reduceItem(int amount) {
Iterator<PlayerIngredient> iterator = found.iterator();
while (iterator.hasNext() && amount > 0) {
ItemStack item = iterator.next().getItem();
// Remove itemStack from list if amount <= 0
if (item.getAmount() <= 0) {
iterator.remove();
continue;
}
// Amount of items it can take from this particular ItemStack
int take = Math.min(item.getAmount(), amount);
amount -= take;
item.setAmount(item.getAmount() - take);
}
}
@NotNull
public Ingredient getIngredient() {
return ingredient;
}
@Nullable
public Set<PlayerIngredient> getFound() {
return found;
}
@NotNull
public String format() {
return ingredient.formatDisplay(isHad() ? ingredient.getDisplay().getPositive() : ingredient.getDisplay().getNegative());
}
}

View File

@ -3,15 +3,18 @@ package net.Indyuce.mmoitems.api.crafting.ingredient;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.crafting.ConditionalDisplay;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory.IngredientLookupMode;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory.PlayerIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.PlayerIngredient;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class Ingredient {
/**
* An ingredient from a crafting station recipe.
* <p>
* See {@link PlayerIngredient} for more information.
*/
public abstract class Ingredient<C extends PlayerIngredient> {
private final String id;
private final int amount;
@ -24,6 +27,10 @@ public abstract class Ingredient {
this.amount = amount;
}
/**
* @return The ingredient type id i.e the string placed
* at the beginning of the line config
*/
public String getId() {
return id;
}
@ -42,61 +49,34 @@ public abstract class Ingredient {
/**
* @return The ingredient key which is used internally by MMOItems to check
* if two ingredients are of the same nature.
* if two ingredients are of the same nature.
* @deprecated Apart from ingredient type keys, keys are not used anymore.
*/
@Deprecated
public abstract String getKey();
/**
* Apply specific placeholders to display the ingredient in the item lore.
*
* @param s String with unparsed placeholders
* @return String with parsed placeholders
*
* @param s String with unparsed placeholders
* @return String with parsed placeholders
*/
public abstract String formatDisplay(String s);
public abstract boolean matches(C playerIngredient);
/**
* When the player right-clicks one of the items in a station, they can
* preview the stats if itself and the components it is made of. This
* is called to displace those preview elements.
*
* @param player Player looking at the recipe
*
* @return The ItemStack to display to the player
*/
@NotNull public abstract ItemStack generateItemStack(@NotNull RPGPlayer player);
@NotNull
public abstract ItemStack generateItemStack(@NotNull RPGPlayer player);
public CheckedIngredient evaluateIngredient(@NotNull IngredientInventory inv) {
return new CheckedIngredient(this, inv.getIngredient(this, IngredientLookupMode.BASIC));
}
public static class CheckedIngredient {
@NotNull private final Ingredient ingredient;
@Nullable private final PlayerIngredient found;
/**
* Instantiated everytime an ingredient is evaluated for a player when a
* CheckedRecipe is being created (when a player is opening a crafting
* station). This helps greatly reducing ingredient checkups by caching
* the items the plugin will need to take off the player's ingredient
*
* @param ingredient The ingredient being evaluated
* @param found The corresponding ingredient found in the player's
* ingredient
*/
private CheckedIngredient(@NotNull Ingredient ingredient, @Nullable PlayerIngredient found) {
this.ingredient = ingredient;
this.found = found;
}
/**
* @return If the player has enough of the specific item or not
*/
public boolean isHad() { return found != null && found.getAmount() >= ingredient.getAmount(); }
@NotNull public Ingredient getIngredient() { return ingredient; }
@Nullable public PlayerIngredient getPlayerIngredient() { return found; }
@NotNull public String format() { return ingredient.formatDisplay(isHad() ? ingredient.getDisplay().getPositive() : ingredient.getDisplay().getNegative()); }
return inv.findMatching(this);
}
}

View File

@ -0,0 +1,44 @@
package net.Indyuce.mmoitems.api.crafting.ingredient;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.crafting.ConditionalDisplay;
import net.Indyuce.mmoitems.api.crafting.LoadedCraftingObject;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.PlayerIngredient;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* A type of ingredient, like a vanilla ingredient or a MMOItems ingredient.
* <p>
* See {@link PlayerIngredient} for more information.
*/
public class IngredientType extends LoadedCraftingObject<Ingredient> {
private final Predicate<NBTItem> check;
private final Function<NBTItem, PlayerIngredient> readIngredient;
public IngredientType(String id, Function<MMOLineConfig, Ingredient> function, ConditionalDisplay display, Predicate<NBTItem> check, Function<NBTItem, PlayerIngredient> readIngredient) {
super(id, function, display);
this.check = check;
this.readIngredient = readIngredient;
}
/**
* @return If the checked item can be handled by this ingredient
*/
public boolean check(NBTItem item) {
return check.test(item);
}
/**
* Reads the ingredient from an NBTItem, called after checking
* that this ingredient type can handle this NBTItem
*
* @return
*/
public PlayerIngredient readPlayerIngredient(NBTItem item) {
return readIngredient.apply(item);
}
}

View File

@ -1,28 +1,29 @@
package net.Indyuce.mmoitems.api.crafting.ingredient;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.util.ui.QuickNumberRange;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.stat.DisplayName;
import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.crafting.ConfigMMOItem;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.MMOItemPlayerIngredient;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.stat.DisplayName;
import net.Indyuce.mmoitems.stat.data.MaterialData;
import io.lumine.mythic.lib.api.MMOLineConfig;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
public class MMOItemIngredient extends Ingredient {
public class MMOItemIngredient extends Ingredient<MMOItemPlayerIngredient> {
private final MMOItemTemplate template;
@NotNull private final QuickNumberRange level;
@NotNull
private final QuickNumberRange level;
private final String display;
public MMOItemIngredient(MMOLineConfig config) {
@ -64,7 +65,28 @@ public class MMOItemIngredient extends Ingredient {
return s.replace("#item#", display).replace("#level#", (level.hasMax() || level.hasMax()) ? "lvl." + level.toString() + " " : "").replace("#amount#", String.valueOf(getAmount()));
}
@NotNull @Override public ItemStack generateItemStack(@NotNull RPGPlayer player) {
@Override
public boolean matches(MMOItemPlayerIngredient playerIngredient) {
// Check for item type
if (!playerIngredient.getType().equals(template.getType().getId()))
return false;
// Check for item id
if (!playerIngredient.getId().equals(template.getId()))
return false;
// Checks for level range
if (SilentNumbers.floor(level.getAsDouble(0)) != 0 && !level.inRange(playerIngredient.getUpgradeLevel()))
return false;
// Yuss
return true;
}
@NotNull
@Override
public ItemStack generateItemStack(@NotNull RPGPlayer player) {
// Generate fresh from the template
MMOItem mmo = template.newBuilder(player).build();
@ -93,17 +115,22 @@ public class MMOItemIngredient extends Ingredient {
}
private String findName() {
String name = null;
if (template.getBaseItemData().containsKey(ItemStats.NAME))
name = template.getBaseItemData().get(ItemStats.NAME).toString().replace("<tier-color>", "").replace("<tier-name>", "").replace("<tier-color-cleaned>", "");
String name;
if (template.getBaseItemData().containsKey(ItemStats.MATERIAL) && name == null)
// By default, take item display name
if (template.getBaseItemData().containsKey(ItemStats.NAME))
name = template.getBaseItemData().get(ItemStats.NAME).toString().replace("<tier-color>", "").replace("<tier-name>", "").replace("<tier-color-cleaned>", "");
// Try and take the material name
else if (template.getBaseItemData().containsKey(ItemStats.MATERIAL))
name = MMOUtils.caseOnWords(((MaterialData) template.getBaseItemData().get(ItemStats.MATERIAL)).getMaterial().name().toLowerCase().replace("_", " "));
if (name == null) { name = "Unrecognized Item"; }
// Ultra rare case to avoid a NPE
else name = "Unrecognized Item";
// Append upgrade level
if (SilentNumbers.floor(level.getAsDouble(0)) != 0) { return DisplayName.appendUpgradeLevel(name, SilentNumbers.floor(level.getAsDouble(0))); }
if (SilentNumbers.floor(level.getAsDouble(0)) != 0)
return DisplayName.appendUpgradeLevel(name, SilentNumbers.floor(level.getAsDouble(0)));
return name;
}
}

View File

@ -1,16 +1,16 @@
package net.Indyuce.mmoitems.api.crafting.ingredient;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.util.LegacyComponent;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.VanillaPlayerIngredient;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class VanillaIngredient extends Ingredient {
public class VanillaIngredient extends Ingredient<VanillaPlayerIngredient> {
private final Material material;
/**
@ -40,8 +40,19 @@ public class VanillaIngredient extends Ingredient {
return s.replace("#item#", display).replace("#amount#", "" + getAmount());
}
@Override
public boolean matches(VanillaPlayerIngredient ing) {
// Check for material
if (ing.getType() != material)
return false;
// Check for display name
return ing.getDisplayName() != null ? ing.getDisplayName().equals(displayName) : displayName == null;
}
@NotNull
@Override
@Override
public ItemStack generateItemStack(@NotNull RPGPlayer player) {
NBTItem item = NBTItem.get(new ItemStack(material, getAmount()));
if (displayName != null) {

View File

@ -0,0 +1,111 @@
package net.Indyuce.mmoitems.api.crafting.ingredient.inventory;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.crafting.ingredient.CheckedIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.IngredientType;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class IngredientInventory {
private final Map<String, Set<PlayerIngredient>> ingredients = new HashMap<>();
/**
* Loads all the possible crafting station ingredients from a player's inventory
*/
public IngredientInventory(Player player) {
this(player.getInventory());
}
/**
* Loads all the possible crafting station ingredients from an inventory
*/
public IngredientInventory(Inventory inv) {
loop:
for (ItemStack item : inv.getContents())
if (item != null && item.getType() != Material.AIR && item.getAmount() > 0) {
NBTItem nbt = MythicLib.plugin.getVersion().getWrapper().getNBTItem(item);
for (IngredientType ingredient : MMOItems.plugin.getCrafting().getIngredients()) {
if (ingredient.check(nbt)) {
addIngredient(nbt, ingredient);
continue loop;
}
}
}
}
/**
* Registers an ingredient.
*
* @param item The actual item in the inventory
* @param ingredient The type of the ingredient added
*/
public void addIngredient(NBTItem item, IngredientType ingredient) {
String key = ingredient.getId();
// Add to existing set
if (ingredients.containsKey(key))
ingredients.get(key).add(ingredient.readPlayerIngredient(item));
// Initialize
else {
Set<PlayerIngredient> ingredients = new HashSet<>();
ingredients.add(ingredient.readPlayerIngredient(item));
this.ingredients.put(key, ingredients);
}
}
@Nullable
public CheckedIngredient findMatching(@NotNull Ingredient ingredient) {
Set<PlayerIngredient> found = new HashSet<>();
if (!ingredients.containsKey(ingredient.getId()))
return new CheckedIngredient(ingredient, found);
for (PlayerIngredient checked : ingredients.get(ingredient.getId()))
if (ingredient.matches(checked))
found.add(checked);
return new CheckedIngredient(ingredient, found);
}
/**
* @deprecated First use {@link #findMatching(Ingredient)} and cache its
* result to use the isHad() method of returned class instead.
*/
@Deprecated
public boolean hasIngredient(Ingredient ingredient) {
return findMatching(ingredient).isHad();
}
/**
* Could use a boolean because there are only two
* states possible, but makes things clearer.
*
* @deprecated Since 6.6 where ingredients were recoded.
*/
@Deprecated
public enum IngredientLookupMode {
/**
* Item level must be ignored when the player is using an upgrading
* recipe, otherwise recipe cannot identify right item
*/
IGNORE_ITEM_LEVEL,
/**
* Scans ingredient inventory considering item levels
*/
BASIC
}
}

View File

@ -0,0 +1,44 @@
package net.Indyuce.mmoitems.api.crafting.ingredient.inventory;
import com.google.gson.JsonParser;
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
*/
private final String type, id;
/**
* Used to check if the level of the player's item matches
* the level range given by the recipe ingredient.
*/
private final int upgradeLevel;
// TODO also add support for item level range? Quite easy to implement with the new PlayerIngredient interface.
public MMOItemPlayerIngredient(NBTItem item) {
super(item);
this.type = item.getString("MMOITEMS_ITEM_TYPE");
this.id = item.getString("MMOITEMS_ITEM_ID");
String upgradeString = item.getString("MMOITEMS_UPGRADE");
this.upgradeLevel = !upgradeString.isEmpty() ? new JsonParser().parse(upgradeString).getAsJsonObject().get("Level").getAsInt() : 0;
}
public String getType() {
return type;
}
public String getId() {
return id;
}
public int getUpgradeLevel() {
return upgradeLevel;
}
}

View File

@ -0,0 +1,59 @@
package net.Indyuce.mmoitems.api.crafting.ingredient.inventory;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.crafting.ConditionalDisplay;
import net.Indyuce.mmoitems.api.crafting.ingredient.CheckedIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.IngredientType;
import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe;
import net.Indyuce.mmoitems.manager.CraftingManager;
import org.bukkit.inventory.ItemStack;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Code architecture for all the classes concerning Ingredients.
* <p>
* MMOItems based ingredients / vanilla based ingredients are called IngredientTypes.
* One {@link IngredientType} is loaded for every type of ingredient there exists.
* External plugins can register other types of ingredients using
* {@link CraftingManager#registerIngredient(String, Function, ConditionalDisplay, Predicate, Function)}.
* <p>
* Once all the ingredient types are loaded, stations are loaded from the config files.
* That means loading the ingredients from the recipes which is what {@link Ingredient} is used for.
* <p>
* When a player opens a crafting station, MMOItems must first calculate all the ingredients
* they have in their inventory. A {@link IngredientInventory} is created, calculating all
* the ingredients the player has using the {@link PlayerIngredient} interface.
* <p>
* The second step when opening a crafting station is comparing the ingredients required for one
* crafting recipe to the current player's ingredients, which is done using {@link CheckedIngredient}
* when creating {@link CheckedRecipe}.
*
* @author indyuce
*/
public abstract class PlayerIngredient {
/**
* Redirects to the player's item in his inventory. This can be
* used later by MMOItems to reduce the amount of items in his inventory
* to take off ingredients from him.
*/
private final ItemStack item;
public PlayerIngredient(NBTItem item) {
// Throw NBTItem to garbage collector because no longer needed
this.item = item.getItem();
}
public ItemStack getItem() {
return item;
}
public int getAmount() {
return item.getAmount();
}
}

View File

@ -0,0 +1,31 @@
package net.Indyuce.mmoitems.api.crafting.ingredient.inventory;
import io.lumine.mythic.lib.api.item.NBTItem;
import org.bukkit.Material;
import org.bukkit.inventory.meta.ItemMeta;
import javax.annotation.Nullable;
public class VanillaPlayerIngredient extends PlayerIngredient {
private final Material material;
@Nullable
private final String displayName;
public VanillaPlayerIngredient(NBTItem item) {
super(item);
this.material = item.getItem().getType();
ItemMeta meta = item.getItem().getItemMeta();
this.displayName = meta.hasDisplayName() ? meta.getDisplayName() : null;
}
public Material getType() {
return material;
}
@Nullable
public String getDisplayName() {
return displayName;
}
}

View File

@ -1,18 +1,17 @@
package net.Indyuce.mmoitems.api.crafting.recipe;
import net.Indyuce.mmoitems.api.crafting.condition.CheckedCondition;
import net.Indyuce.mmoitems.api.crafting.condition.Condition;
import net.Indyuce.mmoitems.api.crafting.ingredient.CheckedIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory;
import net.Indyuce.mmoitems.api.player.PlayerData;
import org.bukkit.inventory.ItemStack;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.inventory.ItemStack;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.condition.Condition;
import net.Indyuce.mmoitems.api.crafting.condition.Condition.CheckedCondition;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient.CheckedIngredient;
import net.Indyuce.mmoitems.api.player.PlayerData;
public class CheckedRecipe {
private final Recipe recipe;
@ -54,6 +53,10 @@ public class CheckedRecipe {
return recipe;
}
/**
* @return True if both conditions are met and ingredients are gathered
* @deprecated Not used internally anymore
*/
@Deprecated
public boolean isUnlocked() {
return ingredientsHad && conditionsMet;

View File

@ -4,7 +4,7 @@ import io.lumine.mythic.lib.api.util.SmartGive;
import net.Indyuce.mmoitems.api.crafting.ConfigMMOItem;
import net.Indyuce.mmoitems.api.crafting.CraftingStation;
import net.Indyuce.mmoitems.api.crafting.CraftingStatus.CraftingQueue;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory;
import net.Indyuce.mmoitems.api.event.PlayerUseCraftingStationEvent;
import net.Indyuce.mmoitems.api.item.util.ConfigItems;
import net.Indyuce.mmoitems.api.player.PlayerData;
@ -19,7 +19,7 @@ public class CraftingRecipe extends Recipe {
private final ConfigMMOItem output;
/*
* there can't be any crafting time for upgrading recipes since there is no
* There can't be any crafting time for upgrading recipes since there is no
* way to save an MMOItem in the config file TODO save as ItemStack
*/
private final double craftingTime;
@ -96,4 +96,9 @@ public class CraftingRecipe extends Recipe {
public ItemStack display(CheckedRecipe recipe) {
return ConfigItems.CRAFTING_RECIPE_DISPLAY.newBuilder(recipe).build();
}
@Override
public CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv) {
return new CheckedRecipe(this, data, inv);
}
}

View File

@ -3,9 +3,9 @@ package net.Indyuce.mmoitems.api.crafting.recipe;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.crafting.CraftingStation;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.condition.Condition;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.trigger.Trigger;
import net.Indyuce.mmoitems.api.player.PlayerData;
import org.apache.commons.lang.Validate;
@ -51,7 +51,6 @@ public abstract class Recipe {
for (String format : config.getStringList("ingredients"))
try {
Ingredient ingredient = MMOItems.plugin.getCrafting().getIngredient(new MMOLineConfig(format));
Validate.notNull(ingredient, "Could not match ingredient");
ingredients.add(ingredient);
} catch (IllegalArgumentException exception) {
throw new IllegalArgumentException("Could not load ingredient '" + format + "': " + exception.getMessage());
@ -63,7 +62,6 @@ public abstract class Recipe {
for (String format : config.getStringList("conditions"))
try {
Condition condition = MMOItems.plugin.getCrafting().getCondition(new MMOLineConfig(format));
Validate.notNull(condition, "Could not match condition");
conditions.add(condition);
} catch (IllegalArgumentException exception) {
throw new IllegalArgumentException("Could not load condition '" + format + "': " + exception.getMessage());
@ -131,20 +129,22 @@ public abstract class Recipe {
options.put(option, value);
}
public CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv) {
return new CheckedRecipe(this, data, inv);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Recipe && ((Recipe) obj).id.equals(id);
}
/**
* THE method that checks if a player can use a recipe or not. This checks for
* conditions and ingredients and saves all the info needed to display everything
* in the item lore.
*
* @param data Player trying to use the recipe
* @param inv The ingredients of the player
* @return Class that knows if the player can use the recipe
*/
public abstract CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv);
/**
* 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.
*
*
* @param data Player crafting the item
* @param inv The player's ingredients
* @param recipe The recipe used to craft the item
@ -156,18 +156,23 @@ public abstract class Recipe {
* Applies extra conditions when a player has just clicked on a recipe item
* in the GUI. This method is called after checking for the recipe
* conditions and ingredients.
*
* @param data The 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 player can use the recipe
*
* @param data The 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 player can use the recipe
*/
public abstract boolean canUse(PlayerData data, IngredientInventory inv, CheckedRecipe recipe, CraftingStation station);
public abstract ItemStack display(CheckedRecipe recipe);
public enum RecipeOption {
@Override
public boolean equals(Object obj) {
return obj instanceof Recipe && ((Recipe) obj).id.equals(id);
}
public static enum RecipeOption {
/**
* Hide the crafting recipe when one of the condition is not met

View File

@ -1,27 +1,25 @@
package net.Indyuce.mmoitems.api.crafting.recipe;
import java.util.Random;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.crafting.ConfigMMOItem;
import net.Indyuce.mmoitems.api.crafting.CraftingStation;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory.IngredientLookupMode;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory.PlayerIngredient;
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.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 io.lumine.mythic.lib.MythicLib;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import java.util.Random;
public class UpgradingRecipe extends Recipe {
private final ConfigMMOItem item;
@ -32,9 +30,7 @@ public class UpgradingRecipe extends Recipe {
public UpgradingRecipe(ConfigurationSection config) {
super(config);
/*
* load item being upgraded.
*/
// load item being upgraded.
item = new ConfigMMOItem(config.getConfigurationSection("item"));
ingredient = new MMOItemIngredient(item);
}
@ -44,12 +40,12 @@ public class UpgradingRecipe extends Recipe {
}
@Override
public void whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe uncastRecipe, CraftingStation station) {
UpgradingRecipeInfo recipe = (UpgradingRecipeInfo) uncastRecipe;
public void whenUsed(PlayerData data, IngredientInventory inv, CheckedRecipe castRecipe, CraftingStation station) {
CheckedUpgradingRecipe recipe = (CheckedUpgradingRecipe) castRecipe;
recipe.getUpgradeData().upgrade(recipe.getMMOItem());
recipe.getUpgraded().setItemMeta(recipe.getMMOItem().newBuilder().build().getItemMeta());
uncastRecipe.getRecipe().getTriggers().forEach(trigger -> trigger.whenCrafting(data));
castRecipe.getRecipe().getTriggers().forEach(trigger -> trigger.whenCrafting(data));
if (!data.isOnline())
return;
@ -60,14 +56,9 @@ public class UpgradingRecipe extends Recipe {
@Override
public boolean canUse(PlayerData data, IngredientInventory inv, CheckedRecipe uncastRecipe, CraftingStation station) {
/*
* try to find the item which is meant to be updated. null check is
* ugly, BUT it does halve calculations done because it does not calls
* two map lookups. it is not needed to check for the amount because
* only one item is upgraded.
*/
PlayerIngredient upgraded = inv.getIngredient(ingredient, IngredientLookupMode.IGNORE_ITEM_LEVEL);
if (upgraded == null) {
// Find the item which should be upgraded
CheckedIngredient upgraded = inv.findMatching(ingredient);
if (!upgraded.isHad()) {
if (!data.isOnline())
return false;
@ -76,11 +67,15 @@ public class UpgradingRecipe extends Recipe {
return false;
}
UpgradingRecipeInfo recipe = (UpgradingRecipeInfo) uncastRecipe;
if (!(recipe.mmoitem = new LiveMMOItem(MythicLib.plugin.getVersion().getWrapper().getNBTItem(upgraded.getFirstItem())))
.hasData(ItemStats.UPGRADE))
// Finds the item that will be upgraded
NBTItem firstItem = NBTItem.get(upgraded.getFound().stream().findFirst().get().getItem());
// Checks if upgraded item DOES has an upgrade template
CheckedUpgradingRecipe recipe = (CheckedUpgradingRecipe) uncastRecipe;
if (!(recipe.mmoitem = new LiveMMOItem(firstItem)).hasData(ItemStats.UPGRADE))
return false;
// Checks for max upgrade level
if (!(recipe.upgradeData = (UpgradeData) recipe.getMMOItem().getData(ItemStats.UPGRADE)).canLevelUp()) {
if (!data.isOnline())
return false;
@ -90,11 +85,16 @@ public class UpgradingRecipe extends Recipe {
return false;
}
// Checks for failure
if (random.nextDouble() > recipe.getUpgradeData().getSuccess()) {
// Should the item be destroyed when failing to upgrade
if (recipe.getUpgradeData().destroysOnFail())
recipe.getUpgraded().setAmount(recipe.getUpgraded().getAmount() - 1);
recipe.getIngredients().forEach(ingredient -> ingredient.getPlayerIngredient().reduceItem(ingredient.getIngredient().getAmount()));
// Take away ingredients
recipe.getIngredients().forEach(ingredient -> ingredient.takeAway());
if (!data.isOnline())
return false;
@ -113,14 +113,18 @@ public class UpgradingRecipe extends Recipe {
@Override
public CheckedRecipe evaluateRecipe(PlayerData data, IngredientInventory inv) {
return new UpgradingRecipeInfo(this, data, inv);
return new CheckedUpgradingRecipe(this, data, inv);
}
public static class UpgradingRecipeInfo extends CheckedRecipe {
/**
* Used to cache the LiveMMOItem instance and UpgradeData
* which take a little performance to calculate.
*/
public class CheckedUpgradingRecipe extends CheckedRecipe {
private LiveMMOItem mmoitem;
private UpgradeData upgradeData;
public UpgradingRecipeInfo(Recipe recipe, PlayerData data, IngredientInventory inv) {
public CheckedUpgradingRecipe(Recipe recipe, PlayerData data, IngredientInventory inv) {
super(recipe, data, inv);
}

View File

@ -3,25 +3,19 @@ package net.Indyuce.mmoitems.api.item.template;
import io.lumine.mythic.lib.api.util.PostLoadObject;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.ItemTier;
import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.item.ItemReference;
import net.Indyuce.mmoitems.api.item.build.MMOItemBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.*;
@ -149,7 +143,6 @@ public class MMOItemTemplate extends PostLoadObject implements ItemReference {
return options.contains(option);
}
public MMOItemBuilder newBuilder(@Nullable Player player) {
if (player != null) { return newBuilder(PlayerData.get(player).getRPG()); }
return newBuilder((RPGPlayer) null);

View File

@ -7,7 +7,7 @@ import io.lumine.mythic.lib.api.util.LegacyComponent;
import io.lumine.mythic.utils.adventure.text.Component;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.crafting.ConditionalDisplay;
import net.Indyuce.mmoitems.api.crafting.condition.Condition.CheckedCondition;
import net.Indyuce.mmoitems.api.crafting.condition.CheckedCondition;
import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.CraftingRecipe;
import net.Indyuce.mmoitems.api.item.util.ConfigItem;

View File

@ -1,13 +1,12 @@
package net.Indyuce.mmoitems.api.item.util.crafting;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.util.LegacyComponent;
import io.lumine.mythic.utils.adventure.text.Component;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.crafting.ConditionalDisplay;
import net.Indyuce.mmoitems.api.crafting.condition.Condition.CheckedCondition;
import net.Indyuce.mmoitems.api.crafting.condition.CheckedCondition;
import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.UpgradingRecipe;
import net.Indyuce.mmoitems.api.item.util.ConfigItem;
@ -16,11 +15,7 @@ import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.*;
public class UpgradingRecipeDisplay extends ConfigItem {
public UpgradingRecipeDisplay() {

View File

@ -1,19 +1,25 @@
package net.Indyuce.mmoitems.comp.mythicmobs.crafting;
import java.util.Optional;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.xikage.mythicmobs.MythicMobs;
import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter;
import io.lumine.xikage.mythicmobs.items.MythicItem;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import io.lumine.mythic.lib.api.MMOLineConfig;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class MythicItemIngredient extends Ingredient {
import java.util.Optional;
/**
* No way of checking if an item was generated using MythicMobs? There does
* not seem to be any NBTTag for that using the latest dev build
*
* @deprecated Not implemented yet
*/
@Deprecated
public class MythicItemIngredient extends Ingredient<MythicItemPlayerIngredient> {
private final MythicItem mythicitem;
private final String display;
@ -39,8 +45,13 @@ public class MythicItemIngredient extends Ingredient {
return s.replace("#item#", display).replace("#amount#", "" + getAmount());
}
@Override
public boolean matches(MythicItemPlayerIngredient playerIngredient) {
return false;
}
@NotNull
@Override
@Override
public ItemStack generateItemStack(@NotNull RPGPlayer player) {
return BukkitAdapter.adapt(mythicitem.generateItemStack(getAmount()));
}

View File

@ -0,0 +1,29 @@
package net.Indyuce.mmoitems.comp.mythicmobs.crafting;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.PlayerIngredient;
/**
* @deprecated Not implemented yet
*/
@Deprecated
public class MythicItemPlayerIngredient extends PlayerIngredient {
private final String type, id;
public MythicItemPlayerIngredient(NBTItem item) {
super(item);
type = item.getString("MYTHIC_TYPE").toLowerCase();
// No idea what to use here
id = null;
}
public String getType() {
return type;
}
public String getId() {
return id;
}
}

View File

@ -6,7 +6,7 @@ import io.lumine.mythic.lib.api.util.LegacyComponent;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import io.lumine.mythic.utils.adventure.text.Component;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient.CheckedIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.CheckedIngredient;
import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.CraftingRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.UpgradingRecipe;

View File

@ -8,9 +8,9 @@ import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.crafting.CraftingStation;
import net.Indyuce.mmoitems.api.crafting.CraftingStatus.CraftingQueue;
import net.Indyuce.mmoitems.api.crafting.CraftingStatus.CraftingQueue.CraftingInfo;
import net.Indyuce.mmoitems.api.crafting.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.Layout;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.IngredientInventory;
import net.Indyuce.mmoitems.api.crafting.recipe.CheckedRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.CraftingRecipe;
import net.Indyuce.mmoitems.api.crafting.recipe.Recipe;
@ -34,6 +34,7 @@ import java.util.UUID;
public class CraftingStationView extends PluginInventory {
private final CraftingStation station;
private final Layout layout;
private List<CheckedRecipe> recipes;
private IngredientInventory ingredients;
@ -128,6 +129,7 @@ public class CraftingStationView extends PluginInventory {
public void whenClicked(InventoryClickEvent event) {
if (!playerData.isOnline())
return;
event.setCancelled(true);
if (!MMOUtils.isMetaItem(event.getCurrentItem(), false))
return;
@ -159,18 +161,17 @@ public class CraftingStationView extends PluginInventory {
NBTItem item = MythicLib.plugin.getVersion().getWrapper().getNBTItem(event.getCurrentItem());
String tag = item.getString("recipeId");
if (!tag.equals("")) {
if (!tag.isEmpty()) {
CheckedRecipe recipe = getRecipe(tag);
if (event.isRightClick()) {
if (event.isRightClick())
new CraftingStationPreview(this, recipe).open();
return;
else {
processRecipe(recipe);
open();
}
processRecipe(recipe);
open();
}
if (!(tag = item.getString("queueId")).equals("")) {
if (!(tag = item.getString("queueId")).isEmpty()) {
UUID uuid = UUID.fromString(tag);
CraftingInfo recipeInfo = playerData.getCrafting().getQueue(station).getCraft(uuid);
CraftingRecipe recipe = recipeInfo.getRecipe();
@ -247,7 +248,7 @@ public class CraftingStationView extends PluginInventory {
return;
recipe.getRecipe().whenUsed(playerData, ingredients, recipe, station);
recipe.getIngredients().forEach(ingredient -> ingredient.getPlayerIngredient().reduceItem(ingredient.getIngredient().getAmount()));
recipe.getIngredients().forEach(ingredient -> ingredient.takeAway());
recipe.getConditions().forEach(condition -> condition.getCondition().whenCrafting(playerData));
updateData();

View File

@ -1,6 +1,5 @@
package net.Indyuce.mmoitems.manager;
import com.google.gson.JsonParser;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.util.AltChar;
@ -8,16 +7,22 @@ import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.ConfigFile;
import net.Indyuce.mmoitems.api.crafting.ConditionalDisplay;
import net.Indyuce.mmoitems.api.crafting.CraftingStation;
import net.Indyuce.mmoitems.api.crafting.LoadedCraftingObject;
import net.Indyuce.mmoitems.api.crafting.condition.*;
import net.Indyuce.mmoitems.api.crafting.ingredient.Ingredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.IngredientType;
import net.Indyuce.mmoitems.api.crafting.ingredient.MMOItemIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.VanillaIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.MMOItemPlayerIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.PlayerIngredient;
import net.Indyuce.mmoitems.api.crafting.ingredient.inventory.VanillaPlayerIngredient;
import net.Indyuce.mmoitems.api.crafting.trigger.*;
import net.Indyuce.mmoitems.comp.mythicmobs.crafting.MythicItemIngredient;
import net.Indyuce.mmoitems.comp.mythicmobs.crafting.MythicMobsSkillTrigger;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.io.File;
import java.util.*;
import java.util.function.Function;
@ -26,18 +31,19 @@ import java.util.logging.Level;
public class CraftingManager implements Reloadable {
/*
* order matters when trying to recognize an ingredient type: if none
/**
* Order matters when trying to recognize an ingredient type: if none
* ingredient matches, the item is considered as a vanilla item.
*/
private final List<IngredientType> ingredients = new ArrayList<>();
private final Set<LoadedObject<Condition>> conditions = new HashSet<>();
private final Set<LoadedObject<Trigger>> triggers = new HashSet<>();
private final Set<LoadedCraftingObject<Condition>> conditions = new HashSet<>();
private final Set<LoadedCraftingObject<Trigger>> triggers = new HashSet<>();
private final Map<String, CraftingStation> stations = new HashMap<>();
public CraftingManager() {
// conditions
// Conditions
registerCondition("level", LevelCondition::new, new ConditionalDisplay("&a" + AltChar.check + " Requires Level #level#", "&c" + AltChar.cross + " Requires Level #level#"));
registerCondition("permission", PermissionCondition::new, null);
registerCondition("placeholder", PlaceholderCondition::new, null);
@ -46,26 +52,22 @@ public class CraftingManager implements Reloadable {
registerCondition("food", FoodCondition::new, new ConditionalDisplay("&a" + AltChar.check + " Requires #food# Food", "&c" + AltChar.cross + " Requires #food# Food"));
registerCondition("class", ClassCondition::new, new ConditionalDisplay("&a" + AltChar.check + " Required Class: #class#", "&c" + AltChar.cross + " Required Class: #class#"));
// triggers
// Triggers
registerTrigger("command", CommandTrigger::new);
registerTrigger("message", MessageTrigger::new);
registerTrigger("sound", SoundTrigger::new);
registerTrigger("vanilla", VanillaTrigger::new);
registerTrigger("mmoitem", MMOItemTrigger::new);
// ingredients
registerIngredient("vanilla", VanillaIngredient::new, new ConditionalDisplay("&8" + AltChar.check + " &7#amount# #item#", "&c" + AltChar.cross + " &7#amount# #item#"), nbt -> true, item -> item.getItem().getType().name().toLowerCase() + "_" + (item.getItem().hasItemMeta() ? item.getItem().getItemMeta().getDisplayName() : null));
registerIngredient("mmoitem", MMOItemIngredient::new, new ConditionalDisplay("&8" + AltChar.check + " &7#amount# #level##item#", "&c" + AltChar.cross + " &7#amount# #level##item#"), NBTItem::hasType, item -> {
String upgradeString = item.getString("MMOITEMS_UPGRADE");
int level = !upgradeString.isEmpty() ? new JsonParser().parse(upgradeString).getAsJsonObject().get("Level").getAsInt() : 0;
return item.getString("MMOITEMS_ITEM_TYPE").toLowerCase() + (level != 0 ? "-" + level : "") + "_" + item.getString("MMOITEMS_ITEM_ID").toLowerCase();
});
// Ingredients
registerIngredient("vanilla", VanillaIngredient::new, new ConditionalDisplay("&8" + AltChar.check + " &7#amount# #item#", "&c" + AltChar.cross + " &7#amount# #item#"), nbt -> true, VanillaPlayerIngredient::new);
registerIngredient("mmoitem", MMOItemIngredient::new, new ConditionalDisplay("&8" + AltChar.check + " &7#amount# #level##item#", "&c" + AltChar.cross + " &7#amount# #level##item#"), NBTItem::hasType, MMOItemPlayerIngredient::new);
// mm comp
// MythicMobs native compatibility
if (Bukkit.getPluginManager().getPlugin("MythicMobs") != null) {
registerIngredient("mythicitem", MythicItemIngredient::new,
/*registerIngredient("mythicitem", MythicItemIngredient::new,
new ConditionalDisplay("&8" + AltChar.check + " &7#amount# #item#", "&c" + AltChar.cross + " &7#amount# #item#"),
nbt -> nbt.hasTag("MYTHIC_TYPE"), nbt -> nbt.getString("MYTHIC_TYPE").toLowerCase());
nbt -> nbt.hasTag("MYTHIC_TYPE"), MythicItemPlayerIngredient::new);*/
registerTrigger("mmskill", MythicMobsSkillTrigger::new);
}
}
@ -75,7 +77,7 @@ public class CraftingManager implements Reloadable {
ConfigFile language = new ConfigFile("/language", "crafting-stations");
for (LoadedObject<Condition> condition : getConditions())
for (LoadedCraftingObject<Condition> condition : getConditions())
if (condition.hasDisplay()) {
String path = "condition." + condition.getId();
if (!language.getConfig().contains(path)) {
@ -134,6 +136,11 @@ public class CraftingManager implements Reloadable {
return stations.get(id);
}
/**
* Finds the corresponding ingredient type, and from there
* load the corresponding ingredient from the line config
*/
@NotNull
public Ingredient getIngredient(MMOLineConfig config) {
String key = config.getKey();
@ -141,113 +148,91 @@ public class CraftingManager implements Reloadable {
if (ingredient.getId().equals(key))
return ingredient.load(config);
return null;
throw new IllegalArgumentException("Could not match ingredient");
}
/**
* Finds the corresponding condition type, and from there
* load the corresponding condition from the line config
*/
@NotNull
public Condition getCondition(MMOLineConfig config) {
String key = config.getKey();
for (LoadedObject<Condition> condition : conditions)
for (LoadedCraftingObject<Condition> condition : conditions)
if (condition.getId().equalsIgnoreCase(key))
return condition.load(config);
return null;
throw new IllegalArgumentException("Could not match condition");
}
/**
* Finds the corresponding trigger type, and from there
* load the corresponding trigger from the line config
*/
@NotNull
public Trigger getTrigger(MMOLineConfig config) {
String key = config.getKey();
for (LoadedObject<Trigger> trigger : triggers)
for (LoadedCraftingObject<Trigger> trigger : triggers)
if (trigger.getId().equalsIgnoreCase(key))
return trigger.load(config);
return null;
throw new IllegalArgumentException("Could not match trigger");
}
public List<IngredientType> getIngredients() {
return ingredients;
}
public Set<LoadedObject<Condition>> getConditions() {
public Set<LoadedCraftingObject<Condition>> getConditions() {
return conditions;
}
public Set<LoadedObject<Trigger>> getTriggers() {
public Set<LoadedCraftingObject<Trigger>> getTriggers() {
return triggers;
}
public void registerIngredient(String id, Function<MMOLineConfig, Ingredient> function, ConditionalDisplay display, Predicate<NBTItem> check, Function<NBTItem, String> read) {
ingredients.add(0, new IngredientType(id, function, display, check, read));
/**
* Registers a type of ingredient in MMOItems crafting stations
* <p>
* See {@link IngredientType} for more information.
*
* @param id The ingredient id
* @param function Function that loads an ingredient from a line config
* @param display How it displays in the item lore
* @param check Should return true if an item can be handled by this ingredient type
* @param readIngredient After checking if this ingredient type can handle an item,
* method called to provide the corresponding PlayerIngredient
*/
public void registerIngredient(String id, Function<MMOLineConfig, Ingredient> function, ConditionalDisplay display, Predicate<NBTItem> check, Function<NBTItem, PlayerIngredient> readIngredient) {
ingredients.add(0, new IngredientType(id, function, display, check, readIngredient));
}
public void registerCondition(String id, Function<MMOLineConfig, Condition> function, ConditionalDisplay display) {
conditions.add(new LoadedObject<>(id, function, display));
/**
* Registers a type of condition in MMOItems crafting stations
*
* @param id Condition ID
* @param function Function that loads a condition from a line conf
* @param display How it displays in the item lore, null if it should not
*/
public void registerCondition(String id, Function<MMOLineConfig, Condition> function, @Nullable ConditionalDisplay display) {
conditions.add(new LoadedCraftingObject<>(id, function, display));
}
/**
* Registers a type of trigger in MMOItems crafting stations. Unlike
* conditions or ingredients, triggers don't need to be displayed
* in the item lore therefore no 'display' argument is required.
*
* @param id Trigger ID
* @param function Function that loads that type of trigger from a line configuration
*/
public void registerTrigger(String id, Function<MMOLineConfig, Trigger> function) {
triggers.add(new LoadedObject<>(id, function, null));
triggers.add(new LoadedCraftingObject<Trigger>(id, function, null));
}
public Collection<CraftingStation> getAll() {
return stations.values();
}
public static class LoadedObject<C> {
private final String id;
private final Function<MMOLineConfig, C> function;
private ConditionalDisplay display;
public LoadedObject(String id, Function<MMOLineConfig, C> function, ConditionalDisplay display) {
this.id = id;
this.function = function;
this.display = display;
}
public String getId() {
return id;
}
public void setDisplay(ConditionalDisplay display) {
this.display = display;
}
public boolean hasDisplay() {
return display != null;
}
public ConditionalDisplay getDisplay() {
return display;
}
public C load(MMOLineConfig config) {
return function.apply(config);
}
}
public static class IngredientType extends LoadedObject<Ingredient> {
private final Predicate<NBTItem> check;
private final Function<NBTItem, String> read;
public IngredientType(String id, Function<MMOLineConfig, Ingredient> function, ConditionalDisplay display, Predicate<NBTItem> check, Function<NBTItem, String> read) {
super(id, function, display);
this.check = check;
this.read = read;
}
/*
* returns true if the checked item can be handled by this ingredient
*/
public boolean check(NBTItem item) {
return check.test(item);
}
/*
* reads the ingredient read from an NBTItem
*/
public String readKey(NBTItem item) {
return read.apply(item);
}
}
}