diff --git a/build.gradle.kts b/build.gradle.kts index 0a71e7f..fbb6d02 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "xyz.alexcrea" -version = "1.3.2-A2" +version = "1.4.0a" repositories { mavenCentral() diff --git a/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java b/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java index b7f351b..eb74480 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java +++ b/src/main/java/xyz/alexcrea/cuanvil/config/ConfigHolder.java @@ -6,6 +6,7 @@ import org.bukkit.configuration.file.YamlConfiguration; import xyz.alexcrea.cuanvil.group.EnchantConflictManager; import xyz.alexcrea.cuanvil.group.ItemGroupManager; +import xyz.alexcrea.cuanvil.recipe.CustomAnvilRecipeManager; import xyz.alexcrea.cuanvil.util.MetricsUtil; import java.io.File; @@ -18,12 +19,14 @@ public abstract class ConfigHolder { public static ItemGroupConfigHolder ITEM_GROUP_HOLDER; public static ConflictConfigHolder CONFLICT_HOLDER; public static UnitRepairHolder UNIT_REPAIR_HOLDER; + public static CustomAnvilCraftHolder CUSTOM_RECIPE_HOLDER; public static boolean loadConfig() { DEFAULT_CONFIG = new DefaultConfigHolder(); ITEM_GROUP_HOLDER = new ItemGroupConfigHolder(); CONFLICT_HOLDER = new ConflictConfigHolder(); UNIT_REPAIR_HOLDER = new UnitRepairHolder(); + CUSTOM_RECIPE_HOLDER = new CustomAnvilCraftHolder(); boolean result = reloadAllFromDisk(true); if (result) { @@ -41,6 +44,9 @@ public static boolean reloadAllFromDisk(boolean hardfail) { sucess = CONFLICT_HOLDER.reloadFromDisk(hardfail); if (!sucess) return false; sucess = UNIT_REPAIR_HOLDER.reloadFromDisk(hardfail); + if (!sucess) return false; + sucess = CUSTOM_RECIPE_HOLDER.reloadFromDisk(hardfail); + return sucess; } @@ -83,6 +89,7 @@ protected File getLastBackup() { // Save logic public boolean saveToDisk(boolean doBackup) { + CustomAnvil.Companion.log("Saving "+getConfigFileName()); if (doBackup) { if (!saveBackup()) { CustomAnvil.instance.getLogger().severe("Could not save backup. see above."); @@ -104,6 +111,7 @@ public boolean saveToDisk(boolean doBackup) { return false; } + CustomAnvil.Companion.log(getConfigFileName()+" saved successfully"); return true; } @@ -242,6 +250,7 @@ public void reload() { public static class UnitRepairHolder extends ResourceConfigHolder { private final static String ITEM_GROUP_FILE_NAME = "unit_repair_item"; + private UnitRepairHolder() { super(ITEM_GROUP_FILE_NAME); } @@ -253,4 +262,25 @@ public void reload() { } + // Class for custom anvil craft + public static class CustomAnvilCraftHolder extends ResourceConfigHolder { + private final static String CUSTOM_RECIPE_FILE_NAME = "custom_recipes"; + CustomAnvilRecipeManager recipeManager; + + private CustomAnvilCraftHolder() { + super(CUSTOM_RECIPE_FILE_NAME); + } + + public CustomAnvilRecipeManager getRecipeManager() { + return recipeManager; + } + + @Override + public void reload() { + this.recipeManager = new CustomAnvilRecipeManager(); + this.recipeManager.prepareRecipes(this.configuration); + } + } + + } diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/AbstractEnchantConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java similarity index 98% rename from src/main/java/xyz/alexcrea/cuanvil/gui/config/AbstractEnchantConfigGui.java rename to src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java index da8e34d..7c6d31e 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/AbstractEnchantConfigGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/AbstractEnchantConfigGui.java @@ -1,4 +1,4 @@ -package xyz.alexcrea.cuanvil.gui.config; +package xyz.alexcrea.cuanvil.gui.config.global; import com.github.stefvanschie.inventoryframework.gui.GuiItem; import com.github.stefvanschie.inventoryframework.pane.Orientable; diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java new file mode 100644 index 0000000..11cbaed --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/CustomRecipeConfigGui.java @@ -0,0 +1,94 @@ +package xyz.alexcrea.cuanvil.gui.config.global; + +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import org.bukkit.Material; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.gui.config.settings.subsetting.CustomRecipeSubSettingGui; +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe; +import xyz.alexcrea.cuanvil.util.CasedStringUtil; + +import java.util.Arrays; +import java.util.List; + +public class CustomRecipeConfigGui extends MappedElementListConfigGui { + + + public final static CustomRecipeConfigGui INSTANCE = new CustomRecipeConfigGui(); + + static { + INSTANCE.init(); + } + + private CustomRecipeConfigGui() { + super("Custom Recipe Config"); + + } + + @Override + protected ItemStack createItemForGeneric(AnvilCustomRecipe recipe) { + // Get base item to display + ItemStack craftResultItem = recipe.getResultItem(); + ItemStack displaydItem; + if(craftResultItem == null){ + displaydItem = new ItemStack(Material.BARRIER); + }else{ + displaydItem = craftResultItem.clone(); + } + + // edit displayed item + ItemMeta meta = displaydItem.getItemMeta(); + + meta.setDisplayName("\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(recipe.getName()) + " \u00A7fCustom recipe"); + meta.addItemFlags(ItemFlag.values()); + + boolean shouldWork = recipe.validate(); + + meta.setLore(Arrays.asList( + "\u00A77Should work: \u00A7"+(shouldWork ? "aYes" : "cNo"), + "\u00A77Exact count: \u00A7"+(recipe.getExactCount() ? "aYes" : "cNo"), + "\u00A77Recipe Xp Cost: \u00A7e"+recipe.getXpCostPerCraft() + + )); + + displaydItem.setItemMeta(meta); + return displaydItem; + } + + @Override + protected CustomRecipeSubSettingGui newInstanceOfGui(AnvilCustomRecipe generic, GuiItem item) { + return new CustomRecipeSubSettingGui(this, generic, item); + } + + @Override + protected String genericDisplayedName() { + return "custom recipe"; + } + + @Override + protected AnvilCustomRecipe createAndSaveNewEmptyGeneric(String name) { + // Create new empty conflict and display it to the admin + AnvilCustomRecipe recipe = new AnvilCustomRecipe( + name, + AnvilCustomRecipe.Companion.getDEFAULT_EXACT_COUNT_CONFIG(), + AnvilCustomRecipe.Companion.getDEFAULT_XP_COST_CONFIG(), + AnvilCustomRecipe.Companion.getDEFAULT_LEFT_ITEM_CONFIG(), + AnvilCustomRecipe.Companion.getDEFAULT_RIGHT_ITEM_CONFIG(), + AnvilCustomRecipe.Companion.getDEFAULT_RESULT_ITEM_CONFIG()); + + ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().cleanAddNew(recipe); + + // Save recipe to file + recipe.saveToFile(); + + return recipe; + } + + + @Override + protected List getEveryDisplayableInstanceOfGeneric() { + return ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().getRecipeList(); + } +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/ElementListGlobalConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/ElementListGlobalConfigGui.java new file mode 100644 index 0000000..c37daa4 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/ElementListGlobalConfigGui.java @@ -0,0 +1,299 @@ +package xyz.alexcrea.cuanvil.gui.config.global; + +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import com.github.stefvanschie.inventoryframework.pane.Orientable; +import com.github.stefvanschie.inventoryframework.pane.OutlinePane; +import com.github.stefvanschie.inventoryframework.pane.Pane; +import com.github.stefvanschie.inventoryframework.pane.PatternPane; +import com.github.stefvanschie.inventoryframework.pane.util.Pattern; +import io.delilaheve.CustomAnvil; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui; +import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; +import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +public abstract class ElementListGlobalConfigGui< T > extends ValueUpdatableGui { + + private final String namePrefix; + + public ElementListGlobalConfigGui(@NotNull String title) { + super(6, title, CustomAnvil.instance); + this.namePrefix = title; + } + + + protected OutlinePane firstPage; + protected ArrayList pages; + protected HashMap pageMap; + protected PatternPane backgroundPane; + + public void init() { + // Back item panel + Pattern pattern = new Pattern( + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + "B11L1R11C" + ); + this.backgroundPane = new PatternPane(0, 0, 9, 6, Pane.Priority.LOW, pattern); + + GuiGlobalItems.addBackItem(this.backgroundPane, MainConfigGui.INSTANCE); + + GuiGlobalItems.addBackgroundItem(this.backgroundPane); + this.backgroundPane.bindItem('1', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); + addPane(this.backgroundPane); + + // Page init + this.pages = new ArrayList<>(); + this.pageMap = new HashMap<>(); + + // enchant item panel + this.firstPage = createEmptyPage(); + this.pages.add(this.firstPage); + + prepareStaticValues(); + reloadValues(); + } + + protected GuiItem goLeftItem; + protected GuiItem goRightItem; + + protected void prepareStaticValues(){ + // Left item creation for consumer & bind + this.goLeftItem = new GuiItem(new ItemStack(Material.RED_TERRACOTTA), event -> { + HumanEntity viewer = event.getWhoClicked(); + UUID playerUUID = viewer.getUniqueId(); + int page = this.pageMap.getOrDefault(playerUUID, 0); + this.pageMap.put(playerUUID, page - 1); + + ItemStack cursor = viewer.getItemOnCursor(); + viewer.setItemOnCursor(new ItemStack(Material.AIR)); + + show(viewer); + + viewer.setItemOnCursor(cursor); + }, CustomAnvil.instance); + + // Right item creation for consumer & bind + this.goRightItem = new GuiItem(new ItemStack(Material.GREEN_TERRACOTTA), event -> { + HumanEntity viewer = event.getWhoClicked(); + UUID playerUUID = viewer.getUniqueId(); + int page = pageMap.getOrDefault(playerUUID, 0); + this.pageMap.put(playerUUID, page + 1); + + ItemStack cursor = viewer.getItemOnCursor(); + viewer.setItemOnCursor(new ItemStack(Material.AIR)); + + show(viewer); + + viewer.setItemOnCursor(cursor); + }, CustomAnvil.instance); + + this.backgroundPane.bindItem('C', prepareCreateNewItem()); + } + protected void reloadValues(){ + this.firstPage.clear(); + this.pages.clear(); + this.pages.add(this.firstPage); + + for (T conflict : getEveryDisplayableInstanceOfGeneric()) { + updateValueForGeneric(conflict, false); + } + + update(); + } + + protected abstract GuiItem prepareCreateNewItem(); + + protected OutlinePane createEmptyPage() { + OutlinePane page = new OutlinePane(0, 0, 9, 5); + page.align(OutlinePane.Alignment.BEGIN); + page.setOrientation(Orientable.Orientation.HORIZONTAL); + + return page; + } + + public int getPlayerPageID(UUID uuid) { + int pageId = this.pageMap.getOrDefault(uuid, 0); + if (pageId >= this.pages.size()) { + pageId = this.pages.size() - 1; + } + return pageId; + } + + protected void addToPage(GuiItem guiItem) { + // Get first available page or create one + OutlinePane page = this.pages.get(this.pages.size() - 1); + if (page.getItems().size() >= 5 * 9) { + page = createEmptyPage(); + this.pages.add(page); + } + + page.addItem(guiItem); + } + + private void removeFromPage(GuiItem guiItem) { + // get item page + OutlinePane page = null; + int pageID = 0; + while (pageID < this.pages.size()) { + OutlinePane tempPage = this.pages.get(pageID); + if (tempPage.getItems().contains(guiItem)) { + page = tempPage; + break; + } + pageID++; + } + + if (page == null) {// Why... + return; + } + removeFromPage(page, pageID, guiItem); + } + + private void removeFromPage(OutlinePane page, int pageID, GuiItem guiItem) { + page.removeItem(guiItem); + + // There is now a slot available, let fill it if possible + if (pageID < (this.pages.size() - 1)) { + OutlinePane newPage = this.pages.get(pageID + 1); + GuiItem nextPageItem = newPage.getItems().get(0); + + removeFromPage(newPage, pageID + 1, nextPageItem); + + OutlinePane thisPage = this.pages.get(pageID); + thisPage.addItem(nextPageItem); + } else if (pageID > 0 && page.getItems().isEmpty()) { + this.pages.remove(pageID); + } + } + + public void placeArrow(int page, boolean customise) { + + // Place left arrow + addPane(this.backgroundPane); + if (page > 0) { + if (customise) { + ItemStack leftItem = this.goLeftItem.getItem(); + ItemMeta leftMeta = leftItem.getItemMeta(); + + leftMeta.setDisplayName("\u00A7eReturn to page " + (page)); + + leftItem.setItemMeta(leftMeta); + this.goLeftItem.setItem(leftItem); + } + + this.backgroundPane.bindItem('L', this.goLeftItem); + } else { + this.backgroundPane.bindItem('L', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); + } + + // Place right arrow + if (page < pages.size() - 1) { + if (customise) { + ItemStack rightItem = this.goRightItem.getItem(); + ItemMeta rightMeta = rightItem.getItemMeta(); + + rightMeta.setDisplayName("\u00A7eGo to page " + (page + 2)); + + rightItem.setItemMeta(rightMeta); + this.goRightItem.setItem(rightItem); + } + + this.backgroundPane.bindItem('R', this.goRightItem); + } else { + this.backgroundPane.bindItem('R', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); + } + } + + @Override // assume will not be called in multiple thread + public void show(@NotNull HumanEntity humanEntity) { + int pageID = getPlayerPageID(humanEntity.getUniqueId()); + OutlinePane page = this.pages.get(pageID); + + getPanes().clear(); + + // display the page arrow pane + placeArrow(pageID, true); + // and add actual page + addPane(page); + + // set title + StringBuilder title = new StringBuilder(this.namePrefix); + int pagesSize = this.pages.size(); + if(pagesSize > 1){ + title.append(" (").append(pageID + 1).append('/').append(pagesSize).append(')'); + } + setTitle(title.toString()); + + super.show(humanEntity); + + } + + @Override // assume will not be called in multiple thread + public void click(@NotNull InventoryClickEvent event) { + int pageID = getPlayerPageID(event.getWhoClicked().getUniqueId()); + OutlinePane page = this.pages.get(pageID); + + getPanes().clear(); + + // set the page arrow pane + placeArrow(pageID, false); + // and add actual page + addPane(page); + + super.click(event); + } + + // ------------------------- + // Methods using generic T + // ------------------------- + + public void updateValueForGeneric(T generic, boolean shouldUpdate) { + ItemStack item = createItemForGeneric(generic); + + updateGeneric(generic, item); + + if (shouldUpdate) { + update(); + } + + } + + public void removeGeneric(T generic) { + GuiItem item = findGuiItemForRemoval(generic); + if(item == null) return; + removeFromPage(item); + + update(); + } + + protected abstract GuiItem findGuiItemForRemoval(T generic); + + protected abstract ItemStack createItemForGeneric(T generic); + + protected abstract void updateGeneric(T generic, ItemStack usedItem); + + protected abstract List getEveryDisplayableInstanceOfGeneric(); + + @Override + public void updateGuiValues() { + // Not the optimised way to update this gui + // TODO maybe rework ValueUpdatableGui and it's dependency to allow a 1 item reload every time. + + reloadValues(); + } + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java index f5cadf6..0b9e22f 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantConflictGui.java @@ -1,34 +1,20 @@ package xyz.alexcrea.cuanvil.gui.config.global; import com.github.stefvanschie.inventoryframework.gui.GuiItem; -import com.github.stefvanschie.inventoryframework.gui.type.ChestGui; -import com.github.stefvanschie.inventoryframework.pane.Orientable; -import com.github.stefvanschie.inventoryframework.pane.OutlinePane; -import com.github.stefvanschie.inventoryframework.pane.Pane; -import com.github.stefvanschie.inventoryframework.pane.PatternPane; -import com.github.stefvanschie.inventoryframework.pane.util.Pattern; -import io.delilaheve.CustomAnvil; -import org.bukkit.Material; import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import org.jetbrains.annotations.NotNull; import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import xyz.alexcrea.cuanvil.group.IncludeGroup; import xyz.alexcrea.cuanvil.gui.config.settings.subsetting.EnchantConflictSubSettingGui; -import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions; -import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant; import xyz.alexcrea.cuanvil.util.CasedStringUtil; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; +import java.util.Arrays; +import java.util.List; -public class EnchantConflictGui extends ChestGui { +public class EnchantConflictGui extends MappedElementListConfigGui { public final static EnchantConflictGui INSTANCE = new EnchantConflictGui(); @@ -36,194 +22,37 @@ public class EnchantConflictGui extends ChestGui { INSTANCE.init(); } - private final HashMap conflictGuiMap; - private EnchantConflictGui() { - super(6, "Conflict Config", CustomAnvil.instance); - this.conflictGuiMap = new HashMap<>(); - } - - private OutlinePane firstPage; - private ArrayList pages; - private HashMap pageMap; - private PatternPane backgroundPane; - - private void init() { - // Back item panel - Pattern pattern = new Pattern( - GuiSharedConstant.EMPTY_GUI_FULL_LINE, - GuiSharedConstant.EMPTY_GUI_FULL_LINE, - GuiSharedConstant.EMPTY_GUI_FULL_LINE, - GuiSharedConstant.EMPTY_GUI_FULL_LINE, - GuiSharedConstant.EMPTY_GUI_FULL_LINE, - "B11L1R11C" - ); - this.backgroundPane = new PatternPane(0, 0, 9, 6, Pane.Priority.LOW, pattern); - - GuiGlobalItems.addBackItem(this.backgroundPane, MainConfigGui.INSTANCE); - - GuiGlobalItems.addBackgroundItem(this.backgroundPane); - this.backgroundPane.bindItem('1', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); - addPane(this.backgroundPane); - - // Page init - this.pages = new ArrayList<>(); - this.pageMap = new HashMap<>(); - - // enchant item panel - this.firstPage = createEmptyPage(); - this.pages.add(this.firstPage); - - prepareOtherValues(); - reloadValues(); - } - - - private GuiItem goLeftItem; - private GuiItem goRightItem; - - private void prepareOtherValues() { - // Left item creation for consumer & bind - this.goLeftItem = new GuiItem(new ItemStack(Material.RED_TERRACOTTA), event -> { - HumanEntity viewer = event.getWhoClicked(); - UUID playerUUID = viewer.getUniqueId(); - int page = this.pageMap.getOrDefault(playerUUID, 0); - this.pageMap.put(playerUUID, page - 1); - - ItemStack cursor = viewer.getItemOnCursor(); - viewer.setItemOnCursor(new ItemStack(Material.AIR)); - - show(viewer); - - viewer.setItemOnCursor(cursor); - }, CustomAnvil.instance); - - // Right item creation for consumer & bind - this.goRightItem = new GuiItem(new ItemStack(Material.GREEN_TERRACOTTA), event -> { - HumanEntity viewer = event.getWhoClicked(); - UUID playerUUID = viewer.getUniqueId(); - int page = pageMap.getOrDefault(playerUUID, 0); - this.pageMap.put(playerUUID, page + 1); - - ItemStack cursor = viewer.getItemOnCursor(); - viewer.setItemOnCursor(new ItemStack(Material.AIR)); - - show(viewer); - - viewer.setItemOnCursor(cursor); - }, CustomAnvil.instance); - - // Create new conflict item - ItemStack createItem = new ItemStack(Material.PAPER); - ItemMeta createMeta = createItem.getItemMeta(); - - createMeta.setDisplayName("\u00A7aCreate new conflict"); - createMeta.setLore(Arrays.asList( - "\u00A77Create a new anvil restriction.", - "\u00A77You will be asked to name the conflict in chat.", - "\u00A77Then, you should edit the conflict config as you need" - )); - - createItem.setItemMeta(createMeta); - - this.backgroundPane.bindItem('C', new GuiItem(createItem, (clickEvent) -> { - clickEvent.setCancelled(true); - HumanEntity player = clickEvent.getWhoClicked(); - - // check permission - if (!player.hasPermission(CustomAnvil.editConfigPermission)) { - player.closeInventory(); - player.sendMessage(GuiGlobalActions.NO_EDIT_PERM); - return; - } - player.closeInventory(); - - player.sendMessage("\u00A7eWrite the conflict you want to create in the chat.\n" + - "\u00A7eOr write \u00A7ccancel \u00A7eto go back to conflict config menu"); - - CustomAnvil.Companion.getChatListener().setListenedCallback(player, prepareCreateItemConsumer(player)); - - }, CustomAnvil.instance)); + super( "Conflict Config"); } - private Consumer prepareCreateItemConsumer(HumanEntity player) { - AtomicReference> selfRef = new AtomicReference<>(); - Consumer selfCallback = (message) -> { - if (message == null) return; - - message = message.toLowerCase(Locale.ROOT); - if ("cancel".equalsIgnoreCase(message)) { - player.sendMessage("conflict creation cancelled..."); - show(player); - return; - } - - message = message.replace(' ', '_'); - - // Try to find if it already exists in a for loop - // Not the most efficient on large number of conflict, but it should not run often. - for (EnchantConflictGroup conflict : ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList()) { - if (conflict.getName().equalsIgnoreCase(message)) { - player.sendMessage("\u00A7cPlease enter a conflict name that do not already exist..."); - // wait next message. - CustomAnvil.Companion.getChatListener().setListenedCallback(player, selfRef.get()); - return; - } - } - - // Create new empty conflict and display it to the admin - EnchantConflictGroup conflict = new EnchantConflictGroup( - message, - new IncludeGroup("new_group"), - 0); - - ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList().add(conflict); - updateValueForConflict(conflict, true); - - // save empty conflict in config - String[] emptyStringArray = new String[0]; + @Override + protected EnchantConflictGroup createAndSaveNewEmptyGeneric(String name){ + // Create new empty conflict and display it to the admin + EnchantConflictGroup conflict = new EnchantConflictGroup( + name, + new IncludeGroup("new_group"), + 0); - FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig(); - config.set(message + ".enchantments", emptyStringArray); - config.set(message + ".notAffectedGroups", emptyStringArray); - config.set(message + ".maxEnchantmentBeforeConflict", 0); + ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList().add(conflict); - if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) { - ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE); - } + // save empty conflict in config + String[] emptyStringArray = new String[0]; - // show the new conflict config to the player - this.conflictGuiMap.get(conflict).show(player); + FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig(); + config.set(name + ".enchantments", emptyStringArray); + config.set(name + ".notAffectedGroups", emptyStringArray); + config.set(name + ".maxEnchantmentBeforeConflict", 0); - }; - - selfRef.set(selfCallback); - return selfCallback; - } - - private OutlinePane createEmptyPage() { - OutlinePane page = new OutlinePane(0, 0, 9, 5); - page.align(OutlinePane.Alignment.BEGIN); - page.setOrientation(Orientable.Orientation.HORIZONTAL); - - return page; - } - - public void reloadValues() { - this.conflictGuiMap.forEach((conflict, gui) -> gui.cleanUnused()); - this.conflictGuiMap.clear(); - this.firstPage.clear(); - this.pages.clear(); - this.pages.add(this.firstPage); - - for (EnchantConflictGroup conflict : ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList()) { - updateValueForConflict(conflict, false); + if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) { + ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE); } - update(); + return conflict; } - public static ItemStack createItemForConflict(EnchantConflictGroup conflict) { + @Override + public ItemStack createItemForGeneric(EnchantConflictGroup conflict) { ItemStack item = new ItemStack(conflict.getRepresentativeMaterial()); ItemMeta meta = item.getItemMeta(); @@ -239,169 +68,19 @@ public static ItemStack createItemForConflict(EnchantConflictGroup conflict) { return item; } - public void updateValueForConflict(EnchantConflictGroup conflict, boolean shouldUpdate) { - EnchantConflictSubSettingGui gui = this.conflictGuiMap.get(conflict); - ItemStack item = createItemForConflict(conflict); - - GuiItem guiItem; - if (gui == null) { - // Create new sub setting gui - guiItem = new GuiItem(item, CustomAnvil.instance); - gui = new EnchantConflictSubSettingGui(this, conflict, guiItem); - - guiItem.setAction(GuiGlobalActions.openGuiAction(gui)); - - this.conflictGuiMap.put(conflict, gui); - addToPage(guiItem); - } else { - // Replace item with the updated one - guiItem = gui.getParentItemForThisGui(); - guiItem.setItem(item); - } - - gui.updateLocal(); - if (shouldUpdate) { - update(); - } - - } - - public void removeConflict(EnchantConflictGroup conflict) { - EnchantConflictSubSettingGui gui = this.conflictGuiMap.get(conflict); - if (gui == null) return; - - this.conflictGuiMap.remove(conflict); - removeFromPage(gui.getParentItemForThisGui()); - - update(); - } - - private void addToPage(GuiItem guiItem) { - // Get first available page or create one - OutlinePane page = this.pages.get(this.pages.size() - 1); - if (page.getItems().size() >= 5 * 9) { - page = createEmptyPage(); - this.pages.add(page); - } - - page.addItem(guiItem); - } - - private void removeFromPage(GuiItem guiItem) { - // get item page - OutlinePane page = null; - int pageID = 0; - while (pageID < this.pages.size()) { - OutlinePane tempPage = this.pages.get(pageID); - if (tempPage.getItems().contains(guiItem)) { - page = tempPage; - break; - } - pageID++; - } - - if (page == null) {// Why... - return; - } - removeFromPage(page, pageID, guiItem); - } - - private void removeFromPage(OutlinePane page, int pageID, GuiItem guiItem) { - page.removeItem(guiItem); - - // There is now a slot available, let fill it if possible - if (pageID < (this.pages.size() - 1)) { - OutlinePane newPage = this.pages.get(pageID + 1); - GuiItem nextPageItem = newPage.getItems().get(0); - - removeFromPage(newPage, pageID + 1, nextPageItem); - - OutlinePane thisPage = this.pages.get(pageID); - thisPage.addItem(nextPageItem); - } else if (pageID > 0 && page.getItems().isEmpty()) { - this.pages.remove(pageID); - } - } - - - public int getPlayerPageID(UUID uuid) { - int pageId = this.pageMap.getOrDefault(uuid, 0); - if (pageId >= this.pages.size()) { - pageId = this.pages.size() - 1; - } - return pageId; + @Override + protected EnchantConflictSubSettingGui newInstanceOfGui(EnchantConflictGroup conflict, GuiItem item) { + return new EnchantConflictSubSettingGui(this, conflict, item); } - public void placeArrow(int page, boolean customise) { - - // Place left arrow - addPane(this.backgroundPane); - if (page > 0) { - if (customise) { - ItemStack leftItem = this.goLeftItem.getItem(); - ItemMeta leftMeta = leftItem.getItemMeta(); - - leftMeta.setDisplayName("\u00A7eReturn to page " + (page)); - - leftItem.setItemMeta(leftMeta); - this.goLeftItem.setItem(leftItem); - } - - this.backgroundPane.bindItem('L', this.goLeftItem); - } else { - this.backgroundPane.bindItem('L', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); - } - - // Place right arrow - if (page < pages.size() - 1) { - if (customise) { - ItemStack rightItem = this.goRightItem.getItem(); - ItemMeta rightMeta = rightItem.getItemMeta(); - - rightMeta.setDisplayName("\u00A7eGo to page " + (page + 2)); - - rightItem.setItemMeta(rightMeta); - this.goRightItem.setItem(rightItem); - } - - this.backgroundPane.bindItem('R', this.goRightItem); - } else { - this.backgroundPane.bindItem('R', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); - } + @Override + protected String genericDisplayedName() { + return "conflict"; } - @Override // assume will not be called in multiple thread - public void show(@NotNull HumanEntity humanEntity) { - int pageID = getPlayerPageID(humanEntity.getUniqueId()); - OutlinePane page = this.pages.get(pageID); - - getPanes().clear(); - - // display the page arrow pane - placeArrow(pageID, true); - // and add actual page - addPane(page); - - // set title - setTitle("Conflict Config (" + (pageID + 1) + "/" + (pages.size()) + ")"); - - super.show(humanEntity); - - } - - @Override // assume will not be called in multiple thread - public void click(@NotNull InventoryClickEvent event) { - int pageID = getPlayerPageID(event.getWhoClicked().getUniqueId()); - OutlinePane page = this.pages.get(pageID); - - getPanes().clear(); - - // set the page arrow pane - placeArrow(pageID, false); - // and add actual page - addPane(page); - - super.click(event); + @Override + protected List getEveryDisplayableInstanceOfGeneric() { + return ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList(); } } diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantCostConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantCostConfigGui.java index 2c2427b..2a1937b 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantCostConfigGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantCostConfigGui.java @@ -8,7 +8,6 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.enchant.EnchantmentProperties; import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity; -import xyz.alexcrea.cuanvil.gui.config.AbstractEnchantConfigGui; import xyz.alexcrea.cuanvil.gui.config.settings.EnchantCostSettingsGui; import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; import xyz.alexcrea.cuanvil.util.CasedStringUtil; diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java index ae89b54..999f2a7 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/EnchantLimitConfigGui.java @@ -4,7 +4,6 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import xyz.alexcrea.cuanvil.config.ConfigHolder; -import xyz.alexcrea.cuanvil.gui.config.AbstractEnchantConfigGui; import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui; import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; import xyz.alexcrea.cuanvil.util.CasedStringUtil; diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MainConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MainConfigGui.java index dce3ff7..a69cc7a 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MainConfigGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MainConfigGui.java @@ -29,7 +29,7 @@ private MainConfigGui() { private void init() { Pattern pattern = new Pattern( "I00000000", - "012304560", + "012304567", "Q00000000" ); PatternPane pane = new PatternPane(0, 0, 9, 3, pattern); @@ -70,7 +70,7 @@ private void init() { GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, EnchantCostConfigGui.INSTANCE); pane.bindItem('3', enchantCostItem); - // Enchantment Conflicts + // Enchantment Conflicts item ItemStack EnchantConflictItemstack = new ItemStack(Material.OAK_FENCE); ItemMeta enchantConflictMeta = EnchantConflictItemstack.getItemMeta(); @@ -93,6 +93,17 @@ private void init() { pane.bindItem('5', wip5); pane.bindItem('6', wip6); + // Custom recipe item + ItemStack customRecipeItemstack = new ItemStack(Material.CRAFTING_TABLE); + ItemMeta customRecipeMeta = EnchantConflictItemstack.getItemMeta(); + + customRecipeMeta.setDisplayName("\u00A7aCustom recipes"); + customRecipeMeta.setLore(Collections.singletonList("\u00A77Click here to open anvil custom recipe menu")); + customRecipeItemstack.setItemMeta(customRecipeMeta); + + GuiItem customRecipeItem = GuiGlobalItems.goToGuiItem(customRecipeItemstack, CustomRecipeConfigGui.INSTANCE); + pane.bindItem('7', customRecipeItem); + // quit item ItemStack quitItemstack = new ItemStack(Material.BARRIER); ItemMeta quitMeta = quitItemstack.getItemMeta(); diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MappedElementListConfigGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MappedElementListConfigGui.java new file mode 100644 index 0000000..7349dca --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/global/MappedElementListConfigGui.java @@ -0,0 +1,156 @@ +package xyz.alexcrea.cuanvil.gui.config.global; + +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import io.delilaheve.CustomAnvil; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.gui.config.settings.subsetting.MappedToListSubSettingGui; +import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions; +import xyz.alexcrea.cuanvil.interfaces.Named; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +public abstract class MappedElementListConfigGui< T extends Named, S extends MappedToListSubSettingGui> extends ElementListGlobalConfigGui< T > { + + + protected final HashMap elementGuiMap; + public MappedElementListConfigGui(@NotNull String title) { + super(title); + this.elementGuiMap = new HashMap<>(); + + } + + @Override + protected GuiItem prepareCreateNewItem(){ + // Create new conflict item + ItemStack createItem = new ItemStack(Material.PAPER); + ItemMeta createMeta = createItem.getItemMeta(); + + createMeta.setDisplayName("\u00A7aCreate new "+genericDisplayedName()); + createMeta.setLore(Arrays.asList( + "\u00A77Create a new "+genericDisplayedName()+".", + "\u00A77You will be asked to name the "+genericDisplayedName()+" in chat.", + "\u00A77Then, you should edit the "+genericDisplayedName()+" config as you need" + )); + + createItem.setItemMeta(createMeta); + + return new GuiItem(createItem, (clickEvent) -> { + clickEvent.setCancelled(true); + HumanEntity player = clickEvent.getWhoClicked(); + + // check permission + if (!player.hasPermission(CustomAnvil.editConfigPermission)) { + player.closeInventory(); + player.sendMessage(GuiGlobalActions.NO_EDIT_PERM); + return; + } + player.closeInventory(); + + player.sendMessage("\u00A7eWrite the "+genericDisplayedName()+" name you want to create in the chat.\n" + + "\u00A7eOr write \u00A7ccancel \u00A7eto go back to "+genericDisplayedName()+" config menu"); + + CustomAnvil.Companion.getChatListener().setListenedCallback(player, prepareCreateItemConsumer(player)); + + }, CustomAnvil.instance); + } + + @Override + public void reloadValues() { + this.elementGuiMap.forEach((conflict, gui) -> gui.cleanUnused()); + this.elementGuiMap.clear(); + + super.reloadValues(); + } + + @Override + protected void updateGeneric(T generic, ItemStack usedItem) { + S gui = this.elementGuiMap.get(generic); + + GuiItem guiItem; + if (gui == null) { + // Create new sub setting gui + guiItem = new GuiItem(usedItem, CustomAnvil.instance); + gui = newInstanceOfGui(generic, guiItem); + + guiItem.setAction(GuiGlobalActions.openGuiAction(gui)); + + this.elementGuiMap.put(generic, gui); + addToPage(guiItem); + } else { + // Replace item with the updated one + guiItem = gui.getParentItemForThisGui(); + guiItem.setItem(usedItem); + } + gui.updateLocal(); + + } + + @Override + protected GuiItem findGuiItemForRemoval(T generic) { + S gui = this.elementGuiMap.get(generic); + if (gui == null) return null; + + this.elementGuiMap.remove(generic); + return gui.getParentItemForThisGui(); + } + + protected Consumer prepareCreateItemConsumer(HumanEntity player){ + AtomicReference> selfRef = new AtomicReference<>(); + Consumer selfCallback = (message) -> { + if (message == null) return; + + // check permission + if (!player.hasPermission(CustomAnvil.editConfigPermission)) { + player.sendMessage(GuiGlobalActions.NO_EDIT_PERM); + return; + } + + message = message.toLowerCase(Locale.ROOT); + if ("cancel".equalsIgnoreCase(message)) { + player.sendMessage(genericDisplayedName()+" creation cancelled..."); + show(player); + return; + } + + message = message.replace(' ', '_'); + + // Try to find if it already exists in a for loop + // Not the most efficient on large number of conflict, but it should not run often. + for (T generic : getEveryDisplayableInstanceOfGeneric()) { + if (generic.getName().equalsIgnoreCase(message)) { + player.sendMessage("\u00A7cPlease enter a "+genericDisplayedName()+" name that do not already exist..."); + // wait next message. + CustomAnvil.Companion.getChatListener().setListenedCallback(player, selfRef.get()); + return; + } + } + + T generic = createAndSaveNewEmptyGeneric(message); + + updateValueForGeneric(generic, true); + + // show the new conflict config to the player + this.elementGuiMap.get(generic).show(player); + + update(); + }; + + selfRef.set(selfCallback); + return selfCallback; + } + + protected abstract S newInstanceOfGui(T generic, GuiItem item); + + protected abstract String genericDisplayedName(); + + protected abstract T createAndSaveNewEmptyGeneric(String name); + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/ItemSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/ItemSettingGui.java new file mode 100644 index 0000000..8f44e5e --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/ItemSettingGui.java @@ -0,0 +1,254 @@ +package xyz.alexcrea.cuanvil.gui.config.settings; + +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import com.github.stefvanschie.inventoryframework.pane.PatternPane; +import com.github.stefvanschie.inventoryframework.pane.util.Pattern; +import io.delilaheve.CustomAnvil; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui; +import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; +import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant; +import xyz.alexcrea.cuanvil.util.MetricsUtil; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +/** + * An instance of a gui used to edit an item setting. + */ +public class ItemSettingGui extends AbstractSettingGui { + + private final ItemSettingFactory holder; + private final ItemStack before; + private ItemStack now; + + /** + * Create an item setting config gui. + * + * @param holder Configuration factory of this setting. + * @param now The defined value of this setting. + */ + protected ItemSettingGui(ItemSettingFactory holder, ItemStack now) { + super(3, holder.getTitle(), holder.parent); + this.holder = holder; + this.before = now; + this.now = now; + + prepareStaticItems(); + updateValueDisplay(); + } + + @Override + public Pattern getGuiPattern() { + return new Pattern( + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + "D0-0v0+0s", + "B0000000S" + ); + } + + + public void prepareStaticItems(){ + prepareReturnToDefault(); + + ItemStack item = new ItemStack(Material.YELLOW_TERRACOTTA); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName("\u00A7eTemporary close this menu"); + meta.setLore(Collections.singletonList("\u00A77Allow you to chose other item then return here.")); + item.setItemMeta(meta); + GuiItem guiItem = new GuiItem(item, event -> { + event.setCancelled(true); + + HumanEntity player = event.getWhoClicked(); + + CustomAnvil.Companion.getChatListener().setListenedCallback(player, (message) ->{ + + if(message == null) return; + show(player); + + }); + + player.sendMessage("\u00A7eWrite something in chat to return to the item config menu."); + player.closeInventory(); + }, CustomAnvil.instance); + + getPane().bindItem('s', guiItem); + } + + + protected GuiItem returnToDefault; + + /** + * Prepare "return to default value" gui item. + */ + protected void prepareReturnToDefault() { + ItemStack item = new ItemStack(Material.COMMAND_BLOCK); + ItemMeta meta = item.getItemMeta(); + + meta.setDisplayName("\u00A7eReset to default value"); + meta.setLore(Collections.singletonList("\u00A77Default value is: " + holder.defaultVal)); + item.setItemMeta(meta); + returnToDefault = new GuiItem(item, event -> { + event.setCancelled(true); + now = holder.defaultVal; + updateValueDisplay(); + update(); + }, CustomAnvil.instance); + } + + protected final static List CLICK_LORE = Collections.singletonList("\u00A77Click Here with an item to change the value"); + + /** + * Update item using the setting value to match the new value + */ + protected void updateValueDisplay() { + PatternPane pane = getPane(); + + // Get displayed value for this config. + ItemStack displayedItem; + if(this.now != null){ + displayedItem = this.now.clone(); + }else{ + displayedItem = new ItemStack(Material.BARRIER); + ItemMeta valueMeta = displayedItem.getItemMeta(); + + valueMeta.setDisplayName("\u00A74NO ITEM SET"); + valueMeta.setLore(CLICK_LORE); + + displayedItem.setItemMeta(valueMeta); + } + + GuiItem resultItem = new GuiItem(displayedItem, setItemAsCursor(), CustomAnvil.instance); + pane.bindItem('v', resultItem); + + // reset to default + GuiItem returnToDefault; + if (now != holder.defaultVal) { + returnToDefault = this.returnToDefault; + } else { + returnToDefault = GuiGlobalItems.backgroundItem(); + } + pane.bindItem('D', returnToDefault); + + } + + /** + * @return A consumer to update the current setting's value. + */ + protected Consumer setItemAsCursor() { //TODO redo consumer + return event -> { + event.setCancelled(true); + + HumanEntity player = event.getWhoClicked(); + ItemStack cursor = player.getItemOnCursor(); + + if(cursor.getType().isAir()) return; + + this.now = cursor; + + updateValueDisplay(); + update(); + }; + } + + @Override + public boolean onSave() { + holder.config.getConfig().set(holder.configPath, this.now); + + MetricsUtil.INSTANCE.notifyChange(this.holder.config, this.holder.configPath); + if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) { + return holder.config.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE); + } + return true; + } + + @Override + public boolean hadChange() { + if(now == null) { + return before != null; + } + + return !now.equals(before); + } + + /** + * Create aa item setting factory from setting's parameters. + * + * @param title The title of the gui. + * @param parent Parent gui to go back when completed. + * @param configPath Configuration path of this setting. + * @param config Configuration holder of this setting. + * @param defaultVal Default value if not found on the config. + * @return A factory for an item setting gui. + */ + public static ItemSettingGui.ItemSettingFactory itemFactory(@NotNull String title, ValueUpdatableGui parent, + String configPath, ConfigHolder config, + ItemStack defaultVal) { + return new ItemSettingGui.ItemSettingFactory( + title, parent, + configPath, config, + defaultVal); + } + + /** + * A factory for an item setting gui that hold setting's information. + */ + public static class ItemSettingFactory extends SettingGuiFactory { + @NotNull + String title; + ValueUpdatableGui parent; + ItemStack defaultVal; + + /** + * Constructor for an item setting gui factory. + * + * @param title The title of the gui. + * @param parent Parent gui to go back when completed. + * @param configPath Configuration path of this setting. + * @param config Configuration holder of this setting. + * @param defaultVal Default value if not found on the config. + */ + protected ItemSettingFactory( + @NotNull String title, ValueUpdatableGui parent, + String configPath, ConfigHolder config, + ItemStack defaultVal) { + super(configPath, config); + this.title = title; + this.parent = parent; + + this.defaultVal = defaultVal; + } + + /** + * @return Get setting's gui title. + */ + @NotNull + public String getTitle() { + return title; + } + + /** + * @return The configured value for the associated setting. + */ + public ItemStack getConfiguredValue() { + return this.config.getConfig().getItemStack(this.configPath, this.defaultVal); + } + + @Override + public AbstractSettingGui create() { + // Get current value or default + ItemStack now = getConfiguredValue(); + // create new gui + return new ItemSettingGui(this, now); + } + + } +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/CustomRecipeSubSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/CustomRecipeSubSettingGui.java new file mode 100644 index 0000000..148330c --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/CustomRecipeSubSettingGui.java @@ -0,0 +1,189 @@ +package xyz.alexcrea.cuanvil.gui.config.settings.subsetting; + +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import com.github.stefvanschie.inventoryframework.pane.PatternPane; +import com.github.stefvanschie.inventoryframework.pane.util.Pattern; +import io.delilaheve.CustomAnvil; +import kotlin.ranges.IntRange; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.gui.config.ConfirmActionGui; +import xyz.alexcrea.cuanvil.gui.config.global.CustomRecipeConfigGui; +import xyz.alexcrea.cuanvil.gui.config.settings.BoolSettingsGui; +import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui; +import xyz.alexcrea.cuanvil.gui.config.settings.ItemSettingGui; +import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions; +import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; +import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant; +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe; +import xyz.alexcrea.cuanvil.recipe.CustomAnvilRecipeManager; +import xyz.alexcrea.cuanvil.util.CasedStringUtil; + +import java.util.Collections; +import java.util.function.Supplier; + +public class CustomRecipeSubSettingGui extends MappedToListSubSettingGui { + + private final CustomRecipeConfigGui parent; + private final AnvilCustomRecipe anvilRecipe; + private final PatternPane pane; + private boolean shouldWork = true; + + public CustomRecipeSubSettingGui( + @NotNull CustomRecipeConfigGui parent, + @NotNull AnvilCustomRecipe anvilRecipe, + @NotNull GuiItem parentItemForThisGui) { + super(parentItemForThisGui, 3, "\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(anvilRecipe.getName()) + " \u00A78Config"); + this.parent = parent; + this.anvilRecipe = anvilRecipe; + + Pattern pattern = new Pattern( + GuiSharedConstant.EMPTY_GUI_FULL_LINE, + "01203450D", + "B00000000" + ); + this.pane = new PatternPane(0, 0, 9, 3, pattern); + addPane(this.pane); + + prepareStaticValues(); + } + + BoolSettingsGui.BoolSettingFactory exactCountFactory; + IntSettingsGui.IntSettingFactory xpCostFactory; + ItemSettingGui.ItemSettingFactory leftItemFactory; + ItemSettingGui.ItemSettingFactory rightItemFactory; + ItemSettingGui.ItemSettingFactory resultItemFactory; + + private void prepareStaticValues() { + + GuiGlobalItems.addBackItem(this.pane, this.parent); + GuiGlobalItems.addBackgroundItem(this.pane); + + // Delete item + ItemStack deleteItem = new ItemStack(Material.RED_TERRACOTTA); + ItemMeta deleteMeta = deleteItem.getItemMeta(); + + deleteMeta.setDisplayName("\u00A74DELETE RECIPE"); + deleteMeta.setLore(Collections.singletonList("\u00A7cCaution with this button !")); + + deleteItem.setItemMeta(deleteMeta); + this.pane.bindItem('D', new GuiItem(deleteItem, GuiGlobalActions.openGuiAction(createDeleteGui()), CustomAnvil.instance)); + + // Displayed item will be updated later + + IntRange costRange = AnvilCustomRecipe.Companion.getXP_COST_CONFIG_RANGE(); + this.exactCountFactory = BoolSettingsGui.boolFactory("\u00A78Exact count ?", this, + this.anvilRecipe.getName()+"."+AnvilCustomRecipe.EXACT_COUNT_CONFIG, ConfigHolder.CUSTOM_RECIPE_HOLDER, + AnvilCustomRecipe.Companion.getDEFAULT_EXACT_COUNT_CONFIG()); + + this.xpCostFactory = IntSettingsGui.intFactory("\u00A78Recipe Xp Cost", this, + this.anvilRecipe.getName()+"."+AnvilCustomRecipe.XP_COST_CONFIG, ConfigHolder.CUSTOM_RECIPE_HOLDER, + costRange.getFirst(), costRange.getLast(), AnvilCustomRecipe.Companion.getDEFAULT_XP_COST_CONFIG(), 1, 5, 10); + + + this.leftItemFactory = ItemSettingGui.itemFactory("\u00A7eRecipe Left \u00A78Item", this, + this.anvilRecipe.getName()+"."+AnvilCustomRecipe.LEFT_ITEM_CONFIG, ConfigHolder.CUSTOM_RECIPE_HOLDER, + AnvilCustomRecipe.Companion.getDEFAULT_LEFT_ITEM_CONFIG()); + + this.rightItemFactory = ItemSettingGui.itemFactory("\u00A7eRecipe Right \u00A78Item", this, + this.anvilRecipe.getName()+"."+AnvilCustomRecipe.RIGHT_ITEM_CONFIG, ConfigHolder.CUSTOM_RECIPE_HOLDER, + AnvilCustomRecipe.Companion.getDEFAULT_RIGHT_ITEM_CONFIG()); + + this.resultItemFactory = ItemSettingGui.itemFactory("\u00A7aRecipe Result \u00A78Item", this, + this.anvilRecipe.getName()+"."+AnvilCustomRecipe.RESULT_ITEM_CONFIG, ConfigHolder.CUSTOM_RECIPE_HOLDER, + AnvilCustomRecipe.Companion.getDEFAULT_RESULT_ITEM_CONFIG()); + } + + private ConfirmActionGui createDeleteGui() { + Supplier deleteSupplier = () -> { + CustomAnvilRecipeManager manager = ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager(); + + // Remove from manager + manager.cleanRemove(this.anvilRecipe); + + // Remove from parent + this.parent.removeGeneric(this.anvilRecipe); + + // Remove self + cleanUnused(); + + // Update config file storage + ConfigHolder.CUSTOM_RECIPE_HOLDER.getConfig().set(this.anvilRecipe.getName(), null); + + // Save + boolean success = true; + if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) { + success = ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE); + } + + return success; + }; + + return new ConfirmActionGui("\u00A7cDelete \u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(this.anvilRecipe.getName()) + "\u00A7c?", + "\u00A77Confirm that you want to delete this conflict.", + this, this.parent, deleteSupplier + ); + } + + @Override + public void updateGuiValues() { + // update value from config to conflict + this.anvilRecipe.updateFromFile(); + + // Parent should call updateLocal with this call + this.parent.updateValueForGeneric(this.anvilRecipe, true); + } + + public void updateLocal() { + if (!this.shouldWork) return; + + GuiItem exactCountItem = GuiGlobalItems.boolSettingGuiItem(this.exactCountFactory); + this.pane.bindItem('1', exactCountItem); + + GuiItem xpCostItem = GuiGlobalItems.intSettingGuiItem(this.xpCostFactory, Material.EXPERIENCE_BOTTLE); + this.pane.bindItem('2', xpCostItem); + + GuiItem leftGuiItem = GuiGlobalItems.itemSettingGuiItem(this.leftItemFactory); + this.pane.bindItem('3', leftGuiItem); + + GuiItem rightGuiItem = GuiGlobalItems.itemSettingGuiItem(this.rightItemFactory); + this.pane.bindItem('4', rightGuiItem); + + GuiItem resultGuiItem = GuiGlobalItems.itemSettingGuiItem(this.resultItemFactory); + this.pane.bindItem('5', resultGuiItem); + + update(); + } + + public void cleanUnused() { + for (HumanEntity viewer : getViewers()) { + this.parent.show(viewer); + } + this.shouldWork = false; + + // Just in case something is extremely wrong + GuiItem background = GuiGlobalItems.backgroundItem(); + this.pane.bindItem('1', background); + this.pane.bindItem('2', background); + this.pane.bindItem('3', background); + this.pane.bindItem('4', background); + this.pane.bindItem('5', background); + + this.pane.bindItem('D', background); + } + + @Override + public void show(@NotNull HumanEntity humanEntity) { + if (this.shouldWork) { + super.show(humanEntity); + } else { + this.parent.show(humanEntity); + } + } + + +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/EnchantConflictSubSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/EnchantConflictSubSettingGui.java index 6671d12..b9cfefa 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/EnchantConflictSubSettingGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/EnchantConflictSubSettingGui.java @@ -14,7 +14,6 @@ import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup; import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import xyz.alexcrea.cuanvil.group.EnchantConflictManager; -import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui; import xyz.alexcrea.cuanvil.gui.config.ConfirmActionGui; import xyz.alexcrea.cuanvil.gui.config.SelectEnchantmentContainer; import xyz.alexcrea.cuanvil.gui.config.SelectGroupContainer; @@ -34,24 +33,22 @@ import java.util.function.Supplier; import java.util.logging.Level; -public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements SelectEnchantmentContainer, SelectGroupContainer { +public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui implements SelectEnchantmentContainer, SelectGroupContainer { private final EnchantConflictGui parent; private final EnchantConflictGroup enchantConflict; - private final GuiItem parentItemForThisGui; private final PatternPane pane; - private boolean shouldWorld = true; + private boolean shouldWork = true; public EnchantConflictSubSettingGui( @NotNull EnchantConflictGui parent, @NotNull EnchantConflictGroup enchantConflict, @NotNull GuiItem parentItemForThisGui) { - super(3, - "\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + " \u00A78Config", - CustomAnvil.instance); + super(parentItemForThisGui, + 3, + "\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + " \u00A78Config"); this.parent = parent; this.enchantConflict = enchantConflict; - this.parentItemForThisGui = parentItemForThisGui; Pattern pattern = new Pattern( GuiSharedConstant.EMPTY_GUI_FULL_LINE, @@ -126,7 +123,7 @@ private ConfirmActionGui createDeleteGui() { manager.conflictList.remove(this.enchantConflict); // Remove from parent - this.parent.removeConflict(this.enchantConflict); + this.parent.removeGeneric(this.enchantConflict); // Remove self cleanUnused(); @@ -143,7 +140,7 @@ private ConfirmActionGui createDeleteGui() { return success; }; - return new ConfirmActionGui("\u00A7cDelete \u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + "\u00A7c?", + return new ConfirmActionGui("\u00A7cDelete \u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(this.enchantConflict.getName()) + "\u00A7c?", "\u00A77Confirm that you want to delete this conflict.", this, this.parent, deleteSupplier ); @@ -151,12 +148,17 @@ private ConfirmActionGui createDeleteGui() { @Override public void updateGuiValues() { - this.parent.updateValueForConflict(this.enchantConflict, true); - // Parent should call updateLocal + // update value from config to conflict + int minBeforeBlock = ConfigHolder.CONFLICT_HOLDER.getConfig().getInt(this.enchantConflict.getName()+'.'+EnchantConflictManager.ENCH_MAX_PATH, 0); + this.enchantConflict.setMinBeforeBlock(minBeforeBlock); + + // Parent should call updateLocal with this call + this.parent.updateValueForGeneric(this.enchantConflict, true); } + @Override public void updateLocal() { - if (!this.shouldWorld) return; + if (!this.shouldWork) return; // Prepare enchantment lore ArrayList enchantLore = new ArrayList<>(); @@ -231,11 +233,12 @@ public void updateLocal() { update(); } + @Override public void cleanUnused() { for (HumanEntity viewer : getViewers()) { this.parent.show(viewer); } - this.shouldWorld = false; + this.shouldWork = false; // Just in case something is extremely wrong GuiItem background = GuiGlobalItems.backgroundItem(); @@ -247,17 +250,13 @@ public void cleanUnused() { @Override public void show(@NotNull HumanEntity humanEntity) { - if (this.shouldWorld) { + if (this.shouldWork) { super.show(humanEntity); } else { this.parent.show(humanEntity); } } - public GuiItem getParentItemForThisGui() { - return parentItemForThisGui; - } - // Select enchantment container methods @Override @@ -267,7 +266,7 @@ public Set getSelectedEnchantments() { @Override public boolean setSelectedEnchantments(Set enchantments) { - if (!this.shouldWorld) { + if (!this.shouldWork) { CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " enchants but sub config is destroyed"); return false; } @@ -312,7 +311,7 @@ public Set getSelectedGroups() { @Override public boolean setSelectedGroups(Set groups) { - if (!this.shouldWorld) { + if (!this.shouldWork) { CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " groups but sub config is destroyed"); return false; } @@ -347,4 +346,5 @@ public Set illegalGroups() { return Collections.emptySet(); } + } diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/MappedToListSubSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/MappedToListSubSettingGui.java new file mode 100644 index 0000000..bcadc73 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/subsetting/MappedToListSubSettingGui.java @@ -0,0 +1,28 @@ +package xyz.alexcrea.cuanvil.gui.config.settings.subsetting; + +import com.github.stefvanschie.inventoryframework.gui.GuiItem; +import io.delilaheve.CustomAnvil; +import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui; + +public abstract class MappedToListSubSettingGui extends ValueUpdatableGui { + + private final GuiItem item; + public MappedToListSubSettingGui( + GuiItem item, + int rows, + @NotNull String title) { + super(rows, title, CustomAnvil.instance); + this.item = item; + } + + + public GuiItem getParentItemForThisGui() { + return item; + } + + + public abstract void updateLocal(); // TODO + + public abstract void cleanUnused(); // TODO +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/util/GuiGlobalItems.java b/src/main/java/xyz/alexcrea/cuanvil/gui/util/GuiGlobalItems.java index fb10605..f8b4656 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/util/GuiGlobalItems.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/util/GuiGlobalItems.java @@ -12,8 +12,10 @@ import xyz.alexcrea.cuanvil.gui.config.settings.AbstractSettingGui; import xyz.alexcrea.cuanvil.gui.config.settings.BoolSettingsGui; import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui; +import xyz.alexcrea.cuanvil.gui.config.settings.ItemSettingGui; import xyz.alexcrea.cuanvil.util.CasedStringUtil; +import java.nio.charset.MalformedInputException; import java.util.Collections; /** @@ -264,6 +266,48 @@ public static GuiItem intSettingGuiItem( return intSettingGuiItem(factory, itemMat, CasedStringUtil.detectToUpperSpacedCase(configPath)); } + /** + * Create a new item setting GuiItem. + * This item will create and open an item setting GUI from the factory. + * Item's name will be the factory set title. + * + * @param factory The setting's GUI factory. + * @param name Name of the item. + * @return A formatted GuiItem that will create and open a GUI for the item setting. + */ + public static GuiItem itemSettingGuiItem( + @NotNull ItemSettingGui.ItemSettingFactory factory, + @NotNull String name + ) { + ItemStack item = factory.getConfiguredValue(); + if(item == null || item.getType().isAir()){ + item = new ItemStack(Material.BARRIER); + }else{ + item = item.clone(); + } + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("\u00A7a" + name); + + item.setItemMeta(meta); + + return openSettingGuiItem(item, factory); + } + + /** + * Create a new item setting GuiItem. + * This item will create and open an item setting GUI from the factory. + * Item's name will be the factory set title. + * + * @param factory The setting's GUI factory. + * @return A formatted GuiItem that will create and open a GUI for the item setting. + */ + public static GuiItem itemSettingGuiItem( + @NotNull ItemSettingGui.ItemSettingFactory factory + ) { + String configPath = getConfigNameFromPath(factory.getConfigPath()); + return itemSettingGuiItem(factory, CasedStringUtil.detectToUpperSpacedCase(configPath)); + } + /** * Create an arbitrary GuiItem from a unique setting and item's property. * diff --git a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt index eab2c6b..4de60f4 100644 --- a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt +++ b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt @@ -25,6 +25,7 @@ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Repairable import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.group.ConflictType +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import kotlin.math.min @@ -58,6 +59,21 @@ class AnvilEventListener : Listener { val player = event.view.player if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return + // Test custom recipe + val recipe = getCustomRecipe(first, second) + CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") + if(recipe != null){ + val amount = getCustomRecipeAmount(recipe, first, second) + + val resultItem: ItemStack = recipe.resultItem!!.clone() + resultItem.amount *= amount + + event.result = resultItem + handleAnvilXp(inventory, event, recipe.xpCostPerCraft * amount, true) + + return + } + // Test rename lonely item if (second == null) { val resultItem = first.clone() @@ -139,6 +155,7 @@ class AnvilEventListener : Listener { CustomAnvil.log("no anvil fuse type found") event.result = null } + } private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory): Int { @@ -168,6 +185,16 @@ class AnvilEventListener : Listener { val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT) + // Test custom recipe + val recipe = getCustomRecipe(leftItem, rightItem) + if(recipe != null){ + event.result = Event.Result.ALLOW + onCustomCraft( + event, recipe, player, + leftItem, rightItem, output, inventory) + return + } + val canMerge = leftItem.canMergeWith(rightItem) val unitRepairResult = leftItem.getRepair(rightItem) val allowed = (rightItem == null) @@ -198,6 +225,70 @@ class AnvilEventListener : Listener { } } + private fun onCustomCraft(event: InventoryClickEvent, + recipe: AnvilCustomRecipe, + player: Player, + leftItem: ItemStack, + rightItem: ItemStack?, + output: ItemStack, + inventory: AnvilInventory) { + event.result = Event.Result.DENY + + if(recipe.leftItem == null) return // in case it changed + + val amount = getCustomRecipeAmount(recipe, leftItem, rightItem) + val xpCost = amount * recipe.xpCostPerCraft + + if ((player.gameMode != GameMode.CREATIVE) && (player.level < xpCost)) return + + // We give the item manually + // But first we check if we should give the item + val slotDestination = getActionSlot(event, player) + if (slotDestination.type == SlotType.NO_SLOT) return + + // If not creative middle click... + if (event.click != ClickType.MIDDLE) { + // We remove what should be removed + leftItem.amount -= amount * recipe.leftItem!!.amount + inventory.setItem(ANVIL_INPUT_LEFT, leftItem) + + if(rightItem != null){ + if(recipe.rightItem == null) return // in case it changed + + rightItem.amount -= amount * recipe.rightItem!!.amount + inventory.setItem(ANVIL_INPUT_RIGHT, rightItem) + } + player.level -= amount + + // Then we try to find the new values for the anvil + val newAmount = getCustomRecipeAmount(recipe, leftItem, rightItem) + + CustomAnvil.verboseLog("new amount is $newAmount") + if(newAmount <= 0 || recipe.exactCount){ + inventory.setItem(ANVIL_OUTPUT_SLOT, null) + }else{ + val resultItem: ItemStack = recipe.resultItem!!.clone() + resultItem.amount *= newAmount + + val newXp = newAmount * newAmount + + inventory.repairCost = newXp + event.view.setProperty(REPAIR_COST, newXp) + + inventory.setItem(ANVIL_OUTPUT_SLOT, resultItem) + } + } + + // Finally, we add the item to the player + if (slotDestination.type == SlotType.CURSOR) { + player.setItemOnCursor(output) + } else {// We assume SlotType == SlotType.INVENTORY + player.inventory.setItem(slotDestination.slot, output) + } + + + } + private fun onUnitRepairExtract( leftItem: ItemStack, rightItem: ItemStack, @@ -370,16 +461,61 @@ class AnvilEventListener : Listener { return rightValue + illegalPenalty } + private fun getCustomRecipe ( + leftItem: ItemStack, + rightItem: ItemStack?) : AnvilCustomRecipe? { + + val recipeList = ConfigHolder.CUSTOM_RECIPE_HOLDER.recipeManager.recipeByMat[leftItem.type] ?: return null + + CustomAnvil.verboseLog("Testing " + recipeList.size+" recipe...") + for (recipe in recipeList) { + if(recipe.testItem(leftItem, rightItem)){ + return recipe + } + } + + return null + } + + private fun getCustomRecipeAmount( + recipe: AnvilCustomRecipe, + leftItem: ItemStack, + rightItem: ItemStack? + ): Int{ + return if(recipe.exactCount) { + if(leftItem.amount != recipe.leftItem!!.amount){ + 0 + }else if(rightItem != null && rightItem.amount != recipe.rightItem!!.amount){ + 0 + }else{ + 1 + } + } + else { + // test amount + val resultItem = recipe.resultItem!! // we know exist as the recipe was returned to us + val maxResultAmount = resultItem.type.maxStackSize/resultItem.amount + val maxLeftAmount = leftItem.amount/recipe.leftItem!!.amount + val maxRightAmount = if(rightItem == null){ maxLeftAmount } else{ rightItem.amount/recipe.rightItem!!.amount } + + CustomAnvil.verboseLog("resultItem: $resultItem, maxResultAmount: $maxResultAmount, maxLeftAmount: $maxLeftAmount, maxRightAmount: $maxRightAmount") + + min(min(maxResultAmount, maxLeftAmount), maxRightAmount) + } + } + + /** * Display xp needed for the work on the anvil inventory */ private fun handleAnvilXp( inventory: AnvilInventory, event: PrepareAnvilEvent, - anvilCost: Int + anvilCost: Int, + ignoreRules: Boolean = false ) { // Test repair cost limit - val finalAnvilCost = if (ConfigOptions.limitRepairCost) { + val finalAnvilCost = if (ConfigOptions.limitRepairCost && !ignoreRules) { min(anvilCost, ConfigOptions.limitRepairValue) } else { anvilCost @@ -392,8 +528,10 @@ class AnvilEventListener : Listener { .server .scheduler .runTask(CustomAnvil.instance, Runnable { - if (ConfigOptions.removeRepairLimit) { + if (ConfigOptions.removeRepairLimit || ignoreRules) { inventory.maximumRepairCost = Int.MAX_VALUE + } else{ + inventory.maximumRepairCost = 40 // minecraft default } inventory.repairCost = finalAnvilCost diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt index 3e5c5d0..44a4bef 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictGroup.kt @@ -3,12 +3,13 @@ package xyz.alexcrea.cuanvil.group import io.delilaheve.CustomAnvil import org.bukkit.Material import org.bukkit.enchantments.Enchantment +import xyz.alexcrea.cuanvil.interfaces.Named class EnchantConflictGroup( - val name: String, + private val name: String, private val cantConflict: AbstractMaterialGroup, - val minBeforeBlock: Int -) { + var minBeforeBlock: Int +): Named { private val enchantments = HashSet() @@ -62,4 +63,8 @@ class EnchantConflictGroup( return Material.ENCHANTED_BOOK } + override fun getName(): String { + return name + } + } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt index 80aebf7..45a9f36 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/group/EnchantConflictManager.kt @@ -139,9 +139,9 @@ class EnchantConflictManager { var result = ConflictType.NO_CONFLICT for (conflict in conflictList) { - CustomAnvil.verboseLog("Is against ${conflict.name}") + CustomAnvil.verboseLog("Is against ${conflict.getName()}") val conflicting = conflict.allowed(base, mat) - CustomAnvil.verboseLog("Was against ${conflict.name} and conflicting: $conflicting ") + CustomAnvil.verboseLog("Was against ${conflict.getName()} and conflicting: $conflicting ") if (!conflicting) { if (conflict.getEnchants().size <= 1) { result = ConflictType.SMALL_CONFLICT diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/interfaces/Named.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/interfaces/Named.kt new file mode 100644 index 0000000..e3cd288 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/interfaces/Named.kt @@ -0,0 +1,7 @@ +package xyz.alexcrea.cuanvil.interfaces + +interface Named { + + fun getName(): String + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt new file mode 100644 index 0000000..fbd2866 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt @@ -0,0 +1,170 @@ +package xyz.alexcrea.cuanvil.recipe + +import io.delilaheve.CustomAnvil +import org.bukkit.configuration.ConfigurationSection +import org.bukkit.inventory.ItemStack +import xyz.alexcrea.cuanvil.config.ConfigHolder +import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant +import xyz.alexcrea.cuanvil.interfaces.Named + +class AnvilCustomRecipe( + private val name: String, + var exactCount: Boolean, + //var exactLeft: Boolean, + //var exactRight: Boolean, + + var xpCostPerCraft: Int, + + var leftItem: ItemStack?, + var rightItem: ItemStack?, + var resultItem: ItemStack?, +): Named { + + // Static config name + companion object { + const val EXACT_COUNT_CONFIG = "exact_count" + //const val EXACT_LEFT_CONFIG = "exact_left" + //const val EXACT_RIGHT_CONFIG = "exact_right" + + const val XP_COST_CONFIG = "xp_cost" + + const val LEFT_ITEM_CONFIG = "left_item" + const val RIGHT_ITEM_CONFIG = "right_item" + const val RESULT_ITEM_CONFIG = "result_item" + + + val DEFAULT_EXACT_COUNT_CONFIG = true + //val DEFAULT_EXACT_LEFT_CONFIG = true + //val DEFAULT_EXACT_RIGHT_CONFIG = true + + val DEFAULT_XP_COST_CONFIG = 1 + + val DEFAULT_LEFT_ITEM_CONFIG: ItemStack? = null + val DEFAULT_RIGHT_ITEM_CONFIG: ItemStack? = null + val DEFAULT_RESULT_ITEM_CONFIG: ItemStack? = null; + + val XP_COST_CONFIG_RANGE = 0..255 + + fun getFromConfig(name: String, configSection: ConfigurationSection?): AnvilCustomRecipe? { + if(configSection == null) return null; + return AnvilCustomRecipe( + name, + configSection.getBoolean(EXACT_COUNT_CONFIG, DEFAULT_EXACT_COUNT_CONFIG), + //configSection.getBoolean(EXACT_LEFT_CONFIG, true), + //configSection.getBoolean(EXACT_RIGHT_CONFIG, true), + + configSection.getInt(XP_COST_CONFIG, DEFAULT_XP_COST_CONFIG), + + configSection.getItemStack(LEFT_ITEM_CONFIG, DEFAULT_LEFT_ITEM_CONFIG), + configSection.getItemStack(RIGHT_ITEM_CONFIG, DEFAULT_RIGHT_ITEM_CONFIG), + configSection.getItemStack(RESULT_ITEM_CONFIG, DEFAULT_RESULT_ITEM_CONFIG), + + ) + } + + fun getFromConfig(name: String): AnvilCustomRecipe? { + return getFromConfig(name, ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getConfigurationSection(name)) + } + } + + fun validate(): Boolean { + return (leftItem != null) && !(leftItem!!.type.isAir) && (leftItem!!.amount > 0) && + //(rightItem != null) && !(rightItem!!.type.isAir) && (rightItem!!.amount > 0) && + ((rightItem == null) || (!(rightItem!!.type.isAir) && (rightItem!!.amount > 0))) && + (resultItem != null) && !(resultItem!!.type.isAir) && (resultItem!!.amount > 0) + + } + + fun saveToFile(){ + val fileConfig = ConfigHolder.CUSTOM_RECIPE_HOLDER.config + + fileConfig.set("$name.$EXACT_COUNT_CONFIG", exactCount) + //fileConfig.set("$name.$EXACT_LEFT_CONFIG", exactLeft) + //fileConfig.set("$name.$EXACT_RIGHT_CONFIG", exactRight) + + fileConfig.set("$name.$XP_COST_CONFIG", xpCostPerCraft) + + fileConfig.set("$name.$LEFT_ITEM_CONFIG", leftItem) + fileConfig.set("$name.$RIGHT_ITEM_CONFIG", rightItem) + fileConfig.set("$name.$RESULT_ITEM_CONFIG", resultItem) + + + if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) { + ConfigHolder.CUSTOM_RECIPE_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE); + } + } + + fun updateFromFile(){ + this.exactCount = ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getBoolean( + "$name.$EXACT_COUNT_CONFIG", + DEFAULT_EXACT_COUNT_CONFIG + ) + + this.xpCostPerCraft = ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getInt( + "$name.$XP_COST_CONFIG", + DEFAULT_XP_COST_CONFIG + ) + + // Update items + val leftItem = ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getItemStack( + "$name.$LEFT_ITEM_CONFIG", + DEFAULT_LEFT_ITEM_CONFIG + ) + + this.rightItem = ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getItemStack( + "$name.$RIGHT_ITEM_CONFIG", + DEFAULT_RIGHT_ITEM_CONFIG + ) + + this.resultItem = ConfigHolder.CUSTOM_RECIPE_HOLDER.config.getItemStack( + "$name.$RESULT_ITEM_CONFIG", + DEFAULT_RESULT_ITEM_CONFIG + ) + + // Update material map + ConfigHolder.CUSTOM_RECIPE_HOLDER.recipeManager.cleanSetLeftItem(this, leftItem) + + } + + fun testItem(item1: ItemStack, item2: ItemStack?): Boolean { + CustomAnvil.verboseLog("Testing $name $leftItem") + // We assume this function can be call only if leftItem != null + + // Test is valid + if(!validate()) return false + + val leftSimilar = leftItem!!.isSimilar(item1) + CustomAnvil.verboseLog("Validated test !") + + // test of left item + if(!leftSimilar) return false // Test similar + if(exactCount){ + if((leftItem!!.amount != item1.amount)) return false // test exact amount + }else if(item1.amount < leftItem!!.amount) return false // test if it has at least the amount we ask + + CustomAnvil.verboseLog("Left item passed !") + + // we don't know if right item can be + if(rightItem == null){ // null test + if(item2 != null) return false + }else { + val rightSimilar = rightItem!!.isSimilar(item2) + CustomAnvil.verboseLog("Right similar: $rightSimilar") + if(!rightSimilar) return false // test if similar when not null + + if(exactCount) { + if (rightItem!!.amount != item2!!.amount) return false // test exact amount + }else if(item2!!.amount < rightItem!!.amount) return false // test if it has at least the amount we ask + } + + CustomAnvil.verboseLog("Right item passed !") + + return true + } + + override fun getName(): String { + return name + } + + +} diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt new file mode 100644 index 0000000..8f9dece --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/CustomAnvilRecipeManager.kt @@ -0,0 +1,75 @@ +package xyz.alexcrea.cuanvil.recipe + +import io.delilaheve.CustomAnvil +import org.bukkit.Material +import org.bukkit.configuration.file.FileConfiguration +import org.bukkit.inventory.ItemStack + +class CustomAnvilRecipeManager { + + lateinit var recipeList: ArrayList + + lateinit var recipeByMat: LinkedHashMap> + + fun prepareRecipes(config: FileConfiguration) { + recipeList = ArrayList() + recipeByMat = LinkedHashMap() + + // read all configs + val keys = config.getKeys(false) + for (key in keys) { + val recipe = AnvilCustomRecipe.getFromConfig(key) + if(recipe == null){ + CustomAnvil.log("Can't load recipe $key") + continue + } + + cleanAddNew(recipe) + } + + } + + + fun cleanAddNew(recipe: AnvilCustomRecipe){ + recipeList.add(recipe) + val leftItem = recipe.leftItem + if(leftItem != null){ + addToMatMap(recipe, leftItem) + } + + } + + fun cleanSetLeftItem(recipe: AnvilCustomRecipe, leftItem: ItemStack?){ + // Remove left item mat if exist + val oldLeftItem = recipe.leftItem + if(oldLeftItem != null){ + val oldMat = oldLeftItem.type + + val test = recipeByMat[oldMat] + test!!.remove(recipe) + } + if(leftItem != null){ + addToMatMap(recipe, leftItem) + } + + recipe.leftItem = leftItem + } + + private fun addToMatMap(recipe: AnvilCustomRecipe, leftItem: ItemStack){ + var recipeList = recipeByMat[leftItem.type] + if(recipeList == null){ + recipeList = ArrayList() + recipeByMat[leftItem.type] = recipeList + } + recipeList.add(recipe) + + } + + fun cleanRemove(recipe: AnvilCustomRecipe) { + + recipeList.remove(recipe) + cleanSetLeftItem(recipe, null) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/MetricsUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/MetricsUtil.kt index 0f60e48..614116d 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/MetricsUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/MetricsUtil.kt @@ -13,18 +13,21 @@ object MetricsUtil { private const val enchantConflictConfigHash = 1406650190 private const val itemGroupsConfigHash = 1406650190 private const val unitRepairItemConfigHash = 536871958 + private const val customAnvilCraftConfigHash = 0 private const val baseConfigPieName = "isDefaultBaseConfig" private const val enchantLimitsConfigPieName = "isDefaultEnchantLimitsConfig" private const val enchantValuesConfigPieName = "isDefaultEnchantValuesConfig" private const val enchantConflictConfigPieName = "isDefaultEnchantConflictConfig" private const val itemGroupsConfigPieName = "isDefaultItemGroupsConfig" private const val unitRepairItemConfigPieName = "isDefaultUnitRepairItemConfig" + private const val customAnvilCraftConfigPieName = "isDefaultCustomAnvilCraftConfig" private var isDefaultBaseConfig = true private var isDefaultEnchantLimitsConfig = true private var isDefaultEnchantValuesConfig = true private var isDefaultEnchantConflictConfig = true private var isDefaultItemGroupsConfig = true private var isDefaultUnitRepairItemConfig = true + private var isDefaultCustomAnvilCraftConfig = true /** * Get hash of a key, value a pair of a configuration section @@ -73,6 +76,7 @@ object MetricsUtil { val enchantConflictConfig = getConfigurationHash(ConfigHolder.CONFLICT_HOLDER.config) val itemGroupConfig = getConfigurationHash(ConfigHolder.ITEM_GROUP_HOLDER.config) val unitRepairConfig = getConfigurationHash(ConfigHolder.UNIT_REPAIR_HOLDER.config) + val customRecipeConfig = getConfigurationHash(ConfigHolder.CUSTOM_RECIPE_HOLDER.config) // Test if default isDefaultBaseConfig = baseConfigHash == baseConfig isDefaultEnchantLimitsConfig = enchantLimitsConfigHash == limitEnchantConfig @@ -80,6 +84,7 @@ object MetricsUtil { isDefaultEnchantConflictConfig = enchantConflictConfigHash == enchantConflictConfig isDefaultItemGroupsConfig = itemGroupsConfigHash == itemGroupConfig isDefaultUnitRepairItemConfig = unitRepairItemConfigHash == unitRepairConfig + isDefaultCustomAnvilCraftConfig = customAnvilCraftConfigHash == customRecipeConfig // If not default and debug flag active, print the hash. if (ConfigOptions.debugLog) { if (!isDefaultBaseConfig) { @@ -100,6 +105,9 @@ object MetricsUtil { if (!isDefaultUnitRepairItemConfig) { CustomAnvil.log("unitRepairConfig: $unitRepairConfig") } + if (!isDefaultCustomAnvilCraftConfig) { + CustomAnvil.log("customRecipeConfig: $customRecipeConfig") + } } } @@ -141,6 +149,9 @@ object MetricsUtil { metric.addCustomChart(Metrics.SimplePie(unitRepairItemConfigPieName) { isDefaultUnitRepairItemConfig.toString() }) + metric.addCustomChart(Metrics.SimplePie(customAnvilCraftConfigPieName) { + isDefaultCustomAnvilCraftConfig.toString() + }) } } \ No newline at end of file diff --git a/src/main/resources/custom_recipes.yml b/src/main/resources/custom_recipes.yml new file mode 100644 index 0000000..57c2220 --- /dev/null +++ b/src/main/resources/custom_recipes.yml @@ -0,0 +1,5 @@ +# ---------------------------------------------------- +# This config file is to store custom craft +# It is recommended to use the in game config editor for this configuration. +# /customanvilconfig With ca.config.edit permission +# ---------------------------------------------------- diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 52fe71d..d372cbb 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ main: io.delilaheve.CustomAnvil name: CustomAnvil prefix: "Custom Anvil" -version: 1.3.2-A2 +version: 1.4.0a description: Allow to customise anvil mechanics api-version: 1.18 load: POSTWORLD