mirror of
https://gitlab.com/phoenix-dvpmt/mmoitems.git
synced 2024-12-23 04:47:34 +01:00
Crafting stat has doubled its power:
+ Edit any number of recipes for the item (not just one) from the GUI + Specify the output amount of each recipe + Choose what ingredients turn into per-recipe (like milk buckets turning empty when crafting cakes) + Much better API ~ Option to hide the recipe from the recipe book, and to get a book that unlocks it, but recipes unlocked this way are lost when relogging... **Requires latest MythicLib**
This commit is contained in:
parent
33a191ffaf
commit
646b440281
@ -215,7 +215,7 @@ public class ItemStats {
|
||||
// Crafting Stats
|
||||
CRAFTING = new Crafting(),
|
||||
CRAFT_PERMISSION = new StringStat("CRAFT_PERMISSION", VersionMaterial.OAK_SIGN.toMaterial(), "Crafting Recipe Permission", new String[]{"The permission needed to craft this item.", "Changing this value requires &o/mi reload recipes&7."}, new String[]{"all"}),
|
||||
CRAFT_AMOUNT = new DoubleStat("CRAFTED_AMOUNT", Material.WOODEN_AXE, "Crafted Amount", new String[]{"The stack count for", "this item when crafted."}, new String[]{"all"}),
|
||||
//CRAFT_AMOUNT = new DoubleStat("CRAFTED_AMOUNT", Material.WOODEN_AXE, "Crafted Amount", new String[]{"The stack count for", "this item when crafted."}, new String[]{"all"}),
|
||||
|
||||
// Unique Stats
|
||||
AUTOSMELT = new BooleanStat("AUTOSMELT", Material.COAL, "Autosmelt", new String[]{"If set to true, your tool will", "automaticaly smelt mined ores."}, new String[]{"tool"}),
|
||||
|
@ -38,6 +38,7 @@ import net.Indyuce.mmoitems.comp.rpg.DefaultHook;
|
||||
import net.Indyuce.mmoitems.comp.rpg.McMMOHook;
|
||||
import net.Indyuce.mmoitems.comp.rpg.RPGHandler;
|
||||
import net.Indyuce.mmoitems.gui.PluginInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeBrowserGUI;
|
||||
import net.Indyuce.mmoitems.gui.listener.GuiListener;
|
||||
import net.Indyuce.mmoitems.listener.*;
|
||||
import net.Indyuce.mmoitems.manager.*;
|
||||
@ -140,6 +141,7 @@ public class MMOItems extends LuminePlugin {
|
||||
new SpigotPlugin(39267, this).checkForUpdate();
|
||||
new MMOItemsMetrics();
|
||||
|
||||
RecipeBrowserGUI.registerNativeRecipes();
|
||||
abilityManager.initialize();
|
||||
configManager = new ConfigManager();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.Indyuce.mmoitems.api;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.droptable.DropTable;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.util.NumericStatFormula;
|
||||
@ -8,142 +9,162 @@ import net.Indyuce.mmoitems.comp.itemglow.TierColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class ItemTier {
|
||||
private final String name, id;
|
||||
@NotNull private final String name, id;
|
||||
|
||||
// unidentification
|
||||
private final UnidentificationInfo unidentificationInfo;
|
||||
// Unidentification
|
||||
@NotNull private final UnidentificationInfo unidentificationInfo;
|
||||
|
||||
// deconstruction
|
||||
private final DropTable deconstruct;
|
||||
@Nullable private final DropTable deconstruct;
|
||||
|
||||
// item glow options
|
||||
private final TierColor color;
|
||||
private final boolean hint;
|
||||
@Nullable private TierColor color = null;
|
||||
private boolean hint = false;
|
||||
|
||||
// item generation
|
||||
private final double chance;
|
||||
private final NumericStatFormula capacity;
|
||||
@Nullable private final NumericStatFormula capacity;
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
@NotNull private static final Random RANDOM = new Random();
|
||||
private static final boolean GLOW = Bukkit.getPluginManager().getPlugin("GlowAPI") != null;
|
||||
|
||||
public ItemTier(ConfigurationSection config) {
|
||||
/**
|
||||
* Load an ItemTier from the YML Configuration Itself
|
||||
*
|
||||
* @param config Configuration section to get all values from
|
||||
*/
|
||||
public ItemTier(@NotNull ConfigurationSection config) {
|
||||
|
||||
// The name and ID, crucial parts.
|
||||
id = config.getName().toUpperCase().replace("-", "_");
|
||||
name = MythicLib.plugin.parseColors(config.getString("name"));
|
||||
deconstruct = config.contains("deconstruct-item") ? new DropTable(config.getConfigurationSection("deconstruct-item")) : null;
|
||||
unidentificationInfo = new UnidentificationInfo(config.getConfigurationSection("unidentification"));
|
||||
|
||||
// Deconstruct and Unidentification
|
||||
deconstruct = config.contains("deconstruct-item") ? new DropTable(config.getConfigurationSection("deconstruct-item")) : null;
|
||||
|
||||
ConfigurationSection unidentificationSection = config.getConfigurationSection("unidentification");
|
||||
if (unidentificationSection == null) { unidentificationInfo = getDefaultUnident(); }
|
||||
else { unidentificationInfo = new UnidentificationInfo(unidentificationSection); }
|
||||
|
||||
//noinspection ErrorNotRethrown
|
||||
try {
|
||||
hint = config.contains("item-glow") && config.getBoolean("item-glow.hint");
|
||||
color = config.contains("item-glow") ? new TierColor(config.getString("item-glow.color"), GLOW) : null;
|
||||
|
||||
// Is it defined?
|
||||
ConfigurationSection glowSection = config.getConfigurationSection("item-glow");
|
||||
|
||||
// Alr then lets read it
|
||||
if (glowSection != null) {
|
||||
|
||||
// Does it hint?
|
||||
hint = glowSection.getBoolean("hint");
|
||||
|
||||
// Does it color?
|
||||
color = new TierColor(config.getString("color", "WHITE"), GLOW);
|
||||
}
|
||||
|
||||
} catch (NoClassDefFoundError | IllegalAccessException | NoSuchFieldException | SecurityException exception) {
|
||||
throw new IllegalArgumentException("Could not load tier color: " + exception.getMessage());
|
||||
|
||||
// No hints
|
||||
hint = false;
|
||||
color = null;
|
||||
|
||||
// Grrr but GlowAPI crashing shall not crash MMOItems tiers wtf
|
||||
MMOItems.print(null, "Could not load glow color for tier $r{0}$b;$f {1}", "Tier Hints", id, exception.getMessage());
|
||||
}
|
||||
|
||||
// What are the chances?
|
||||
chance = config.getDouble("generation.chance");
|
||||
capacity = config.contains("generation.capacity") ? new NumericStatFormula(config.getConfigurationSection("generation.capacity")) : null;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@NotNull public String getId() { return id; }
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@NotNull public String getName() { return name; }
|
||||
|
||||
public boolean hasDropTable() {
|
||||
return deconstruct != null;
|
||||
}
|
||||
public boolean hasDropTable() { return deconstruct != null; }
|
||||
|
||||
public DropTable getDropTable() {
|
||||
return deconstruct;
|
||||
}
|
||||
@Nullable public DropTable getDropTable() { return deconstruct; }
|
||||
|
||||
public boolean hasColor() {
|
||||
return color != null;
|
||||
}
|
||||
public boolean hasColor() { return color != null; }
|
||||
|
||||
public TierColor getColor() {
|
||||
return color;
|
||||
}
|
||||
@Nullable public TierColor getColor() { return color; }
|
||||
|
||||
public boolean isHintEnabled() {
|
||||
return hint;
|
||||
}
|
||||
public boolean isHintEnabled() { return hint; }
|
||||
|
||||
/**
|
||||
* @return The chance of the tier being chosen when generating a random item
|
||||
*/
|
||||
public double getGenerationChance() {
|
||||
return chance;
|
||||
}
|
||||
public double getGenerationChance() { return chance; }
|
||||
|
||||
/**
|
||||
* @return If the item tier has a modifier capacity ie if this tier let
|
||||
* generated items have modifiers
|
||||
*/
|
||||
public boolean hasCapacity() {
|
||||
return capacity != null;
|
||||
}
|
||||
public boolean hasCapacity() { return capacity != null; }
|
||||
|
||||
/**
|
||||
* @return The formula for modifier capacity which can be then rolled to
|
||||
* generate a random amount of modifier capacity when generating a
|
||||
* random item
|
||||
*/
|
||||
public NumericStatFormula getModifierCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
@Nullable public NumericStatFormula getModifierCapacity() { return capacity; }
|
||||
|
||||
public UnidentificationInfo getUnidentificationInfo() {
|
||||
return unidentificationInfo;
|
||||
}
|
||||
@NotNull public UnidentificationInfo getUnidentificationInfo() { return unidentificationInfo; }
|
||||
|
||||
/**
|
||||
* @return Reads the deconstruction drop table. This may return a list
|
||||
* containing multiple items and they should all be added to the
|
||||
* player's inventory
|
||||
*/
|
||||
public List<ItemStack> getDeconstructedLoot(PlayerData player) {
|
||||
public List<ItemStack> getDeconstructedLoot(@NotNull PlayerData player) {
|
||||
//noinspection ConstantConditions
|
||||
return hasDropTable() ? deconstruct.read(player, false) : new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Default unidentification info, if it is missing in the config.
|
||||
*/
|
||||
@NotNull private UnidentificationInfo getDefaultUnident() { return new UnidentificationInfo(UnidentificationInfo.UNIDENT_NAME, UnidentificationInfo.UNIDENT_PREFIX, 0); }
|
||||
|
||||
public class UnidentificationInfo {
|
||||
private final String name, prefix;
|
||||
@NotNull private final String unidentificationName, prefix;
|
||||
private final int range;
|
||||
|
||||
public UnidentificationInfo(ConfigurationSection config) {
|
||||
this(color(config.getString("name")), color(config.getString("prefix")), config.getInt("range"));
|
||||
public static final String UNIDENT_NAME = "Unidentified Item";
|
||||
public static final String UNIDENT_PREFIX = "Unknown";
|
||||
|
||||
public UnidentificationInfo(@NotNull ConfigurationSection config) {
|
||||
this(color(config.getString("name", UNIDENT_NAME)), color(config.getString("prefix", UNIDENT_PREFIX)), config.getInt("range"));
|
||||
}
|
||||
|
||||
public UnidentificationInfo(String name, String prefix, int range) {
|
||||
this.name = name;
|
||||
public UnidentificationInfo(@NotNull String name, @NotNull String prefix, int range) {
|
||||
unidentificationName = name;
|
||||
this.prefix = prefix;
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
@NotNull public String getPrefix() { return prefix; }
|
||||
|
||||
public String getDisplayName() {
|
||||
return name;
|
||||
@NotNull public String getDisplayName() {
|
||||
return unidentificationName;
|
||||
}
|
||||
|
||||
public int[] calculateRange(int level) {
|
||||
int min = (int) Math.max(1, (level - (double) range * RANDOM.nextDouble()));
|
||||
return new int[] { min, min + range };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String color(String str) {
|
||||
private String color(@Nullable String str) {
|
||||
return MythicLib.plugin.parseColors(str);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ReforgeOptions {
|
||||
public static boolean dropRestoredGems;
|
||||
|
||||
@ -28,6 +30,42 @@ public class ReforgeOptions {
|
||||
public void setKeepCase(@NotNull String kc) { keepCase = kc; }
|
||||
@NotNull public String getKeepCase() { return keepCase; }
|
||||
|
||||
@NotNull ArrayList<String> blacklistedItems = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Apparently, people like to use MMOItems for quests. This
|
||||
* will make items NEVER update with RevID
|
||||
*
|
||||
* @param mmoitemID Item ID. Listen, including MMOItem Type as
|
||||
* well is unnecessary hassle, complicates the
|
||||
* implementation.
|
||||
*
|
||||
* People who name all their items "1", "2", ...
|
||||
* can learn to not use magic numbers ffs.
|
||||
*
|
||||
* @return If this item should not update with RevID (when passing
|
||||
* these options, of course).
|
||||
*/
|
||||
public boolean isBlacklisted(@NotNull String mmoitemID) { return blacklistedItems.contains(mmoitemID); }
|
||||
|
||||
/**
|
||||
* Apparently, people like to use MMOItems for quests. This
|
||||
* will make items NEVER update with RevID
|
||||
*
|
||||
* @param mmoitemID Item ID. Listen, including MMOItem Type as
|
||||
* well is unnecessary hassle, complicates the
|
||||
* implementation.
|
||||
*
|
||||
* People who name all their items "1", "2", ...
|
||||
* can learn to not use magic numbers ffs.
|
||||
*/
|
||||
public void addToBlacklist(@NotNull String mmoitemID) { blacklistedItems.add(mmoitemID); }
|
||||
|
||||
/**
|
||||
* No MMOItem-ID restrictions on RevID.
|
||||
*/
|
||||
public void clearBlacklist() { blacklistedItems.clear(); }
|
||||
|
||||
public ReforgeOptions(ConfigurationSection config) {
|
||||
this.keepName = config.getBoolean("display-name");
|
||||
this.keepLore = config.getBoolean("lore");
|
||||
|
@ -1,25 +1,27 @@
|
||||
package net.Indyuce.mmoitems.api.crafting.recipe;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.ingredients.MythicBlueprintInventory;
|
||||
import io.lumine.mythic.lib.api.crafting.ingredients.MythicRecipeInventory;
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.crafting.ingredients.*;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MRORecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MythicRecipeOutput;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicCachedResult;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicCraftingManager;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.vmp.VanillaInventoryMapping;
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
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.api.interaction.GemStone;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.stat.Enchants;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.stat.data.EnchantListData;
|
||||
import net.Indyuce.mmoitems.stat.data.GemSocketsData;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
@ -47,12 +49,165 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
* @param enchantmentTreatment Should enchantments be destroyed?
|
||||
* @param upgradeTreatment How will upgrades combine?
|
||||
*/
|
||||
public CustomSmithingRecipe(@NotNull MMOItemTemplate outputItem, boolean dropGemstones, @NotNull SmithingCombinationType enchantmentTreatment, @NotNull SmithingCombinationType upgradeTreatment) {
|
||||
public CustomSmithingRecipe(@NotNull MMOItemTemplate outputItem, boolean dropGemstones, @NotNull SmithingCombinationType enchantmentTreatment, @NotNull SmithingCombinationType upgradeTreatment, int outputAmount) {
|
||||
this.outputItem = outputItem;
|
||||
this.dropGemstones = dropGemstones;
|
||||
this.enchantmentTreatment = enchantmentTreatment;
|
||||
this.upgradeTreatment = upgradeTreatment; }
|
||||
this.upgradeTreatment = upgradeTreatment;
|
||||
this.outputAmount = outputAmount;
|
||||
}
|
||||
|
||||
//region Advanced Variant
|
||||
|
||||
/**
|
||||
* If this is not null, then the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
@Nullable
|
||||
MythicRecipe mainInputConsumption;
|
||||
/**
|
||||
* If this is not null, then the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
@Nullable public MythicRecipe getMainInputConsumption() { return mainInputConsumption; }
|
||||
/**
|
||||
* @param mic If this is not null, then the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
public void setMainInputConsumption(@Nullable MythicRecipe mic) { mainInputConsumption = nullifyIfEmpty(mic); }
|
||||
|
||||
/**
|
||||
* @return If the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
public boolean hasInputConsumption() { return ingotInputConsumption != null || mainInputConsumption != null; }
|
||||
|
||||
/**
|
||||
* @param mic Some mythic recipe
|
||||
*
|
||||
* @return <code>null</code> if there is not a single actual item in this MythicRecipe,
|
||||
* or the MythicRecipe itself.
|
||||
*/
|
||||
@Nullable public MythicRecipe nullifyIfEmpty(@Nullable MythicRecipe mic) {
|
||||
|
||||
// Null is just null
|
||||
if (mic == null) { return null; }
|
||||
|
||||
// Anything not air will count a success
|
||||
for (MythicRecipeIngredient mri : mic.getIngredients()) {
|
||||
if (mri == null) { continue; }
|
||||
if (mri.getIngredient().isDefinesItem()) { return mic; } }
|
||||
|
||||
// Nope, nothing that wasn't air
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is not null, then the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
@Nullable
|
||||
MythicRecipe ingotInputConsumption;
|
||||
/**
|
||||
* If this is not null, then the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
@Nullable public MythicRecipe getIngotInputConsumption() { return ingotInputConsumption; }
|
||||
/**
|
||||
* @param mic If this is not null, then the ingredients themselves will change as this output resolves
|
||||
* (like milk buckets turning into normal buckets when crafting a cake).
|
||||
*/
|
||||
public void setIngotInputConsumption(@Nullable MythicRecipe mic) { ingotInputConsumption = nullifyIfEmpty(mic); }
|
||||
|
||||
|
||||
/**
|
||||
* Generates a new, independent MythicRecipeInventory
|
||||
* from the recipe, with random output where possible.
|
||||
*
|
||||
* @return A new result to be given to the player.
|
||||
*/
|
||||
@NotNull MythicRecipeInventory generateResultOf(@NotNull MythicRecipe mythicRecipe) {
|
||||
|
||||
// Rows yes
|
||||
HashMap<Integer, ItemStack[]> rowsInformation = new HashMap<>();
|
||||
|
||||
// Ok it doesn't exist lets build it
|
||||
for (MythicRecipeIngredient mmIngredient : mythicRecipe.getIngredients()) {
|
||||
|
||||
// Ignore
|
||||
if (mmIngredient == null) { continue; }
|
||||
|
||||
// Identify Ingredient
|
||||
ShapedIngredient shaped = ((ShapedIngredient) mmIngredient);
|
||||
MythicIngredient ingredient = mmIngredient.getIngredient();
|
||||
|
||||
// Does not define an item? I sleep
|
||||
if (!ingredient.isDefinesItem()) { continue; }
|
||||
|
||||
// Any errors yo?
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Recipe of " + getOutputItem().getType().getId() + " " + getOutputItem().getId());
|
||||
|
||||
/*
|
||||
* First we must get the material of the base, a dummy
|
||||
* item basically (since this is for display) which we
|
||||
* may only display if its the only substitute of this
|
||||
* ingredient.
|
||||
*
|
||||
* If the ingredient has more substitutes, the ingredient
|
||||
* description will be used instead, replacing the meta of
|
||||
* this item entirely.
|
||||
*/
|
||||
ItemStack gen = mmIngredient.getIngredient().getRandomSubstituteItem(ffp);
|
||||
|
||||
// Valid?
|
||||
if (gen != null) {
|
||||
|
||||
// Get current row
|
||||
ItemStack[] row = rowsInformation.get(-shaped.getVerticalOffset());
|
||||
if (row == null) { row = new ItemStack[(shaped.getHorizontalOffset() + 1)]; }
|
||||
if (row.length < (shaped.getHorizontalOffset() + 1)) {
|
||||
ItemStack[] newRow = new ItemStack[(shaped.getHorizontalOffset() + 1)];
|
||||
//noinspection ManualArrayCopy
|
||||
for (int r = 0; r < row.length; r++) { newRow[r] = row[r]; }
|
||||
row = newRow;
|
||||
}
|
||||
|
||||
// Yes
|
||||
row[shaped.getHorizontalOffset()] = gen;
|
||||
|
||||
// Put
|
||||
rowsInformation.put(-shaped.getVerticalOffset(), row);
|
||||
|
||||
// Log those
|
||||
} else {
|
||||
|
||||
// All those invalid ones should log.
|
||||
ffp.sendTo(FriendlyFeedbackCategory.ERROR, MythicLib.plugin.getServer().getConsoleSender());
|
||||
}
|
||||
}
|
||||
|
||||
// Add all rows into new
|
||||
MythicRecipeInventory ret = new MythicRecipeInventory();
|
||||
for (Integer h : rowsInformation.keySet()) { ret.setRow(h, rowsInformation.get(h)); }
|
||||
|
||||
// Yes
|
||||
return ret;
|
||||
}
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* The amount of output produced by one smithing
|
||||
*/
|
||||
int outputAmount;
|
||||
/**
|
||||
* @return The amount of output produced by one smithing
|
||||
*/
|
||||
public int getOutputAmount() { return outputAmount; }
|
||||
/**
|
||||
* @param amount The amount of output produced by one smithing
|
||||
*/
|
||||
public void setOutputAmount(int amount) { outputAmount = amount; }
|
||||
|
||||
/**
|
||||
* The MMOItem that results from the completion of these recipes.
|
||||
@ -169,9 +324,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
Ref<ArrayList<ItemStack>> droppedGemstones = new Ref<>();
|
||||
MMOItem display = fromCombinationWith(itemMMO, ingotMMO, player, droppedGemstones);
|
||||
|
||||
// Result
|
||||
MythicRecipeInventory result = otherInventories.getResultInventory().clone();
|
||||
result.setItemAt(map.getResultWidth(map.getResultInventoryStart()), map.getResultHeight(map.getResultInventoryStart()), display.newBuilder().build());
|
||||
//RDR// MythicCraftingManager.log("\u00a78RDR \u00a748\u00a77 Custom Smithing Recipe Result\u00a7e" + times + "\u00a77 times\u00a78 ~\u00a71 " + eventTrigger.getAction().toString());
|
||||
|
||||
/*
|
||||
* Crafting the item only once allows to put it in the cursor.
|
||||
@ -182,6 +335,10 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
*/
|
||||
if (times == 1 && (eventTrigger.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY)) {
|
||||
|
||||
// Result
|
||||
MythicRecipeInventory result = otherInventories.getResultInventory().clone();
|
||||
result.setItemAt(map.getResultWidth(map.getResultInventoryStart()), map.getResultHeight(map.getResultInventoryStart()), display.newBuilder().build());
|
||||
|
||||
/*
|
||||
* When crafting with the cursor, we must make sure that:
|
||||
*
|
||||
@ -202,7 +359,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
|
||||
// Apply the result
|
||||
//RDR//MythicCraftingManager.log("\u00a78RDR \u00a748\u00a77 Processing Result Inventory");
|
||||
processInventory(resultInventory, result, times);
|
||||
processInventory(resultInventory, result, 1);
|
||||
//RR//for (String str : resultInventory.toStrings("\u00a78Result \u00a79PR-")) { MythicCraftingManager.log(str); }
|
||||
|
||||
//RDR//MythicCraftingManager.log("\u00a78RDR \u00a749\u00a77 Finding item to put on cursor");
|
||||
@ -250,7 +407,56 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
// Apply result to the cursor
|
||||
eventTrigger.getView().setCursor(actualCursor);
|
||||
|
||||
// Player is crafting to completion - move to inventory style.
|
||||
// Consume ingredients
|
||||
consumeIngredients(otherInventories, cache, eventTrigger.getInventory(), map, 1);
|
||||
|
||||
/*
|
||||
* Ok now, the ingredients have been consumed, the item is now in the cursor of the player.
|
||||
*
|
||||
* We must now read each of the affected inventories again and apply them with changes.
|
||||
*/
|
||||
if (hasInputConsumption()) {
|
||||
|
||||
// Items to spit back to the player
|
||||
ArrayList<ItemStack> inputConsumptionOverflow = new ArrayList<>();
|
||||
|
||||
// Changes in the main inventory?
|
||||
if (getMainInputConsumption() != null) {
|
||||
|
||||
// Extract the new values
|
||||
MythicRecipeInventory mainRead = map.getMainMythicInventory(eventTrigger.getInventory());
|
||||
|
||||
// Generate a result from the main input consumption
|
||||
MythicRecipeInventory addedStuff = generateResultOf(getMainInputConsumption());
|
||||
|
||||
// Include overflow
|
||||
inputConsumptionOverflow.addAll(MRORecipe.stackWhatsPossible(mainRead, addedStuff));
|
||||
|
||||
// Apply
|
||||
map.applyToMainInventory(eventTrigger.getInventory(), mainRead, false);
|
||||
}
|
||||
|
||||
// Changes in the main inventory?
|
||||
if (getIngotInputConsumption() != null) {
|
||||
|
||||
// Extract the new values
|
||||
MythicRecipeInventory sideRead = map.getSideMythicInventory("ingot", eventTrigger.getInventory());
|
||||
|
||||
// Generate a result from the main input consumption
|
||||
MythicRecipeInventory addedStuff = generateResultOf(getIngotInputConsumption());
|
||||
|
||||
// Include overflow
|
||||
inputConsumptionOverflow.addAll(MRORecipe.stackWhatsPossible(sideRead, addedStuff));
|
||||
|
||||
// Apply
|
||||
map.applyToSideInventory(eventTrigger.getInventory(), sideRead, "ingot", false);
|
||||
}
|
||||
|
||||
// Distribute in inventory call
|
||||
MRORecipe.distributeInInventoryOrDrop(eventTrigger.getWhoClicked().getInventory(), inputConsumptionOverflow, eventTrigger.getWhoClicked().getLocation());
|
||||
}
|
||||
|
||||
// Player is crafting to completion - move to inventory style.
|
||||
} else {
|
||||
|
||||
/*
|
||||
@ -259,7 +465,6 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
//RDR//MythicCraftingManager.log("\u00a78RDR \u00a747\u00a77 Reading/Generating Result");
|
||||
|
||||
// Build the result
|
||||
ArrayList<ItemStack> outputItems = MRORecipe.toItemsList(result);
|
||||
HashMap<Integer, ItemStack> modifiedInventory = null;
|
||||
Inventory inven = player.getInventory();
|
||||
int trueTimes = 0;
|
||||
@ -268,10 +473,41 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
for (int t = 1; t <= times; t++) {
|
||||
//RDR//MythicCraftingManager.log("\u00a78RDR \u00a748\u00a77 Iteration \u00a7c#" + t);
|
||||
|
||||
// Result
|
||||
MythicRecipeInventory result = otherInventories.getResultInventory().clone();
|
||||
result.setItemAt(map.getResultWidth(map.getResultInventoryStart()), map.getResultHeight(map.getResultInventoryStart()), display.newBuilder().build());
|
||||
ArrayList<ItemStack> localOutput = MRORecipe.toItemsList(result);
|
||||
|
||||
//RR//for (String str : localResult.toStrings("\u00a78Result \u00a79RR-")) { io.lumine.mythic.lib.api.crafting.recipes.MythicCraftingManager.log(str); }
|
||||
|
||||
/*
|
||||
* Is this generating other kinds of output? Account for them
|
||||
*/
|
||||
if (hasInputConsumption()) {
|
||||
|
||||
// Changes in the main inventory?
|
||||
if (getMainInputConsumption() != null) {
|
||||
|
||||
// Generate a result from the main input consumption
|
||||
MythicRecipeInventory addedStuff = generateResultOf(getMainInputConsumption());
|
||||
|
||||
// Add these to the output
|
||||
localOutput.addAll(MRORecipe.toItemsList(addedStuff));
|
||||
}
|
||||
|
||||
// Changes in the ingot inventory?
|
||||
if (getIngotInputConsumption() != null) {
|
||||
|
||||
// Generate a result from the main input consumption
|
||||
MythicRecipeInventory addedStuff = generateResultOf(getIngotInputConsumption());
|
||||
|
||||
// Add these to the output
|
||||
localOutput.addAll(MRORecipe.toItemsList(addedStuff));
|
||||
}
|
||||
}
|
||||
|
||||
// Send to
|
||||
HashMap<Integer, ItemStack> localIterationResult = MRORecipe.distributeInInventory(inven, outputItems, modifiedInventory);
|
||||
HashMap<Integer, ItemStack> localIterationResult = MRORecipe.distributeInInventory(inven, localOutput, modifiedInventory);
|
||||
|
||||
// Failed? Break
|
||||
if (localIterationResult == null) {
|
||||
@ -280,7 +516,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
//RR//io.lumine.mythic.lib.api.crafting.recipes.MythicCraftingManager.log("\u00a78Result \u00a7cIC\u00a77 Iteration Cancelled: \u00a7cNo Inventory Space");
|
||||
break;
|
||||
|
||||
// Prepare for next iteration
|
||||
// Prepare for next iteration
|
||||
} else {
|
||||
|
||||
// Store changes
|
||||
@ -302,6 +538,9 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
|
||||
// Set
|
||||
inven.setItem(s, putt); }
|
||||
|
||||
// Consume ingredients
|
||||
consumeIngredients(otherInventories, cache, eventTrigger.getInventory(), map, times);
|
||||
}
|
||||
|
||||
// Drop?
|
||||
@ -316,9 +555,6 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
|
||||
// Drop to the world
|
||||
player.getWorld().dropItem(player.getLocation(), drop); } }
|
||||
|
||||
// Consume ingredients
|
||||
consumeIngredients(otherInventories, cache, eventTrigger.getInventory(), map, times);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,7 +604,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
GemStone.ApplyResult res = asGem.applyOntoItem(gen, gen.getType(), "", false, true);
|
||||
|
||||
// None?
|
||||
if (res.getType().equals(GemStone.ResultType.SUCCESS) && (res.getResultAsMMOItem() != null)) {
|
||||
if (res.getType() == GemStone.ResultType.SUCCESS && (res.getResultAsMMOItem() != null)) {
|
||||
|
||||
// Success that's nice
|
||||
gen = res.getResultAsMMOItem();
|
||||
@ -383,7 +619,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
Ref.setValue(rem, remainingStones);
|
||||
|
||||
// Enchantments?
|
||||
if (!getEnchantmentTreatment().equals(SmithingCombinationType.NONE)) {
|
||||
if (getEnchantmentTreatment() != SmithingCombinationType.NONE) {
|
||||
|
||||
// Get enchantment data
|
||||
EnchantListData genEnchants = (EnchantListData) gen.getData(ItemStats.ENCHANTS); if (genEnchants == null) { genEnchants = (EnchantListData) ItemStats.ENCHANTS.getClearStatData(); }
|
||||
@ -409,7 +645,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
}
|
||||
|
||||
// All right whats the level stuff up now
|
||||
if (gen.hasUpgradeTemplate() && !(getUpgradeTreatment().equals(SmithingCombinationType.NONE))) {
|
||||
if (gen.hasUpgradeTemplate() && getUpgradeTreatment() != SmithingCombinationType.NONE) {
|
||||
|
||||
// All right get the levels of them both
|
||||
int itemLevel = 0; if (item != null) { itemLevel = item.getUpgradeLevel(); }
|
||||
@ -428,5 +664,6 @@ public class CustomSmithingRecipe extends MythicRecipeOutput {
|
||||
// That's it
|
||||
return gen;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import net.Indyuce.mmoitems.api.Type;
|
||||
import net.Indyuce.mmoitems.api.droptable.item.BlockDropItem;
|
||||
import net.Indyuce.mmoitems.api.droptable.item.DropItem;
|
||||
import net.Indyuce.mmoitems.api.droptable.item.MMOItemDropItem;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
@ -21,18 +22,22 @@ public class DropTable {
|
||||
private final Map<String, Subtable> subtables = new HashMap<>();
|
||||
|
||||
public DropTable(ConfigurationSection config) {
|
||||
Validate.notNull(config, "Could not read the config");
|
||||
Validate.notNull(config, "Could not read the drop table config");
|
||||
for (String key : config.getKeys(false))
|
||||
try {
|
||||
|
||||
// add subtable to list & then to map
|
||||
// Add subtable to list & then to map
|
||||
for (int j = 0; j < config.getInt(key + ".coef"); j++)
|
||||
subtablesList.add(key);
|
||||
|
||||
// Include parsed subtable
|
||||
subtables.put(key, new Subtable(config.getConfigurationSection(key)));
|
||||
|
||||
// Ew
|
||||
} catch (IllegalArgumentException exception) {
|
||||
MMOItems.plugin.getLogger().log(Level.WARNING,
|
||||
"Could not read subtable '" + key + "' from drop table '" + config.getName() + "': " + exception.getMessage());
|
||||
|
||||
// Print that error message
|
||||
MMOItems.print(null, "Could not read subtable '$r{0}$b' from drop table '$e{1}$b';&f {2}", key, config.getName(), exception.getMessage());
|
||||
}
|
||||
|
||||
Validate.notEmpty(subtablesList, "Your droptable must contain at least one subtable");
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.Indyuce.mmoitems.api.edition;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.edition.input.AnvilGUI;
|
||||
import net.Indyuce.mmoitems.api.edition.input.ChatEdition;
|
||||
@ -79,11 +80,27 @@ public class StatEdition implements Edition {
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Perform WhenInput Operation
|
||||
stat.whenInput(inv, input, info);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
|
||||
} catch (IllegalArgumentException exception) {
|
||||
inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + exception.getMessage());
|
||||
|
||||
// Add message to the FFP
|
||||
if (!exception.getMessage().isEmpty()) { inv.getFFP().log(FriendlyFeedbackCategory.ERROR, exception.getMessage()); }
|
||||
MMOItems.log("\u00a73Sending \u00a7e" + inv.getFFP().messagesTotal() + "\u00a73 to \u00a7e" + inv.getPlayer().getName());
|
||||
|
||||
// Log all
|
||||
inv.getFFP().sendTo(FriendlyFeedbackCategory.ERROR, inv.getPlayer());
|
||||
inv.getFFP().sendTo(FriendlyFeedbackCategory.FAILURE, inv.getPlayer());
|
||||
inv.getFFP().clearFeedback();
|
||||
|
||||
MMOItems.log("\u00a73Cleared to \u00a7e" + inv.getFFP().messagesTotal());
|
||||
|
||||
// No success
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.item.util.DynamicLore;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.stat.DisplayName;
|
||||
import net.Indyuce.mmoitems.stat.Enchants;
|
||||
import net.Indyuce.mmoitems.stat.data.DoubleData;
|
||||
import net.Indyuce.mmoitems.stat.data.EnchantListData;
|
||||
@ -150,7 +151,7 @@ public class ItemStackBuilder {
|
||||
builtMMOItem.setData(stat, s.recalculate(l));
|
||||
|
||||
// Add to NBT, if the gemstones were not purged
|
||||
if ((!s.isClear() || stat instanceof Enchants)) {
|
||||
if ((!s.isClear() || stat instanceof Enchants || stat instanceof DisplayName)) {
|
||||
|
||||
//GEM//MMOItems.log("\u00a7a -+- \u00a77Recording History");
|
||||
addItemTag(new ItemTag(histroy_keyword + stat.getId(), s.toNBTString())); }
|
||||
|
@ -2,6 +2,7 @@ package net.Indyuce.mmoitems.api.item.mmoitem;
|
||||
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.ItemTier;
|
||||
import net.Indyuce.mmoitems.MMOUtils;
|
||||
import net.Indyuce.mmoitems.api.Type;
|
||||
import net.Indyuce.mmoitems.api.UpgradeTemplate;
|
||||
@ -208,6 +209,12 @@ public class MMOItem implements ItemReference {
|
||||
mergeableStatHistory.put(stat.getNBTPath(), hist);
|
||||
}
|
||||
|
||||
//region Upgrading API
|
||||
/**
|
||||
* @return The tier of this item, if it has one.
|
||||
*/
|
||||
@Nullable public ItemTier getTier() { return MMOItems.plugin.getTiers().findTier(this); }
|
||||
|
||||
/**
|
||||
* Upgrades this MMOItem one level.
|
||||
* <p></p>
|
||||
|
@ -3,32 +3,36 @@ package net.Indyuce.mmoitems.api.recipe;
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.version.VersionMaterial;
|
||||
import net.Indyuce.mmoitems.MMOUtils;
|
||||
import net.Indyuce.mmoitems.manager.RecipeManager;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum CraftingType {
|
||||
SHAPED(21, "The C. Table Recipe (Shaped) for this item", VersionMaterial.CRAFTING_TABLE),
|
||||
SHAPELESS(22, "The C. Table Recipe (Shapeless) for this item", VersionMaterial.CRAFTING_TABLE),
|
||||
FURNACE(23, "The Furnace Recipe for this item", Material.FURNACE),
|
||||
BLAST(29, "The Blast Furnace Recipe for this item", VersionMaterial.BLAST_FURNACE, 1, 14),
|
||||
SMOKER(30, "The Smoker Recipe for this item", VersionMaterial.SMOKER, 1, 14),
|
||||
CAMPFIRE(32, "The Campfire Recipe for this item", VersionMaterial.CAMPFIRE, 1, 14),
|
||||
SMITHING(33, "The Smithing Recipe for this item", VersionMaterial.SMITHING_TABLE, 1, 15);
|
||||
SHAPED(21, "The C. Table Recipe (Shaped) for this item", VersionMaterial.CRAFTING_TABLE, null),
|
||||
SHAPELESS(22, "The C. Table Recipe (Shapeless) for this item", VersionMaterial.CRAFTING_TABLE, null),
|
||||
FURNACE(23, "The Furnace Recipe for this item", Material.FURNACE, RecipeManager.BurningRecipeType.FURNACE),
|
||||
BLAST(29, "The Blast Furnace Recipe for this item", VersionMaterial.BLAST_FURNACE, RecipeManager.BurningRecipeType.BLAST, 1, 14),
|
||||
SMOKER(30, "The Smoker Recipe for this item", VersionMaterial.SMOKER, RecipeManager.BurningRecipeType.SMOKER, 1, 14),
|
||||
CAMPFIRE(32, "The Campfire Recipe for this item", VersionMaterial.CAMPFIRE, RecipeManager.BurningRecipeType.CAMPFIRE, 1, 14),
|
||||
SMITHING(33, "The Smithing Recipe for this item", VersionMaterial.SMITHING_TABLE, null, 1, 15);
|
||||
|
||||
private final int slot;
|
||||
private final String lore;
|
||||
private final Material material;
|
||||
private final int[] mustBeHigher;
|
||||
private final RecipeManager.BurningRecipeType burning;
|
||||
|
||||
private CraftingType(int slot, String lore, VersionMaterial material, int... mustBeHigher) {
|
||||
this(slot, lore, material.toMaterial(), mustBeHigher);
|
||||
private CraftingType(int slot, String lore, VersionMaterial material, @Nullable RecipeManager.BurningRecipeType burn, int... mustBeHigher) {
|
||||
this(slot, lore, material.toMaterial(), burn, mustBeHigher);
|
||||
}
|
||||
|
||||
private CraftingType(int slot, String lore, Material material, int... mustBeHigher) {
|
||||
private CraftingType(int slot, String lore, Material material, @Nullable RecipeManager.BurningRecipeType burn, int... mustBeHigher) {
|
||||
this.slot = slot;
|
||||
this.lore = lore;
|
||||
this.material = material;
|
||||
this.mustBeHigher = mustBeHigher;
|
||||
this.burning = burn;
|
||||
}
|
||||
|
||||
public ItemStack getItem() {
|
||||
@ -46,6 +50,7 @@ public enum CraftingType {
|
||||
public String getLore() {
|
||||
return lore;
|
||||
}
|
||||
public RecipeManager.BurningRecipeType getBurningType() { return burning; }
|
||||
|
||||
public boolean shouldAdd() {
|
||||
return mustBeHigher.length == 0 || MythicLib.plugin.getVersion().isStrictlyHigher(mustBeHigher);
|
||||
|
@ -182,321 +182,4 @@ public class CustomRecipe implements Comparable<CustomRecipe> {
|
||||
}
|
||||
return recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this list of strings as a Shapeless Recipe
|
||||
* to craft this MMOItem.
|
||||
*
|
||||
* @param type The TYPE of the crafted MMOItem
|
||||
* @param id The ID of the crafted MMOItem
|
||||
* @param recipe The compactly-stored recipe information
|
||||
* @return A baked recipe, ready to deploy.
|
||||
* @throws IllegalArgumentException If the recipe is in incorrect format
|
||||
*/
|
||||
@NotNull public static MythicRecipeBlueprint generateShapeless(@NotNull Type type, @NotNull String id, @NotNull List<String> recipe, @NotNull String namespaceKey) throws IllegalArgumentException {
|
||||
|
||||
// Get it
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(type, id);
|
||||
Validate.isTrue(template != null, "Unexpected Error Occurred: Template does not exist.");
|
||||
|
||||
// Identify the Provided UIFilters
|
||||
ArrayList<MythicRecipeIngredient> poofs = new ArrayList<>();
|
||||
|
||||
// Error yes
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Recipe of $u" + type + " " + id);
|
||||
|
||||
// Read from the recipe
|
||||
boolean nonAirFound = false;
|
||||
for (String str : recipe) {
|
||||
|
||||
// Null is a sleeper
|
||||
if (str == null || str.equals("AIR")) { continue; }
|
||||
|
||||
// Add
|
||||
ProvidedUIFilter p = readIngredientFrom(str, ffp);
|
||||
nonAirFound = true;
|
||||
poofs.add(new MythicRecipeIngredient(p));
|
||||
}
|
||||
if (!nonAirFound) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Shapeless recipe containing only AIR, $fignored$b.")); }
|
||||
|
||||
// Build Main
|
||||
ShapedRecipe shapedRecipe = ShapedRecipe.single(namespaceKey, new ProvidedUIFilter(MMOItemUIFilter.get(), type.getId(), id, Math.max(template.getCraftedAmount(), 1)));
|
||||
|
||||
// Make ingredients
|
||||
ShapelessRecipe inputRecipe = new ShapelessRecipe(namespaceKey, poofs);
|
||||
|
||||
// Create Output
|
||||
MythicRecipeOutput outputRecipe = new MRORecipe(shapedRecipe);
|
||||
|
||||
// That's our blueprint :)
|
||||
return new MythicRecipeBlueprint(inputRecipe, outputRecipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this list of strings as a Shaped Recipe
|
||||
* to craft this MMOItem.
|
||||
*
|
||||
* @param type The TYPE of the crafted MMOItem
|
||||
* @param id The ID of the crafted MMOItem
|
||||
* @param recipe The compactly-stored recipe information
|
||||
* @return A baked recipe, ready to deploy.
|
||||
* @throws IllegalArgumentException If the recipe is in incorrect format
|
||||
*/
|
||||
@NotNull public static MythicRecipeBlueprint generateShaped(@NotNull Type type, @NotNull String id, @NotNull List<String> recipe, @NotNull String namespaceKey) throws IllegalArgumentException {
|
||||
|
||||
// Get it
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(type, id);
|
||||
Validate.isTrue(template != null, "Unexpected Error Occurred: Template does not exist.");
|
||||
|
||||
// Identify the Provided UIFilters
|
||||
ArrayList<ShapedIngredient> poofs = new ArrayList<>();
|
||||
|
||||
// Error yes
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Recipe of $u" + type + " " + id);
|
||||
int rowNumber = 0;
|
||||
|
||||
// All right lets read them
|
||||
boolean nonAirFound = false;
|
||||
for (String row : recipe) {
|
||||
|
||||
/*
|
||||
* This row could be in either legacy or new format, and we will assume no combination of them.
|
||||
*
|
||||
* Either:
|
||||
* ANYTHING ANY.THING ANYTHING
|
||||
*
|
||||
* or
|
||||
* A NYT THIN G|A NYT THING|A NYT THIN G
|
||||
*/
|
||||
|
||||
// What are the three ingredients encoded in this row?
|
||||
String[] positions;
|
||||
|
||||
if (row.contains("|")) {
|
||||
|
||||
// Split by |s
|
||||
positions = row.split("\\|");
|
||||
|
||||
// Is legacy
|
||||
} else {
|
||||
|
||||
// Split by spaces
|
||||
positions = row.split(" ");
|
||||
}
|
||||
|
||||
// Size not 3? BRUH
|
||||
if (positions.length != 3) { throw new IllegalArgumentException("Invalid crafting table row $u" + row + "$b ($fNot exactly 3 ingredients wide$b)."); }
|
||||
|
||||
// Identify
|
||||
ProvidedUIFilter left = readIngredientFrom(positions[0], ffp);
|
||||
ProvidedUIFilter center = readIngredientFrom(positions[1], ffp);
|
||||
ProvidedUIFilter right = readIngredientFrom(positions[2], ffp);
|
||||
if (!left.isAir()) { nonAirFound = true; }
|
||||
if (!center.isAir()) { nonAirFound = true; }
|
||||
if (!right.isAir()) { nonAirFound = true; }
|
||||
|
||||
/*
|
||||
* To detect if a recipe can be crafted in the survival inventory (and remove extra AIR),
|
||||
* we must see that a whole row AND a whole column be air. Not any column or row though,
|
||||
* but any of those that do not cross the center.
|
||||
*
|
||||
* If a single left item is not air, LEFT is no longer an unsharped column.
|
||||
* If a single right item is not air, RIGHT is no longer an unsharped column.
|
||||
*
|
||||
* All items must be air in TOP or BOTTOM for they to be unsharped.
|
||||
*/
|
||||
|
||||
// Bake
|
||||
ShapedIngredient leftIngredient = new ShapedIngredient(left, 0, -rowNumber);
|
||||
ShapedIngredient centerIngredient = new ShapedIngredient(center, 1, -rowNumber);
|
||||
ShapedIngredient rightIngredient = new ShapedIngredient(right, 2, -rowNumber);
|
||||
|
||||
// Parse and add
|
||||
poofs.add(leftIngredient);
|
||||
poofs.add(centerIngredient);
|
||||
poofs.add(rightIngredient);
|
||||
|
||||
// Prepare for next row
|
||||
rowNumber++;
|
||||
}
|
||||
if (!nonAirFound) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Shaped recipe containing only AIR, $fignored$b.")); }
|
||||
|
||||
// Build Main
|
||||
ShapedRecipe shapedRecipe = ShapedRecipe.single(namespaceKey, new ProvidedUIFilter(MMOItemUIFilter.get(), type.getId(), id, Math.max(template.getCraftedAmount(), 1)));
|
||||
|
||||
// Make ingredients
|
||||
ShapedRecipe inputRecipe = ShapedRecipe.unsharpen((new ShapedRecipe(namespaceKey, poofs)));
|
||||
|
||||
// Create Output
|
||||
MythicRecipeOutput outputRecipe = new MRORecipe(shapedRecipe);
|
||||
|
||||
// That's our blueprint :)
|
||||
return new MythicRecipeBlueprint(inputRecipe, outputRecipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads this list of strings as a Smithing Recipe to craft this MMOItem.
|
||||
*
|
||||
* @param type The TYPE of the crafted MMOItem
|
||||
* @param id The ID of the crafted MMOItem
|
||||
* @param item First item you need to put in the Smithing Station
|
||||
* @param ingot Second item to put in the smithing station
|
||||
* @return A baked recipe, ready to deploy.
|
||||
* @throws IllegalArgumentException If the recipe is in incorrect format
|
||||
*/
|
||||
@NotNull public static MythicRecipeBlueprint generateSmithing(@NotNull Type type, @NotNull String id, @NotNull String item, @NotNull String ingot, boolean dropGems, @NotNull String enchantmentBehaviour, @NotNull String upgradeBehaviour, @NotNull String namespaceKey) throws IllegalArgumentException {
|
||||
|
||||
// Get it
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(type, id);
|
||||
Validate.isTrue(template != null, "Unexpected Error Occurred: Template does not exist.");
|
||||
SmithingCombinationType upgradeEffect = SmithingCombinationType.valueOf(upgradeBehaviour.toUpperCase());
|
||||
SmithingCombinationType enchantEffect = SmithingCombinationType.valueOf(enchantmentBehaviour.toUpperCase());
|
||||
|
||||
// Identify the Provided UIFilters
|
||||
ArrayList<ShapedIngredient> poofs = new ArrayList<>();
|
||||
|
||||
// Error yes
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Recipe of $u" + type + " " + id);
|
||||
int rowNumber = 0;
|
||||
|
||||
// All right lets read them
|
||||
ProvidedUIFilter itemPoof = readIngredientFrom(item, ffp);
|
||||
ProvidedUIFilter ingotPoof = readIngredientFrom(ingot, ffp);
|
||||
if (itemPoof.isAir() || ingotPoof.isAir()) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Smithing recipe containing AIR, $fignored$b.")); }
|
||||
|
||||
MythicRecipeIngredient itemIngredient = new MythicRecipeIngredient(itemPoof);
|
||||
MythicRecipeIngredient ingotIngredient = new MythicRecipeIngredient(ingotPoof);
|
||||
|
||||
// Make ingredients
|
||||
ShapelessRecipe inputItem = new ShapelessRecipe(namespaceKey, itemIngredient);
|
||||
ShapelessRecipe inputIngot = new ShapelessRecipe(namespaceKey, ingotIngredient);
|
||||
|
||||
// Create Output
|
||||
MythicRecipeOutput outputRecipe = new CustomSmithingRecipe(template, dropGems, enchantEffect, upgradeEffect);
|
||||
|
||||
MythicRecipeBlueprint recipe = new MythicRecipeBlueprint(inputItem, outputRecipe);
|
||||
recipe.addSideCheck("ingot", inputIngot);
|
||||
|
||||
// That's our blueprint :)
|
||||
return recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* To support legacy formats, at least for now, we use this method
|
||||
* to read individual ingredients.
|
||||
* <p></p>
|
||||
* It supports the formats:
|
||||
* <p><code>MATERIAL</code> (legacy vanilla material)
|
||||
* </p><code>TYPE.ID</code> (legacy MMOItem)
|
||||
* <p><code>KEY ARGUMENT DATA AMOUNT</code> (current)
|
||||
* </p>
|
||||
*
|
||||
* @param str String that's should be in one of the formats above.
|
||||
* @param ffp To tell what happened
|
||||
*
|
||||
* @throws IllegalArgumentException If not in the correct format.
|
||||
*
|
||||
* @return An ingredient read from this string.
|
||||
*/
|
||||
@NotNull public static ProvidedUIFilter readIngredientFrom(@NotNull String str, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
/*
|
||||
* This entry, is it a vanilla material?
|
||||
*
|
||||
* Then build it as material.
|
||||
*/
|
||||
Material asMaterial = null;
|
||||
try { asMaterial = Material.valueOf(str.toUpperCase().replace(" ", "_").replace("-", "_")); } catch (IllegalArgumentException ignored) {}
|
||||
if (asMaterial != null) {
|
||||
|
||||
// Is it AIR?
|
||||
if (asMaterial.isAir()) {
|
||||
|
||||
ProvidedUIFilter result = new ProvidedUIFilter(VanillaUIFilter.get(), "AIR", "0");
|
||||
result.setAmountRange(new QuickNumberRange(null, null));
|
||||
return result; }
|
||||
|
||||
// We snooze if its AIR or such
|
||||
if (!asMaterial.isItem()) { throw new IllegalArgumentException("Invalid Ingredient $u" + str + "$b ($fNot an Item$b)."); }
|
||||
|
||||
// All right create filter and go
|
||||
ProvidedUIFilter poof = UIFilterManager.getUIFilter("v", asMaterial.toString(), "", "1..", ffp);
|
||||
|
||||
// Valid?
|
||||
if (poof != null) {
|
||||
|
||||
// Add
|
||||
return poof;
|
||||
|
||||
} else {
|
||||
|
||||
// Send all I guess
|
||||
ffp.sendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
|
||||
ffp.sendTo(FriendlyFeedbackCategory.FAILURE, MMOItems.getConsole());
|
||||
|
||||
// Ew
|
||||
throw new IllegalArgumentException("Invalid Ingredient $u" + str);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not a vanilla material, lets try to read it as a Legacy MMOItem thing.
|
||||
*
|
||||
* It must have a dot, and no spaces.
|
||||
*/
|
||||
if (str.contains(".") && !str.contains(" ")) {
|
||||
|
||||
// Split by dot
|
||||
String[] split = str.split("\\.");
|
||||
|
||||
// Exactly two?
|
||||
if (split.length == 2) {
|
||||
|
||||
// Well
|
||||
String iType = split[0];
|
||||
String iID = split[1];
|
||||
|
||||
// All right create filter and go
|
||||
ProvidedUIFilter poof = UIFilterManager.getUIFilter("m", iType, iID, "1..", ffp);
|
||||
|
||||
// Valid?
|
||||
if (poof != null) {
|
||||
|
||||
// Add
|
||||
return poof;
|
||||
|
||||
} else {
|
||||
|
||||
// Send all I guess
|
||||
ffp.sendAllTo(MMOItems.getConsole());
|
||||
|
||||
// Ew
|
||||
throw new IllegalArgumentException("Invalid Ingredient $u" + str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not a vanilla Material, but what about a UIFilter itself?
|
||||
*/
|
||||
ProvidedUIFilter poof = UIFilterManager.getUIFilter(str, ffp);
|
||||
|
||||
// Valid?
|
||||
if (poof != null) {
|
||||
|
||||
// Add
|
||||
return poof;
|
||||
|
||||
} else {
|
||||
|
||||
// Send all I guess
|
||||
ffp.sendAllTo(MMOItems.getConsole());
|
||||
|
||||
// Ew
|
||||
throw new IllegalArgumentException("Invalid Ingredient $u" + str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,6 +181,9 @@ public class MMOItemReforger {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void update(@Nullable RPGPlayer player, @NotNull ReforgeOptions options) {
|
||||
|
||||
// Cancel if blacklisted
|
||||
if (options.isBlacklisted(miID)) { return; }
|
||||
|
||||
// Initialize as Volatile, find source template. GemStones require a Live MMOItem though (to correctly load all Stat Histories and sh)
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(miTypeName, miID); ItemMeta meta = nbtItem.getItem().getItemMeta();
|
||||
if (template == null) { MMOItems.print(null, "Could not find template for $r{0} {1}$b. ", "MMOItems Reforger", miTypeName.toString(), miID); mmoItem = null; return; }
|
||||
@ -419,6 +422,8 @@ public class MMOItemReforger {
|
||||
*/
|
||||
int regenerate(@Nullable RPGPlayer player, @NotNull MMOItemTemplate template) {
|
||||
|
||||
if (mmoItem == null) { loadLiveMMOItem(); }
|
||||
|
||||
int determinedItemLevel;
|
||||
if (player == null) {
|
||||
|
||||
@ -497,6 +502,9 @@ public class MMOItemReforger {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void reforge(@Nullable RPGPlayer player, @NotNull ReforgeOptions options) {
|
||||
|
||||
// Cancel if blacklisted
|
||||
if (options.isBlacklisted(miID)) { return; }
|
||||
|
||||
// Initialize as Volatile, find source template. GemStones require a Live MMOItem though (to correctly load all Stat Histories and sh)
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(miTypeName, miID); ItemMeta meta = nbtItem.getItem().getItemMeta();
|
||||
if (template == null) { MMOItems.print(null, "Could not find template for $r{0} {1}$b. ", "MMOItems Reforger", miTypeName.toString(), miID); mmoItem = null; return; }
|
||||
|
@ -7,6 +7,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class PluginInventory implements InventoryHolder {
|
||||
protected final PlayerData playerData;
|
||||
@ -14,7 +15,7 @@ public abstract class PluginInventory implements InventoryHolder {
|
||||
|
||||
protected int page = 1;
|
||||
|
||||
public PluginInventory(Player player) {
|
||||
public PluginInventory(@NotNull Player player) {
|
||||
this(PlayerData.get(player));
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
package net.Indyuce.mmoitems.gui.edition;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.ConfigFile;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.item.template.TemplateModifier;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.PluginInventory;
|
||||
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
|
||||
import net.Indyuce.mmoitems.stat.type.ItemStat;
|
||||
@ -18,6 +20,7 @@ import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -46,11 +49,16 @@ public abstract class EditionInventory extends PluginInventory {
|
||||
private TemplateModifier editedModifier;
|
||||
|
||||
private ItemStack cachedItem;
|
||||
private int previousPage;
|
||||
int previousPage;
|
||||
|
||||
public EditionInventory(Player player, MMOItemTemplate template) {
|
||||
public EditionInventory(@NotNull Player player, @NotNull MMOItemTemplate template) {
|
||||
super(player);
|
||||
|
||||
// For logging back to the player
|
||||
ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Edition");
|
||||
|
||||
// For building the Inventory
|
||||
this.template = template;
|
||||
this.configFile = template.getType().getConfigFile();
|
||||
player.getOpenInventory();
|
||||
@ -158,4 +166,7 @@ public abstract class EditionInventory extends PluginInventory {
|
||||
public int getPreviousPage() {
|
||||
return previousPage;
|
||||
}
|
||||
|
||||
@NotNull final FriendlyFeedbackProvider ffp;
|
||||
@NotNull public FriendlyFeedbackProvider getFFP() { return ffp; }
|
||||
}
|
||||
|
@ -0,0 +1,269 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_Shaped;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_Shapeless;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_Smithing;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy.RMGRR_LBBlast;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy.RMGRR_LBCampfire;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy.RMGRR_LBFurnace;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy.RMGRR_LBSmoker;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The first menu when a player clicks the 'Crafting' stat is the
|
||||
* Recipe Browser GUI. It shows all the loaded types of recipes.
|
||||
* <br> <br>
|
||||
* From there, the user will choose a type of recipe, and see the
|
||||
* list of recipes of that type through a {@link RecipeListGUI}.
|
||||
* <br> <br>
|
||||
* Finally, the user will choose one of the recipes in the list to
|
||||
* edit or delete or whatever.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RecipeBrowserGUI extends EditionInventory {
|
||||
|
||||
/*
|
||||
* Item Stacks used in this inven
|
||||
*/
|
||||
@NotNull final ItemStack nextPage = ItemFactory.of(Material.ARROW).name(FFPMMOItems.get().getExampleFormat() + "Next Page").build();
|
||||
@NotNull final ItemStack prevPage = ItemFactory.of(Material.ARROW).name(FFPMMOItems.get().getExampleFormat() + "Previous Page").build();
|
||||
@NotNull final ItemStack noRecipe = ItemFactory.of(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name("").build();
|
||||
|
||||
int currentPage;
|
||||
|
||||
/**
|
||||
* Not gonna lie, I think this class doesnt support concurrent edition of
|
||||
* the same template. Lets just hope no two users decide to do crafting
|
||||
* stuff for the same item at the same time. Its untested.
|
||||
*
|
||||
* @param player Player that is editing recipes
|
||||
* @param template Template being edited
|
||||
*/
|
||||
public RecipeBrowserGUI(@NotNull Player player, @NotNull MMOItemTemplate template) {
|
||||
super(player, template);
|
||||
|
||||
// Start with defaults
|
||||
currentPage = 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
// Create and prepare
|
||||
Inventory inv = Bukkit.createInventory(this, 54, "Choose Recipe Type");
|
||||
|
||||
// Put buttons
|
||||
addEditionInventoryItems(inv, true);
|
||||
|
||||
// Arrange yes
|
||||
arrangeInventory(inv);
|
||||
|
||||
// That's it lets GOOOO
|
||||
return inv;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map containing a link between Recipe Type and Absolute Inventory Slot
|
||||
* (to know that, when a player clicks the inventory thing, which recipe
|
||||
* they are meaning)
|
||||
*/
|
||||
@NotNull final HashMap<Integer, RecipeRegistry> recipeTypeMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Updates the inventory, refreshes the page number whatever.
|
||||
*
|
||||
* @param inv Inventory object to edit
|
||||
*/
|
||||
void arrangeInventory(@NotNull Inventory inv) {
|
||||
|
||||
// Start fresh
|
||||
recipeTypeMap.clear();
|
||||
|
||||
// Include page buttons
|
||||
if (currentPage > 0) { inv.setItem(27, prevPage); }
|
||||
if (registeredRecipes.size() >= ((currentPage + 1) * 21)) { inv.setItem(36, nextPage); }
|
||||
|
||||
// Well order them I guess
|
||||
HashMap<Integer, RecipeRegistry> reg = new HashMap<>(); int op = 0;
|
||||
for (RecipeRegistry r : registeredRecipes.values()) { reg.put(op, r); op++; }
|
||||
|
||||
// Fill the space I guess
|
||||
for (int p = 21 * currentPage; p < (21 * (currentPage + 1)); p++) {
|
||||
|
||||
//CNT//MMOItems.log("\u00a77Running \u00a73" + p);
|
||||
|
||||
/*
|
||||
* The job of this is to identify which slots of this
|
||||
* inventory will trigger which action.
|
||||
*
|
||||
* If the slot has a recipe to edit, a connection will
|
||||
* be made between clicking this and which recipe to
|
||||
* edit via the HashMap 'recipeMap'
|
||||
*
|
||||
* But for that we must calculate which absolute slot
|
||||
* of this inventory are we talking about...
|
||||
*/
|
||||
int absolute = page(p);
|
||||
|
||||
/*
|
||||
* Going through the whole page, first thing
|
||||
* to check is that there is a recipe here.
|
||||
*
|
||||
* Note that clicking the very next glass pane
|
||||
* creates a new recipe.
|
||||
*/
|
||||
if (p >= registeredRecipes.size()) {
|
||||
|
||||
// Just snooze
|
||||
inv.setItem(absolute, noRecipe);
|
||||
|
||||
// There exists a recipe for this slot
|
||||
} else {
|
||||
|
||||
// Which will it be?
|
||||
RecipeRegistry rr = reg.get(p);
|
||||
|
||||
// Display name
|
||||
inv.setItem(absolute, rr.getDisplayListItem());
|
||||
|
||||
// Store
|
||||
recipeTypeMap.put(absolute, rr);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static int page(int p) {
|
||||
|
||||
// Remove multiples of 21
|
||||
int red = SilentNumbers.floor(p / 21.00D);
|
||||
p -= red * 21;
|
||||
|
||||
//CNT//MMOItems.log("\u00a73+\u00a77 Reduced to \u00a79" + p);
|
||||
|
||||
/*
|
||||
* A page is the third, fourth, and fifth rows, excluding the first and last column.
|
||||
*
|
||||
* #1 Obtain the relative column, and relative row
|
||||
*
|
||||
* #2 Convert to absolute inventory positions
|
||||
*/
|
||||
int relRow = SilentNumbers.floor(p / 7.00D);
|
||||
int relCol = p - (7 * relRow);
|
||||
|
||||
//CNT//MMOItems.log("\u00a73+\u00a77 Row \u00a79" + relRow + "\u00a77, Col\u00a79 " + relCol);
|
||||
|
||||
// Starting at the third row, each row adds 9 slots.
|
||||
int rowAdditive = 18 + (relRow * 9);
|
||||
int columnAdditive = relCol + 1;
|
||||
|
||||
// Sum to obtain final
|
||||
|
||||
//CNT//MMOItems.log("\u00a7a+\u00a77 Out \u00a7b" + (rowAdditive + columnAdditive));
|
||||
return rowAdditive + columnAdditive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenClicked(InventoryClickEvent event) {
|
||||
|
||||
// Clicked inventory was not the observed inventory? Not our business
|
||||
if ((event.getView().getTopInventory() != event.getClickedInventory())) { return; }
|
||||
|
||||
// Disallow any clicking.
|
||||
event.setCancelled(true);
|
||||
|
||||
// Selecting a recipe type to browse
|
||||
if (event.getAction() == InventoryAction.PICKUP_ALL) {
|
||||
|
||||
// Previous page
|
||||
if (event.getSlot() == 27) {
|
||||
|
||||
// Retreat page
|
||||
currentPage--;
|
||||
arrangeInventory(event.getView().getTopInventory());
|
||||
|
||||
// Next Page
|
||||
} else if (event.getSlot() == 36) {
|
||||
|
||||
// Advance page
|
||||
currentPage++;
|
||||
arrangeInventory(event.getView().getTopInventory());
|
||||
|
||||
// Create a new recipe
|
||||
} else if (event.getSlot() > 18) {
|
||||
|
||||
// A recipe exists of this name?
|
||||
RecipeRegistry recipeType = recipeTypeMap.get(event.getSlot());
|
||||
|
||||
// Well, found anything?
|
||||
if (recipeType != null) {
|
||||
|
||||
// Open that menu for the player
|
||||
new RecipeListGUI(player, template, recipeType).open(getPreviousPage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region As Recipe Type Manager
|
||||
/**
|
||||
* This is called when MMOItems loads and registers the recipes that
|
||||
* come with the plugin.
|
||||
*
|
||||
* There is no reason to call it again thereafter so I wont even attempt
|
||||
* to prevent duplicate registrations of these.
|
||||
*/
|
||||
public static void registerNativeRecipes() {
|
||||
|
||||
registerRecipe(new RMGRR_Smithing());
|
||||
registerRecipe(new RMGRR_Shapeless());
|
||||
registerRecipe(new RMGRR_Shaped());
|
||||
|
||||
/*
|
||||
* These don't go through mythiclib, I, gunging, merely
|
||||
* transcribed them to fit in the new crafting recipe
|
||||
* code but don't really know how they work.
|
||||
*
|
||||
* After the RMGRR_LegacyBurning.sendToMythicLib method (that
|
||||
* sends the recipes back to RecipeManager.registerBurningRecipe)
|
||||
* I have no clue what happens; didn't even read that far.
|
||||
*/
|
||||
registerRecipe(new RMGRR_LBFurnace());
|
||||
registerRecipe(new RMGRR_LBBlast());
|
||||
registerRecipe(new RMGRR_LBSmoker());
|
||||
registerRecipe(new RMGRR_LBCampfire());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static final HashMap<String, RecipeRegistry> registeredRecipes = new HashMap<>();
|
||||
public static void registerRecipe(@NotNull RecipeRegistry recipe) {
|
||||
|
||||
// Register that yes
|
||||
registeredRecipes.put(recipe.getRecipeConfigPath(), recipe);
|
||||
}
|
||||
|
||||
@NotNull public static Set<String> getRegisteredRecipes() { return registeredRecipes.keySet(); }
|
||||
|
||||
/**
|
||||
* @param name This is only guaranteed to be NotNull if you got it from {@link #getRegisteredRecipes()}.
|
||||
*
|
||||
* @return The loaded recipe attached to this path. Must be loaded.
|
||||
*/
|
||||
@NotNull public static RecipeRegistry getRegisteredRecipe(@NotNull String name) { return registeredRecipes.get(name); }
|
||||
//endregion
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The Recipe List GUI is in charge of showing all the recipes
|
||||
* of a specific kind, and allowing the user to choose which
|
||||
* to edit, delete, or create.
|
||||
* <br> <br>
|
||||
* Because theoretically the recipes could overflow, this does
|
||||
* support paging which is UGH who is ever going to make over 20
|
||||
* recipes for a single item but whatever.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RecipeListGUI extends EditionInventory {
|
||||
|
||||
@NotNull final ItemStack nextPage = ItemFactory.of(Material.ARROW).name("\u00a77Next Page").build();
|
||||
@NotNull final ItemStack prevPage = ItemFactory.of(Material.ARROW).name("\u00a77Previous Page").build();
|
||||
@NotNull final ItemStack noRecipe = ItemFactory.of(Material.BLACK_STAINED_GLASS_PANE).name("\u00a77No Recipe").build();
|
||||
|
||||
@NotNull final RecipeRegistry recipeType;
|
||||
@NotNull public RecipeRegistry getRecipeRegistry() { return recipeType; }
|
||||
|
||||
@NotNull final ItemStack listedItem;
|
||||
@NotNull public ItemStack getListedItem() { return listedItem; }
|
||||
|
||||
@NotNull final ArrayList<String> recipeNames = new ArrayList<>();
|
||||
@NotNull public ArrayList<String> getRecipeNames() { return recipeNames; }
|
||||
|
||||
boolean invalidRecipe;
|
||||
|
||||
public RecipeListGUI(@NotNull Player player, @NotNull MMOItemTemplate template, @NotNull RecipeRegistry kind) {
|
||||
super(player, template);
|
||||
recipeType = kind;
|
||||
|
||||
// Which item to fill the area with?
|
||||
listedItem = getRecipeRegistry().getDisplayListItem();
|
||||
|
||||
// Obtain the crafting section
|
||||
ConfigurationSection section = RecipeMakerGUI.getSection(getEditedSection(), "crafting");
|
||||
ConfigurationSection type = RecipeMakerGUI.getSection(section, kind.getRecipeConfigPath());
|
||||
|
||||
// What is all the recipes within this kind?
|
||||
for (String recipeName : type.getValues(false).keySet()) {
|
||||
if (recipeName == null || recipeName.isEmpty()) { continue; }
|
||||
|
||||
/*
|
||||
* This is now a for loop going through all the recipes
|
||||
* written onto this item's config.
|
||||
*/
|
||||
recipeNames.add(recipeName);
|
||||
}
|
||||
}
|
||||
|
||||
int currentPage;
|
||||
int createSlot = -1;
|
||||
@NotNull final HashMap<Integer, String> recipeMap = new HashMap<>();
|
||||
|
||||
@NotNull @Override public Inventory getInventory() {
|
||||
|
||||
// Create and prepare
|
||||
Inventory inv = Bukkit.createInventory(this, 54, "Choose " + getRecipeRegistry().getRecipeTypeName() + " Recipe");
|
||||
|
||||
// Put buttons
|
||||
addEditionInventoryItems(inv, true);
|
||||
|
||||
// Arrange yes
|
||||
arrangeInventory(inv);
|
||||
|
||||
// That's it lets GOOOO
|
||||
return inv;
|
||||
}
|
||||
/**
|
||||
* Updates the inventory, refreshes the page number whatever.
|
||||
*
|
||||
* @param inv Inventory object to edit
|
||||
*/
|
||||
void arrangeInventory(@NotNull Inventory inv) {
|
||||
|
||||
// Start fresh
|
||||
recipeMap.clear();
|
||||
createSlot = -1;
|
||||
|
||||
// Include page buttons
|
||||
if (currentPage > 0) { inv.setItem(27, prevPage); }
|
||||
if (recipeNames.size() >= ((currentPage + 1) * 21)) { inv.setItem(36, nextPage); }
|
||||
|
||||
// Fill the space I guess
|
||||
for (int p = 21 * currentPage; p < 21 * (currentPage + 1); p++) {
|
||||
|
||||
/*
|
||||
* The job of this is to identify which slots of this
|
||||
* inventory will trigger which action.
|
||||
*
|
||||
* If the slot has a recipe to edit, a connection will
|
||||
* be made between clicking this and which recipe to
|
||||
* edit via the HashMap 'recipeMap'
|
||||
*
|
||||
* But for that we must calculate which absolute slot
|
||||
* of this inventory are we talking about...
|
||||
*/
|
||||
int absolute = page(p);
|
||||
|
||||
/*
|
||||
* Going through the whole page, first thing
|
||||
* to check is that there is a recipe here.
|
||||
*
|
||||
* Note that clicking the very next glass pane
|
||||
* creates a new recipe.
|
||||
*/
|
||||
if (p == recipeNames.size()) {
|
||||
|
||||
// Rename list item...
|
||||
inv.setItem(absolute, RecipeMakerGUI.rename(new ItemStack(Material.NETHER_STAR), FFPMMOItems.get().getBodyFormat() + "Create new " + SilentNumbers.getItemName(getListedItem(), false)));
|
||||
|
||||
// If this slot is clicked, a new recipe will be created.
|
||||
createSlot = absolute;
|
||||
|
||||
// The current item is greater, fill with empty glass panes
|
||||
} else if (p > recipeNames.size()) {
|
||||
|
||||
// Just snooze
|
||||
inv.setItem(absolute, noRecipe);
|
||||
|
||||
// There exists a recipe for this slot
|
||||
} else {
|
||||
|
||||
// Display name
|
||||
inv.setItem(absolute, RecipeMakerGUI.rename(getListedItem().clone(), FFPMMOItems.get().getBodyFormat() + "Edit " + FFPMMOItems.get().getInputFormat() + recipeNames.get(p)));
|
||||
|
||||
// Store
|
||||
recipeMap.put(absolute, recipeNames.get(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
public static int page(int p) {
|
||||
|
||||
// Remove multiples of 21
|
||||
int red = SilentNumbers.floor(p / 21.00D);
|
||||
p -= red * 21;
|
||||
|
||||
/*
|
||||
* A page is the third, fourth, and fifth rows, excluding the first and last column.
|
||||
*
|
||||
* #1 Obtain the relative column, and relative row
|
||||
*
|
||||
* #2 Convert to absolute inventory positions
|
||||
*/
|
||||
int relRow = SilentNumbers.floor(p / 7.00D);
|
||||
int relCol = p - (7 * relRow);
|
||||
|
||||
// Starting at the third row, each row adds 9 slots.
|
||||
int rowAdditive = 18 + (relRow * 9);
|
||||
int columnAdditive = relCol + 1;
|
||||
|
||||
// Sum to obtain final
|
||||
return rowAdditive + columnAdditive;
|
||||
}
|
||||
|
||||
|
||||
@Override public void whenClicked(InventoryClickEvent event) {
|
||||
|
||||
// Clicked inventory was not the observed inventory? Not our business
|
||||
if ((event.getView().getTopInventory() != event.getClickedInventory())) { return; }
|
||||
|
||||
// Disallow any clicking.
|
||||
event.setCancelled(true);
|
||||
|
||||
if (invalidRecipe) { return; }
|
||||
|
||||
// Selecting a recipe to edit (or creating?)
|
||||
if (event.getAction() == InventoryAction.PICKUP_ALL) {
|
||||
|
||||
// Previous page
|
||||
if (event.getSlot() == 27) {
|
||||
|
||||
// Retreat page
|
||||
currentPage--;
|
||||
arrangeInventory(event.getView().getTopInventory());
|
||||
|
||||
// Next Page
|
||||
} else if (event.getSlot() == 36) {
|
||||
|
||||
// Advance page
|
||||
currentPage++;
|
||||
arrangeInventory(event.getView().getTopInventory());
|
||||
|
||||
// Create a new recipe
|
||||
} else if (event.getSlot() == createSlot) {
|
||||
|
||||
// Well make sure tha name is not taken
|
||||
String chadName = String.valueOf(recipeMap.size() + 1);
|
||||
if (recipeMap.containsValue(chadName)) { chadName = chadName + "_" + UUID.randomUUID(); }
|
||||
|
||||
// Create a new one with that chad name
|
||||
getRecipeRegistry().openForPlayer(this, chadName);
|
||||
|
||||
// Might be clicking a recipe to edit then
|
||||
} else if (event.getSlot() > 18) {
|
||||
|
||||
// A recipe exists of this name?
|
||||
String recipeName = recipeMap.get(event.getSlot());
|
||||
|
||||
// Well, found anything?
|
||||
if (recipeName != null) {
|
||||
|
||||
// Open that menu for the player
|
||||
getRecipeRegistry().openForPlayer(this, recipeName);
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting a recipe
|
||||
} else if (event.getAction() == InventoryAction.PICKUP_HALF) {
|
||||
|
||||
// A recipe exists of this name?
|
||||
String recipeName = recipeMap.get(event.getSlot());
|
||||
|
||||
// Seems there was
|
||||
if (recipeName != null) {
|
||||
|
||||
// Delete that
|
||||
ConfigurationSection section = RecipeMakerGUI.getSection(getEditedSection(), "crafting");
|
||||
ConfigurationSection type = RecipeMakerGUI.getSection(section, getRecipeRegistry().getRecipeConfigPath());
|
||||
recipeNames.remove(recipeName);
|
||||
type.set(recipeName, null);
|
||||
|
||||
// Refresh
|
||||
arrangeInventory(event.getView().getTopInventory());
|
||||
|
||||
// Register edition
|
||||
registerTemplateEdition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.interpreters;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The legacy recipes that are not supported by MythicLib that all happen to have to do
|
||||
* with burning stuff - furnaces, campfires, the other furnaces...
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMGRI_BurningLegacy implements RMG_RecipeInterpreter{
|
||||
|
||||
/**
|
||||
* Interestingly enough, they onl require one input.
|
||||
*/
|
||||
@NotNull ProvidedUIFilter input;
|
||||
/**
|
||||
* @return The stuff that must be smelted / cooked
|
||||
*/
|
||||
@NotNull public ProvidedUIFilter getInput() { return input; }
|
||||
/**
|
||||
* Setting it to null will make it into AIR tho but ok.
|
||||
* This method does not update it in the Config Files.
|
||||
*
|
||||
* @param input The stuff that must be smelted
|
||||
*/
|
||||
public void setInput(@Nullable ProvidedUIFilter input) { this.input = input == null ? RecipeMakerGUI.AIR : input; }
|
||||
|
||||
@NotNull final ConfigurationSection section;
|
||||
/**
|
||||
* @return The recipe name section of this recipe. <br>
|
||||
* <br>
|
||||
* Basically <b><code>[ID].base.crafting.shaped.[name]</code></b> section
|
||||
*/
|
||||
@NotNull public ConfigurationSection getSection() { return section; }
|
||||
|
||||
/**
|
||||
* Generate an interpreter from this configuration section.
|
||||
*
|
||||
* @param recipeNameSection <b><code>[ID].base.crafting.furnace.[name]</code></b> section
|
||||
*/
|
||||
public RMGRI_BurningLegacy(@NotNull ConfigurationSection recipeNameSection) {
|
||||
|
||||
// Save
|
||||
section = recipeNameSection;
|
||||
|
||||
// Furnaces support only input
|
||||
//noinspection ConstantConditions
|
||||
input = ProvidedUIFilter.getFromString(RecipeMakerGUI.poofFromLegacy(recipeNameSection.getString(RecipeMakerGUI.INPUT_INGREDIENTS)), null);
|
||||
if (input == null) { input = RecipeMakerGUI.AIR.clone(); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editInput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
if (slot != 0) { return; }
|
||||
|
||||
// Just edit bro
|
||||
setInput(input);
|
||||
|
||||
// Save
|
||||
section.set(ITEM, input.toString());
|
||||
}
|
||||
|
||||
@Override public void editOutput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) { }
|
||||
|
||||
@Override public void deleteInput(@NotNull ConfigurationSection section, int slot) { editInput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
|
||||
@Override public void deleteOutput(@NotNull ConfigurationSection section, int slot) { }
|
||||
|
||||
@Nullable @Override public ProvidedUIFilter getInput(int slot) { if (slot == 0) { return input; } return null; }
|
||||
|
||||
@Nullable @Override public ProvidedUIFilter getOutput(int slot) { return null; }
|
||||
|
||||
public static final String ITEM = "item";
|
||||
public static final String TIME = "time";
|
||||
public static final String EXPERIENCE = "experience";
|
||||
}
|
@ -0,0 +1,280 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.interpreters;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
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.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is in charge of converting Shaped Recipes to and fro YML format,
|
||||
* as well as editing it in a YML configuration and such. <br> <br>
|
||||
*
|
||||
* YML Save Format: <br> <code>
|
||||
*
|
||||
* - A|B|C <br>
|
||||
* - D|E|F <br>
|
||||
* - G|H|I
|
||||
* </code>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMGRI_Shaped implements RMG_RecipeInterpreter {
|
||||
|
||||
/**
|
||||
* Builds a valid 3x3 matrix of input/output recipe.
|
||||
*
|
||||
* @param config List as it is saved in the config.
|
||||
*
|
||||
* @return Transcribed into array of arrays.
|
||||
*/
|
||||
@NotNull ProvidedUIFilter[][] buildIngredientsFromList(@NotNull List<String> config) {
|
||||
|
||||
// Start with a base
|
||||
ProvidedUIFilter[][] ret = new ProvidedUIFilter[3][3];
|
||||
|
||||
// Each row ig
|
||||
for (int r = 0; r < 3; r++) {
|
||||
|
||||
// Get current row
|
||||
String row = config.size() > r ? config.get(r) : null;
|
||||
//READ//MMOItems.log("\u00a7b*\u00a77 Reading\u00a7b " + row);
|
||||
|
||||
// Update it ig
|
||||
String s = updateRow(row);
|
||||
//READ//MMOItems.log("\u00a7b*\u00a77 Updated to\u00a7b " + row);
|
||||
|
||||
// Split
|
||||
String[] poofs = s.split("\\|");
|
||||
|
||||
// Parse
|
||||
for (int p = 0; p < 3; p++) {
|
||||
|
||||
String poof = poofs.length > p ? poofs[p] : null;
|
||||
//READ//MMOItems.log("\u00a7b*\u00a77 Coord\u00a7b " + r + " " + p + "\u00a77 as\u00a73 " + poof);
|
||||
|
||||
// Parse
|
||||
ProvidedUIFilter parsed = ProvidedUIFilter.getFromString(poof, null);
|
||||
if (parsed == null) { parsed = RecipeMakerGUI.AIR.clone(); }
|
||||
|
||||
// Add
|
||||
ret[r][p] = parsed; } }
|
||||
|
||||
// And that's your result
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Turns something like <br> <code>
|
||||
*
|
||||
* [ A, B, C ], <br>
|
||||
* [ D, E, F ], <br>
|
||||
* [ G, H, I ] <br>
|
||||
*
|
||||
* </code> <br>
|
||||
* into <br> <code>
|
||||
*
|
||||
* - A|B|C <br>
|
||||
* - D|E|F <br>
|
||||
* - G|H|I
|
||||
* </code>
|
||||
*
|
||||
* @param ingredients Array of arrays of UIFilters
|
||||
*
|
||||
* @return A list of strings to save in a YML Config
|
||||
*/
|
||||
@NotNull ArrayList<String> toYML(@NotNull ProvidedUIFilter[][] ingredients) {
|
||||
|
||||
// Well, build it would ye?
|
||||
ArrayList<String> ret = new ArrayList<>();
|
||||
|
||||
for (int r = 0; r < 3; r++) {
|
||||
|
||||
// Get row
|
||||
ProvidedUIFilter[] poofs = ingredients.length > r ? ingredients[r] : new ProvidedUIFilter[3];
|
||||
|
||||
// Concatenate
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Build
|
||||
for (ProvidedUIFilter poof : poofs) {
|
||||
ProvidedUIFilter providedUIFilter = poof;
|
||||
if (providedUIFilter == null) { providedUIFilter = RecipeMakerGUI.AIR.clone(); }
|
||||
|
||||
// Add bar
|
||||
if (sb.length() != 0) { sb.append("|"); }
|
||||
|
||||
// Add poof
|
||||
sb.append(providedUIFilter);
|
||||
}
|
||||
|
||||
ret.add(sb.toString());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@NotNull final ProvidedUIFilter[][] inputRecipe;
|
||||
/**
|
||||
* Sets the ingredient in the rows matrix.
|
||||
*
|
||||
* @param slot The slot, which must be between 0 and 8 (or this method will do nothing)
|
||||
* @param poof Ingredient to register
|
||||
*/
|
||||
public void setInput(int slot, @NotNull ProvidedUIFilter poof) {
|
||||
if (slot < 0 || slot > 8) { return; }
|
||||
inputRecipe[SilentNumbers.floor(slot / 3.0)][slot - (3 * SilentNumbers.floor(slot / 3.0))] = poof;
|
||||
}
|
||||
@Nullable
|
||||
@Override public ProvidedUIFilter getInput(int slot) {
|
||||
if (slot < 0 || slot > 8) { return null; }
|
||||
return inputRecipe[SilentNumbers.floor(slot / 3.0)][slot - (3 * SilentNumbers.floor(slot / 3.0))];
|
||||
}
|
||||
|
||||
@NotNull final ProvidedUIFilter[][] outputRecipe;
|
||||
/**
|
||||
* Sets the ingredient in the rows matrix.
|
||||
*
|
||||
* @param slot The slot, which must be between 0 and 8 (or this method will do nothing)
|
||||
* @param poof Ingredient to register
|
||||
*/
|
||||
public void setOutput(int slot, @NotNull ProvidedUIFilter poof) {
|
||||
if (slot < 0 || slot > 8) { return; }
|
||||
outputRecipe[SilentNumbers.floor(slot / 3.0)][slot - (3 * SilentNumbers.floor(slot / 3.0))] = poof;
|
||||
}
|
||||
@Nullable
|
||||
@Override public ProvidedUIFilter getOutput(int slot) {
|
||||
if (slot < 0 || slot > 8) { return null; }
|
||||
return outputRecipe[SilentNumbers.floor(slot / 3.0)][slot - (3 * SilentNumbers.floor(slot / 3.0))];
|
||||
}
|
||||
|
||||
@NotNull final ConfigurationSection section;
|
||||
/**
|
||||
* @return The recipe name section of this recipe. <br>
|
||||
* <br>
|
||||
* Basically <b><code>[ID].base.crafting.shaped.[name]</code></b> section
|
||||
*/
|
||||
@NotNull public ConfigurationSection getSection() { return section; }
|
||||
|
||||
/**
|
||||
* Generate an interpreter from this <i>updated</i> configuration section.
|
||||
* <br><br>
|
||||
* By 'updated' I mean that, for now, we <b>should call {@link RecipeMakerGUI#moveInput()}
|
||||
* on this configuration before passing it here</b>, to move the input list from being the recipe name
|
||||
* section itself to the 'input' section within.
|
||||
*
|
||||
* @param recipeNameSection <b><code>[ID].base.crafting.shaped.[name]</code></b> section
|
||||
*/
|
||||
public RMGRI_Shaped(@NotNull ConfigurationSection recipeNameSection) {
|
||||
|
||||
// Save
|
||||
section = recipeNameSection;
|
||||
|
||||
// Build Input list
|
||||
inputRecipe = buildIngredientsFromList(section.getStringList(RecipeMakerGUI.INPUT_INGREDIENTS));
|
||||
outputRecipe = buildIngredientsFromList(section.getStringList(RecipeMakerGUI.OUTPUT_INGREDIENTS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editInput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
// Just edit bro
|
||||
setInput(slot, input);
|
||||
|
||||
// Save
|
||||
section.set(RecipeMakerGUI.INPUT_INGREDIENTS, toYML(inputRecipe));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editOutput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
// Just edit bro
|
||||
setOutput(slot, input);
|
||||
|
||||
// Save
|
||||
section.set(RecipeMakerGUI.OUTPUT_INGREDIENTS, toYML(outputRecipe));
|
||||
}
|
||||
|
||||
@Override public void deleteInput(@NotNull ConfigurationSection section, int slot) { editInput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
|
||||
@Override public void deleteOutput(@NotNull ConfigurationSection section, int slot) { editOutput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
|
||||
//region Updater, to update old recipes
|
||||
/**
|
||||
* No matter what input, the output will always be three Provided UIFilters
|
||||
* separated by bars, as expected in the current system, filling with AIR
|
||||
* where necessary.
|
||||
*
|
||||
* @param curr Current string
|
||||
*
|
||||
* @return A row in correct format
|
||||
*/
|
||||
@NotNull public static String updateRow(@Nullable String curr) {
|
||||
if (curr == null || curr.isEmpty()) { return emptyRow;}
|
||||
|
||||
// Bars used? I guess we can check that its written correctly
|
||||
if (curr.contains("|")) {
|
||||
|
||||
// Split by bars
|
||||
String[] curSplit = curr.split("\\|");
|
||||
|
||||
// Correct length?
|
||||
if (curSplit.length == 3) {
|
||||
|
||||
// Assumed to be updated.
|
||||
return curr;
|
||||
|
||||
} else {
|
||||
|
||||
// Make sure it is of size three
|
||||
StringBuilder ret = new StringBuilder();
|
||||
|
||||
// Must append three
|
||||
for (int r = 0; r < 3; r++) {
|
||||
|
||||
// Append a bar after the first
|
||||
if (r != 0) { ret.append("|"); }
|
||||
|
||||
// Array has it?
|
||||
if (r < curSplit.length) { ret.append(RecipeMakerGUI.poofFromLegacy(curSplit[r])); } else { ret.append("v AIR 0"); }
|
||||
}
|
||||
|
||||
// Build and return
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
// Not bars, but spaces, might be old format
|
||||
} else if (curr.contains(" ")) {
|
||||
|
||||
// Make string builder
|
||||
StringBuilder ret = new StringBuilder();
|
||||
String[] curSplit = curr.split(" ");
|
||||
|
||||
// Must append three
|
||||
for (int r = 0; r < 3; r++) {
|
||||
|
||||
// Append a bar after the first
|
||||
if (r != 0) { ret.append("|"); }
|
||||
|
||||
// Array has it?
|
||||
if (r < curSplit.length) { ret.append(RecipeMakerGUI.poofFromLegacy(curSplit[r])); } else { ret.append("v AIR 0"); }
|
||||
}
|
||||
|
||||
// Build and return
|
||||
return ret.toString();
|
||||
|
||||
// No spaces nor bars, this will just be the first ingredient of the row I guess
|
||||
} else {
|
||||
|
||||
// Just that i guess
|
||||
return RecipeMakerGUI.poofFromLegacy(curr) + "|v AIR 0|v AIR 0";
|
||||
}
|
||||
}
|
||||
public static final String emptyRow = "v AIR 0|v AIR 0|v AIR 0";
|
||||
//endregion
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.interpreters;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is in charge of converting Shapeless Recipes to and fro YML format,
|
||||
* as well as editing it in a YML configuration and such. <br> <br>
|
||||
*
|
||||
* YML Save Format: <br> <code>
|
||||
*
|
||||
* - A <br>
|
||||
* - B <br>
|
||||
* - C <br>
|
||||
* - D <br>
|
||||
* - E <br>
|
||||
* - F <br>
|
||||
* - G <br>
|
||||
* - H <br>
|
||||
* - I <br>
|
||||
* </code>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMGRI_Shapeless implements RMG_RecipeInterpreter {
|
||||
|
||||
/**
|
||||
* Builds a valid 3x3 matrix of input/output recipe.
|
||||
*
|
||||
* @param config List as it is saved in the config.
|
||||
*
|
||||
* @return Transcribed into array of arrays.
|
||||
*/
|
||||
@NotNull ProvidedUIFilter[] buildIngredientsFromList(@NotNull List<String> config) {
|
||||
|
||||
// Start with a base
|
||||
ProvidedUIFilter[] ret = new ProvidedUIFilter[9];
|
||||
|
||||
// Each row ig
|
||||
for (int r = 0; r < 9; r++) {
|
||||
|
||||
// Get current row
|
||||
String row = config.size() > r ? config.get(r) : null;
|
||||
|
||||
// Update it ig
|
||||
String poof = RecipeMakerGUI.poofFromLegacy(row);
|
||||
|
||||
// Parse
|
||||
ProvidedUIFilter parsed = ProvidedUIFilter.getFromString(poof, null);
|
||||
if (parsed == null) { parsed = RecipeMakerGUI.AIR.clone(); }
|
||||
|
||||
// Add
|
||||
ret[r] = parsed;
|
||||
}
|
||||
|
||||
// And that's your result
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Turns something like <br> <code>
|
||||
*
|
||||
* [ A, B, C, D, E, F, G, H, I ] <br>
|
||||
*
|
||||
* </code> <br>
|
||||
* into <br> <code>
|
||||
*
|
||||
* - A <br>
|
||||
* - B <br>
|
||||
* - C <br>
|
||||
* - D <br>
|
||||
* - E <br>
|
||||
* - F <br>
|
||||
* - G <br>
|
||||
* - H <br>
|
||||
* - I <br>
|
||||
* </code>
|
||||
*
|
||||
* @param ingredients Array of arrays of UIFilters
|
||||
*
|
||||
* @return A list of strings to save in a YML Config
|
||||
*/
|
||||
@NotNull ArrayList<String> toYML(@NotNull ProvidedUIFilter[] ingredients) {
|
||||
|
||||
// Well, build it would ye?
|
||||
ArrayList<String> ret = new ArrayList<>();
|
||||
|
||||
for (int r = 0; r < 9; r++) {
|
||||
|
||||
// Get row
|
||||
ProvidedUIFilter poof = ingredients.length > r ? ingredients[r] : RecipeMakerGUI.AIR.clone();
|
||||
|
||||
// Add poof
|
||||
ret.add(poof.toString());
|
||||
}
|
||||
|
||||
// Thats it
|
||||
return ret;
|
||||
}
|
||||
|
||||
@NotNull final ProvidedUIFilter[] inputRecipe;
|
||||
/**
|
||||
* Sets the ingredient in the rows matrix.
|
||||
*
|
||||
* @param slot The slot, which must be between 0 and 8 (or this method will do nothing)
|
||||
* @param poof Ingredient to register
|
||||
*/
|
||||
public void setInput(int slot, @NotNull ProvidedUIFilter poof) {
|
||||
if (slot < 0 || slot > 8) { return; }
|
||||
inputRecipe[slot] = poof;
|
||||
}
|
||||
@Nullable
|
||||
@Override public ProvidedUIFilter getInput(int slot) {
|
||||
if (slot < 0 || slot > 8) { return null; }
|
||||
return inputRecipe[slot];
|
||||
}
|
||||
|
||||
@NotNull final ProvidedUIFilter[] outputRecipe;
|
||||
/**
|
||||
* Sets the ingredient in the rows matrix.
|
||||
*
|
||||
* @param slot The slot, which must be between 0 and 8 (or this method will do nothing)
|
||||
* @param poof Ingredient to register
|
||||
*/
|
||||
public void setOutput(int slot, @NotNull ProvidedUIFilter poof) {
|
||||
if (slot < 0 || slot > 8) { return; }
|
||||
outputRecipe[slot] = poof;
|
||||
}
|
||||
@Nullable
|
||||
@Override public ProvidedUIFilter getOutput(int slot) {
|
||||
if (slot < 0 || slot > 8) { return null; }
|
||||
return outputRecipe[slot];
|
||||
}
|
||||
|
||||
@NotNull final ConfigurationSection section;
|
||||
/**
|
||||
* @return The recipe name section of this recipe. <br>
|
||||
* <br>
|
||||
* Basically <b><code>[ID].base.crafting.shapeless.[name]</code></b> section
|
||||
*/
|
||||
@NotNull public ConfigurationSection getSection() { return section; }
|
||||
|
||||
/**
|
||||
* Generate an interpreter from this <i>updated</i> configuration section.
|
||||
* <br><br>
|
||||
* By 'updated' I mean that, for now, we <b>should call {@link RecipeMakerGUI#moveInput(ConfigurationSection, String)}
|
||||
* on this configuration before passing it here</b>, to move the input list from being the recipe name
|
||||
* section itself to the 'input' section within.
|
||||
*
|
||||
* @param recipeNameSection <b><code>[ID].base.crafting.shapeless.[name]</code></b> section
|
||||
*/
|
||||
public RMGRI_Shapeless(@NotNull ConfigurationSection recipeNameSection) {
|
||||
|
||||
// Save
|
||||
section = recipeNameSection;
|
||||
|
||||
// Build Input list
|
||||
inputRecipe = buildIngredientsFromList(section.getStringList(RecipeMakerGUI.INPUT_INGREDIENTS));
|
||||
outputRecipe = buildIngredientsFromList(section.getStringList(RecipeMakerGUI.OUTPUT_INGREDIENTS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editInput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
// Just edit bro
|
||||
setInput(slot, input);
|
||||
|
||||
// Save
|
||||
section.set(RecipeMakerGUI.INPUT_INGREDIENTS, toYML(inputRecipe));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editOutput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
// Just edit bro
|
||||
setOutput(slot, input);
|
||||
|
||||
// Save
|
||||
section.set(RecipeMakerGUI.OUTPUT_INGREDIENTS, toYML(outputRecipe));
|
||||
}
|
||||
|
||||
@Override public void deleteInput(@NotNull ConfigurationSection section, int slot) { editInput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
|
||||
@Override public void deleteOutput(@NotNull ConfigurationSection section, int slot) { editOutput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
}
|
@ -0,0 +1,227 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.interpreters;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* This class is in charge of converting Smithing Recipes to and fro YML format,
|
||||
* as well as editing it in a YML configuration and such. <br> <br>
|
||||
*
|
||||
* YML Save Format: <br> <code>
|
||||
*
|
||||
* - A|B <br>
|
||||
* </code>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMGRI_Smithing implements RMG_RecipeInterpreter {
|
||||
|
||||
/**
|
||||
* Turns something like <br> <code>
|
||||
* [ A, B ]
|
||||
*
|
||||
* </code> <br> <br>
|
||||
* into <br> <code>
|
||||
*
|
||||
* - A|B
|
||||
* </code>
|
||||
*
|
||||
* @param item first input
|
||||
* @param ingot second input
|
||||
*
|
||||
* @return A string to save in a YML Config
|
||||
*/
|
||||
@NotNull String toYML(@NotNull ProvidedUIFilter item, @NotNull ProvidedUIFilter ingot) {
|
||||
|
||||
// Well, build it would ye?
|
||||
return item + "|" + ingot;
|
||||
}
|
||||
|
||||
@NotNull ProvidedUIFilter inputItem;
|
||||
@NotNull public ProvidedUIFilter getInputItem() { return inputItem; }
|
||||
public void setInputItem(@NotNull ProvidedUIFilter inputItem) { this.inputItem = inputItem; }
|
||||
|
||||
@NotNull ProvidedUIFilter outputItem;
|
||||
@NotNull public ProvidedUIFilter getOutputItem() { return outputItem; }
|
||||
public void setOutputItem(@NotNull ProvidedUIFilter outputItem) { this.outputItem = outputItem; }
|
||||
|
||||
@NotNull ProvidedUIFilter inputIngot;
|
||||
@NotNull public ProvidedUIFilter getInputIngot() { return inputIngot; }
|
||||
public void setInputIngot(@NotNull ProvidedUIFilter inputIngot) { this.inputIngot = inputIngot; }
|
||||
|
||||
@NotNull ProvidedUIFilter outputIngot;
|
||||
@NotNull public ProvidedUIFilter getOutputIngot() { return outputIngot; }
|
||||
public void setOutputIngot(@NotNull ProvidedUIFilter outputIngot) { this.outputIngot = outputIngot; }
|
||||
|
||||
|
||||
@NotNull final ConfigurationSection section;
|
||||
/**
|
||||
* @return The recipe name section of this recipe. <br>
|
||||
* <br>
|
||||
* Basically <b><code>[ID].base.crafting.shaped.[name]</code></b> section
|
||||
*/
|
||||
@NotNull public ConfigurationSection getSection() { return section; }
|
||||
|
||||
/**
|
||||
* Generate an interpreter from this <i>updated</i> configuration section.
|
||||
* <br><br>
|
||||
* By 'updated' I mean that, for now, we <b>should call {@link RecipeMakerGUI#moveInput()}
|
||||
* on this configuration before passing it here</b>, to move the input list from being the recipe name
|
||||
* section itself to the 'input' section within.
|
||||
*
|
||||
* @param recipeNameSection <b><code>[ID].base.crafting.shaped.[name]</code></b> section
|
||||
*/
|
||||
public RMGRI_Smithing(@NotNull ConfigurationSection recipeNameSection) {
|
||||
|
||||
// Save
|
||||
section = recipeNameSection;
|
||||
|
||||
/*
|
||||
* Read input and output from the file
|
||||
*/
|
||||
String input = updateIngredients(section.getString(RecipeMakerGUI.INPUT_INGREDIENTS));
|
||||
String output = updateIngredients(section.getString(RecipeMakerGUI.OUTPUT_INGREDIENTS));
|
||||
|
||||
// Split
|
||||
String[] inputSplit = input.split("\\|");
|
||||
String[] outputSplit = output.split("\\|");
|
||||
|
||||
ProvidedUIFilter inputItemParse = ProvidedUIFilter.getFromString(inputSplit[0], null);
|
||||
ProvidedUIFilter outputItemParse = ProvidedUIFilter.getFromString(outputSplit[0], null);
|
||||
ProvidedUIFilter inputIngotParse = ProvidedUIFilter.getFromString(inputSplit[1], null);
|
||||
ProvidedUIFilter outputIngotParse = ProvidedUIFilter.getFromString(outputSplit[1], null);
|
||||
|
||||
// Build Input list
|
||||
inputItem = inputItemParse != null ? inputItemParse : RecipeMakerGUI.AIR.clone();
|
||||
inputIngot = inputIngotParse != null ? inputIngotParse : RecipeMakerGUI.AIR.clone();
|
||||
outputItem = outputItemParse != null ? outputItemParse : RecipeMakerGUI.AIR.clone();
|
||||
outputIngot = outputIngotParse != null ? outputIngotParse : RecipeMakerGUI.AIR.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param slot The slot, which must be between 0 and 8 (or this method will do nothing)
|
||||
* @param poof Ingredient to register
|
||||
*/
|
||||
public void setInput(int slot, @NotNull ProvidedUIFilter poof) {
|
||||
if (slot == 0) { setInputItem(poof); } else if (slot == 1) { setInputIngot(poof); }
|
||||
}
|
||||
@Nullable
|
||||
@Override public ProvidedUIFilter getInput(int slot) {
|
||||
if (slot == 0) { return getInputItem(); } else if (slot == 1) { return getInputIngot(); }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param slot The slot, which must be between 0 and 8 (or this method will do nothing)
|
||||
* @param poof Ingredient to register
|
||||
*/
|
||||
public void setOutput(int slot, @NotNull ProvidedUIFilter poof) {
|
||||
if (slot == 0) { setOutputItem(poof); } else if (slot == 1) { setOutputIngot(poof); }
|
||||
}
|
||||
@Nullable
|
||||
@Override public ProvidedUIFilter getOutput(int slot) {
|
||||
if (slot == 0) { return getOutputItem(); } else if (slot == 1) { return getOutputIngot(); }
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editInput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
// Just edit bro
|
||||
setInput(slot, input);
|
||||
|
||||
// Save
|
||||
section.set(RecipeMakerGUI.INPUT_INGREDIENTS, toYML(getInputItem(), getInputIngot()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editOutput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot) {
|
||||
|
||||
// Just edit bro
|
||||
setOutput(slot, input);
|
||||
|
||||
// Save
|
||||
section.set(RecipeMakerGUI.OUTPUT_INGREDIENTS, toYML(getOutputItem(), getOutputIngot()));
|
||||
}
|
||||
|
||||
@Override public void deleteInput(@NotNull ConfigurationSection section, int slot) { editInput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
|
||||
@Override public void deleteOutput(@NotNull ConfigurationSection section, int slot) { editOutput(section, RecipeMakerGUI.AIR.clone(), slot); }
|
||||
|
||||
//region Updater, to update old recipes
|
||||
/**
|
||||
* No matter what input, the output will always be three Provided UIFilters
|
||||
* separated by bars, as expected in the current system, filling with AIR
|
||||
* where necessary.
|
||||
*
|
||||
* @param curr Current string
|
||||
*
|
||||
* @return A row in correct format
|
||||
*/
|
||||
@NotNull public static String updateIngredients(@Nullable String curr) {
|
||||
if (curr == null || curr.isEmpty()) { return emptyIngredients;}
|
||||
|
||||
// Bars used? I guess we can check that its written correctly
|
||||
if (curr.contains("|")) {
|
||||
|
||||
// Split by bars
|
||||
String[] curSplit = curr.split("\\|");
|
||||
|
||||
// Correct length?
|
||||
if (curSplit.length == 2) {
|
||||
|
||||
// Assumed to be updated.
|
||||
return curr;
|
||||
|
||||
} else {
|
||||
|
||||
// Make sure it is of size three
|
||||
StringBuilder ret = new StringBuilder();
|
||||
|
||||
// Must append three
|
||||
for (int r = 0; r < 2; r++) {
|
||||
|
||||
// Append a bar after the first
|
||||
if (r != 0) { ret.append("|"); }
|
||||
|
||||
// Array has it?
|
||||
if (r < curSplit.length) { ret.append(RecipeMakerGUI.poofFromLegacy(curSplit[r])); } else { ret.append("v AIR -"); }
|
||||
}
|
||||
|
||||
// Build and return
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
// Not bars, but spaces, might be old format
|
||||
} else if (curr.contains(" ")) {
|
||||
|
||||
// Make string builder
|
||||
StringBuilder ret = new StringBuilder();
|
||||
String[] curSplit = curr.split(" ");
|
||||
|
||||
// Must append three
|
||||
for (int r = 0; r < 2; r++) {
|
||||
|
||||
// Append a bar after the first
|
||||
if (r != 0) { ret.append("|"); }
|
||||
|
||||
// Array has it?
|
||||
if (r < curSplit.length) { ret.append(RecipeMakerGUI.poofFromLegacy(curSplit[r])); } else { ret.append("v AIR -"); }
|
||||
}
|
||||
|
||||
// Build and return
|
||||
return ret.toString();
|
||||
|
||||
// No spaces nor bars, this will just be the first ingredient of the row I guess
|
||||
} else {
|
||||
|
||||
// Just that i guess
|
||||
return RecipeMakerGUI.poofFromLegacy(curr) + "|v AIR 0";
|
||||
}
|
||||
}
|
||||
public static final String emptyIngredients = "v AIR -|v AIR -";
|
||||
//endregion
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.interpreters;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* When the user inputs an ingredient, after clicking the target slot number,
|
||||
* it is the job of the Recipe Interpreter to edit the ConfigurationSection.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public interface RMG_RecipeInterpreter {
|
||||
|
||||
/**
|
||||
* Edits the configuration section's INPUT list.
|
||||
*
|
||||
* @param section <b>The 'crafting' section - [ID].base.crafting</b>
|
||||
* <br><br>
|
||||
* Note that this is not editing the recipe type nor name itself.
|
||||
* It is up to the interpreter to create the sections if missing
|
||||
* or edit them if they are already there.
|
||||
* <br><br>
|
||||
* Ex: [ID].base.crafting.shaped.1.input
|
||||
*
|
||||
* @param input The user's input, item that will be required
|
||||
*
|
||||
* @param slot Slot that the item is going into
|
||||
*/
|
||||
void editInput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot);
|
||||
|
||||
/**
|
||||
* Edits the configuration section's OUTPUT list.
|
||||
*
|
||||
* @param section <b>The 'crafting' section - [ID].base.crafting</b>
|
||||
* <br><br>
|
||||
* Note that this is not editing the recipe type nor name itself.
|
||||
* It is up to the interpreter to create the sections if missing
|
||||
* or edit them if they are already there.
|
||||
* <br><br>
|
||||
* Ex: [ID].base.crafting.shaped.1.output
|
||||
*
|
||||
* @param input The user's input, item that will be required
|
||||
*
|
||||
* @param slot Slot that the item is going into
|
||||
*/
|
||||
void editOutput(@NotNull ConfigurationSection section, @NotNull ProvidedUIFilter input, int slot);
|
||||
|
||||
/**
|
||||
* Edits the configuration section's INPUT list.
|
||||
*
|
||||
* @param section <b>The 'crafting' section - [ID].base.crafting</b>
|
||||
* <br><br>
|
||||
* Note that this is not editing the recipe type nor name itself.
|
||||
* It is up to the interpreter to create the sections if missing
|
||||
* or edit them if they are already there.
|
||||
* <br><br>
|
||||
* Ex: [ID].base.crafting.shaped.1.input
|
||||
*
|
||||
* @param slot Slot that is getting reset
|
||||
*/
|
||||
void deleteInput(@NotNull ConfigurationSection section, int slot);
|
||||
|
||||
/**
|
||||
* Edits the configuration section's OUTPUT list.
|
||||
*
|
||||
* @param section <b>The 'crafting' section - [ID].base.crafting</b>
|
||||
* <br><br>
|
||||
* Note that this is not editing the recipe type nor name itself.
|
||||
* It is up to the interpreter to create the sections if missing
|
||||
* or edit them if they are already there.
|
||||
* <br><br>
|
||||
* Ex: [ID].base.crafting.shaped.1.output
|
||||
*
|
||||
* @param slot Slot that is getting reset
|
||||
*/
|
||||
void deleteOutput(@NotNull ConfigurationSection section, int slot);
|
||||
|
||||
/**
|
||||
* Fetch the Provided UI Filter in the YML configuration
|
||||
* that corresponds to this slot of the input.
|
||||
*
|
||||
* @param slot Slot
|
||||
*
|
||||
* @return Identified filter, if found and valid.
|
||||
*/
|
||||
@Nullable ProvidedUIFilter getInput(int slot);
|
||||
|
||||
/**
|
||||
* Fetch the Provided UI Filter in the YML configuration
|
||||
* that corresponds to this slot of the output.
|
||||
*
|
||||
* @param slot Slot
|
||||
*
|
||||
* @return Identified filter, if found and valid.
|
||||
*/
|
||||
@Nullable ProvidedUIFilter getOutput(int slot);
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import io.lumine.mythic.utils.version.ServerVersion;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.api.edition.StatEdition;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BannerMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The user will specify the output amount of a recipe on
|
||||
* a per-recipe basis, and will use this button for that.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_AmountOutput extends RecipeButtonAction {
|
||||
|
||||
/**
|
||||
* The button that displays how much output this recipe will produce.
|
||||
*
|
||||
* @param inv Inventory this button is part of
|
||||
* @param resultItem Output item of this recipe
|
||||
*/
|
||||
public RBA_AmountOutput(@NotNull RecipeMakerGUI inv, @NotNull ItemStack resultItem) {
|
||||
super(inv);
|
||||
|
||||
// Get item
|
||||
button = RecipeMakerGUI.rename(ItemFactory.of(resultItem.getType()).lore(SilentNumbers.chop(
|
||||
"The amount of items produced every time the player crafts."
|
||||
, 65, "\u00a77")).build(), "\u00a7cChoose Output Amount");
|
||||
|
||||
|
||||
// Update CMD ~ The stupid warning for 'ItemMEta might be false' is so annoying bruh
|
||||
@NotNull ItemMeta buttonMeta = Objects.requireNonNull(button.getItemMeta());
|
||||
@NotNull ItemMeta resultMeta = Objects.requireNonNull(resultItem.getItemMeta());
|
||||
|
||||
if (ServerVersion.get().getMinor() >= 14 && buttonMeta.hasCustomModelData()) { buttonMeta.setCustomModelData(resultMeta.getCustomModelData()); }
|
||||
if (resultMeta instanceof LeatherArmorMeta) { ((LeatherArmorMeta) buttonMeta).setColor(((LeatherArmorMeta) resultMeta).getColor()); }
|
||||
if (resultMeta instanceof BannerMeta) { ((BannerMeta) buttonMeta).setPatterns(((BannerMeta) resultMeta).getPatterns()); }
|
||||
buttonMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_DYE, ItemFlag.HIDE_POTION_EFFECTS);
|
||||
button.setItemMeta(buttonMeta);
|
||||
}
|
||||
|
||||
@NotNull public final String[] amountLog = {
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "Write in the chat the amount of output of this recipe."),
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "It must be an integer number, ex $e4$b.")};
|
||||
|
||||
/**
|
||||
* When the player clicks the display item, it means they want to change
|
||||
* the amount output of this recipe. Thus, they are queried to write
|
||||
* an integer number in the chat.
|
||||
*
|
||||
* @return If the player clicked the display item
|
||||
*/
|
||||
@Override public boolean runPrimary() {
|
||||
|
||||
// Query user for input
|
||||
new StatEdition(inv, ItemStats.CRAFTING, RecipeMakerGUI.PRIMARY, this).enable(amountLog);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* The player has written a number that will be set as the output amount of this recipe.
|
||||
* <br>
|
||||
* The amount is saved in YML path {@code [ID].crafting.[recipe].[name].amount}
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException If the player did not write an integer number
|
||||
*/
|
||||
@Override public void primaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException {
|
||||
|
||||
// Parse
|
||||
Integer val = SilentNumbers.IntegerParse(message);
|
||||
if (val == null) { throw new IllegalArgumentException("Expected an integer number instead of $u" + message); }
|
||||
if (val > 64) { throw new IllegalArgumentException("Max stack size is $e64$b, Minecraft doesnt support $u" + message); }
|
||||
if (val <= 0) { throw new IllegalArgumentException("Min output stack size is $e0$b, you specified $u" + message); }
|
||||
|
||||
// Set value
|
||||
getInv().getNameSection().set(AMOUNT_INGREDIENTS, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player left-clicks the display item, the behaviour is just reset
|
||||
* the amount to 1 output. No need to query the user.
|
||||
*
|
||||
* @return If the player clicked the display item
|
||||
*/
|
||||
@Override public boolean runSecondary() {
|
||||
|
||||
// Set value
|
||||
getInv().getNameSection().set(AMOUNT_INGREDIENTS, null);
|
||||
clickSFX();
|
||||
|
||||
/*
|
||||
* Register template edition. This is only done automatically
|
||||
* on the input process methods, not on the run button ones.
|
||||
*/
|
||||
inv.registerTemplateEdition();
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Straight from the file, the amount output of this recipe.
|
||||
*/
|
||||
public int getOutputAmount() { return getInv().getNameSection().getInt(AMOUNT_INGREDIENTS, 1); }
|
||||
|
||||
/**
|
||||
* The user needs to input nothing; Thus this method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void secondaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
|
||||
@NotNull final ItemStack button;
|
||||
|
||||
@Override
|
||||
@NotNull public ItemStack getButton() {
|
||||
|
||||
// Dupe and change amount
|
||||
ItemStack ret = button.clone();
|
||||
ret.setAmount(getOutputAmount());
|
||||
|
||||
// That's it
|
||||
return RecipeMakerGUI.addLore(ret, SilentNumbers.toArrayList( "",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Right click to reset to 1.",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Left click to edit amount." ));
|
||||
}
|
||||
|
||||
public static final String AMOUNT_INGREDIENTS = "amount";
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.QuickNumberRange;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.type.RBA_DoubleButton;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RBA_CookingTime extends RBA_DoubleButton {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_CookingTime(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
public static final String FURNACE_TIME = "time";
|
||||
@NotNull @Override public String getDoubleConfigPath() { return FURNACE_TIME; }
|
||||
|
||||
@Nullable @Override public QuickNumberRange getRange() { return new QuickNumberRange(0D, null); }
|
||||
|
||||
@Override public boolean requireInteger() { return true; }
|
||||
|
||||
public static final double DEFAULT = 200;
|
||||
@Override public double getDefaultValue() { return DEFAULT; }
|
||||
|
||||
@NotNull final ItemStack doubleButton = RecipeMakerGUI.addLore(ItemFactory.of(Material.CLOCK).name("\u00a7cDuration").lore(SilentNumbers.chop(
|
||||
"How long it takes this recipe to finish 'cooking' x)"
|
||||
, 65, "\u00a77")).build(), SilentNumbers.toArrayList(""));
|
||||
@NotNull @Override public ItemStack getDoubleButton() { return doubleButton; }
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.type.RBA_BooleanButton;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Will the extra gems (that didn't fit in the other item)
|
||||
* drop to the ground (as opposed to being lost)?
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_DropGems extends RBA_BooleanButton {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_DropGems(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
public static final String SMITH_GEMS = "drop-gems";
|
||||
@NotNull @Override public String getBooleanConfigPath() { return SMITH_GEMS; }
|
||||
|
||||
@NotNull final ItemStack booleanButton = RecipeMakerGUI.addLore(ItemFactory.of(Material.EMERALD).name("\u00a7aDrop Gemstones").lore(SilentNumbers.chop(
|
||||
"Usually, gemstones that dont fit the new item are lost. Enable this to make them drop (and be recovered) instead."
|
||||
, 65, "\u00a77")).build(), SilentNumbers.toArrayList(""));
|
||||
@NotNull @Override public ItemStack getBooleanButton() { return booleanButton; }
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.QuickNumberRange;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.type.RBA_DoubleButton;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Experience from furnace recipes and stuff
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_Experience extends RBA_DoubleButton {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_Experience(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
public static final String FURNACE_EXPERIENCE = "exp";
|
||||
@NotNull @Override public String getDoubleConfigPath() { return FURNACE_EXPERIENCE; }
|
||||
|
||||
@Nullable @Override public QuickNumberRange getRange() { return new QuickNumberRange(0D, null); }
|
||||
|
||||
@Override public boolean requireInteger() { return false; }
|
||||
|
||||
public static final double DEFAULT = 0.35;
|
||||
@Override public double getDefaultValue() { return DEFAULT; }
|
||||
|
||||
@NotNull final ItemStack doubleButton = RecipeMakerGUI.addLore(ItemFactory.of(Material.EXPERIENCE_BOTTLE).name("\u00a7aExperience").lore(SilentNumbers.chop(
|
||||
"This recipe gives experience when crafted, how much?"
|
||||
, 65, "\u00a77")).build(), SilentNumbers.toArrayList(""));
|
||||
@NotNull @Override public ItemStack getDoubleButton() { return doubleButton; }
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.type.RBA_BooleanButton;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.KnowledgeBookMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Prevents the recipe from automatically being sent to players
|
||||
* to see freely in the recipe book.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_HideFromBook extends RBA_BooleanButton {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_HideFromBook(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
public static final String BOOK_HIDDEN = "hidden";
|
||||
@NotNull @Override public String getBooleanConfigPath() { return BOOK_HIDDEN; }
|
||||
|
||||
@Override public boolean runSecondary() {
|
||||
|
||||
// Set value
|
||||
ItemStack book = new ItemStack(Material.KNOWLEDGE_BOOK);
|
||||
ItemMeta iMeta = book.getItemMeta();
|
||||
|
||||
// Edit meta
|
||||
if (iMeta instanceof KnowledgeBookMeta) {
|
||||
|
||||
// Add recipe
|
||||
((KnowledgeBookMeta) iMeta).addRecipe(MMOItems.plugin.getRecipes().getRecipeKey(
|
||||
getInv().getEdited().getType(),
|
||||
getInv().getEdited().getId(),
|
||||
getInv().getRecipeRegistry().getRecipeConfigPath(),
|
||||
getInv().getRecipeName()));
|
||||
}
|
||||
|
||||
// Set meta
|
||||
book.setItemMeta(iMeta);
|
||||
|
||||
// Give it to the player
|
||||
getInv().getPlayer().getInventory().addItem(book);
|
||||
getInv().getPlayer().playSound(getInv().getPlayer().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 2);
|
||||
|
||||
// Done
|
||||
return true; }
|
||||
|
||||
@NotNull final ItemStack booleanButton = RecipeMakerGUI.addLore(ItemFactory.of(Material.KNOWLEDGE_BOOK).name("\u00a7cHide from Crafting Book").lore(SilentNumbers.chop(
|
||||
"Even if the crafting book is enabled, this recipe wont be automatically unlocked by players."
|
||||
, 65, "\u00a77")).build(), SilentNumbers.toArrayList(""));
|
||||
|
||||
@NotNull @Override public ItemStack getBooleanButton() { return booleanButton; }
|
||||
|
||||
@NotNull @Override public ItemStack getButton() {
|
||||
|
||||
// Dictate the correct one
|
||||
String input = isEnabled() ? "\u00a7cNO\u00a78, it's hidden." : "\u00a7aYES\u00a78, it's shown.";
|
||||
|
||||
// Copy and send
|
||||
return RecipeMakerGUI.addLore(getBooleanButton().clone(),
|
||||
SilentNumbers.toArrayList(
|
||||
"", "\u00a77Currently in Book? " + input, "",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Right click to generate recipe unlock book.",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Left click to toggle this option." ));
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Button to switch between Input and Output modes of the station.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_InputOutput extends RecipeButtonAction {
|
||||
|
||||
boolean showingInput;
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_InputOutput(@NotNull RecipeMakerGUI inv) {
|
||||
super(inv);
|
||||
|
||||
// By default, input is shown.
|
||||
showingInput = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runPrimary() {
|
||||
getInv().switchInput();
|
||||
getInv().refreshInventory();
|
||||
clickSFX();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void primaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
|
||||
/**
|
||||
* Nothing happens
|
||||
*/
|
||||
@Override public boolean runSecondary() { return false; }
|
||||
|
||||
|
||||
/**
|
||||
* This method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void secondaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
|
||||
@NotNull final ItemStack button = RecipeMakerGUI.addLore(ItemFactory.of(Material.CRAFTING_TABLE).name("\u00a7cSwitch to Output Mode").lore(SilentNumbers.chop(
|
||||
"INPUT is the ingredients of the recipe, but (like milk buckets when crafting a cake) these ingredients may not be entirely consumed. In such cases, use the OUTPUT mode to specify what the ingredients will turn into."
|
||||
, 63, "\u00a77")).build(), SilentNumbers.toArrayList(""));
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack getButton() {
|
||||
|
||||
// Dictate the correct one
|
||||
String input = getInv().isShowingInput() ? "\u00a76INPUT" : "\u00a73OUTPUT";
|
||||
|
||||
// Copy and send
|
||||
return RecipeMakerGUI.addLore(button.clone(), SilentNumbers.toArrayList("\u00a77Currently Showing: " + input, "",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Left click to switch mode." ));
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.api.crafting.recipe.SmithingCombinationType;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.type.RBA_ChooseableButton;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Which behaviour do Enchantments follow when the player smiths items?
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_SmithingEnchantments extends RBA_ChooseableButton {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_SmithingEnchantments(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
@NotNull final ItemStack chooseableButton = ItemFactory.of(Material.ENCHANTING_TABLE).name("\u00a7aEnchantment Transfer").lore(SilentNumbers.chop(
|
||||
"What will happen to the enchantments of the ingredients? Will enchanted ingredients produce an enchanted output item?"
|
||||
, 65, "\u00a77")).build();
|
||||
|
||||
@NotNull @Override public ItemStack getChooseableButton() { return chooseableButton; }
|
||||
|
||||
public static final String SMITH_ENCHANTS = "enchantments";
|
||||
@NotNull @Override public String getChooseableConfigPath() { return SMITH_ENCHANTS; }
|
||||
@NotNull @Override public ArrayList<String> getChooseableList() { return RBA_SmithingUpgrades.getSmithingList(); }
|
||||
@NotNull @Override public String getDefaultValue() { return SmithingCombinationType.MAXIMUM.toString(); }
|
||||
@NotNull @Override public String getChooseableDefinition(@NotNull String ofChooseable) {
|
||||
SmithingCombinationType sct = SmithingCombinationType.MAXIMUM;
|
||||
try { sct = SmithingCombinationType.valueOf(getCurrentChooseableValue()); } catch (IllegalArgumentException ignored) {}
|
||||
|
||||
switch (sct) {
|
||||
case EVEN:
|
||||
return "For each enchantment, will take the average of that enchantment's level across the ingredients.";
|
||||
case NONE:
|
||||
return "Will ignore the enchantments of any ingredients.";
|
||||
case MAXIMUM:
|
||||
return "Output will have the best enchantment from each ingredient";
|
||||
case MINIMUM:
|
||||
return "Output will have worst enchantment from each ingredient with that enchantment.";
|
||||
case ADDITIVE:
|
||||
return "The enchantments of all ingredients will add together.";
|
||||
|
||||
default: return "Unknown behaviour. Add description in net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_SmithingEnchantments";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.api.crafting.recipe.SmithingCombinationType;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.type.RBA_ChooseableButton;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Which behaviour do Upgrades follow when the player smiths items?
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RBA_SmithingUpgrades extends RBA_ChooseableButton {
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_SmithingUpgrades(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
@NotNull final ItemStack chooseableButton = ItemFactory.of(Material.ANVIL).name("\u00a7aUpgrades Transfer").lore(SilentNumbers.chop(
|
||||
"What will happen to the upgrades of the ingredients? Will upgraded ingredients produce an upgraded output item?"
|
||||
, 65, "\u00a77")).build();
|
||||
|
||||
@NotNull @Override public ItemStack getChooseableButton() { return chooseableButton; }
|
||||
|
||||
public static final String SMITH_UPGRADES = "upgrades";
|
||||
@NotNull @Override public String getChooseableConfigPath() { return SMITH_UPGRADES; }
|
||||
@NotNull @Override public ArrayList<String> getChooseableList() { return getSmithingList(); }
|
||||
@NotNull @Override public String getDefaultValue() { return SmithingCombinationType.MAXIMUM.toString(); }
|
||||
@NotNull @Override public String getChooseableDefinition(@NotNull String ofChooseable) {
|
||||
SmithingCombinationType sct = SmithingCombinationType.MAXIMUM;
|
||||
try { sct = SmithingCombinationType.valueOf(getCurrentChooseableValue()); } catch (IllegalArgumentException ignored) {}
|
||||
|
||||
switch (sct) {
|
||||
case EVEN:
|
||||
return "Will take the average of the upgrade levels of the combined items.";
|
||||
case NONE:
|
||||
return "Will ignore the upgrade levels of any ingredients.";
|
||||
case MAXIMUM:
|
||||
return "Output will have the upgrade level of the most upgraded ingredient.";
|
||||
case MINIMUM:
|
||||
return "Output will have the upgrade level of the least-upgraded upgradeable ingredient.";
|
||||
case ADDITIVE:
|
||||
return "The upgrade levels of the ingredients will be added, and the result will be the crafted item's level.";
|
||||
|
||||
default: return "Unknown behaviour. Add description in net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_SmithingUpgrades";
|
||||
}
|
||||
}
|
||||
|
||||
static ArrayList<String> smithingList;
|
||||
/**
|
||||
* @return The allowed values of the smithing combination type list
|
||||
*/
|
||||
@NotNull static ArrayList<String> getSmithingList() {
|
||||
if (smithingList != null) { return smithingList; }
|
||||
smithingList = new ArrayList<>();
|
||||
for (SmithingCombinationType sct : SmithingCombinationType.values()) { smithingList.add(sct.toString()); }
|
||||
return smithingList; }
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba;
|
||||
|
||||
import net.Indyuce.mmoitems.api.edition.StatEdition;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import net.Indyuce.mmoitems.stat.type.ItemStat;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* When the user clicks the Edition Inventory of a Recipe Maker,
|
||||
* it might be a special button specific to a recipe. In such a
|
||||
* case, RMGs may register all their buttons as RBAs, and perform
|
||||
* operations with low maintenance.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public abstract class RecipeButtonAction {
|
||||
|
||||
/**
|
||||
* The edition inventory this is a button of
|
||||
*/
|
||||
@NotNull final RecipeMakerGUI inv;
|
||||
/**
|
||||
* @return The edition inventory this is a button of
|
||||
*/
|
||||
@NotNull public RecipeMakerGUI getInv() { return inv; }
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RecipeButtonAction(@NotNull RecipeMakerGUI inv) { this.inv = inv; }
|
||||
|
||||
/**
|
||||
* Called when the player left-clicks a slot. <br>
|
||||
* <b>Important: When initializing a {@link StatEdition#StatEdition(EditionInventory, ItemStat, Object...)} you
|
||||
* must pass {@link RecipeMakerGUI#PRIMARY} as the first <i>info</i> object!</b> Also, make sure to pass {@code this}
|
||||
* as the second argument for {@link #primaryProcessInput(String, Object...)} to be called.
|
||||
*
|
||||
* @return <code>true</code> if and only if this action succeeded. Most importantly,
|
||||
* indicates that the absolute slot the user clicked corresponds to this
|
||||
* button being clicked.
|
||||
*/
|
||||
public abstract boolean runPrimary();
|
||||
/**
|
||||
* Run the function performed by this button, based on the user's input.
|
||||
*
|
||||
* This will be called when {@link #runPrimary()} (int, EditionInventory)} succeeds and calls
|
||||
* {@link StatEdition#StatEdition(EditionInventory, ItemStat, Object...)}
|
||||
* to query the user for input.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException If anything goes wrong.
|
||||
*/
|
||||
public abstract void primaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Called when the player right-clicks a slot. <br>
|
||||
* <b>Important: When initializing a {@link StatEdition#StatEdition(EditionInventory, ItemStat, Object...)} you
|
||||
* must pass {@link RecipeMakerGUI#SECONDARY} as the first <i>info</i> object!</b> Also, make sure to pass {@code this}
|
||||
* as the second argument for {@link #secondaryProcessInput(String, Object...)} to be called.
|
||||
*
|
||||
* @return <code>true</code> if and only if this action succeeded. Most importantly,
|
||||
* indicates that the absolute slot the user clicked corresponds to this
|
||||
* button being clicked.
|
||||
*/
|
||||
public abstract boolean runSecondary();
|
||||
/**
|
||||
* Run the function performed by this button, based on the user's input.
|
||||
*
|
||||
* This will be called when {@link #runSecondary()} succeeds and calls
|
||||
* {@link StatEdition#StatEdition(EditionInventory, ItemStat, Object...)}
|
||||
* to query the user for input.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException If anything goes wrong.
|
||||
*/
|
||||
public abstract void secondaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @return The ItemStack that will act as the button in the GUI, to activate this class.
|
||||
*/
|
||||
@NotNull public abstract ItemStack getButton();
|
||||
|
||||
/**
|
||||
* Plays a clicking sound
|
||||
*/
|
||||
public void clickSFX() { getInv().getPlayer().playSound(getInv().getPlayer().getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); }
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba.type;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RecipeButtonAction;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A button that toggles between true and false.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public abstract class RBA_BooleanButton extends RecipeButtonAction {
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_BooleanButton(@NotNull RecipeMakerGUI inv) {
|
||||
super(inv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Straight from the file, if this option is set to TRUE.
|
||||
*/
|
||||
public boolean isEnabled() { return getInv().getNameSection().getBoolean(getBooleanConfigPath(), false); }
|
||||
/**
|
||||
* @return The path to save this value in the config
|
||||
*/
|
||||
@NotNull public abstract String getBooleanConfigPath();
|
||||
|
||||
@Override public boolean runPrimary() {
|
||||
|
||||
// Flip value
|
||||
getInv().getNameSection().set(getBooleanConfigPath(), !isEnabled());
|
||||
clickSFX();
|
||||
|
||||
/*
|
||||
* Register template edition. This is only done automatically
|
||||
* on the input process methods, not on the run button ones.
|
||||
*/
|
||||
getInv().registerTemplateEdition();
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* The user needs to input nothing; Thus this method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void primaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
|
||||
@Override public boolean runSecondary() {
|
||||
|
||||
// Remove value
|
||||
getInv().getNameSection().set(getBooleanConfigPath(), null);
|
||||
clickSFX();
|
||||
|
||||
/*
|
||||
* Register template edition. This is only done automatically
|
||||
* on the input process methods, not on the run button ones.
|
||||
*/
|
||||
getInv().registerTemplateEdition();
|
||||
|
||||
// Done
|
||||
return true; }
|
||||
/**
|
||||
* The user needs to input nothing; Thus this method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void secondaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
|
||||
/**
|
||||
* @return The button ItemStack with its name and description. To
|
||||
* it, all the chooseable values will be appended (as well
|
||||
* as the definition of the current value chosen) when asked
|
||||
* for in {@link #getButton()}
|
||||
*/
|
||||
@NotNull public abstract ItemStack getBooleanButton();
|
||||
/**
|
||||
* @return Same as {@link #getBooleanButton()} but with
|
||||
* the current value information appended to it.
|
||||
*/
|
||||
@NotNull @Override public ItemStack getButton() {
|
||||
// Dictate the correct one
|
||||
String input = isEnabled() ? "\u00a7aTRUE" : "\u00a7cFALSE";
|
||||
|
||||
// Copy and send
|
||||
return RecipeMakerGUI.addLore(getBooleanButton().clone(),
|
||||
SilentNumbers.toArrayList(
|
||||
"", "\u00a77Current Value: " + input, "",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Right click to reset \u00a78(to\u00a74 FALSE\u00a78)\u00a7e.",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Left click to toggle this option." ));
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba.type;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RecipeButtonAction;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A button that cycles among a list of options, rather
|
||||
* than just a number or TRUE/FALSE.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public abstract class RBA_ChooseableButton extends RecipeButtonAction {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_ChooseableButton(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
/**
|
||||
* Cycles to the next value
|
||||
*
|
||||
* @return True
|
||||
*/
|
||||
@Override public boolean runPrimary() {
|
||||
|
||||
// Get current
|
||||
String current = getCurrentChooseableValue();
|
||||
|
||||
// Included?
|
||||
int currentIndex = getChooseableList().indexOf(current);
|
||||
|
||||
// Invalid value? Cancel and default
|
||||
if (currentIndex == -1) { return runSecondary(); }
|
||||
|
||||
// Increase and Cap
|
||||
currentIndex++;
|
||||
if (currentIndex >= getChooseableList().size()) { currentIndex = 0; }
|
||||
|
||||
// Get
|
||||
String next = getChooseableList().get(currentIndex);
|
||||
|
||||
// Edits into persistent files
|
||||
getInv().getNameSection().set(getChooseableConfigPath(), next);
|
||||
clickSFX();
|
||||
|
||||
// Save
|
||||
getInv().registerTemplateEdition();
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Resets the list to the default value.
|
||||
*
|
||||
* @return True
|
||||
*/
|
||||
@Override public boolean runSecondary() {
|
||||
// Clear the saved value
|
||||
getInv().getNameSection().set(getChooseableConfigPath(), null);
|
||||
clickSFX();
|
||||
|
||||
// Save
|
||||
getInv().registerTemplateEdition();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The button ItemStack with its name and description. To
|
||||
* it, all the chooseable values will be appended (as well
|
||||
* as the definition of the current value chosen) when asked
|
||||
* for in {@link #getButton()}
|
||||
*/
|
||||
@NotNull public abstract ItemStack getChooseableButton();
|
||||
/**
|
||||
* @return Same as {@link #getChooseableButton()} but with
|
||||
* the chooseable information appended to it.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemStack getButton() {
|
||||
|
||||
// Whats the current?
|
||||
String current = getCurrentChooseableValue();
|
||||
|
||||
// Build lore to add: Current value and definition
|
||||
ArrayList<String> addedDefinitions = new ArrayList<>();
|
||||
addedDefinitions.add("");
|
||||
addedDefinitions.add("\u00a77Current Value:\u00a73 " + current);
|
||||
addedDefinitions.addAll(SilentNumbers.chop(getChooseableDefinition(current), 50, " \u00a7b\u00a7o"));
|
||||
addedDefinitions.add("");
|
||||
addedDefinitions.add(ChatColor.YELLOW + AltChar.listDash + " Right click to return to default value.");
|
||||
addedDefinitions.add(ChatColor.YELLOW + AltChar.listDash + " Left click to cycle through the options:");
|
||||
for (String str : getChooseableList()) {
|
||||
|
||||
// Is it the one?
|
||||
String pick = ChatColor.GOLD.toString();
|
||||
if (str.equals(current)) { pick = ChatColor.RED.toString() + ChatColor.BOLD;}
|
||||
|
||||
addedDefinitions.add(pick + " " + AltChar.smallListDash + " \u00a77" + str); }
|
||||
|
||||
// Clone button and add the lore
|
||||
return RecipeMakerGUI.addLore(getChooseableButton().clone(), addedDefinitions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path to save this value in the config
|
||||
*/
|
||||
@NotNull public abstract String getChooseableConfigPath();
|
||||
|
||||
/**
|
||||
* @return The list of different options the player may choose from.
|
||||
*/
|
||||
@NotNull public abstract ArrayList<String> getChooseableList();
|
||||
/**
|
||||
* @return The value currently written onto the files.
|
||||
*/
|
||||
@NotNull public String getCurrentChooseableValue() {
|
||||
|
||||
// Get or default
|
||||
String ret = getInv().getNameSection().getString(getChooseableConfigPath());
|
||||
return ret != null ? ret : getDefaultValue();
|
||||
}
|
||||
/**
|
||||
* @return Of al the entries in {@link #getChooseableList()}, which
|
||||
* is the default / initial one?
|
||||
*/
|
||||
@NotNull public abstract String getDefaultValue();
|
||||
/**
|
||||
* @return Definition of what this choosing type does, for display in lore.
|
||||
*
|
||||
* @param ofChooseable Entry contained in the {@link #getChooseableList()} list.
|
||||
*/
|
||||
@NotNull public abstract String getChooseableDefinition(@NotNull String ofChooseable);
|
||||
|
||||
|
||||
/**
|
||||
* This method doesnt run
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void secondaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
/**
|
||||
* This method doesnt run
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void primaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.rba.type;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
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.edition.StatEdition;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RecipeButtonAction;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A button that stores a numeric value yes.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public abstract class RBA_DoubleButton extends RecipeButtonAction {
|
||||
|
||||
/**
|
||||
* A button of an Edition Inventory. Nice!
|
||||
*
|
||||
* @param inv The edition inventory this is a button of
|
||||
*/
|
||||
public RBA_DoubleButton(@NotNull RecipeMakerGUI inv) { super(inv); }
|
||||
|
||||
/**
|
||||
* @return Straight from the file, if this option is set to TRUE.
|
||||
*/
|
||||
public double getValue() { return getInv().getNameSection().getDouble(getDoubleConfigPath(), getDefaultValue()); }
|
||||
/**
|
||||
* @return The path to save this value in the config
|
||||
*/
|
||||
@NotNull public abstract String getDoubleConfigPath();
|
||||
|
||||
@NotNull public final String[] amountLog = {
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "Write in the chat a number, ex $e2.5$b.")};
|
||||
|
||||
@NotNull public final String[] integerLog = {
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "Write in the chat an integer number, ex $e8$b.")};
|
||||
|
||||
@Override public boolean runPrimary() {
|
||||
|
||||
// Query user for input
|
||||
new StatEdition(getInv(), ItemStats.CRAFTING, RecipeMakerGUI.PRIMARY, this).enable(requireInteger() ? integerLog : amountLog);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user needs to input nothing; Thus this method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@Override public void primaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException {
|
||||
|
||||
Double number;
|
||||
if (requireInteger()) {
|
||||
|
||||
// Parse Integer
|
||||
Integer asInteger = SilentNumbers.IntegerParse(message);
|
||||
if (asInteger == null) { throw new IllegalArgumentException("Expected integer number instead of $u" + message); }
|
||||
|
||||
// ...
|
||||
number = Double.valueOf(asInteger);
|
||||
} else {
|
||||
|
||||
// Parse Double
|
||||
number = SilentNumbers.DoubleParse(message);
|
||||
if (number == null) { throw new IllegalArgumentException("Expected a number instead of $u" + message); }
|
||||
}
|
||||
|
||||
// Out of range?
|
||||
if (getRange() != null) {
|
||||
|
||||
// Out of range?
|
||||
if (!getRange().inRange(number)) {
|
||||
|
||||
throw new IllegalArgumentException("Number $r" + number + "$b is out of range. Expected " + getRange().toStringColored());
|
||||
} }
|
||||
|
||||
// Set value
|
||||
getInv().getNameSection().set(getDoubleConfigPath(), number);
|
||||
}
|
||||
|
||||
@Nullable public abstract QuickNumberRange getRange();
|
||||
public abstract boolean requireInteger();
|
||||
|
||||
@Override public boolean runSecondary() {
|
||||
|
||||
// Remove value
|
||||
getInv().getNameSection().set(getDoubleConfigPath(), null);
|
||||
clickSFX();
|
||||
|
||||
/*
|
||||
* Register template edition. This is only done automatically
|
||||
* on the input process methods, not on the run button ones.
|
||||
*/
|
||||
getInv().registerTemplateEdition();
|
||||
|
||||
// Done
|
||||
return true; }
|
||||
|
||||
/**
|
||||
* The user needs to input nothing; Thus this method never runs.
|
||||
*
|
||||
* @param message Input from the user
|
||||
* @param info Additional objects, specific to each case, provided.
|
||||
*
|
||||
* @throws IllegalArgumentException Never
|
||||
*/
|
||||
@SuppressWarnings("NoopMethodInAbstractClass")
|
||||
@Override public void secondaryProcessInput(@NotNull String message, Object... info) throws IllegalArgumentException { }
|
||||
|
||||
public abstract double getDefaultValue();
|
||||
|
||||
/**
|
||||
* @return The button ItemStack with its name and description. To
|
||||
* it, all the chooseable values will be appended (as well
|
||||
* as the definition of the current value chosen) when asked
|
||||
* for in {@link #getButton()}
|
||||
*/
|
||||
@NotNull public abstract ItemStack getDoubleButton();
|
||||
/**
|
||||
* @return Same as {@link #getDoubleButton()} but with
|
||||
* the current value information appended to it.
|
||||
*/
|
||||
@NotNull @Override public ItemStack getButton() {
|
||||
|
||||
// Copy and send
|
||||
return RecipeMakerGUI.addLore(getDoubleButton().clone(),
|
||||
SilentNumbers.toArrayList(
|
||||
"", "\u00a77Current Value: " + getValue(), "",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Right click to reset \u00a78(to\u00a74 " + getDefaultValue() + "\u00a78)\u00a7e.",
|
||||
ChatColor.YELLOW + AltChar.listDash + " Left click to toggle this option." ));
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.recipes;
|
||||
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_BurningLegacy;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_Shaped;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMG_RecipeInterpreter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_CookingTime;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_Experience;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_HideFromBook;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
/**
|
||||
* The legacy recipes that are not supported by MythicLib that all happen to have to do
|
||||
* with burning stuff - furnaces, campfires, the other furnaces...
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMG_BurningLegacy extends RecipeMakerGUI {
|
||||
|
||||
@NotNull
|
||||
HashMap<Integer, Integer> inputLinks = new HashMap<>();
|
||||
|
||||
/**
|
||||
* An editor for a Shaped Recipe. Because the recipe is loaded from the YML when this is created,
|
||||
* concurrent modifications of the same recipe are unsupported.
|
||||
*
|
||||
* @param player Player editing the recipe ig
|
||||
* @param template Template of which a recipe is being edited
|
||||
* @param recipeName Name of this recipe
|
||||
*/
|
||||
public RMG_BurningLegacy(@NotNull Player player, @NotNull MMOItemTemplate template, @NotNull String recipeName, @NotNull RecipeRegistry recipeRegistry) {
|
||||
super(player, template, recipeName, recipeRegistry);
|
||||
addButton(new RBA_HideFromBook(this));
|
||||
addButton(new RBA_Experience(this));
|
||||
addButton(new RBA_CookingTime(this));
|
||||
|
||||
// NO OUTPUT
|
||||
if (!isShowingInput()) { switchInput(); }
|
||||
|
||||
// Get section and build interpreter
|
||||
interpreter = new RMGRI_BurningLegacy(getNameSection());
|
||||
|
||||
// Bind inputs - Furnace only has which item to smelt
|
||||
inputLinks.put(40, 0);
|
||||
}
|
||||
|
||||
@Override public int getButtonsRow() { return 2; }
|
||||
|
||||
@Override
|
||||
public void putRecipe(@NotNull Inventory target) {
|
||||
|
||||
// Fill inputs
|
||||
for (Integer s : inputLinks.keySet()) { target.setItem(s, getDisplay(isShowingInput(), inputLinks.get(s))); }
|
||||
}
|
||||
|
||||
@Override
|
||||
int getInputSlot(int absolute) {
|
||||
|
||||
// Not an input? Not our business
|
||||
@Nullable Integer found = inputLinks.get(absolute);
|
||||
|
||||
// Found or negative
|
||||
return found != null ? found : -1;
|
||||
}
|
||||
|
||||
@NotNull final RMGRI_BurningLegacy interpreter;
|
||||
@NotNull @Override public RMG_RecipeInterpreter getInterpreter() { return interpreter; }
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.recipes;
|
||||
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_Shaped;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMG_RecipeInterpreter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_HideFromBook;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_InputOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Edits shaped recipes, very nice.
|
||||
* <br> <br> <code>
|
||||
* - - - - - - - - - <br>
|
||||
* 0 1 2 = 0 1 2 - - <br>
|
||||
* 3 4 5 = 3 4 5 - R <br>
|
||||
* 6 7 8 = 6 7 8 - - </code>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMG_Shaped extends RecipeMakerGUI {
|
||||
|
||||
@NotNull HashMap<Integer, Integer> inputLinks = new HashMap<>();
|
||||
|
||||
/**
|
||||
* An editor for a Shaped Recipe. Because the recipe is loaded from the YML when this is created,
|
||||
* concurrent modifications of the same recipe are unsupported.
|
||||
*
|
||||
* @param player Player editing the recipe ig
|
||||
* @param template Template of which a recipe is being edited
|
||||
* @param recipeName Name of this recipe
|
||||
*/
|
||||
public RMG_Shaped(@NotNull Player player, @NotNull MMOItemTemplate template, @NotNull String recipeName, @NotNull RecipeRegistry recipeRegistry) {
|
||||
super(player, template, recipeName, recipeRegistry);
|
||||
addButton(new RBA_InputOutput(this));
|
||||
addButton(new RBA_HideFromBook(this));
|
||||
|
||||
// Get section and build interpreter
|
||||
interpreter = new RMGRI_Shaped(getNameSection());
|
||||
|
||||
// Bind inputs
|
||||
inputLinks.put(30, 0);
|
||||
inputLinks.put(31, 1);
|
||||
inputLinks.put(32, 2);
|
||||
|
||||
inputLinks.put(39, 3);
|
||||
inputLinks.put(40, 4);
|
||||
inputLinks.put(41, 5);
|
||||
|
||||
inputLinks.put(48, 6);
|
||||
inputLinks.put(49, 7);
|
||||
inputLinks.put(50, 8);
|
||||
}
|
||||
|
||||
@Override public int getButtonsRow() { return 1; }
|
||||
|
||||
@Override
|
||||
public void putRecipe(@NotNull Inventory target) {
|
||||
|
||||
// Fill inputs
|
||||
for (Integer s : inputLinks.keySet()) { target.setItem(s, getDisplay(isShowingInput(), inputLinks.get(s))); }
|
||||
}
|
||||
|
||||
@Override
|
||||
int getInputSlot(int absolute) {
|
||||
|
||||
// Not an input? Not our business
|
||||
@Nullable Integer found = inputLinks.get(absolute);
|
||||
|
||||
// Found or negative
|
||||
return found != null ? found : -1;
|
||||
}
|
||||
|
||||
@NotNull final RMGRI_Shaped interpreter;
|
||||
@NotNull
|
||||
@Override
|
||||
public RMG_RecipeInterpreter getInterpreter() { return interpreter; }
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.recipes;
|
||||
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_Shapeless;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMG_RecipeInterpreter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_HideFromBook;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_InputOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Edits shapeless recipes, very nice.
|
||||
* <br> <br> <code>
|
||||
* - - - - - - - - - <br>
|
||||
* 0 1 2 = 0 1 2 - - <br>
|
||||
* 3 4 5 = 3 4 5 - R <br>
|
||||
* 6 7 8 = 6 7 8 - - </code>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMG_Shapeless extends RecipeMakerGUI {
|
||||
|
||||
@NotNull
|
||||
final HashMap<Integer, Integer> inputLinks = new HashMap<>();
|
||||
|
||||
/**
|
||||
* An editor for a Shapeless Recipe. Because the recipe is loaded from the YML when this is created,
|
||||
* concurrent modifications of the same recipe are unsupported.
|
||||
*
|
||||
* @param player Player editing the recipe ig
|
||||
* @param template Template of which a recipe is being edited
|
||||
* @param recipeName Name of this recipe
|
||||
*/
|
||||
public RMG_Shapeless(@NotNull Player player, @NotNull MMOItemTemplate template, @NotNull String recipeName, @NotNull RecipeRegistry recipeRegistry) {
|
||||
super(player, template, recipeName, recipeRegistry);
|
||||
addButton(new RBA_InputOutput(this));
|
||||
addButton(new RBA_HideFromBook(this));
|
||||
|
||||
// Get section and build interpreter
|
||||
ConfigurationSection crafting = RecipeMakerGUI.getSection(getEditedSection(), "crafting");
|
||||
ConfigurationSection recipe = RecipeMakerGUI.getSection(crafting, getRecipeRegistry().getRecipeConfigPath());
|
||||
ConfigurationSection name = RecipeMakerGUI.getSection(recipe, getRecipeName());
|
||||
interpreter = new RMGRI_Shapeless(name);
|
||||
|
||||
// Bind inputs
|
||||
inputLinks.put(30, 0);
|
||||
inputLinks.put(31, 1);
|
||||
inputLinks.put(32, 2);
|
||||
|
||||
inputLinks.put(39, 3);
|
||||
inputLinks.put(40, 4);
|
||||
inputLinks.put(41, 5);
|
||||
|
||||
inputLinks.put(48, 6);
|
||||
inputLinks.put(49, 7);
|
||||
inputLinks.put(50, 8);
|
||||
}
|
||||
@Override public int getButtonsRow() { return 1; }
|
||||
|
||||
@Override
|
||||
public void putRecipe(@NotNull Inventory target) {
|
||||
|
||||
// Fill inputs
|
||||
for (Integer s : inputLinks.keySet()) { target.setItem(s, getDisplay(isShowingInput(), inputLinks.get(s))); }
|
||||
}
|
||||
|
||||
@Override
|
||||
int getInputSlot(int absolute) {
|
||||
|
||||
// Not an input? Not our business
|
||||
@Nullable Integer found = inputLinks.get(absolute);
|
||||
|
||||
// Found or negative
|
||||
return found != null ? found : -1;
|
||||
}
|
||||
|
||||
@NotNull final RMGRI_Shapeless interpreter;
|
||||
@NotNull @Override public RMG_RecipeInterpreter getInterpreter() { return interpreter; }
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.recipes;
|
||||
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_Smithing;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMG_RecipeInterpreter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_DropGems;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_InputOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_SmithingEnchantments;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_SmithingUpgrades;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Edits smithing recipes, very nice.
|
||||
* <br> <br> <code>
|
||||
* - - - - - - - - - <br>
|
||||
* - - - = - - - - - <br>
|
||||
* 0 - 1 = 0 - 1 - R <br>
|
||||
* - - - = - - - - - </code>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RMG_Smithing extends RecipeMakerGUI {
|
||||
|
||||
@NotNull
|
||||
final HashMap<Integer, Integer> inputLinks = new HashMap<>();
|
||||
|
||||
/**
|
||||
* An editor for a Shaped Recipe. Because the recipe is loaded from the YML when this is created,
|
||||
* concurrent modifications of the same recipe are unsupported.
|
||||
*
|
||||
* @param player Player editing the recipe ig
|
||||
* @param template Template of which a recipe is being edited
|
||||
* @param recipeName Name of this recipe
|
||||
*/
|
||||
public RMG_Smithing(@NotNull Player player, @NotNull MMOItemTemplate template, @NotNull String recipeName, @NotNull RecipeRegistry recipeRegistry) {
|
||||
super(player, template, recipeName, recipeRegistry);
|
||||
|
||||
// Get section and build interpreter
|
||||
ConfigurationSection crafting = RecipeMakerGUI.getSection(getEditedSection(), "crafting");
|
||||
ConfigurationSection recipe = RecipeMakerGUI.getSection(crafting, getRecipeRegistry().getRecipeConfigPath());
|
||||
ConfigurationSection name = RecipeMakerGUI.getSection(recipe, getRecipeName());
|
||||
interpreter = new RMGRI_Smithing(name);
|
||||
|
||||
// Bind inputs
|
||||
inputLinks.put(39, 0);
|
||||
inputLinks.put(41, 1);
|
||||
|
||||
// Extra buttons
|
||||
addButton(new RBA_InputOutput(this));
|
||||
addButton(new RBA_SmithingUpgrades(this));
|
||||
addButton(new RBA_SmithingEnchantments(this));
|
||||
addButton(new RBA_DropGems(this));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void putRecipe(@NotNull Inventory target) {
|
||||
|
||||
// Fill inputs
|
||||
for (Integer s : inputLinks.keySet()) { target.setItem(s, getDisplay(isShowingInput(), inputLinks.get(s))); }
|
||||
}
|
||||
|
||||
@Override
|
||||
int getInputSlot(int absolute) {
|
||||
|
||||
// Not an input? Not our business
|
||||
@Nullable Integer found = inputLinks.get(absolute);
|
||||
|
||||
// Found or negative
|
||||
return found != null ? found : -1;
|
||||
}
|
||||
@Override public int getButtonsRow() { return 1; }
|
||||
|
||||
@NotNull final RMGRI_Smithing interpreter;
|
||||
@NotNull
|
||||
@Override
|
||||
public RMG_RecipeInterpreter getInterpreter() { return interpreter; }
|
||||
}
|
@ -0,0 +1,835 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.recipes;
|
||||
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.crafting.uifilters.VanillaUIFilter;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.UIFilterManager;
|
||||
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.QuickNumberRange;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.edition.StatEdition;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMG_RecipeInterpreter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_AmountOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_InputOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RecipeButtonAction;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Inventory displayed when the user edits one of the many
|
||||
* recipes associated to an item. It has many functions:
|
||||
* <br><br>
|
||||
* * Choose amount of output <br>
|
||||
* * Choose input (of course) <br>
|
||||
* * Specify output in the input slots <br>
|
||||
* * Preview the recipe <br>
|
||||
* * Reload the recipe <br>
|
||||
* * Make it auto-unlock in crafting book <br>
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class RecipeMakerGUI extends EditionInventory {
|
||||
|
||||
/**
|
||||
* An editor for a recipe of this crafting system.
|
||||
* <br> <br>
|
||||
* Mind the difference between Recipe and Recipe Type: <br> <code>
|
||||
*
|
||||
* > Recipe: This specific recipe being edited. An item may have multiple recipes of the same type. <br><br>
|
||||
* > Recipe Type: How to use the recipe, is it Shaped, Shapeless, Smithing, Smelting...?
|
||||
* </code>
|
||||
*
|
||||
* @param player Player to display the Edition Inventory to
|
||||
* @param template MMOItem Template being edited
|
||||
* @param recipeName Name of this particular Recipe
|
||||
* @param recipeRegistry Load/Save Information of this Recipe Type
|
||||
*/
|
||||
public RecipeMakerGUI(@NotNull Player player, @NotNull MMOItemTemplate template, @NotNull String recipeName, @NotNull RecipeRegistry recipeRegistry) {
|
||||
super(player, template);
|
||||
|
||||
// Store name
|
||||
this.recipeName = recipeName;
|
||||
this.recipeRegistry = recipeRegistry;
|
||||
|
||||
// Create Inventory
|
||||
myInventory = Bukkit.createInventory(this, 54, "Edit " + getRecipeRegistry().getRecipeTypeName() + " Recipe");
|
||||
|
||||
// Update old formats
|
||||
moveInput();
|
||||
|
||||
// Identify sections
|
||||
craftingSection = getSection(getEditedSection(), "crafting");
|
||||
typeSection = getSection(craftingSection, getRecipeRegistry().getRecipeConfigPath());
|
||||
nameSection = getSection(typeSection, recipeName);
|
||||
|
||||
// In general, they all have the amount output button
|
||||
//noinspection NestedAssignment
|
||||
addButton(amountButton = new RBA_AmountOutput(this, getCachedItem().clone()));
|
||||
}
|
||||
|
||||
// Button Bar Buttons
|
||||
@NotNull final ItemStack nextButtonPage = ItemFactory.of(Material.SPECTRAL_ARROW).name("\u00a7eMore Options \u00a7c»").build();
|
||||
@NotNull final ItemStack prevButtonPage = ItemFactory.of(Material.SPECTRAL_ARROW).name("\u00a7c« \u00a7eMore Options").build();
|
||||
@NotNull public final ItemStack noButton = ItemFactory.of(Material.IRON_BARS).name("\u00a78---").build();
|
||||
|
||||
// Ingredient-Related Buttons
|
||||
@NotNull public final ItemStack emptySlot = ItemFactory.of(Material.BARRIER).name("\u00a77No Item").build();
|
||||
@NotNull public final ItemStack airSlot = ItemFactory.of(Material.STRUCTURE_VOID).name("\u00a77No Item").build();
|
||||
|
||||
@NotNull final Inventory myInventory;
|
||||
/**
|
||||
* @return The inventory displayed to the player.
|
||||
*/
|
||||
@NotNull public Inventory getMyInventory() { return myInventory; }
|
||||
|
||||
/**
|
||||
*[ID].base.crafting
|
||||
*/
|
||||
@NotNull final ConfigurationSection craftingSection;
|
||||
/**
|
||||
* @return [ID].base.crafting
|
||||
*/
|
||||
@NotNull public ConfigurationSection getCraftingSection() { return craftingSection; }
|
||||
|
||||
/**
|
||||
*[ID].base.crafting.[TYPE]
|
||||
*/
|
||||
@NotNull final ConfigurationSection typeSection;
|
||||
/**
|
||||
* @return [ID].base.crafting.[TYPE]
|
||||
*/
|
||||
@NotNull public ConfigurationSection getTypeSection() { return typeSection; }
|
||||
|
||||
/**
|
||||
*[ID].base.crafting.[TYPE].[NAME]
|
||||
*/
|
||||
@NotNull final ConfigurationSection nameSection;
|
||||
/**
|
||||
* @return [ID].base.crafting.[TYPE].[NAME]
|
||||
*/
|
||||
@NotNull public ConfigurationSection getNameSection() { return nameSection; }
|
||||
|
||||
@NotNull final RecipeRegistry recipeRegistry;
|
||||
/**
|
||||
* @return The information to save and load this recipe.
|
||||
*/
|
||||
@NotNull public RecipeRegistry getRecipeRegistry() { return recipeRegistry; }
|
||||
|
||||
|
||||
/**
|
||||
* The reference to the Amount Button, for ease of access
|
||||
* of the ItemStack displayed for the output of this recipe.
|
||||
*/
|
||||
@NotNull final RBA_AmountOutput amountButton;
|
||||
/**
|
||||
* @return The reference to the Amount Button, for ease of access
|
||||
* of the ItemStack displayed for the output of this recipe.
|
||||
*/
|
||||
@NotNull public RBA_AmountOutput getAmountButton() { return amountButton; }
|
||||
|
||||
@NotNull final String recipeName;
|
||||
/**
|
||||
* @return An item may have multiple recipes, this is the name
|
||||
* of the one being edited. So far, historically, they
|
||||
* have just been a number.
|
||||
* <br>
|
||||
* <br>
|
||||
* In YML, <code>[ID].crafting.[recipe].[name]</code> this
|
||||
* string is the value of [name]
|
||||
* <br>
|
||||
* Ex. <code>STEEL_SWORD.crafting.shaped.1</code>
|
||||
*/
|
||||
@NotNull public String getRecipeName() { return recipeName; }
|
||||
|
||||
int buttonsPage;
|
||||
/**
|
||||
* Map containing the absolute inventory slot links to the buttons placed there.
|
||||
*/
|
||||
@NotNull final HashMap<Integer, RecipeButtonAction> buttonsMap = new HashMap<>();
|
||||
/**
|
||||
* Puts the general buttons, used for any Recipe Maker variant.
|
||||
* <br><br>
|
||||
* The general template, where K is the edge, = is the equals edge,
|
||||
* and r is the result item, looks like this:
|
||||
* <br>
|
||||
* <code>K K K K K K K K K </code><br>
|
||||
* <code>K K K = K K K K K </code><br>
|
||||
* <code>K K K = K K K K r </code><br>
|
||||
* <code>K K K = K K K K K </code>
|
||||
* <br><br>
|
||||
* This is further edited, then, in {@link #putRecipe(Inventory)}, where
|
||||
* for example, the crafting recipe, will show the items and empty slots
|
||||
* in the correct places:
|
||||
* <br>
|
||||
* <code>K K K K K K K K K </code><br>
|
||||
* <code>g g g = - - - K K </code><br>
|
||||
* <code>- s - = - - - K r </code><br>
|
||||
* <code>- s - = - - - K K </code>
|
||||
*
|
||||
* @param target Inventory being edited
|
||||
*/
|
||||
public void putButtons(@NotNull Inventory target) {
|
||||
|
||||
// Clear
|
||||
buttonsMap.clear();
|
||||
|
||||
// Include page buttons
|
||||
if (buttonsPage > 0) { myInventory.setItem((getButtonsRow() * 9) + 8, prevButtonPage); }
|
||||
if (buttonsMap.size() >= ((buttonsPage + 1) * 7)) { myInventory.setItem((getButtonsRow() * 9), nextButtonPage); }
|
||||
|
||||
// Fill the space I guess
|
||||
for (int p = 7 * buttonsPage; p < 7 * (buttonsPage + 1); p++) {
|
||||
|
||||
/*
|
||||
* The job of this is to identify which slots of this
|
||||
* inventory will trigger which action.
|
||||
*
|
||||
* If the slot has a recipe to edit, a connection will
|
||||
* be made between clicking this and which recipe to
|
||||
* edit via the HashMap 'recipeMap'
|
||||
*
|
||||
* But for that we must calculate which absolute slot
|
||||
* of this inventory are we talking about...
|
||||
*/
|
||||
int absolute = buttonRowPageClamp(p);
|
||||
|
||||
/*
|
||||
* Going through the whole page, first thing
|
||||
* to check is that there is a recipe here.
|
||||
*
|
||||
* Note that clicking the very next glass pane
|
||||
* creates a new recipe.
|
||||
*/
|
||||
if (p >= buttons.size()) {
|
||||
|
||||
// Just snooze
|
||||
target.setItem(absolute, noButton);
|
||||
|
||||
// There exists a recipe for this slot
|
||||
} else {
|
||||
|
||||
// Get button
|
||||
RecipeButtonAction rmg = buttons.get(p);
|
||||
|
||||
// Display
|
||||
target.setItem(absolute, rmg.getButton());
|
||||
|
||||
// Store
|
||||
buttonsMap.put(absolute, rmg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Restrains a number between 1 and 7, which is the allowed
|
||||
* absolute slot values to put buttons onto.
|
||||
* <br> <br>
|
||||
* Basically, button #4 will be assigned to the fifth slot (#4) of
|
||||
* the {@link #getButtonsRow()}th Row, just like button #11
|
||||
*
|
||||
* @param p Button number, Ex. 8
|
||||
* @return Slot of the inventory it will be placed, Ex. 10
|
||||
*/
|
||||
public int buttonRowPageClamp(int p) {
|
||||
|
||||
/*
|
||||
* A page is the seven center slots of the #getButtonsRow()
|
||||
*
|
||||
* #1 Obtain the relative column, and relative row
|
||||
*
|
||||
* #2 Convert to absolute inventory positions
|
||||
*/
|
||||
int red = SilentNumbers.floor(p / 7.00D);
|
||||
p -= red * 7;
|
||||
|
||||
/*
|
||||
* A page is the seven center slots of the #getButtonsRow()
|
||||
*
|
||||
* #1 Obtain the relative column
|
||||
*
|
||||
* #2 Convert to absolute inventory positions
|
||||
*/
|
||||
int rowAdditive = (getButtonsRow() * 9);
|
||||
int columnAdditive = p + 1;
|
||||
|
||||
// Sum to obtain final
|
||||
return rowAdditive + columnAdditive;
|
||||
}
|
||||
/**
|
||||
* Should probably avoid this being row #0, since that will occlude
|
||||
* the back button and those other edition inventory buttons.
|
||||
*
|
||||
* @return The inventory row at which the buttons will display.
|
||||
*/
|
||||
public abstract int getButtonsRow();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param absolute Absolute slot clicked by the player, for example,
|
||||
* 0 is the top left corner of the edition inventory.
|
||||
*
|
||||
* @return <code>-1</code> If the slot is not one of the <b>input ingredient</b>
|
||||
* slots, or a number greater or equal to zero depending on which input
|
||||
* ingredient it is.
|
||||
*/
|
||||
abstract int getInputSlot(int absolute);
|
||||
/**
|
||||
* Puts all the buttons onto this inventory.
|
||||
*/
|
||||
public void refreshInventory() {
|
||||
addEditionInventoryItems(getMyInventory(), true);
|
||||
putButtons(getMyInventory());
|
||||
putRecipe(getMyInventory());
|
||||
}
|
||||
/**
|
||||
* Puts the buttons specific for this kind of recipe, display
|
||||
* in the correct places the input and output items.
|
||||
*
|
||||
* @param target The inventory being edited
|
||||
*
|
||||
* @see #putButtons(Inventory) for a better, more lengthy description.
|
||||
*/
|
||||
public abstract void putRecipe(@NotNull Inventory target);
|
||||
/**
|
||||
* Get the item stack associated with this slot, depending
|
||||
* on Input or Output being edited. If it is air, it will
|
||||
* return the chad {@link #emptySlot} ItemStack.
|
||||
*
|
||||
* @param input Should fetch from the INPUT section of the YML Config?
|
||||
* @param slot Which slot of the crafting table?
|
||||
*
|
||||
* @return The correct stack to display.
|
||||
*/
|
||||
@NotNull public ItemStack getDisplay(boolean input, int slot) {
|
||||
|
||||
// Find poof
|
||||
ProvidedUIFilter poof = input ? getInterpreter().getInput(slot) : getInterpreter().getOutput(slot);
|
||||
|
||||
// Null equals fail
|
||||
if (poof == null || poof.isAir()) { return isShowingInput() ? emptySlot : airSlot; }
|
||||
|
||||
// Generate display
|
||||
return poof.getDisplayStack(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* All the buttons added by this recipe.
|
||||
*/
|
||||
@NotNull final ArrayList<RecipeButtonAction> buttons = new ArrayList<>();
|
||||
/**
|
||||
* Registers a button to check when clicking the Edition Inventory for this recipe.
|
||||
*
|
||||
* @param rba Method to run and evaluate the button click.
|
||||
*/
|
||||
public void addButton(@NotNull RecipeButtonAction rba) { buttons.add(rba); }
|
||||
|
||||
@NotNull public final String[] recipeLog = {
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "Write in the chat the item you want, follow any format:"),
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "Vanilla: $e[MATERIAL] [AMOUNT] $bex $eDIAMOND 2.."),
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "MMOItem: $e[TYPE].[ID] [AMOUNT] $bex $eSWORD.CUTLASS 1.."),
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "Other: $e[KEY] [ARG] [DAT] [AMOUNT]$b (check wiki)"),
|
||||
FriendlyFeedbackProvider.quickForPlayer(FFPMMOItems.get(), "\u00a78Amount is in the range format, $e[min]..[max]\u00a78, assumed to be $r1..\u00a78 if unspecified.")};
|
||||
|
||||
/**
|
||||
* @return The protocols to edit the ConfigurationSection based on the user input.
|
||||
*/
|
||||
@NotNull public abstract RMG_RecipeInterpreter getInterpreter();
|
||||
|
||||
|
||||
@NotNull @Override public Inventory getInventory() {
|
||||
|
||||
// Put buttons
|
||||
refreshInventory();
|
||||
|
||||
// That's it lets GOOOO
|
||||
return myInventory;
|
||||
}
|
||||
@Override public void whenClicked(InventoryClickEvent event) {
|
||||
|
||||
// Clicked inventory was not the observed inventory? Not our business
|
||||
if (event.getView().getTopInventory() != event.getClickedInventory()) { return; }
|
||||
|
||||
// Disallow any clicking.
|
||||
event.setCancelled(true);
|
||||
|
||||
// Was it an ingredient slot?
|
||||
int ingredient = getInputSlot(event.getRawSlot());
|
||||
|
||||
// Setting an ingredient?
|
||||
if (event.getAction() == InventoryAction.PICKUP_ALL) {
|
||||
|
||||
// Is it an input slot?
|
||||
if (ingredient >= 0) {
|
||||
|
||||
// Input or output?
|
||||
if (isShowingInput()) {
|
||||
|
||||
// Query user for input
|
||||
new StatEdition(this, ItemStats.CRAFTING, INPUT, getInterpreter(), ingredient).enable(recipeLog);
|
||||
|
||||
} else {
|
||||
|
||||
// Query user for output
|
||||
new StatEdition(this, ItemStats.CRAFTING, OUTPUT, getInterpreter(), ingredient).enable(recipeLog); }
|
||||
|
||||
// Maybe its a button
|
||||
} else {
|
||||
|
||||
// Find button
|
||||
RecipeButtonAction rmg = buttonsMap.get(event.getRawSlot());
|
||||
|
||||
// Found?
|
||||
if (rmg != null) { rmg.runPrimary(); }
|
||||
}
|
||||
|
||||
// Removing an ingredient?
|
||||
} else if (event.getAction() == InventoryAction.PICKUP_HALF) {
|
||||
|
||||
|
||||
// Is it an input slot?
|
||||
if (ingredient >= 0) {
|
||||
|
||||
// Input or output?
|
||||
if (isShowingInput()) {
|
||||
|
||||
// Delete Input
|
||||
getInterpreter().deleteInput(getSection(getEditedSection(), "crafting"), ingredient);
|
||||
|
||||
} else {
|
||||
|
||||
// Delete Output
|
||||
getInterpreter().deleteOutput(getSection(getEditedSection(), "crafting"), getInputSlot(event.getRawSlot())); }
|
||||
|
||||
// Refresh yes
|
||||
refreshInventory();
|
||||
|
||||
// Maybe its a button
|
||||
} else {
|
||||
|
||||
// Find button
|
||||
RecipeButtonAction rmg = buttonsMap.get(event.getRawSlot());
|
||||
|
||||
// Found?
|
||||
if (rmg != null) { rmg.runSecondary(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region ############------- Input/Output Switch -------############
|
||||
/**
|
||||
* There are several reasons why this was the best way, however
|
||||
* unique looking. The best reason is that, for an user editing
|
||||
* many recipes at once, its quicker to just toggle it globally.
|
||||
*
|
||||
* From a programmatic point of view, this is also easier to implement.
|
||||
*/
|
||||
@NotNull static final HashMap<UUID, Boolean> showingInput = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Change between showing input and output for some user.
|
||||
*
|
||||
* @param whom Player the inventory is built for
|
||||
*/
|
||||
public static void switchInputFor(@NotNull UUID whom) {
|
||||
|
||||
// Flip
|
||||
showingInput.put(whom, !isShowingInputFor(whom));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whom Player the inventory is built for
|
||||
*
|
||||
* @return If the player wants to see the input recipes.
|
||||
*/
|
||||
public static boolean isShowingInputFor(@NotNull UUID whom) {
|
||||
|
||||
// The value, true by default
|
||||
return showingInput.getOrDefault(whom, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the player opening this inventory is looking at INPUT (as opposed to OUTPUT)
|
||||
*/
|
||||
public boolean isShowingInput() { return isShowingInputFor(getPlayer().getUniqueId()); }
|
||||
|
||||
/**
|
||||
* Switch between input and output for this player.
|
||||
*/
|
||||
public void switchInput() { switchInputFor(getPlayer().getUniqueId()); }
|
||||
//endregion
|
||||
|
||||
//region ############------- Constants -------############
|
||||
/*
|
||||
* Sure this could be in an enum but it annoys me to
|
||||
* make a new file just such few constants.
|
||||
*/
|
||||
public static final int INPUT = 0;
|
||||
public static final int OUTPUT = 1;
|
||||
public static final int PRIMARY = 2;
|
||||
public static final int SECONDARY = 3;
|
||||
|
||||
public static final String INPUT_INGREDIENTS = "input";
|
||||
public static final String OUTPUT_INGREDIENTS = "output";
|
||||
|
||||
public static final ProvidedUIFilter AIR = new ProvidedUIFilter(new VanillaUIFilter(), "AIR", "0");
|
||||
|
||||
/**
|
||||
* Why is it so cumbersome to have a one line renaming method?
|
||||
*
|
||||
* @param itm ItemStack.
|
||||
*
|
||||
* @param name Name to give to your item.
|
||||
*
|
||||
* @return The item, renamed.
|
||||
*/
|
||||
@NotNull public static ItemStack rename(@NotNull ItemStack itm, @NotNull String name) {
|
||||
|
||||
// Bruh
|
||||
ItemMeta iMeta = itm.getItemMeta();
|
||||
//noinspection ConstantConditions
|
||||
iMeta.setDisplayName(MythicLib.plugin.parseColors(name));
|
||||
itm.setItemMeta(iMeta);
|
||||
return itm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Why is it so cumbersome to have some lore lines method?
|
||||
*
|
||||
* @param itm ItemStack.
|
||||
*
|
||||
* @param lines Lore to add to the item
|
||||
*
|
||||
* @return The item, lored.
|
||||
*/
|
||||
@NotNull public static ItemStack addLore(@NotNull ItemStack itm, @NotNull ArrayList<String> lines) {
|
||||
|
||||
// Bruh
|
||||
ItemMeta iMeta = itm.getItemMeta();
|
||||
//noinspection ConstantConditions
|
||||
List<String> currentLore = iMeta.getLore();
|
||||
if (currentLore == null) { currentLore = new ArrayList<>(); }
|
||||
|
||||
// Add lore
|
||||
for (String line : lines) { currentLore.add(MythicLib.plugin.parseColors(line)); }
|
||||
iMeta.setLore(currentLore);
|
||||
|
||||
// That's it
|
||||
itm.setItemMeta(iMeta);
|
||||
return itm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Because obviously getConfigurationSection had to be annotated with {@link Nullable} even
|
||||
* through it does basically this same thing.
|
||||
*
|
||||
* @param root Root section
|
||||
* @param path Path to get/create
|
||||
*
|
||||
* @return The subsection of this config.
|
||||
*/
|
||||
@NotNull public static ConfigurationSection getSection(@NotNull ConfigurationSection root, @NotNull String path) {
|
||||
ConfigurationSection section = root.getConfigurationSection(path);
|
||||
if (section == null) { section = root.createSection(path); }
|
||||
return section; }
|
||||
//endregion
|
||||
|
||||
//region ############------- Updating Legacy Formats -------############
|
||||
/**
|
||||
* In the past, crafting recipes only supported (for example) a 3x3 grid of input.
|
||||
* This was stored under [ID].base.crafting.shaped.recipe
|
||||
* <br> <br>
|
||||
* This method moves that into [ID].crafting.shaped.recipe.input
|
||||
*/
|
||||
public void moveInput() {
|
||||
|
||||
ConfigurationSection crafting = getSection(getEditedSection(), "crafting");
|
||||
ConfigurationSection shaped = getSection(crafting, getRecipeRegistry().getRecipeConfigPath());
|
||||
|
||||
// Move to generalized method
|
||||
moveInput(shaped, recipeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute brute force method that will alter this configuration section,
|
||||
* note that code wont run the same after it has been called, as any variable
|
||||
* that is not a ConfigurationSection within 3 levels deep of the passed
|
||||
* section will be cleared.
|
||||
*
|
||||
* For advanced debug purposes only.
|
||||
*
|
||||
* @param section Section to mess up
|
||||
*/
|
||||
public static void tripleDebug(@NotNull ConfigurationSection section) {
|
||||
|
||||
MMOItems.print(null, "\u00a7d-\u00a77 Section \u00a75" + section.getCurrentPath(), null);
|
||||
for (String key : section.getKeys(false)) {
|
||||
MMOItems.print(null,"\u00a7d +\u00a77 " + key, null);
|
||||
|
||||
MMOItems.print(null,"\u00a7d-\u00a7e-\u00a77 As List \u00a75" + section.getCurrentPath() + "." + key + "\u00a77 {\u00a7d" + section.getStringList(key).size() + "\u00a77}", null);
|
||||
for (String listKey : section.getStringList(key)) { MMOItems.print(null,"\u00a7d +\u00a7e-\u00a77" + listKey, null); }
|
||||
|
||||
ConfigurationSection asSection = getSection(section, key);
|
||||
MMOItems.print(null,"\u00a78--\u00a7d-\u00a77 Section \u00a75" + asSection.getCurrentPath(), null);
|
||||
for (String asKey : asSection.getKeys(false)) {
|
||||
MMOItems.print(null,"\u00a78--\u00a7d +\u00a77 " + asKey, null);
|
||||
|
||||
MMOItems.print(null,"\u00a78--\u00a7d-\u00a7e-\u00a77 As List \u00a75" + asSection.getCurrentPath() + "." + asKey + "\u00a77 {\u00a7d" + asSection.getStringList(asKey).size() + "\u00a77}", null);
|
||||
for (String listKey : asSection.getStringList(asKey)) { MMOItems.print(null,"\u00a78--\u00a7d +\u00a7e-\u00a77" + listKey, null); }
|
||||
|
||||
ConfigurationSection asESection = getSection(asSection, asKey);
|
||||
MMOItems.print(null,"\u00a70--\u00a78--\u00a7d-\u00a77 Section \u00a75" + asESection.getCurrentPath(), null);
|
||||
for (String asEKey : asESection.getKeys(false)) {
|
||||
MMOItems.print(null,"\u00a70--\u00a78--\u00a7d +\u00a77 " + asEKey, null);
|
||||
|
||||
MMOItems.print(null,"\u00a70--\u00a78--\u00a7d-\u00a7e-\u00a77 As List \u00a75" + asESection.getCurrentPath() + "." + asEKey + "\u00a77 {\u00a7d" + asESection.getStringList(asEKey).size() + "\u00a77}", null);
|
||||
for (String listKey : asESection.getStringList(asEKey)) { MMOItems.print(null,"\u00a70--\u00a78--\u00a7d +\u00a7e-\u00a77" + listKey, null); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the past, crafting recipes only supported (for example) a 3x3 grid of input.
|
||||
* This was stored under [ID].base.crafting.shaped.recipe
|
||||
* <br> <br>
|
||||
* This method moves that into [ID].crafting.shaped.recipe.input
|
||||
*
|
||||
* @return The recipe name section result from this operation. <br>
|
||||
* [ID].base.crafting.[TYPE].[NAME]
|
||||
*/
|
||||
public static ConfigurationSection moveInput(@NotNull ConfigurationSection recipeSection, @NotNull String nameOfRecipe) {
|
||||
ConfigurationSection name;
|
||||
|
||||
/*
|
||||
* This converts a recipe from the old format into the new.
|
||||
*
|
||||
* This is detected by the recipe name containing list information
|
||||
*/
|
||||
|
||||
if (recipeSection.isConfigurationSection(nameOfRecipe)) {
|
||||
//UPT//MMOItems.log("\u00a7a*\u00a77 Was config section");
|
||||
|
||||
// Get as config section
|
||||
name = getSection(recipeSection, nameOfRecipe);
|
||||
|
||||
// Both must exist for smithing conversion
|
||||
String item_yml = name.getString("input1");
|
||||
String ingot_yml = name.getString("input2");
|
||||
//UPT//MMOItems.log("\u00a7a*\u00a77 I1:\u00a76 " + item_yml + "\u00a77, I2:\u00a73 " + ingot_yml);
|
||||
|
||||
// Is it smithing?
|
||||
if (item_yml != null && ingot_yml != null) {
|
||||
//UPT//MMOItems.log("\u00a7a*\u00a77 Identified as \u00a7aSmithing");
|
||||
|
||||
// Build correctly
|
||||
name.set("input1", null);
|
||||
name.set("input2", null);
|
||||
name.set(INPUT_INGREDIENTS, poofFromLegacy(item_yml) + "|" + poofFromLegacy(ingot_yml));
|
||||
name.set(OUTPUT_INGREDIENTS, "v AIR 0|v AIR 0");
|
||||
}
|
||||
|
||||
} else {
|
||||
//UPT//MMOItems.log("\u00a7a*\u00a77 No config section");
|
||||
|
||||
// Get as String List
|
||||
List<String> sc = recipeSection.getStringList(nameOfRecipe);
|
||||
|
||||
//UPT//MMOItems.log("\u00a78--\u00a7e-\u00a7d+\u00a77 Ingredients: \u00a75" + nameOfRecipe);
|
||||
//UPT//for (String key : sc) { MMOItems.log("\u00a78--\u00a7e-\u00a7d +\u00a77" + key); }
|
||||
|
||||
// Clear
|
||||
recipeSection.set(nameOfRecipe, null);
|
||||
|
||||
// Edit
|
||||
name = getSection(recipeSection, nameOfRecipe);
|
||||
name.set(INPUT_INGREDIENTS, sc);
|
||||
}
|
||||
|
||||
// That's it
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts legacy formats into ProvidedUIFilters
|
||||
*
|
||||
* @param legacy Legacy string
|
||||
* @return Converted string as best as possible
|
||||
*/
|
||||
@NotNull public static String poofFromLegacy(@Nullable String legacy) {
|
||||
if (legacy == null || "[]".equals(legacy)) {
|
||||
//UPT//MMOItems.log("\u00a7b+\u00a77 Null, \u00a7b" + "v AIR - 1..");
|
||||
return "v AIR - 1.."; }
|
||||
|
||||
// Spaces are assumed to be updated
|
||||
if (legacy.contains(" ")) {
|
||||
//UPT//MMOItems.log("\u00a7b+\u00a77 Mirror, \u00a7b" + legacy);
|
||||
return legacy; }
|
||||
|
||||
// Split by amount
|
||||
int aLoc = legacy.indexOf(':');
|
||||
QuickNumberRange amount = new QuickNumberRange(1.0 , null);
|
||||
if (aLoc > 0) {
|
||||
|
||||
String am = legacy.substring(aLoc + 1);
|
||||
legacy = legacy.substring(0, aLoc);
|
||||
Integer du = SilentNumbers.IntegerParse(am);
|
||||
if (du == null) { du = 1; }
|
||||
amount = new QuickNumberRange((double) du, null);
|
||||
}
|
||||
|
||||
if (legacy.contains(".")) {
|
||||
|
||||
// Must be MMOItem
|
||||
String[] mmo = legacy.split("\\.");
|
||||
//UPT//MMOItems.log("\u00a7b+\u00a77 MMOItem, \u00a7bm " + mmo[0] + " " + mmo[1] + " " + amount);
|
||||
|
||||
// Build
|
||||
return "m " + mmo[0] + " " + mmo[1] + " " + amount;
|
||||
|
||||
} else {
|
||||
//UPT//MMOItems.log("\u00a7b+\u00a77 Vanilla, \u00a7bv " + legacy + " - " + amount);
|
||||
|
||||
// That's it
|
||||
return "v " + legacy + " - " + amount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To support legacy formats, at least for now, we use this method
|
||||
* to read individual ingredients.
|
||||
* <p></p>
|
||||
* It supports the formats:
|
||||
* <p><code>MATERIAL</code> (legacy vanilla material)
|
||||
* </p><code>TYPE.ID</code> (legacy MMOItem)
|
||||
* <p><code>KEY ARGUMENT DATA AMOUNT</code> (current)
|
||||
* </p>
|
||||
*
|
||||
* @param str String that's should be in one of the formats above.
|
||||
* @param ffp To tell what happened
|
||||
*
|
||||
* @throws IllegalArgumentException If not in the correct format.
|
||||
*
|
||||
* @return An ingredient read from this string.
|
||||
*/
|
||||
@NotNull public static ProvidedUIFilter readIngredientFrom(@NotNull String str, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
/*
|
||||
* This entry, is it a vanilla material?
|
||||
*
|
||||
* Then build it as material.
|
||||
*/
|
||||
Material asMaterial = null;
|
||||
try { asMaterial = Material.valueOf(str.toUpperCase().replace(" ", "_").replace("-", "_")); } catch (IllegalArgumentException ignored) {}
|
||||
if (asMaterial != null) {
|
||||
|
||||
// Is it AIR?
|
||||
if (asMaterial.isAir()) {
|
||||
|
||||
ProvidedUIFilter result = new ProvidedUIFilter(VanillaUIFilter.get(), "AIR", "0");
|
||||
result.setAmountRange(new QuickNumberRange(null, null));
|
||||
return result; }
|
||||
|
||||
// We snooze if its AIR or such
|
||||
if (!asMaterial.isItem()) { throw new IllegalArgumentException("Invalid Ingredient $u" + str + "$b ($fNot an Item$b)."); }
|
||||
|
||||
// All right create filter and go
|
||||
ProvidedUIFilter poof = UIFilterManager.getUIFilter("v", asMaterial.toString(), "", "1..", ffp);
|
||||
|
||||
// Valid?
|
||||
if (poof != null) {
|
||||
|
||||
// Add
|
||||
return poof;
|
||||
|
||||
} else {
|
||||
|
||||
// Send all I guess
|
||||
ffp.sendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
|
||||
ffp.sendTo(FriendlyFeedbackCategory.FAILURE, MMOItems.getConsole());
|
||||
|
||||
// Ew
|
||||
throw new IllegalArgumentException("Invalid Ingredient $u" + str);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not a vanilla material, lets try to read it as a Legacy MMOItem thing.
|
||||
*
|
||||
* It must have a dot, and no spaces.
|
||||
*/
|
||||
if (str.contains(".") && !str.contains(" ")) {
|
||||
|
||||
// Split by dot
|
||||
String[] split = str.split("\\.");
|
||||
|
||||
// Exactly two?
|
||||
if (split.length == 2) {
|
||||
|
||||
// Well
|
||||
String iType = split[0];
|
||||
String iID = split[1];
|
||||
|
||||
// All right create filter and go
|
||||
ProvidedUIFilter poof = UIFilterManager.getUIFilter("m", iType, iID, "1..", ffp);
|
||||
|
||||
// Valid?
|
||||
if (poof != null) {
|
||||
|
||||
// Add
|
||||
return poof;
|
||||
|
||||
} else {
|
||||
|
||||
// Send all I guess
|
||||
ffp.sendAllTo(MMOItems.getConsole());
|
||||
|
||||
// Ew
|
||||
throw new IllegalArgumentException("Invalid Ingredient $u" + str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Not a vanilla Material, but what about a UIFilter itself?
|
||||
*/
|
||||
ProvidedUIFilter poof = UIFilterManager.getUIFilter(str, ffp);
|
||||
|
||||
// Valid?
|
||||
if (poof != null) {
|
||||
|
||||
// Add
|
||||
return poof;
|
||||
|
||||
} else {
|
||||
|
||||
// Send all I guess
|
||||
ffp.sendAllTo(MMOItems.getConsole());
|
||||
|
||||
// Ew
|
||||
throw new IllegalArgumentException("Invalid Ingredient $u" + str);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeBlueprint;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.recipe.CraftingType;
|
||||
import net.Indyuce.mmoitems.api.recipe.workbench.ingredients.WorkbenchIngredient;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_AmountOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_CookingTime;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_Experience;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_HideFromBook;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RMG_BurningLegacy;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy.BurningRecipeInformation;
|
||||
import net.Indyuce.mmoitems.manager.RecipeManager;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Recipes for furnaces. Ive never worked with these,
|
||||
* and I don't claim to support them. They are just kinda
|
||||
* compatible with the new crafting GUI and THAT'S IT.
|
||||
*
|
||||
* Still using Aria's code.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public abstract class RMGRR_LegacyBurning implements RecipeRegistry {
|
||||
|
||||
@NotNull public abstract CraftingType getLegacyBurningType();
|
||||
|
||||
@NotNull public static String capitalizeFirst(@NotNull String str) { return str.substring(0, 1).toUpperCase() + str.substring(1); }
|
||||
|
||||
@NotNull @Override public String getRecipeConfigPath() { return getLegacyBurningType().name().toLowerCase(); }
|
||||
|
||||
@NotNull @Override public String getRecipeTypeName() { return "§8{§4§oL§8} " + capitalizeFirst(getRecipeConfigPath()); }
|
||||
|
||||
@SuppressWarnings("NotNullFieldNotInitialized")
|
||||
@NotNull ItemStack displayListItem;
|
||||
|
||||
@NotNull @Override public ItemStack getDisplayListItem() {
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (displayListItem == null) {
|
||||
displayListItem = RecipeMakerGUI.rename(getLegacyBurningType().getItem(), FFPMMOItems.get().getExampleFormat() + capitalizeFirst(getRecipeConfigPath()) + " Recipe");
|
||||
|
||||
displayListItem = RecipeMakerGUI.addLore(displayListItem, SilentNumbers.toArrayList(" "));
|
||||
displayListItem = RecipeMakerGUI.addLore(displayListItem, SilentNumbers.chop("\u00a74To accept input amounts, this recipe requires recipe-amounts to be enabled in the config.yml", 60, "\u00a74"));
|
||||
}
|
||||
|
||||
return displayListItem; }
|
||||
|
||||
@Override public void openForPlayer(@NotNull EditionInventory inv, @NotNull String recipeName, Object... otherParams) { new RMG_BurningLegacy(inv.getPlayer(), inv.getEdited(), recipeName, this).open(inv.getPreviousPage()); }
|
||||
|
||||
|
||||
/**
|
||||
* Actually doesnt really send this thing to MythicLib
|
||||
* its just guaranteed to throw an exception and uses
|
||||
* the legacy MMOItems way of registering.
|
||||
*/
|
||||
@NotNull @Override public MythicRecipeBlueprint sendToMythicLib(@NotNull MMOItemTemplate template, @NotNull ConfigurationSection recipeTypeSection, @NotNull String recipeName, @NotNull Ref<NamespacedKey> namespace, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
// Never happening
|
||||
Validate.isTrue(namespace.getValue() != null);
|
||||
|
||||
// Get correct section
|
||||
ConfigurationSection recipeSection = RecipeMakerGUI.getSection(recipeTypeSection, recipeName);
|
||||
|
||||
// Get ingredient
|
||||
String itemIngredient = recipeSection.getString("item");
|
||||
if (itemIngredient == null) { throw new IllegalArgumentException("Missing input ingredient"); }
|
||||
WorkbenchIngredient ingredient = RecipeManager.getWorkbenchIngredient(itemIngredient);
|
||||
|
||||
// Read amount from configuration
|
||||
int outputAmount = recipeSection.getInt(RBA_AmountOutput.AMOUNT_INGREDIENTS, 1);
|
||||
double experience = recipeSection.getDouble(RBA_Experience.FURNACE_EXPERIENCE, RBA_Experience.DEFAULT);
|
||||
int time = recipeSection.getInt(RBA_CookingTime.FURNACE_TIME, SilentNumbers.round(RBA_CookingTime.DEFAULT));
|
||||
boolean hideBook = recipeSection.getBoolean(RBA_HideFromBook.BOOK_HIDDEN, false);
|
||||
|
||||
// Make that recipe yes
|
||||
BurningRecipeInformation info = new BurningRecipeInformation(ingredient, (float) experience, time);
|
||||
|
||||
// Yes
|
||||
MMOItems.plugin.getRecipes().registerBurningRecipe(
|
||||
getLegacyBurningType().getBurningType(),
|
||||
template.newBuilder(0, null).build(),
|
||||
info, outputAmount, namespace.getValue(), hideBook);
|
||||
|
||||
throw new IllegalArgumentException("");
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.ingredients.ShapedIngredient;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MRORecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MythicRecipeOutput;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeBlueprint;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeStation;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.ShapedRecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_Shaped;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_AmountOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_HideFromBook;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RMG_Shaped;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class RMGRR_Shaped implements RecipeRegistry {
|
||||
|
||||
@NotNull @Override public String getRecipeConfigPath() { return "shaped"; }
|
||||
@NotNull @Override public String getRecipeTypeName() { return "Shaped"; }
|
||||
|
||||
@NotNull final ItemStack displayListItem = RecipeMakerGUI.rename(new ItemStack(Material.CRAFTING_TABLE), FFPMMOItems.get().getExampleFormat() + "Shaped Recipe");
|
||||
@NotNull @Override public ItemStack getDisplayListItem() { return displayListItem; }
|
||||
|
||||
@Override public void openForPlayer(@NotNull EditionInventory inv, @NotNull String recipeName, Object... otherParams) {
|
||||
new RMG_Shaped(inv.getPlayer(), inv.getEdited(), recipeName, this).open(inv.getPreviousPage());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MythicRecipeBlueprint sendToMythicLib(@NotNull MMOItemTemplate template, @NotNull ConfigurationSection recipeTypeSection, @NotNull String recipeName, @NotNull Ref<NamespacedKey> namespace, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
// Read some values
|
||||
ConfigurationSection recipeSection = RecipeMakerGUI.moveInput(recipeTypeSection, recipeName);
|
||||
|
||||
NamespacedKey nk = namespace.getValue();
|
||||
if (nk == null) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Illegal (Null) Namespace")); }
|
||||
|
||||
// Identify the input
|
||||
ShapedRecipe input = shapedRecipeFromList(nk.getKey(), new ArrayList<>(recipeSection.getStringList(RecipeMakerGUI.INPUT_INGREDIENTS)), ffp);
|
||||
if (input == null) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Shaped recipe containing only AIR, $fignored$b.")); }
|
||||
|
||||
// Read the options and output
|
||||
ShapedRecipe output = shapedRecipeFromList(nk.getKey(), new ArrayList<>(recipeSection.getStringList(RecipeMakerGUI.OUTPUT_INGREDIENTS)), ffp);
|
||||
int outputAmount = recipeSection.getInt(RBA_AmountOutput.AMOUNT_INGREDIENTS, 1);
|
||||
boolean hideBook = recipeSection.getBoolean(RBA_HideFromBook.BOOK_HIDDEN, false);
|
||||
|
||||
// Build Output
|
||||
ShapedRecipe outputItem = ShapedRecipe.single(nk.getKey(), new ProvidedUIFilter(MMOItemUIFilter.get(), template.getType().getId(), template.getId(), Math.max(outputAmount, 1)));
|
||||
MythicRecipeOutput outputRecipe = new MRORecipe(outputItem, output);
|
||||
|
||||
// That's our blueprint :)
|
||||
MythicRecipeBlueprint ret = new MythicRecipeBlueprint(input, outputRecipe, nk);
|
||||
|
||||
// Enable it
|
||||
ret.deploy(MythicRecipeStation.WORKBENCH, namespace);
|
||||
|
||||
// Hide book if specified
|
||||
if (hideBook) { namespace.setValue(null); }
|
||||
|
||||
// That's it
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for reading list of strings that are intended to be shaped recipes: <br><br>
|
||||
* <code>
|
||||
* m MATERIAL AMETHYST|m MATERIAL AMETHYST|m MATERIAL AMETHYST <br>
|
||||
* v AIR 0|v STICK 0|v AIR 0 <br>
|
||||
* v AIR 0|v STICK 0|v AIR 0
|
||||
* </code>
|
||||
*
|
||||
* @param namespace Some name to give to this thing, it can be anything really.
|
||||
*
|
||||
* @param recipe The list of strings, probably directly from your YML Config
|
||||
*
|
||||
* @param ffp Provider of failure text
|
||||
*
|
||||
* @return The most optimized version of this recipe, ready to be put into a Blueprint.
|
||||
* <br> <br>
|
||||
* Will be <code>null</code> if it would have been only AIR.
|
||||
*
|
||||
* @throws IllegalArgumentException If any ingredient is illegal (wrong syntax or something).
|
||||
*/
|
||||
@Nullable public static ShapedRecipe shapedRecipeFromList(@NotNull String namespace, @NotNull ArrayList<String> recipe, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
// All right lets read them
|
||||
ArrayList<ShapedIngredient> poofs = new ArrayList<>();
|
||||
boolean nonAirFound = false;
|
||||
int rowNumber = 0;
|
||||
//UPT//MMOItems.log("\u00a7e" + namespace + "\u00a77 loading:");
|
||||
|
||||
// Read through the recipe
|
||||
for (String row : recipe) {
|
||||
//UPT//MMOItems.log("\u00a7e-\u00a77 " + row);
|
||||
|
||||
// Update
|
||||
String updatedRow = RMGRI_Shaped.updateRow(row);
|
||||
//UPT//MMOItems.log("\u00a7eU-\u00a77 " + updatedRow);
|
||||
|
||||
/*
|
||||
* This row could be in either legacy or new format, and we will assume no combination of them.
|
||||
*
|
||||
* Either:
|
||||
* ANYTHING ANY.THING ANYTHING
|
||||
*
|
||||
* or
|
||||
* A NYT THIN G|A NYT THING|A NYT THIN G
|
||||
*/
|
||||
|
||||
// What are the three ingredients encoded in this row?
|
||||
String[] positions;
|
||||
|
||||
if (updatedRow.contains("|")) {
|
||||
|
||||
// Split by |s
|
||||
positions = updatedRow.split("\\|");
|
||||
|
||||
// Is legacy
|
||||
} else {
|
||||
|
||||
// Split by spaces
|
||||
positions = updatedRow.split(" ");
|
||||
}
|
||||
|
||||
// Size not 3? BRUH
|
||||
if (positions.length != 3) { throw new IllegalArgumentException("Invalid crafting table row $u" + updatedRow + "$b ($fNot exactly 3 ingredients wide$b)."); }
|
||||
|
||||
// Identify
|
||||
ProvidedUIFilter left = RecipeMakerGUI.readIngredientFrom(positions[0], ffp);
|
||||
ProvidedUIFilter center = RecipeMakerGUI.readIngredientFrom(positions[1], ffp);
|
||||
ProvidedUIFilter right = RecipeMakerGUI.readIngredientFrom(positions[2], ffp);
|
||||
if (!left.isAir()) { nonAirFound = true; }
|
||||
if (!center.isAir()) { nonAirFound = true; }
|
||||
if (!right.isAir()) { nonAirFound = true; }
|
||||
|
||||
/*
|
||||
* To detect if a recipe can be crafted in the survival inventory (and remove extra AIR),
|
||||
* we must see that a whole row AND a whole column be air. Not any column or row though,
|
||||
* but any of those that do not cross the center.
|
||||
*
|
||||
* If a single left item is not air, LEFT is no longer an unsharped column.
|
||||
* If a single right item is not air, RIGHT is no longer an unsharped column.
|
||||
*
|
||||
* All items must be air in TOP or BOTTOM for they to be unsharped.
|
||||
*/
|
||||
|
||||
// Bake
|
||||
ShapedIngredient leftIngredient = new ShapedIngredient(left, 0, -rowNumber);
|
||||
ShapedIngredient centerIngredient = new ShapedIngredient(center, 1, -rowNumber);
|
||||
ShapedIngredient rightIngredient = new ShapedIngredient(right, 2, -rowNumber);
|
||||
|
||||
// Parse and add
|
||||
poofs.add(leftIngredient);
|
||||
poofs.add(centerIngredient);
|
||||
poofs.add(rightIngredient);
|
||||
|
||||
// Prepare for next row
|
||||
rowNumber++;
|
||||
}
|
||||
if (!nonAirFound) { return null; }
|
||||
|
||||
// Make ingredients
|
||||
return ShapedRecipe.unsharpen((new ShapedRecipe(namespace, poofs)));
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.ingredients.MythicRecipeIngredient;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MRORecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MythicRecipeOutput;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeBlueprint;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeStation;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.ShapedRecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.ShapelessRecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_AmountOutput;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RBA_HideFromBook;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RMG_Shapeless;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class RMGRR_Shapeless implements RecipeRegistry {
|
||||
|
||||
@NotNull @Override public String getRecipeTypeName() { return "Shapeless"; }
|
||||
@NotNull @Override public String getRecipeConfigPath() { return "shapeless"; }
|
||||
|
||||
@NotNull final ItemStack displayListItem = RecipeMakerGUI.rename(new ItemStack(Material.OAK_LOG), FFPMMOItems.get().getExampleFormat() + "Shapeless Recipe");
|
||||
@NotNull @Override public ItemStack getDisplayListItem() { return displayListItem; }
|
||||
|
||||
@Override public void openForPlayer(@NotNull EditionInventory inv, @NotNull String recipeName, Object... otherParams) {
|
||||
new RMG_Shapeless(inv.getPlayer(), inv.getEdited(), recipeName, this).open(inv.getPreviousPage());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MythicRecipeBlueprint sendToMythicLib(@NotNull MMOItemTemplate template, @NotNull ConfigurationSection recipeTypeSection, @NotNull String recipeName, @NotNull Ref<NamespacedKey> namespace, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
// Prior Preparations (update old formats)
|
||||
RecipeMakerGUI.moveInput(recipeTypeSection, recipeName);
|
||||
|
||||
// Read some values
|
||||
ConfigurationSection recipeSection = RecipeMakerGUI.getSection(recipeTypeSection, recipeName);
|
||||
NamespacedKey nk = namespace.getValue();
|
||||
if (nk == null) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Illegal (Null) Namespace")); }
|
||||
|
||||
//region Identify the input
|
||||
ArrayList<MythicRecipeIngredient> poofs = new ArrayList<>();
|
||||
ArrayList<String> recipe = new ArrayList<>(recipeSection.getStringList(RecipeMakerGUI.INPUT_INGREDIENTS));
|
||||
|
||||
// Read from the recipe
|
||||
boolean nonAirFound = false;
|
||||
for (String str : recipe) {
|
||||
|
||||
// Null is a sleeper
|
||||
if (str == null || "AIR".equals(str)) { continue; }
|
||||
|
||||
// Add
|
||||
ProvidedUIFilter p = RecipeMakerGUI.readIngredientFrom(str, ffp);
|
||||
nonAirFound = true;
|
||||
poofs.add(new MythicRecipeIngredient(p));
|
||||
}
|
||||
if (!nonAirFound) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Shapeless recipe containing only AIR, $fignored$b.")); }
|
||||
ShapelessRecipe input = new ShapelessRecipe(nk.getKey(), poofs);
|
||||
//endregion
|
||||
|
||||
// Read the options and output
|
||||
ShapedRecipe output = RMGRR_Shaped.shapedRecipeFromList(nk.getKey(), new ArrayList<>(recipeSection.getStringList(RecipeMakerGUI.OUTPUT_INGREDIENTS)), ffp);
|
||||
int outputAmount = recipeSection.getInt(RBA_AmountOutput.AMOUNT_INGREDIENTS, 1);
|
||||
boolean hideBook = recipeSection.getBoolean(RBA_HideFromBook.BOOK_HIDDEN, false);
|
||||
|
||||
// Build Output
|
||||
ShapedRecipe outputItem = ShapedRecipe.single(nk.getKey(), new ProvidedUIFilter(MMOItemUIFilter.get(), template.getType().getId(), template.getId(), Math.max(outputAmount, 1)));
|
||||
MythicRecipeOutput outputRecipe = new MRORecipe(outputItem, output);
|
||||
|
||||
// That's our blueprint :)
|
||||
MythicRecipeBlueprint ret = new MythicRecipeBlueprint(input, outputRecipe, nk);
|
||||
|
||||
// Enable it
|
||||
ret.deploy(MythicRecipeStation.WORKBENCH, namespace);
|
||||
|
||||
// Hide book if specified
|
||||
if (hideBook) { namespace.setValue(null); }
|
||||
|
||||
// That's it
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.ingredients.MythicRecipeIngredient;
|
||||
import io.lumine.mythic.lib.api.crafting.outputs.MythicRecipeOutput;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeBlueprint;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeStation;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.ShapedRecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.ShapelessRecipe;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.api.crafting.recipe.CustomSmithingRecipe;
|
||||
import net.Indyuce.mmoitems.api.crafting.recipe.SmithingCombinationType;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMGRI_Smithing;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.*;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RMG_Smithing;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RMGRR_Smithing implements RecipeRegistry {
|
||||
|
||||
@NotNull @Override public String getRecipeTypeName() { return "Smithing"; }
|
||||
@NotNull @Override public String getRecipeConfigPath() { return "smithing"; }
|
||||
|
||||
@NotNull final ItemStack displayListItem = RecipeMakerGUI.rename(new ItemStack(Material.SMITHING_TABLE), FFPMMOItems.get().getExampleFormat() + "Smithing Recipe");
|
||||
@NotNull @Override public ItemStack getDisplayListItem() { return displayListItem; }
|
||||
|
||||
@Override public void openForPlayer(@NotNull EditionInventory inv, @NotNull String recipeName, Object... otherParams) {
|
||||
new RMG_Smithing(inv.getPlayer(), inv.getEdited(), recipeName, this).open(inv.getPreviousPage());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MythicRecipeBlueprint sendToMythicLib(@NotNull MMOItemTemplate template, @NotNull ConfigurationSection recipeTypeSection, @NotNull String recipeName, @NotNull Ref<NamespacedKey> namespace, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException {
|
||||
|
||||
// Prior Preparations (update old formats)
|
||||
RecipeMakerGUI.moveInput(recipeTypeSection, recipeName);
|
||||
|
||||
// Read some values
|
||||
ConfigurationSection recipeSection = RecipeMakerGUI.getSection(recipeTypeSection, recipeName);
|
||||
NamespacedKey nk = namespace.getValue();
|
||||
if (nk == null) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Illegal (Null) Namespace")); }
|
||||
|
||||
//region Identify the input
|
||||
|
||||
// Find value in files
|
||||
String input = RMGRI_Smithing.updateIngredients(recipeSection.getString(RecipeMakerGUI.INPUT_INGREDIENTS));
|
||||
String[] inputSplit = input.split("\\|");
|
||||
|
||||
// All right lets read them
|
||||
ProvidedUIFilter itemPoof = RecipeMakerGUI.readIngredientFrom(inputSplit[0], ffp);
|
||||
ProvidedUIFilter ingotPoof = RecipeMakerGUI.readIngredientFrom(inputSplit[1], ffp);
|
||||
if (itemPoof.isAir() || ingotPoof.isAir()) { throw new IllegalArgumentException(FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Smithing recipe containing AIR, $fignored$b.")); }
|
||||
|
||||
// Make ingredients
|
||||
MythicRecipeIngredient itemIngredient = new MythicRecipeIngredient(itemPoof);
|
||||
MythicRecipeIngredient ingotIngredient = new MythicRecipeIngredient(ingotPoof);
|
||||
|
||||
// Make input recipes
|
||||
ShapelessRecipe inputItem = new ShapelessRecipe(nk.getKey(), itemIngredient);
|
||||
ShapelessRecipe inputIngot = new ShapelessRecipe(nk.getKey(), ingotIngredient);
|
||||
//endregion
|
||||
|
||||
//region Identify the output of ingredients
|
||||
// Find value in files
|
||||
String output = RMGRI_Smithing.updateIngredients(recipeSection.getString(RecipeMakerGUI.OUTPUT_INGREDIENTS));
|
||||
String[] outputSplit = output.split("\\|");
|
||||
|
||||
// All right lets read them
|
||||
ProvidedUIFilter itemOPoof = RecipeMakerGUI.readIngredientFrom(outputSplit[0], ffp);
|
||||
ProvidedUIFilter ingotOPoof = RecipeMakerGUI.readIngredientFrom(outputSplit[1], ffp);
|
||||
|
||||
// Make output recipes
|
||||
ShapedRecipe outputItem = itemOPoof.isAir() ? null : ShapedRecipe.single(nk.getKey(), itemOPoof);
|
||||
ShapedRecipe outputIngot = ingotOPoof.isAir() ? null : ShapedRecipe.single(nk.getKey(), ingotOPoof);
|
||||
//endregion
|
||||
|
||||
// Read the options and output
|
||||
int outputAmount = recipeSection.getInt(RBA_AmountOutput.AMOUNT_INGREDIENTS, 1);
|
||||
boolean dropGems = recipeSection.getBoolean(RBA_DropGems.SMITH_GEMS, false);
|
||||
SmithingCombinationType upgradeEffect = readSCT(recipeSection.getString(RBA_SmithingEnchantments.SMITH_ENCHANTS));
|
||||
SmithingCombinationType enchantEffect = readSCT(recipeSection.getString(RBA_SmithingUpgrades.SMITH_UPGRADES));
|
||||
|
||||
// Build Output
|
||||
CustomSmithingRecipe outputRecipe = new CustomSmithingRecipe(template, dropGems, enchantEffect, upgradeEffect, outputAmount);
|
||||
outputRecipe.setMainInputConsumption(outputItem);
|
||||
outputRecipe.setIngotInputConsumption(outputIngot);
|
||||
|
||||
// That's our blueprint :)
|
||||
MythicRecipeBlueprint ret = new MythicRecipeBlueprint(inputItem, outputRecipe, nk);
|
||||
ret.addSideCheck("ingot", inputIngot);
|
||||
|
||||
// Enable it
|
||||
ret.deploy(MythicRecipeStation.SMITHING, namespace);
|
||||
|
||||
// That's it
|
||||
return ret;
|
||||
}
|
||||
|
||||
@NotNull SmithingCombinationType readSCT(@Nullable String str) {
|
||||
|
||||
// Default value is max
|
||||
if (str == null) { return SmithingCombinationType.MAXIMUM; }
|
||||
|
||||
// Correct syntax or default
|
||||
try { return SmithingCombinationType.valueOf(str); } catch (IllegalArgumentException ignored) { return SmithingCombinationType.MAXIMUM; }
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeBlueprint;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* With information on displaying and creating Recipes.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public interface RecipeRegistry {
|
||||
|
||||
/**
|
||||
* @return In item YML configurations, recipes are saved under
|
||||
* <code>[ID].crafting.[recipe]</code>, where this string
|
||||
* is the value of [recipe].
|
||||
* <br>
|
||||
* Ex. <code>STEEL_SWORD.crafting.shaped</code>
|
||||
*/
|
||||
@NotNull String getRecipeConfigPath();
|
||||
|
||||
/**
|
||||
* When making an inventory, the chest name reads '{@code Recipe Editor - ######}' <br>
|
||||
* This method retuns the value of that '######'.
|
||||
* <br><br>
|
||||
* For example: <br>
|
||||
* {@code Recipe Editor - Brewing}
|
||||
*
|
||||
* @return The type of recipe this is for.
|
||||
*/
|
||||
@NotNull
|
||||
String getRecipeTypeName();
|
||||
|
||||
/**
|
||||
* @return The item that means this type of recipes.
|
||||
* For example a workbench for shaped recipes,
|
||||
* or a furnace for smelting.
|
||||
*/
|
||||
@NotNull ItemStack getDisplayListItem();
|
||||
|
||||
/**
|
||||
* Opens the correct recipe to the player.
|
||||
*
|
||||
* @param inv Edition Inventory by which the player is opening this
|
||||
* @param recipeName Name of the recipe
|
||||
* @param otherParams Whatever else required by the constructor of the {@link net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI}
|
||||
*/
|
||||
void openForPlayer(@NotNull EditionInventory inv, @NotNull String recipeName, Object... otherParams);
|
||||
|
||||
/**
|
||||
* This is the part that sends the recipe to mythiclib and what not.
|
||||
*
|
||||
* @param recipeTypeSection The configuration section [ID].base.crafting.[TYPE]
|
||||
*
|
||||
* You kind of have access to all other loaded recipes of this type,
|
||||
* not only the one being loaded, but please just load the one passed
|
||||
* as 'recipeName' parameter.
|
||||
*
|
||||
* @param recipeName Name of <u>the</u> recipe that is being loaded.
|
||||
*
|
||||
* @param namespace Namespace under which you should save this recipe.
|
||||
*
|
||||
* It will initially have the Namespaced Key you should use, but
|
||||
* when you pass it to MythicLib, MythicLib will make it null if
|
||||
* the recipe fails to register onto the crafting book, which is
|
||||
* the expected behaviour.
|
||||
*
|
||||
* @return The Activated Recipe Blueprint (so that it can be unloaded when reloading recipes)
|
||||
*
|
||||
* @throws IllegalArgumentException If anything goes wrong. THIS MEANS THE RECIPE WAS NOT ENABLED.
|
||||
*
|
||||
*/
|
||||
@NotNull MythicRecipeBlueprint sendToMythicLib(@NotNull MMOItemTemplate template, @NotNull ConfigurationSection recipeTypeSection, @NotNull String recipeName, @NotNull Ref<NamespacedKey> namespace, @NotNull FriendlyFeedbackProvider ffp) throws IllegalArgumentException;
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy;
|
||||
|
||||
import net.Indyuce.mmoitems.api.recipe.workbench.ingredients.WorkbenchIngredient;
|
||||
import net.Indyuce.mmoitems.manager.RecipeManager;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Used to handle furnace/smoker/campfire/furnace
|
||||
* extra crafting recipe parameters
|
||||
*
|
||||
* @author ASangarin
|
||||
*/
|
||||
public class BurningRecipeInformation {
|
||||
private final WorkbenchIngredient choice;
|
||||
private final float exp;
|
||||
private final int burnTime;
|
||||
|
||||
public BurningRecipeInformation(@NotNull ConfigurationSection config) {
|
||||
|
||||
// Get item
|
||||
String itemIngredient = config.getString("item");
|
||||
if (itemIngredient == null) { throw new IllegalArgumentException("Invalid input ingredient"); }
|
||||
|
||||
// Get
|
||||
choice = RecipeManager.getWorkbenchIngredient(itemIngredient);
|
||||
exp = (float) config.getDouble("exp", 0.35);
|
||||
burnTime = config.getInt("time", 200);
|
||||
}
|
||||
|
||||
public BurningRecipeInformation(@NotNull WorkbenchIngredient ingredient, float exp, int burnTime) {
|
||||
choice = ingredient;
|
||||
this.exp = exp;
|
||||
this.burnTime = burnTime;
|
||||
}
|
||||
|
||||
public int getBurnTime() {
|
||||
return burnTime;
|
||||
}
|
||||
|
||||
public WorkbenchIngredient getChoice() {
|
||||
return choice;
|
||||
}
|
||||
|
||||
public float getExp() {
|
||||
return exp;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy;
|
||||
|
||||
import net.Indyuce.mmoitems.api.recipe.CraftingType;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_LegacyBurning;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The blast furnace yes
|
||||
*/
|
||||
public class RMGRR_LBBlast extends RMGRR_LegacyBurning {
|
||||
@NotNull @Override public CraftingType getLegacyBurningType() { return CraftingType.BLAST; }
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy;
|
||||
|
||||
import net.Indyuce.mmoitems.api.recipe.CraftingType;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_LegacyBurning;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The campfire yes
|
||||
*/
|
||||
public class RMGRR_LBCampfire extends RMGRR_LegacyBurning {
|
||||
@NotNull @Override public CraftingType getLegacyBurningType() { return CraftingType.CAMPFIRE; }
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy;
|
||||
|
||||
import net.Indyuce.mmoitems.api.recipe.CraftingType;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_LegacyBurning;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The furnace yes
|
||||
*/
|
||||
public class RMGRR_LBFurnace extends RMGRR_LegacyBurning {
|
||||
|
||||
@NotNull @Override public CraftingType getLegacyBurningType() { return CraftingType.FURNACE; }
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy;
|
||||
|
||||
import net.Indyuce.mmoitems.api.recipe.CraftingType;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RMGRR_LegacyBurning;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The smoker yes
|
||||
*/
|
||||
public class RMGRR_LBSmoker extends RMGRR_LegacyBurning {
|
||||
@NotNull @Override public CraftingType getLegacyBurningType() { return CraftingType.SMOKER; }
|
||||
}
|
@ -8,8 +8,11 @@ import net.Indyuce.mmoitems.gui.ItemBrowser;
|
||||
import net.Indyuce.mmoitems.gui.PluginInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.ItemEdition;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeBrowserGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeEdition;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeListEdition;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeListGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@ -61,12 +64,18 @@ public class GuiListener implements Listener {
|
||||
|
||||
MMOItemTemplate template = ((EditionInventory) inventory).getEdited();
|
||||
if (item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + AltChar.rightArrow + " Back")) {
|
||||
if (inventory instanceof ItemEdition)
|
||||
new ItemBrowser(player, template.getType()).open();
|
||||
else if (inventory instanceof RecipeEdition)
|
||||
new RecipeListEdition(player, template).open(((EditionInventory) inventory).getPreviousPage());
|
||||
else
|
||||
new ItemEdition(player, template).onPage(((EditionInventory) inventory).getPreviousPage()).open();
|
||||
|
||||
// Open the Item Browser yes
|
||||
if (inventory instanceof ItemEdition) { new ItemBrowser(player, template.getType()).open(); }
|
||||
|
||||
// Open the RECIPE TYPE BROWSER stat thing
|
||||
else if ((inventory instanceof RecipeListGUI)) { new RecipeBrowserGUI(player, template).open(((EditionInventory) inventory).getPreviousPage()); }
|
||||
|
||||
// Open the RECIPE LIST thing
|
||||
else if ((inventory instanceof RecipeMakerGUI)) { new RecipeListGUI(player, template, ((RecipeMakerGUI) inventory).getRecipeRegistry()).open(((EditionInventory) inventory).getPreviousPage()); }
|
||||
|
||||
// Just open the ITEM EDITION I guess
|
||||
else { new ItemEdition(player, template).onPage(((EditionInventory) inventory).getPreviousPage()).open(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,6 +229,9 @@ public class ConfigManager implements Reloadable {
|
||||
revisionOptions = keepData != null ? new ReforgeOptions(keepData) : new ReforgeOptions(false, false, false, false, false, false, false, true);
|
||||
phatLootsOptions = phatLoots != null ? new ReforgeOptions(phatLoots) : new ReforgeOptions(false, false, false, false, false, false, false, true);
|
||||
|
||||
List<String> exemptedPhatLoots = MMOItems.plugin.getConfig().getStringList("item-revision.disable-phat-loot");
|
||||
for (String epl : exemptedPhatLoots) { phatLootsOptions.addToBlacklist(epl); }
|
||||
|
||||
try {
|
||||
defaultItemCapacity = new NumericStatFormula(MMOItems.plugin.getConfig().getConfigurationSection("default-item-capacity"));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
|
@ -4,13 +4,19 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeBlueprint;
|
||||
import io.lumine.mythic.lib.api.crafting.recipes.MythicRecipeStation;
|
||||
import io.lumine.mythic.lib.api.crafting.uifilters.VanillaUIFilter;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import io.lumine.mythic.lib.api.util.Ref;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackMessage;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.api.crafting.recipe.SmithingCombinationType;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeBrowserGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.RecipeRegistry;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.registry.burninglegacy.BurningRecipeInformation;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Material;
|
||||
@ -27,7 +33,6 @@ import org.bukkit.inventory.Recipe;
|
||||
import org.bukkit.inventory.RecipeChoice;
|
||||
import org.bukkit.inventory.SmokingRecipe;
|
||||
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.Type;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
@ -37,7 +42,6 @@ import net.Indyuce.mmoitems.api.recipe.workbench.ingredients.AirIngredient;
|
||||
import net.Indyuce.mmoitems.api.recipe.workbench.ingredients.MMOItemIngredient;
|
||||
import net.Indyuce.mmoitems.api.recipe.workbench.ingredients.VanillaIngredient;
|
||||
import net.Indyuce.mmoitems.api.recipe.workbench.ingredients.WorkbenchIngredient;
|
||||
import net.Indyuce.mmoitems.stat.data.DoubleData;
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -67,6 +71,7 @@ public class RecipeManager implements Reloadable {
|
||||
*/
|
||||
final HashMap<NamespacedKey, MythicRecipeBlueprint> customRecipes = new HashMap<>();
|
||||
final ArrayList<MythicRecipeBlueprint> booklessRecipes = new ArrayList<>();
|
||||
@NotNull ArrayList<NamespacedKey> blacklistedFromAutomaticDiscovery = new ArrayList<>();
|
||||
|
||||
private boolean book, amounts;
|
||||
|
||||
@ -101,50 +106,72 @@ public class RecipeManager implements Reloadable {
|
||||
// Does it have a crafting recipe?
|
||||
if (config.contains(template.getId() + ".base.crafting")) {
|
||||
|
||||
try {
|
||||
// Get section containing the crafting recipes
|
||||
ConfigurationSection section = RecipeMakerGUI.getSection(config, template.getId() + ".base.crafting");
|
||||
|
||||
ConfigurationSection section = config.getConfigurationSection(template.getId() + ".base.crafting");
|
||||
// All loaded recipes
|
||||
for (String recipeType : RecipeBrowserGUI.getRegisteredRecipes()) {
|
||||
|
||||
// Delegate recipes to their parsers
|
||||
if (section.contains("shaped"))
|
||||
section.getConfigurationSection("shaped").getKeys(false).forEach(
|
||||
recipe -> registerRecipe(type, template.getId(), section.getStringList("shaped." + recipe), false, recipe));
|
||||
if (section.contains("shapeless"))
|
||||
section.getConfigurationSection("shapeless").getKeys(false).forEach(
|
||||
recipe -> registerRecipe(type, template.getId(), section.getStringList("shapeless." + recipe), true, recipe));
|
||||
if (section.contains("furnace"))
|
||||
section.getConfigurationSection("furnace").getKeys(false)
|
||||
.forEach(recipe -> registerBurningRecipe(BurningRecipeType.FURNACE, type, template.getId(),
|
||||
new BurningRecipeInformation(section.getConfigurationSection("furnace." + recipe)), recipe));
|
||||
if (section.contains("blast"))
|
||||
section.getConfigurationSection("blast").getKeys(false)
|
||||
.forEach(recipe -> registerBurningRecipe(BurningRecipeType.BLAST, type, template.getId(),
|
||||
new BurningRecipeInformation(section.getConfigurationSection("blast." + recipe)), recipe));
|
||||
if (section.contains("smoker"))
|
||||
section.getConfigurationSection("smoker").getKeys(false)
|
||||
.forEach(recipe -> registerBurningRecipe(BurningRecipeType.SMOKER, type, template.getId(),
|
||||
new BurningRecipeInformation(section.getConfigurationSection("smoker." + recipe)), recipe));
|
||||
if (section.contains("campfire"))
|
||||
section.getConfigurationSection("campfire").getKeys(false)
|
||||
.forEach(recipe -> registerBurningRecipe(BurningRecipeType.CAMPFIRE, type, template.getId(),
|
||||
new BurningRecipeInformation(section.getConfigurationSection("campfire." + recipe)), recipe));
|
||||
if (section.contains("smithing"))
|
||||
section.getConfigurationSection("smithing").getKeys(false).forEach(recipe -> registerSmithingRecipe(type,
|
||||
template.getId(), section.getConfigurationSection("smithing." + recipe), recipe));
|
||||
// Is it in-yo?
|
||||
if (section.contains(recipeType)) {
|
||||
|
||||
// Uh heck
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// Get Registry
|
||||
RecipeRegistry rr = RecipeBrowserGUI.getRegisteredRecipe(recipeType);
|
||||
|
||||
// Add message
|
||||
ffp.log(FriendlyFeedbackCategory.ERROR, "Could not load recipe of $f{0} {1}$b: {2}",
|
||||
type.getId(), template.getId(), exception.getMessage());
|
||||
// Get recipe type section
|
||||
ConfigurationSection typeSection = RecipeMakerGUI.getSection(section, recipeType);
|
||||
|
||||
// Register dem
|
||||
for (String recipeName : typeSection.getKeys(false)) {
|
||||
|
||||
// Generate its key
|
||||
NamespacedKey nk = getRecipeKey(template.getType(), template.getId(), recipeType, recipeName);
|
||||
|
||||
// Wrap
|
||||
Ref<NamespacedKey> nkRef = new Ref<>(nk);
|
||||
|
||||
// Error yes
|
||||
FriendlyFeedbackProvider ffpMinor = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffpMinor.activatePrefix(true, "Recipe of $u" + template.getType() + " " + template.getId());
|
||||
|
||||
// Send to mythiclib
|
||||
try {
|
||||
|
||||
// The result of sending to MythicLib
|
||||
MythicRecipeBlueprint blueprint = rr.sendToMythicLib(template, typeSection, recipeName, nkRef, ffpMinor);
|
||||
|
||||
// Was it registered in the book, then?
|
||||
if (nkRef.getValue() != null) {
|
||||
customRecipes.put(nkRef.getValue(), blueprint);
|
||||
|
||||
// Bookless, include in the other list.
|
||||
} else { booklessRecipes.add(blueprint); }
|
||||
|
||||
|
||||
// Well something went wrong...
|
||||
} catch (IllegalArgumentException error) {
|
||||
|
||||
// Empty message? Snooze that
|
||||
if (!error.getMessage().isEmpty()) {
|
||||
|
||||
// Log error
|
||||
MMOItems.print(null, "Cannot register custom recipe '$u{2}$b' for $e{0} {1}$b;$f {3}", "Custom Crafting", type.getId(), template.getId(), recipeName, error.getMessage());
|
||||
|
||||
// Include failures in the report
|
||||
ffpMinor.sendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
|
||||
ffpMinor.sendTo(FriendlyFeedbackCategory.FAILURE, MMOItems.getConsole());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log all
|
||||
ffp.sendAllTo(MMOItems.getConsole());
|
||||
// Log relevant messages
|
||||
ffp.sendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
|
||||
ffp.sendTo(FriendlyFeedbackCategory.FAILURE, MMOItems.getConsole());
|
||||
|
||||
// Sort recipes
|
||||
sortRecipes();
|
||||
@ -153,97 +180,19 @@ public class RecipeManager implements Reloadable {
|
||||
Bukkit.getScheduler().runTask(MMOItems.plugin, () -> getLoadedLegacyRecipes().forEach(Bukkit::addRecipe));
|
||||
}
|
||||
|
||||
public void registerBurningRecipe(BurningRecipeType recipeType, Type type, String id, BurningRecipeInformation info, String recipeId) {
|
||||
NamespacedKey key = getRecipeKey(type, id, recipeType.getPath(), recipeId);
|
||||
MMOItem mmo = MMOItems.plugin.getMMOItem(type, id);
|
||||
final int amount = mmo.hasData(ItemStats.CRAFT_AMOUNT) ? (int) ((DoubleData) mmo.getData(ItemStats.CRAFT_AMOUNT)).getValue() : 1;
|
||||
public void registerBurningRecipe(@NotNull BurningRecipeType recipeType, @NotNull MMOItem mmo, @NotNull BurningRecipeInformation info, int amount, @NotNull NamespacedKey key, boolean hidden) {
|
||||
|
||||
// Build its item stacc
|
||||
ItemStack stack = mmo.newBuilder().build();
|
||||
stack.setAmount(amount);
|
||||
|
||||
// Do whatever this is / I just wont touch it
|
||||
CookingRecipe<?> recipe = recipeType.provideRecipe(key, stack, info.getChoice().toBukkit(), info.getExp(), info.getBurnTime());
|
||||
|
||||
// Register that recipe lets goo
|
||||
loadedLegacyRecipes.add(recipe);
|
||||
}
|
||||
|
||||
public void registerSmithingRecipe(@NotNull Type type, @NotNull String id, @NotNull ConfigurationSection section, @NotNull String number) throws IllegalArgumentException {
|
||||
Validate.isTrue(section.isString("input1") && section.isString("input2"), "Invalid smithing recipe for '" + type.getId() + " . " + id + "'");
|
||||
|
||||
String item = section.getString("input1");
|
||||
String ingot = section.getString("input2");
|
||||
boolean dropGems = section.getBoolean("drop-gems", false);
|
||||
String upgrade = section.getString("upgrades" );
|
||||
String enchants = section.getString("enchantments" );
|
||||
if (item == null) { item = ""; }
|
||||
if (ingot == null) { ingot = ""; }
|
||||
if (upgrade == null) { upgrade = SmithingCombinationType.MAXIMUM.toString(); }
|
||||
if (enchants == null) { enchants = SmithingCombinationType.MAXIMUM.toString(); }
|
||||
|
||||
MythicRecipeBlueprint blueprint = CustomRecipe.generateSmithing(type, id, item, ingot, dropGems, enchants, upgrade, getRecipeKey(type, id, "smithing", number).getKey());
|
||||
|
||||
// Enable it
|
||||
Ref<NamespacedKey> nk = new Ref<>();
|
||||
blueprint.deploy(MythicRecipeStation.SMITHING, nk);
|
||||
|
||||
// Remember it
|
||||
if (nk.getValue() != null) { customRecipes.put(nk.getValue(), blueprint); } else { booklessRecipes.add(blueprint); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a shapeless or shaped workbench crafting recipe and registers it.
|
||||
*
|
||||
* @param type The item type
|
||||
* @param id The item ID
|
||||
* @param list The list of items (3 lines or 3 ingredients, separated
|
||||
* by spaces)
|
||||
* @param shapeless If the recipe is shapeless or not
|
||||
* @param recipeID Every item can have multiple recipe, there's one number
|
||||
* per recipe to differenciate them
|
||||
*/
|
||||
public void registerRecipe(@NotNull Type type, @NotNull String id, @NotNull List<String> list, boolean shapeless, @NotNull String recipeID) throws IllegalArgumentException {
|
||||
|
||||
/*
|
||||
* The output of the recipe will be the MMOItem of this Type and ID which
|
||||
* is guaranteed to be loaded.
|
||||
*
|
||||
* The input is defined in the list in the following formats:
|
||||
*
|
||||
* SHAPELESS:
|
||||
* + A list of 9 entries, which can be in any order
|
||||
* + Each entry is one item, may be vanilla, MMOItem, or UIFilter.
|
||||
*
|
||||
* SHAPED
|
||||
* + A list of 3 entries, which are in order.
|
||||
* + Each entry is 3 items, separated by spaces, except if UIFilters are used,
|
||||
* which can cause more than 3 items to be apparent.
|
||||
* * Logic to parse UIFilters is included.
|
||||
* + They indicate the rows of the crafting table.
|
||||
*/
|
||||
|
||||
MythicRecipeBlueprint blueprint;
|
||||
if (shapeless) {
|
||||
|
||||
// Generate with no shape
|
||||
blueprint = CustomRecipe.generateShapeless(type, id, list, getRecipeKey(type, id, "shapeless", recipeID).getKey());
|
||||
} else {
|
||||
|
||||
// Generate shaped
|
||||
blueprint = CustomRecipe.generateShaped(type, id, list, getRecipeKey(type, id, "shaped", recipeID).getKey());
|
||||
}
|
||||
|
||||
// Enable it
|
||||
Ref<NamespacedKey> nk = new Ref<>();
|
||||
blueprint.deploy(MythicRecipeStation.WORKBENCH, nk);
|
||||
|
||||
// Remember it
|
||||
if (nk.getValue() != null) { customRecipes.put(nk.getValue(), blueprint); } else { booklessRecipes.add(blueprint);
|
||||
if (book) { MMOItems.print(null, "Cannot register custom {2} recipe for $e{0} {1}$b into crafting book", "Custom Crafting", type.getId(), id, (shapeless ? "shapeless" : "shaped"));} }
|
||||
|
||||
/*
|
||||
CustomRecipe recipe = new CustomRecipe(type, id, list, shapeless);
|
||||
|
||||
if (amounts)
|
||||
registerRecipeAsCustom(recipe);
|
||||
else
|
||||
registerRecipeAsBukkit(recipe, number);
|
||||
*/
|
||||
if (hidden) { blacklistedFromAutomaticDiscovery.add(key); }
|
||||
}
|
||||
|
||||
public void registerRecipeAsCustom(CustomRecipe recipe) {
|
||||
@ -266,7 +215,8 @@ public class RecipeManager implements Reloadable {
|
||||
}
|
||||
public HashMap<NamespacedKey, MythicRecipeBlueprint> getCustomRecipes() { return customRecipes; }
|
||||
|
||||
ArrayList<NamespacedKey> generatedNKs = null;
|
||||
@Nullable
|
||||
ArrayList<NamespacedKey> generatedNKs;
|
||||
public ArrayList<NamespacedKey> getNamespacedKeys() {
|
||||
|
||||
if (generatedNKs != null) { return generatedNKs; }
|
||||
@ -305,6 +255,7 @@ public class RecipeManager implements Reloadable {
|
||||
|
||||
// Clear loaded recipes
|
||||
loadedLegacyRecipes.clear();
|
||||
blacklistedFromAutomaticDiscovery.clear();
|
||||
|
||||
// Disable and forget all blueprints
|
||||
for (NamespacedKey b : customRecipes.keySet()) {
|
||||
@ -330,11 +281,21 @@ public class RecipeManager implements Reloadable {
|
||||
|
||||
public void refreshRecipeBook(Player player) {
|
||||
|
||||
/*
|
||||
* todo For some reason, we have to refresh the book every time
|
||||
* the player joins the server or something; the thing is
|
||||
* that recipes that are hidden from the book are lost when
|
||||
* doing this (if they had unlocked them somehow).
|
||||
* -
|
||||
* Kind of need to somehow remember what recipes have been
|
||||
* unlocked by who so that they don't get lost...
|
||||
*/
|
||||
|
||||
// Book disabled?
|
||||
if (!book) {
|
||||
|
||||
// Hide all recipes
|
||||
for (NamespacedKey key : player.getDiscoveredRecipes()) { if (key.getNamespace().equals("mmoitems")) { player.undiscoverRecipe(key); } }
|
||||
for (NamespacedKey key : player.getDiscoveredRecipes()) { if ("mmoitems".equals(key.getNamespace())) { player.undiscoverRecipe(key); } }
|
||||
|
||||
// Done woah
|
||||
return;
|
||||
@ -345,11 +306,19 @@ public class RecipeManager implements Reloadable {
|
||||
|
||||
// Undiscovers the recipes apparently
|
||||
for (NamespacedKey key : player.getDiscoveredRecipes()) {
|
||||
if (key.getNamespace().equals("mmoitems") && !getNamespacedKeys().contains(key)) { player.undiscoverRecipe(key); } }
|
||||
if ("mmoitems".equals(key.getNamespace()) && !getNamespacedKeys().contains(key)) { player.undiscoverRecipe(key); } }
|
||||
|
||||
// And discovers them again, sweet!
|
||||
// And discovers them again
|
||||
for (NamespacedKey recipe : getNamespacedKeys()) {
|
||||
if (recipe == null) { continue; }
|
||||
|
||||
// Not blacklisted right
|
||||
boolean blacklisted = false;
|
||||
for (NamespacedKey black : blacklistedFromAutomaticDiscovery) {
|
||||
|
||||
if (recipe.equals(black)) { blacklisted = true; break; } }
|
||||
if (blacklisted) { continue; }
|
||||
|
||||
try { if (!player.hasDiscoveredRecipe(recipe)) { player.discoverRecipe(recipe); } }
|
||||
catch (Throwable e) { MMOItems.print(null, "Could not register crafting book recipe for $r{0}$b:$f {1}", "MMOItems Custom Crafting", recipe.getKey(), e.getMessage()); }
|
||||
}
|
||||
@ -361,33 +330,66 @@ public class RecipeManager implements Reloadable {
|
||||
// Discovers all recipes
|
||||
for (NamespacedKey recipe : getNamespacedKeys()) {
|
||||
if (recipe == null) { continue; }
|
||||
|
||||
// Not blacklisted aight
|
||||
boolean blacklisted = false;
|
||||
for (NamespacedKey black : blacklistedFromAutomaticDiscovery) {
|
||||
|
||||
if (recipe.equals(black)) { blacklisted = true; break; } }
|
||||
if (blacklisted) { continue; }
|
||||
|
||||
try { player.discoverRecipe(recipe); }
|
||||
catch (Throwable e) { MMOItems.print(null, "Could not register crafting book recipe for $r{0}$b:$f {1}", "MMOItems Custom Crafting", recipe.getKey(), e.getMessage()); }
|
||||
}
|
||||
}
|
||||
|
||||
public WorkbenchIngredient getWorkbenchIngredient(String input) {
|
||||
String[] split = input.split(":");
|
||||
int amount = split.length > 1 ? Integer.parseInt(split[1]) : 1;
|
||||
@NotNull public static WorkbenchIngredient getWorkbenchIngredient(@NotNull String input) throws IllegalArgumentException {
|
||||
|
||||
if (split[0].contains(".")) {
|
||||
String[] split1 = split[0].split("\\.");
|
||||
Type type = MMOItems.plugin.getTypes().getOrThrow(split1[0].toUpperCase().replace("-", "_").replace(" ", "_"));
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplateOrThrow(type,
|
||||
split1[1].toUpperCase().replace("-", "_").replace(" ", "_"));
|
||||
return new MMOItemIngredient(type, template.getId(), amount);
|
||||
// Read it this other way ~
|
||||
ProvidedUIFilter poof = ProvidedUIFilter.getFromString(RecipeMakerGUI.poofFromLegacy(input), null);
|
||||
|
||||
// Air is AIR
|
||||
if (poof == null) { return new AirIngredient(); }
|
||||
|
||||
// With class, obviously - no need for prefix tho
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
|
||||
// Valid right
|
||||
if (!poof.isValid(ffp)) {
|
||||
|
||||
// Snooze that
|
||||
//noinspection ConstantConditions
|
||||
throw new IllegalArgumentException(SilentNumbers.collapseList(SilentNumbers.transcribeList(ffp.getFeedbackOf(FriendlyFeedbackCategory.ERROR), s -> ((FriendlyFeedbackMessage) s).forConsole(FFPMMOItems.get())), ". "));
|
||||
}
|
||||
|
||||
if (split[0].equalsIgnoreCase("air"))
|
||||
return new AirIngredient();
|
||||
// Get amount
|
||||
int amount = poof.getAmount(0);
|
||||
|
||||
return new VanillaIngredient(Material.valueOf(split[0].toUpperCase().replace("-", "_").replace(" ", "_")), amount);
|
||||
// MMOItem?
|
||||
if (poof.getParent() instanceof MMOItemUIFilter) {
|
||||
|
||||
// Get those
|
||||
Type miType = MMOItems.plugin.getTypes().getOrThrow(poof.getArgument());
|
||||
|
||||
// Find template
|
||||
MMOItemTemplate mmo = MMOItems.plugin.getTemplates().getTemplateOrThrow(miType, poof.getData());
|
||||
|
||||
// Treat is as MMOItem :pogyoo:
|
||||
return new MMOItemIngredient(miType, mmo.getId(), amount);
|
||||
|
||||
// Must be vanilla
|
||||
} else if (poof.getParent() instanceof VanillaUIFilter) {
|
||||
|
||||
return new VanillaIngredient(Material.valueOf(poof.getArgument().toUpperCase().replace("-", "_").replace(" ", "_")), amount);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported ingredient, you may only specify vanilla or mmoitems.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Easier control of furnace, smoker, campfire and blast recipes so there is
|
||||
* no need to have four time the same method to register this type of recipe
|
||||
*
|
||||
*
|
||||
* @author cympe
|
||||
*/
|
||||
public enum BurningRecipeType {
|
||||
@ -412,37 +414,5 @@ public class RecipeManager implements Reloadable {
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RecipeProvider {
|
||||
CookingRecipe<?> provide(NamespacedKey key, ItemStack result, RecipeChoice source, float experience, int cookTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to handle furnace/smoker/campfire/furnace extra crafting recipe
|
||||
* parameters
|
||||
*
|
||||
* @author ASangarin
|
||||
*/
|
||||
public class BurningRecipeInformation {
|
||||
private final WorkbenchIngredient choice;
|
||||
private final float exp;
|
||||
private final int burnTime;
|
||||
|
||||
public BurningRecipeInformation(ConfigurationSection config) {
|
||||
choice = getWorkbenchIngredient(config.getString("item"));
|
||||
exp = (float) config.getDouble("exp", 0.35);
|
||||
burnTime = config.getInt("time", 200);
|
||||
}
|
||||
|
||||
public int getBurnTime() {
|
||||
return burnTime;
|
||||
}
|
||||
|
||||
public WorkbenchIngredient getChoice() {
|
||||
return choice;
|
||||
}
|
||||
|
||||
public float getExp() {
|
||||
return exp;
|
||||
}
|
||||
}
|
||||
public interface RecipeProvider { CookingRecipe<?> provide(NamespacedKey key, ItemStack result, RecipeChoice source, float experience, int cookTime);}
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
package net.Indyuce.mmoitems.manager;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.ConfigFile;
|
||||
import net.Indyuce.mmoitems.api.ItemTier;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.util.message.FFPMMOItems;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class TierManager implements Reloadable{
|
||||
private final Map<String, ItemTier> tiers = new HashMap<>();
|
||||
@ -22,42 +28,105 @@ public class TierManager implements Reloadable{
|
||||
public void reload() {
|
||||
tiers.clear();
|
||||
|
||||
// For logging
|
||||
FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get());
|
||||
ffp.activatePrefix(true, "Tiers");
|
||||
|
||||
ConfigFile config = new ConfigFile("item-tiers");
|
||||
for (String key : config.getConfig().getKeys(false))
|
||||
for (String tierName : config.getConfig().getKeys(false)) {
|
||||
|
||||
// Get section (Using RecipeMakerGUI for @NotNull attribute)
|
||||
ConfigurationSection tierSection = RecipeMakerGUI.getSection(config.getConfig(), tierName);
|
||||
|
||||
// Attempt to register
|
||||
try {
|
||||
register(new ItemTier(config.getConfig().getConfigurationSection(key)));
|
||||
register(new ItemTier(tierSection));
|
||||
|
||||
// Any errors?
|
||||
} catch (IllegalArgumentException exception) {
|
||||
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not load item tier '" + key + "': " + exception.getMessage());
|
||||
|
||||
// Log error
|
||||
ffp.log(FriendlyFeedbackCategory.ERROR, "Cannot register tier '$u{0}$b';$f {1}", tierName, exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Log relevant messages
|
||||
ffp.sendTo(FriendlyFeedbackCategory.ERROR, MMOItems.getConsole());
|
||||
ffp.sendTo(FriendlyFeedbackCategory.FAILURE, MMOItems.getConsole());
|
||||
}
|
||||
|
||||
public void register(ItemTier tier) {
|
||||
tiers.put(tier.getId(), tier);
|
||||
}
|
||||
/**
|
||||
* Set a tier live to be used everywhere in the plugin.
|
||||
*
|
||||
* @param tier Tier to register
|
||||
*/
|
||||
public void register(@NotNull ItemTier tier) { tiers.put(tier.getId(), tier); }
|
||||
|
||||
public boolean has(String id) {
|
||||
/**
|
||||
* @param id Tier name
|
||||
*
|
||||
* @return If a tier of this name is loaded
|
||||
*/
|
||||
public boolean has(@Nullable String id) {
|
||||
|
||||
/*
|
||||
* No null tiers, but for flexibility of use, just
|
||||
* make it @Nullable and make it return false always.
|
||||
*/
|
||||
if (id == null) { return false; }
|
||||
|
||||
// Is the tier loaded?
|
||||
return tiers.containsKey(id);
|
||||
}
|
||||
|
||||
public ItemTier getOrThrow(String id) {
|
||||
Validate.isTrue(tiers.containsKey(id), "Could not find tier with ID '" + id + "'");
|
||||
/**
|
||||
*
|
||||
* @param id Tier name, technically Nullable but this guarantees
|
||||
* that an IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @throws IllegalArgumentException When there is no tier of such name loaded.
|
||||
*
|
||||
* @return Tier of this name.
|
||||
*/
|
||||
@NotNull public ItemTier getOrThrow(@Nullable String id) throws IllegalArgumentException {
|
||||
|
||||
// Well, is it loaded?
|
||||
Validate.isTrue(tiers.containsKey(id), FriendlyFeedbackProvider.quickForConsole(FFPMMOItems.get(), "Could not find tier with ID '$r{0}$b'", id));
|
||||
|
||||
// Well its guaranteed to not be null.
|
||||
return tiers.get(id);
|
||||
}
|
||||
|
||||
public ItemTier get(String id) {
|
||||
/**
|
||||
* Will get the tier of this name, if there is one
|
||||
*
|
||||
* @param id Name of the tier.
|
||||
*/
|
||||
@Nullable public ItemTier get(@Nullable String id) {
|
||||
if (id == null) { return null; }
|
||||
return tiers.get(id);
|
||||
}
|
||||
|
||||
public Collection<ItemTier> getAll() {
|
||||
/**
|
||||
* @return An iterable of all the tiers loaded
|
||||
*/
|
||||
@NotNull public Collection<ItemTier> getAll() {
|
||||
return tiers.values();
|
||||
}
|
||||
|
||||
public ItemTier findTier(MMOItem item) {
|
||||
/**
|
||||
* @param item Item you seek the tier of
|
||||
*
|
||||
* @return The tier of this item, it it has any
|
||||
*/
|
||||
@Nullable public ItemTier findTier(@NotNull MMOItem item) {
|
||||
try {
|
||||
|
||||
// Has that data?
|
||||
return item.hasData(ItemStats.TIER) ? get(item.getData(ItemStats.TIER).toString()) : null;
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Pretty sure there is no way for this exception to be thrown tho
|
||||
} catch (IllegalArgumentException exception) { return null; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class TypeManager implements Reloadable {
|
||||
return map.get(id);
|
||||
}
|
||||
|
||||
public Type getOrThrow(String id) {
|
||||
@NotNull public Type getOrThrow(@Nullable String id) {
|
||||
Validate.isTrue(map.containsKey(id), "Could not find item type with ID '" + id + "'");
|
||||
return map.get(id);
|
||||
}
|
||||
|
@ -4,19 +4,25 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.ProvidedUIFilter;
|
||||
import io.lumine.mythic.lib.api.crafting.uimanager.UIFilterManager;
|
||||
import io.lumine.mythic.lib.api.item.ItemTag;
|
||||
import io.lumine.mythic.lib.api.util.ui.QuickNumberRange;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeBrowserGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.recipes.RecipeMakerGUI;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.interpreters.RMG_RecipeInterpreter;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.rba.RecipeButtonAction;
|
||||
import net.Indyuce.mmoitems.stat.data.StringData;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.MMOUtils;
|
||||
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
|
||||
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeListEdition;
|
||||
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
|
||||
import net.Indyuce.mmoitems.stat.data.type.StatData;
|
||||
import net.Indyuce.mmoitems.stat.type.ItemStat;
|
||||
@ -34,7 +40,7 @@ public class Crafting extends ItemStat {
|
||||
@Override
|
||||
public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) {
|
||||
if (event.getAction() == InventoryAction.PICKUP_ALL)
|
||||
new RecipeListEdition(inv.getPlayer(), inv.getEdited()).open(inv.getPage());
|
||||
new RecipeBrowserGUI(inv.getPlayer(), inv.getEdited()).open(inv.getPage());
|
||||
|
||||
else if (event.getAction() == InventoryAction.PICKUP_HALF && inv.getEditedSection().contains("crafting")) {
|
||||
inv.getEditedSection().set("crafting", null);
|
||||
@ -63,90 +69,164 @@ public class Crafting extends ItemStat {
|
||||
|
||||
@Override
|
||||
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {
|
||||
String type = (String) info[0];
|
||||
|
||||
switch (type) {
|
||||
|
||||
/*
|
||||
* Handles shaped and shapeless crafting recipes
|
||||
* #1 Type - Is it input, output, or a button being pressed?
|
||||
*/
|
||||
case "recipe":
|
||||
int slot = (int) info[2];
|
||||
Validate.notNull(MMOItems.plugin.getRecipes().getWorkbenchIngredient(message), "Invalid ingredient");
|
||||
int type = (int) info[0];
|
||||
|
||||
/*
|
||||
* Handles shaped crafting recipes
|
||||
*/
|
||||
if ((info[1]).equals("shaped")) {
|
||||
List<String> newList = inv.getEditedSection().getStringList("crafting.shaped.1");
|
||||
String[] newArray = newList.get(slot / 3).split(" ");
|
||||
newArray[slot % 3] = message;
|
||||
newList.set(slot / 3, (newArray[0] + " " + newArray[1] + " " + newArray[2]));
|
||||
switch (type) {
|
||||
case RecipeMakerGUI.INPUT:
|
||||
case RecipeMakerGUI.OUTPUT:
|
||||
|
||||
for (String s : newList) {
|
||||
if (s.equals("AIR AIR AIR"))
|
||||
continue;
|
||||
//region Transcribe from old format to new
|
||||
int spc = message.indexOf(' ');
|
||||
QuickNumberRange qnr = null;
|
||||
if (spc > 0) {
|
||||
|
||||
inv.getEditedSection().set("crafting.shaped.1", newList);
|
||||
inv.registerTemplateEdition();
|
||||
break;
|
||||
// Any space? attempt to parse that as a number
|
||||
String qnrp = message.substring(spc + 1);
|
||||
|
||||
// Is it just a number 'X' ?
|
||||
if (SilentNumbers.DoubleTryParse(qnrp)) {
|
||||
|
||||
/*
|
||||
* In technical QNR jargon, X means "requires exactly this",
|
||||
* however, many times when crafting, specifying X means that
|
||||
* crafting it once requires that many ingredients.
|
||||
*
|
||||
* Translating the crafting intention into QNR outputs X..
|
||||
*
|
||||
* If anyone truly means that the recipe can only be crafted
|
||||
* having X in the same slot of the crafting table, they will
|
||||
* have to write X..X
|
||||
*/
|
||||
qnrp += "..";
|
||||
}
|
||||
|
||||
// Parse QNR
|
||||
qnr = QuickNumberRange.getFromString(qnrp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles shapeless crafting recipes
|
||||
* Changes easy MMOItems input into MythicLib NBT Filter.
|
||||
*/
|
||||
} else {
|
||||
List<String> newList = inv.getEditedSection().getStringList("crafting.shapeless.1");
|
||||
newList.set(slot, message);
|
||||
if (spc <= 0 || qnr != null) {
|
||||
|
||||
for (String s : newList) {
|
||||
if (s.equals("AIR"))
|
||||
continue;
|
||||
inv.getEditedSection().set("crafting.shapeless.1", newList);
|
||||
inv.registerTemplateEdition();
|
||||
break;
|
||||
// No amount specified=
|
||||
if (qnr == null) {
|
||||
|
||||
// Default is one and onward, 1..
|
||||
qnr = new QuickNumberRange(1D, null);
|
||||
|
||||
// Amount was specified
|
||||
} else {
|
||||
|
||||
// Crop from message
|
||||
message = message.substring(0, spc);
|
||||
}
|
||||
|
||||
// MMOItem?
|
||||
if (message.contains(".")) {
|
||||
|
||||
// Split
|
||||
String[] midSplit = message.split("\\.");
|
||||
|
||||
// MMOItem UIFilter
|
||||
message = "m " + midSplit[0] + " " + midSplit[1] + " " + qnr;
|
||||
|
||||
// Vanilla material
|
||||
} else {
|
||||
|
||||
// Vanilla UIFilter
|
||||
message = "v " + message + " - " + qnr;
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
break;
|
||||
/*
|
||||
* #2 Recipe Interpreter - Correctly edits the configuration section in the files,
|
||||
* depending on how the recipe is supposed to be saved.
|
||||
*/
|
||||
RMG_RecipeInterpreter interpreter = (RMG_RecipeInterpreter) info[1];
|
||||
|
||||
/*
|
||||
* #3 Slot - Which slot was pressed?
|
||||
*/
|
||||
int slot = (int) info[2];
|
||||
|
||||
// Attempt to get
|
||||
ProvidedUIFilter read = UIFilterManager.getUIFilter(message, inv.getFFP());
|
||||
|
||||
// Null? Cancel
|
||||
if (read == null) { throw new IllegalArgumentException(""); }
|
||||
if (!read.isValid(inv.getFFP())) { throw new IllegalArgumentException(""); }
|
||||
|
||||
// Find section
|
||||
ConfigurationSection section = RecipeMakerGUI.getSection(inv.getEditedSection(), "crafting");
|
||||
section = RecipeMakerGUI.getSection(section, ((RecipeMakerGUI) inv).getRecipeRegistry().getRecipeConfigPath());
|
||||
section = RecipeMakerGUI.getSection(section, ((RecipeMakerGUI) inv).getRecipeName());
|
||||
|
||||
// Redirect
|
||||
if (type == RecipeMakerGUI.INPUT) {
|
||||
interpreter.editInput(section, read, slot);
|
||||
|
||||
// It must be output
|
||||
} else {
|
||||
interpreter.editOutput(section, read, slot); }
|
||||
|
||||
// Save changes
|
||||
inv.registerTemplateEdition();
|
||||
|
||||
break;
|
||||
case RecipeMakerGUI.PRIMARY:
|
||||
case RecipeMakerGUI.SECONDARY:
|
||||
|
||||
/*
|
||||
* No Button Action? That's the end, and is not necessarily
|
||||
* an error (the button might have done what it had to do
|
||||
* already when pressed, if it needed no user input).
|
||||
*/
|
||||
if (info.length < 2) { return; }
|
||||
if (!(info[1] instanceof RecipeButtonAction)) { return; }
|
||||
|
||||
// Delegate
|
||||
|
||||
if (type == RecipeMakerGUI.PRIMARY) {
|
||||
((RecipeButtonAction) info[1]).primaryProcessInput(message, info);
|
||||
|
||||
} else {
|
||||
((RecipeButtonAction) info[1]).secondaryProcessInput(message, info); }
|
||||
|
||||
// Save changes
|
||||
inv.registerTemplateEdition();
|
||||
break;
|
||||
|
||||
default: inv.registerTemplateEdition(); break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles burning recipes ie furnace, campfire, smoker and blast
|
||||
* furnace recipes
|
||||
*/
|
||||
case "item": {
|
||||
String[] args = message.split(" ");
|
||||
Validate.isTrue(args.length == 3, "Invalid format");
|
||||
Validate.notNull(MMOItems.plugin.getRecipes().getWorkbenchIngredient(args[0]), "Invalid ingredient");
|
||||
int time = Integer.parseInt(args[1]);
|
||||
double exp = MMOUtils.parseDouble(args[2]);
|
||||
*
|
||||
case "item": {
|
||||
String[] args = message.split(" ");
|
||||
Validate.isTrue(args.length == 3, "Invalid format");
|
||||
Validate.notNull(MMOItems.plugin.getRecipes().getWorkbenchIngredient(args[0]), "Invalid ingredient");
|
||||
int time = Integer.parseInt(args[1]);
|
||||
double exp = MMOUtils.parseDouble(args[2]);
|
||||
|
||||
inv.getEditedSection().set("crafting." + info[1] + ".1.item", args[0]);
|
||||
inv.getEditedSection().set("crafting." + info[1] + ".1.time", time);
|
||||
inv.getEditedSection().set("crafting." + info[1] + ".1.experience", exp);
|
||||
inv.registerTemplateEdition();
|
||||
break;
|
||||
}
|
||||
inv.getEditedSection().set("crafting." + info[1] + ".1.item", args[0]);
|
||||
inv.getEditedSection().set("crafting." + info[1] + ".1.time", time);
|
||||
inv.getEditedSection().set("crafting." + info[1] + ".1.experience", exp);
|
||||
inv.registerTemplateEdition();
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles smithing recipes
|
||||
*/
|
||||
case "smithing": {
|
||||
String[] args = message.split(" ");
|
||||
Validate.isTrue(args.length == 2, "Invalid format");
|
||||
Validate.notNull(MMOItems.plugin.getRecipes().getWorkbenchIngredient(args[0]), "Invalid first ingredient");
|
||||
Validate.notNull(MMOItems.plugin.getRecipes().getWorkbenchIngredient(args[1]), "Invalid second ingredient");
|
||||
|
||||
inv.getEditedSection().set("crafting.smithing.1.input1", args[0]);
|
||||
inv.getEditedSection().set("crafting.smithing.1.input2", args[1]);
|
||||
inv.registerTemplateEdition();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Recipe type not recognized");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RandomStatData whenInitialized(Object object) {
|
||||
return null;
|
||||
|
@ -20,6 +20,8 @@ import java.util.*;
|
||||
* You could consider them a more advanced DisableStat, while DisableStat only
|
||||
* allows to choose <b>true</b> or <b>false</b>, alternating when clicked, Choose
|
||||
* Stats cycle through a list instead.
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public abstract class ChooseStat extends StringStat {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user