Add Custom craft and its config GUI. (#5)

This commit is contained in:
alexcrea 2024-04-07 02:37:43 +00:00 committed by GitHub
commit 74f36ec899
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1582 additions and 389 deletions

View file

@ -4,7 +4,7 @@ plugins {
} }
group = "xyz.alexcrea" group = "xyz.alexcrea"
version = "1.3.2-A2" version = "1.4.0a"
repositories { repositories {
mavenCentral() mavenCentral()

View file

@ -6,6 +6,7 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import xyz.alexcrea.cuanvil.group.EnchantConflictManager; import xyz.alexcrea.cuanvil.group.EnchantConflictManager;
import xyz.alexcrea.cuanvil.group.ItemGroupManager; import xyz.alexcrea.cuanvil.group.ItemGroupManager;
import xyz.alexcrea.cuanvil.recipe.CustomAnvilRecipeManager;
import xyz.alexcrea.cuanvil.util.MetricsUtil; import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.io.File; import java.io.File;
@ -18,12 +19,14 @@ public abstract class ConfigHolder {
public static ItemGroupConfigHolder ITEM_GROUP_HOLDER; public static ItemGroupConfigHolder ITEM_GROUP_HOLDER;
public static ConflictConfigHolder CONFLICT_HOLDER; public static ConflictConfigHolder CONFLICT_HOLDER;
public static UnitRepairHolder UNIT_REPAIR_HOLDER; public static UnitRepairHolder UNIT_REPAIR_HOLDER;
public static CustomAnvilCraftHolder CUSTOM_RECIPE_HOLDER;
public static boolean loadConfig() { public static boolean loadConfig() {
DEFAULT_CONFIG = new DefaultConfigHolder(); DEFAULT_CONFIG = new DefaultConfigHolder();
ITEM_GROUP_HOLDER = new ItemGroupConfigHolder(); ITEM_GROUP_HOLDER = new ItemGroupConfigHolder();
CONFLICT_HOLDER = new ConflictConfigHolder(); CONFLICT_HOLDER = new ConflictConfigHolder();
UNIT_REPAIR_HOLDER = new UnitRepairHolder(); UNIT_REPAIR_HOLDER = new UnitRepairHolder();
CUSTOM_RECIPE_HOLDER = new CustomAnvilCraftHolder();
boolean result = reloadAllFromDisk(true); boolean result = reloadAllFromDisk(true);
if (result) { if (result) {
@ -41,6 +44,9 @@ public abstract class ConfigHolder {
sucess = CONFLICT_HOLDER.reloadFromDisk(hardfail); sucess = CONFLICT_HOLDER.reloadFromDisk(hardfail);
if (!sucess) return false; if (!sucess) return false;
sucess = UNIT_REPAIR_HOLDER.reloadFromDisk(hardfail); sucess = UNIT_REPAIR_HOLDER.reloadFromDisk(hardfail);
if (!sucess) return false;
sucess = CUSTOM_RECIPE_HOLDER.reloadFromDisk(hardfail);
return sucess; return sucess;
} }
@ -83,6 +89,7 @@ public abstract class ConfigHolder {
// Save logic // Save logic
public boolean saveToDisk(boolean doBackup) { public boolean saveToDisk(boolean doBackup) {
CustomAnvil.Companion.log("Saving "+getConfigFileName());
if (doBackup) { if (doBackup) {
if (!saveBackup()) { if (!saveBackup()) {
CustomAnvil.instance.getLogger().severe("Could not save backup. see above."); CustomAnvil.instance.getLogger().severe("Could not save backup. see above.");
@ -104,6 +111,7 @@ public abstract class ConfigHolder {
return false; return false;
} }
CustomAnvil.Companion.log(getConfigFileName()+" saved successfully");
return true; return true;
} }
@ -242,6 +250,7 @@ public abstract class ConfigHolder {
public static class UnitRepairHolder extends ResourceConfigHolder { public static class UnitRepairHolder extends ResourceConfigHolder {
private final static String ITEM_GROUP_FILE_NAME = "unit_repair_item"; private final static String ITEM_GROUP_FILE_NAME = "unit_repair_item";
private UnitRepairHolder() { private UnitRepairHolder() {
super(ITEM_GROUP_FILE_NAME); super(ITEM_GROUP_FILE_NAME);
} }
@ -253,4 +262,25 @@ public abstract class ConfigHolder {
} }
// 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);
}
}
} }

View file

@ -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.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.pane.Orientable; import com.github.stefvanschie.inventoryframework.pane.Orientable;

View file

@ -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<AnvilCustomRecipe, CustomRecipeSubSettingGui> {
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<AnvilCustomRecipe> getEveryDisplayableInstanceOfGeneric() {
return ConfigHolder.CUSTOM_RECIPE_HOLDER.getRecipeManager().getRecipeList();
}
}

View file

@ -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<OutlinePane> pages;
protected HashMap<UUID, Integer> 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<T> 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();
}
}

View file

@ -1,34 +1,20 @@
package xyz.alexcrea.cuanvil.gui.config.global; package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem; 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.configuration.file.FileConfiguration;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import xyz.alexcrea.cuanvil.group.EnchantConflictGroup;
import xyz.alexcrea.cuanvil.group.IncludeGroup; import xyz.alexcrea.cuanvil.group.IncludeGroup;
import xyz.alexcrea.cuanvil.gui.config.settings.subsetting.EnchantConflictSubSettingGui; 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.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.CasedStringUtil; import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.*; import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference; import java.util.List;
import java.util.function.Consumer;
public class EnchantConflictGui extends ChestGui { public class EnchantConflictGui extends MappedElementListConfigGui<EnchantConflictGroup, EnchantConflictSubSettingGui> {
public final static EnchantConflictGui INSTANCE = new EnchantConflictGui(); public final static EnchantConflictGui INSTANCE = new EnchantConflictGui();
@ -36,194 +22,37 @@ public class EnchantConflictGui extends ChestGui {
INSTANCE.init(); INSTANCE.init();
} }
private final HashMap<EnchantConflictGroup, EnchantConflictSubSettingGui> conflictGuiMap;
private EnchantConflictGui() { private EnchantConflictGui() {
super(6, "Conflict Config", CustomAnvil.instance); super( "Conflict Config");
this.conflictGuiMap = new HashMap<>();
} }
private OutlinePane firstPage; @Override
private ArrayList<OutlinePane> pages; protected EnchantConflictGroup createAndSaveNewEmptyGeneric(String name){
private HashMap<UUID, Integer> pageMap; // Create new empty conflict and display it to the admin
private PatternPane backgroundPane; EnchantConflictGroup conflict = new EnchantConflictGroup(
name,
new IncludeGroup("new_group"),
0);
private void init() { ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList().add(conflict);
// 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); // save empty conflict in config
String[] emptyStringArray = new String[0];
GuiGlobalItems.addBackgroundItem(this.backgroundPane); FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig();
this.backgroundPane.bindItem('1', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM); config.set(name + ".enchantments", emptyStringArray);
addPane(this.backgroundPane); config.set(name + ".notAffectedGroups", emptyStringArray);
config.set(name + ".maxEnchantmentBeforeConflict", 0);
// Page init if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
this.pages = new ArrayList<>(); ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
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));
}
private Consumer<String> prepareCreateItemConsumer(HumanEntity player) {
AtomicReference<Consumer<String>> selfRef = new AtomicReference<>();
Consumer<String> 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];
FileConfiguration config = ConfigHolder.CONFLICT_HOLDER.getConfig();
config.set(message + ".enchantments", emptyStringArray);
config.set(message + ".notAffectedGroups", emptyStringArray);
config.set(message + ".maxEnchantmentBeforeConflict", 0);
if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
}
// show the new conflict config to the player
this.conflictGuiMap.get(conflict).show(player);
};
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);
} }
update(); return conflict;
} }
public static ItemStack createItemForConflict(EnchantConflictGroup conflict) { @Override
public ItemStack createItemForGeneric(EnchantConflictGroup conflict) {
ItemStack item = new ItemStack(conflict.getRepresentativeMaterial()); ItemStack item = new ItemStack(conflict.getRepresentativeMaterial());
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
@ -239,169 +68,19 @@ public class EnchantConflictGui extends ChestGui {
return item; return item;
} }
public void updateValueForConflict(EnchantConflictGroup conflict, boolean shouldUpdate) { @Override
EnchantConflictSubSettingGui gui = this.conflictGuiMap.get(conflict); protected EnchantConflictSubSettingGui newInstanceOfGui(EnchantConflictGroup conflict, GuiItem item) {
ItemStack item = createItemForConflict(conflict); return new EnchantConflictSubSettingGui(this, conflict, item);
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) { @Override
EnchantConflictSubSettingGui gui = this.conflictGuiMap.get(conflict); protected String genericDisplayedName() {
if (gui == null) return; return "conflict";
this.conflictGuiMap.remove(conflict);
removeFromPage(gui.getParentItemForThisGui());
update();
} }
private void addToPage(GuiItem guiItem) { @Override
// Get first available page or create one protected List<EnchantConflictGroup> getEveryDisplayableInstanceOfGeneric() {
OutlinePane page = this.pages.get(this.pages.size() - 1); return ConfigHolder.CONFLICT_HOLDER.getConflictManager().getConflictList();
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;
}
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
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);
} }
} }

View file

@ -8,7 +8,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.enchant.EnchantmentProperties; import xyz.alexcrea.cuanvil.enchant.EnchantmentProperties;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity; 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.config.settings.EnchantCostSettingsGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.util.CasedStringUtil; import xyz.alexcrea.cuanvil.util.CasedStringUtil;

View file

@ -4,7 +4,6 @@ import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import xyz.alexcrea.cuanvil.config.ConfigHolder; 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.config.settings.IntSettingsGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems; import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.util.CasedStringUtil; import xyz.alexcrea.cuanvil.util.CasedStringUtil;

View file

@ -29,7 +29,7 @@ public class MainConfigGui extends ChestGui {
private void init() { private void init() {
Pattern pattern = new Pattern( Pattern pattern = new Pattern(
"I00000000", "I00000000",
"012304560", "012304567",
"Q00000000" "Q00000000"
); );
PatternPane pane = new PatternPane(0, 0, 9, 3, pattern); PatternPane pane = new PatternPane(0, 0, 9, 3, pattern);
@ -70,7 +70,7 @@ public class MainConfigGui extends ChestGui {
GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, EnchantCostConfigGui.INSTANCE); GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, EnchantCostConfigGui.INSTANCE);
pane.bindItem('3', enchantCostItem); pane.bindItem('3', enchantCostItem);
// Enchantment Conflicts // Enchantment Conflicts item
ItemStack EnchantConflictItemstack = new ItemStack(Material.OAK_FENCE); ItemStack EnchantConflictItemstack = new ItemStack(Material.OAK_FENCE);
ItemMeta enchantConflictMeta = EnchantConflictItemstack.getItemMeta(); ItemMeta enchantConflictMeta = EnchantConflictItemstack.getItemMeta();
@ -93,6 +93,17 @@ public class MainConfigGui extends ChestGui {
pane.bindItem('5', wip5); pane.bindItem('5', wip5);
pane.bindItem('6', wip6); 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 // quit item
ItemStack quitItemstack = new ItemStack(Material.BARRIER); ItemStack quitItemstack = new ItemStack(Material.BARRIER);
ItemMeta quitMeta = quitItemstack.getItemMeta(); ItemMeta quitMeta = quitItemstack.getItemMeta();

View file

@ -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<T, S> 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<String> prepareCreateItemConsumer(HumanEntity player){
AtomicReference<Consumer<String>> selfRef = new AtomicReference<>();
Consumer<String> 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);
}

View file

@ -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<String> 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<InventoryClickEvent> 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);
}
}
}

View file

@ -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<Boolean> 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);
}
}
}

View file

@ -14,7 +14,6 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup; import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup;
import xyz.alexcrea.cuanvil.group.EnchantConflictGroup; import xyz.alexcrea.cuanvil.group.EnchantConflictGroup;
import xyz.alexcrea.cuanvil.group.EnchantConflictManager; 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.ConfirmActionGui;
import xyz.alexcrea.cuanvil.gui.config.SelectEnchantmentContainer; import xyz.alexcrea.cuanvil.gui.config.SelectEnchantmentContainer;
import xyz.alexcrea.cuanvil.gui.config.SelectGroupContainer; import xyz.alexcrea.cuanvil.gui.config.SelectGroupContainer;
@ -34,24 +33,22 @@ import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.Level; 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 EnchantConflictGui parent;
private final EnchantConflictGroup enchantConflict; private final EnchantConflictGroup enchantConflict;
private final GuiItem parentItemForThisGui;
private final PatternPane pane; private final PatternPane pane;
private boolean shouldWorld = true; private boolean shouldWork = true;
public EnchantConflictSubSettingGui( public EnchantConflictSubSettingGui(
@NotNull EnchantConflictGui parent, @NotNull EnchantConflictGui parent,
@NotNull EnchantConflictGroup enchantConflict, @NotNull EnchantConflictGroup enchantConflict,
@NotNull GuiItem parentItemForThisGui) { @NotNull GuiItem parentItemForThisGui) {
super(3, super(parentItemForThisGui,
"\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + " \u00A78Config", 3,
CustomAnvil.instance); "\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + " \u00A78Config");
this.parent = parent; this.parent = parent;
this.enchantConflict = enchantConflict; this.enchantConflict = enchantConflict;
this.parentItemForThisGui = parentItemForThisGui;
Pattern pattern = new Pattern( Pattern pattern = new Pattern(
GuiSharedConstant.EMPTY_GUI_FULL_LINE, GuiSharedConstant.EMPTY_GUI_FULL_LINE,
@ -126,7 +123,7 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
manager.conflictList.remove(this.enchantConflict); manager.conflictList.remove(this.enchantConflict);
// Remove from parent // Remove from parent
this.parent.removeConflict(this.enchantConflict); this.parent.removeGeneric(this.enchantConflict);
// Remove self // Remove self
cleanUnused(); cleanUnused();
@ -143,7 +140,7 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
return success; 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.", "\u00A77Confirm that you want to delete this conflict.",
this, this.parent, deleteSupplier this, this.parent, deleteSupplier
); );
@ -151,12 +148,17 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
@Override @Override
public void updateGuiValues() { public void updateGuiValues() {
this.parent.updateValueForConflict(this.enchantConflict, true); // update value from config to conflict
// Parent should call updateLocal 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() { public void updateLocal() {
if (!this.shouldWorld) return; if (!this.shouldWork) return;
// Prepare enchantment lore // Prepare enchantment lore
ArrayList<String> enchantLore = new ArrayList<>(); ArrayList<String> enchantLore = new ArrayList<>();
@ -231,11 +233,12 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
update(); update();
} }
@Override
public void cleanUnused() { public void cleanUnused() {
for (HumanEntity viewer : getViewers()) { for (HumanEntity viewer : getViewers()) {
this.parent.show(viewer); this.parent.show(viewer);
} }
this.shouldWorld = false; this.shouldWork = false;
// Just in case something is extremely wrong // Just in case something is extremely wrong
GuiItem background = GuiGlobalItems.backgroundItem(); GuiItem background = GuiGlobalItems.backgroundItem();
@ -247,17 +250,13 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
@Override @Override
public void show(@NotNull HumanEntity humanEntity) { public void show(@NotNull HumanEntity humanEntity) {
if (this.shouldWorld) { if (this.shouldWork) {
super.show(humanEntity); super.show(humanEntity);
} else { } else {
this.parent.show(humanEntity); this.parent.show(humanEntity);
} }
} }
public GuiItem getParentItemForThisGui() {
return parentItemForThisGui;
}
// Select enchantment container methods // Select enchantment container methods
@Override @Override
@ -267,7 +266,7 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
@Override @Override
public boolean setSelectedEnchantments(Set<Enchantment> enchantments) { public boolean setSelectedEnchantments(Set<Enchantment> enchantments) {
if (!this.shouldWorld) { if (!this.shouldWork) {
CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " enchants but sub config is destroyed"); CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " enchants but sub config is destroyed");
return false; return false;
} }
@ -312,7 +311,7 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
@Override @Override
public boolean setSelectedGroups(Set<AbstractMaterialGroup> groups) { public boolean setSelectedGroups(Set<AbstractMaterialGroup> groups) {
if (!this.shouldWorld) { if (!this.shouldWork) {
CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " groups but sub config is destroyed"); CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " groups but sub config is destroyed");
return false; return false;
} }
@ -347,4 +346,5 @@ public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements S
return Collections.emptySet(); return Collections.emptySet();
} }
} }

View file

@ -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
}

View file

@ -12,8 +12,10 @@ import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.settings.AbstractSettingGui; import xyz.alexcrea.cuanvil.gui.config.settings.AbstractSettingGui;
import xyz.alexcrea.cuanvil.gui.config.settings.BoolSettingsGui; import xyz.alexcrea.cuanvil.gui.config.settings.BoolSettingsGui;
import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui; import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui;
import xyz.alexcrea.cuanvil.gui.config.settings.ItemSettingGui;
import xyz.alexcrea.cuanvil.util.CasedStringUtil; import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.nio.charset.MalformedInputException;
import java.util.Collections; import java.util.Collections;
/** /**
@ -264,6 +266,48 @@ public class GuiGlobalItems {
return intSettingGuiItem(factory, itemMat, CasedStringUtil.detectToUpperSpacedCase(configPath)); 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. * Create an arbitrary GuiItem from a unique setting and item's property.
* *

View file

@ -25,6 +25,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Repairable import org.bukkit.inventory.meta.Repairable
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.group.ConflictType import xyz.alexcrea.cuanvil.group.ConflictType
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import kotlin.math.min import kotlin.math.min
@ -58,6 +59,21 @@ class AnvilEventListener : Listener {
val player = event.view.player val player = event.view.player
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return 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 // Test rename lonely item
if (second == null) { if (second == null) {
val resultItem = first.clone() val resultItem = first.clone()
@ -139,6 +155,7 @@ class AnvilEventListener : Listener {
CustomAnvil.log("no anvil fuse type found") CustomAnvil.log("no anvil fuse type found")
event.result = null event.result = null
} }
} }
private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory): Int { private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory): Int {
@ -168,6 +185,16 @@ class AnvilEventListener : Listener {
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT) 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 canMerge = leftItem.canMergeWith(rightItem)
val unitRepairResult = leftItem.getRepair(rightItem) val unitRepairResult = leftItem.getRepair(rightItem)
val allowed = (rightItem == null) 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( private fun onUnitRepairExtract(
leftItem: ItemStack, leftItem: ItemStack,
rightItem: ItemStack, rightItem: ItemStack,
@ -370,16 +461,61 @@ class AnvilEventListener : Listener {
return rightValue + illegalPenalty 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 * Display xp needed for the work on the anvil inventory
*/ */
private fun handleAnvilXp( private fun handleAnvilXp(
inventory: AnvilInventory, inventory: AnvilInventory,
event: PrepareAnvilEvent, event: PrepareAnvilEvent,
anvilCost: Int anvilCost: Int,
ignoreRules: Boolean = false
) { ) {
// Test repair cost limit // Test repair cost limit
val finalAnvilCost = if (ConfigOptions.limitRepairCost) { val finalAnvilCost = if (ConfigOptions.limitRepairCost && !ignoreRules) {
min(anvilCost, ConfigOptions.limitRepairValue) min(anvilCost, ConfigOptions.limitRepairValue)
} else { } else {
anvilCost anvilCost
@ -392,8 +528,10 @@ class AnvilEventListener : Listener {
.server .server
.scheduler .scheduler
.runTask(CustomAnvil.instance, Runnable { .runTask(CustomAnvil.instance, Runnable {
if (ConfigOptions.removeRepairLimit) { if (ConfigOptions.removeRepairLimit || ignoreRules) {
inventory.maximumRepairCost = Int.MAX_VALUE inventory.maximumRepairCost = Int.MAX_VALUE
} else{
inventory.maximumRepairCost = 40 // minecraft default
} }
inventory.repairCost = finalAnvilCost inventory.repairCost = finalAnvilCost

View file

@ -3,12 +3,13 @@ package xyz.alexcrea.cuanvil.group
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import xyz.alexcrea.cuanvil.interfaces.Named
class EnchantConflictGroup( class EnchantConflictGroup(
val name: String, private val name: String,
private val cantConflict: AbstractMaterialGroup, private val cantConflict: AbstractMaterialGroup,
val minBeforeBlock: Int var minBeforeBlock: Int
) { ): Named {
private val enchantments = HashSet<Enchantment>() private val enchantments = HashSet<Enchantment>()
@ -62,4 +63,8 @@ class EnchantConflictGroup(
return Material.ENCHANTED_BOOK return Material.ENCHANTED_BOOK
} }
override fun getName(): String {
return name
}
} }

View file

@ -139,9 +139,9 @@ class EnchantConflictManager {
var result = ConflictType.NO_CONFLICT var result = ConflictType.NO_CONFLICT
for (conflict in conflictList) { for (conflict in conflictList) {
CustomAnvil.verboseLog("Is against ${conflict.name}") CustomAnvil.verboseLog("Is against ${conflict.getName()}")
val conflicting = conflict.allowed(base, mat) 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 (!conflicting) {
if (conflict.getEnchants().size <= 1) { if (conflict.getEnchants().size <= 1) {
result = ConflictType.SMALL_CONFLICT result = ConflictType.SMALL_CONFLICT

View file

@ -0,0 +1,7 @@
package xyz.alexcrea.cuanvil.interfaces
interface Named {
fun getName(): String
}

View file

@ -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
}
}

View file

@ -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<AnvilCustomRecipe>
lateinit var recipeByMat: LinkedHashMap<Material, ArrayList<AnvilCustomRecipe>>
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)
}
}

View file

@ -13,18 +13,21 @@ object MetricsUtil {
private const val enchantConflictConfigHash = 1406650190 private const val enchantConflictConfigHash = 1406650190
private const val itemGroupsConfigHash = 1406650190 private const val itemGroupsConfigHash = 1406650190
private const val unitRepairItemConfigHash = 536871958 private const val unitRepairItemConfigHash = 536871958
private const val customAnvilCraftConfigHash = 0
private const val baseConfigPieName = "isDefaultBaseConfig" private const val baseConfigPieName = "isDefaultBaseConfig"
private const val enchantLimitsConfigPieName = "isDefaultEnchantLimitsConfig" private const val enchantLimitsConfigPieName = "isDefaultEnchantLimitsConfig"
private const val enchantValuesConfigPieName = "isDefaultEnchantValuesConfig" private const val enchantValuesConfigPieName = "isDefaultEnchantValuesConfig"
private const val enchantConflictConfigPieName = "isDefaultEnchantConflictConfig" private const val enchantConflictConfigPieName = "isDefaultEnchantConflictConfig"
private const val itemGroupsConfigPieName = "isDefaultItemGroupsConfig" private const val itemGroupsConfigPieName = "isDefaultItemGroupsConfig"
private const val unitRepairItemConfigPieName = "isDefaultUnitRepairItemConfig" private const val unitRepairItemConfigPieName = "isDefaultUnitRepairItemConfig"
private const val customAnvilCraftConfigPieName = "isDefaultCustomAnvilCraftConfig"
private var isDefaultBaseConfig = true private var isDefaultBaseConfig = true
private var isDefaultEnchantLimitsConfig = true private var isDefaultEnchantLimitsConfig = true
private var isDefaultEnchantValuesConfig = true private var isDefaultEnchantValuesConfig = true
private var isDefaultEnchantConflictConfig = true private var isDefaultEnchantConflictConfig = true
private var isDefaultItemGroupsConfig = true private var isDefaultItemGroupsConfig = true
private var isDefaultUnitRepairItemConfig = true private var isDefaultUnitRepairItemConfig = true
private var isDefaultCustomAnvilCraftConfig = true
/** /**
* Get hash of a key, value a pair of a configuration section * 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 enchantConflictConfig = getConfigurationHash(ConfigHolder.CONFLICT_HOLDER.config)
val itemGroupConfig = getConfigurationHash(ConfigHolder.ITEM_GROUP_HOLDER.config) val itemGroupConfig = getConfigurationHash(ConfigHolder.ITEM_GROUP_HOLDER.config)
val unitRepairConfig = getConfigurationHash(ConfigHolder.UNIT_REPAIR_HOLDER.config) val unitRepairConfig = getConfigurationHash(ConfigHolder.UNIT_REPAIR_HOLDER.config)
val customRecipeConfig = getConfigurationHash(ConfigHolder.CUSTOM_RECIPE_HOLDER.config)
// Test if default // Test if default
isDefaultBaseConfig = baseConfigHash == baseConfig isDefaultBaseConfig = baseConfigHash == baseConfig
isDefaultEnchantLimitsConfig = enchantLimitsConfigHash == limitEnchantConfig isDefaultEnchantLimitsConfig = enchantLimitsConfigHash == limitEnchantConfig
@ -80,6 +84,7 @@ object MetricsUtil {
isDefaultEnchantConflictConfig = enchantConflictConfigHash == enchantConflictConfig isDefaultEnchantConflictConfig = enchantConflictConfigHash == enchantConflictConfig
isDefaultItemGroupsConfig = itemGroupsConfigHash == itemGroupConfig isDefaultItemGroupsConfig = itemGroupsConfigHash == itemGroupConfig
isDefaultUnitRepairItemConfig = unitRepairItemConfigHash == unitRepairConfig isDefaultUnitRepairItemConfig = unitRepairItemConfigHash == unitRepairConfig
isDefaultCustomAnvilCraftConfig = customAnvilCraftConfigHash == customRecipeConfig
// If not default and debug flag active, print the hash. // If not default and debug flag active, print the hash.
if (ConfigOptions.debugLog) { if (ConfigOptions.debugLog) {
if (!isDefaultBaseConfig) { if (!isDefaultBaseConfig) {
@ -100,6 +105,9 @@ object MetricsUtil {
if (!isDefaultUnitRepairItemConfig) { if (!isDefaultUnitRepairItemConfig) {
CustomAnvil.log("unitRepairConfig: $unitRepairConfig") CustomAnvil.log("unitRepairConfig: $unitRepairConfig")
} }
if (!isDefaultCustomAnvilCraftConfig) {
CustomAnvil.log("customRecipeConfig: $customRecipeConfig")
}
} }
} }
@ -141,6 +149,9 @@ object MetricsUtil {
metric.addCustomChart(Metrics.SimplePie(unitRepairItemConfigPieName) { metric.addCustomChart(Metrics.SimplePie(unitRepairItemConfigPieName) {
isDefaultUnitRepairItemConfig.toString() isDefaultUnitRepairItemConfig.toString()
}) })
metric.addCustomChart(Metrics.SimplePie(customAnvilCraftConfigPieName) {
isDefaultCustomAnvilCraftConfig.toString()
})
} }
} }

View file

@ -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
# ----------------------------------------------------

View file

@ -1,7 +1,7 @@
main: io.delilaheve.CustomAnvil main: io.delilaheve.CustomAnvil
name: CustomAnvil name: CustomAnvil
prefix: "Custom Anvil" prefix: "Custom Anvil"
version: 1.3.2-A2 version: 1.4.0a
description: Allow to customise anvil mechanics description: Allow to customise anvil mechanics
api-version: 1.18 api-version: 1.18
load: POSTWORLD load: POSTWORLD