Create Conflict config gui. (#4)

This commit is contained in:
alexcrea 2024-03-31 00:06:49 +00:00 committed by GitHub
commit 19255809cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 2332 additions and 683 deletions

View file

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

View file

@ -49,12 +49,15 @@ public abstract class ConfigHolder {
private static final File BACKUP_FOLDER = new File(CustomAnvil.instance.getDataFolder(), "backup");
protected FileConfiguration configuration;
protected ConfigHolder() {
}
public abstract boolean reloadFromDisk(boolean hardFail);
public abstract void reload();
public FileConfiguration getConfig() {
return configuration;
}
@ -65,12 +68,15 @@ public abstract class ConfigHolder {
protected String getConfigFileExtension() {
return ".yml";
}
protected File getConfigFile() {
return new File(CustomAnvil.instance.getDataFolder(), getConfigFileName() + getConfigFileExtension());
}
protected File getFirstBackup() {
return new File(BACKUP_FOLDER, getConfigFileName() + "-first" + getConfigFileExtension());
}
protected File getLastBackup() {
return new File(BACKUP_FOLDER, getConfigFileName() + "-latest" + getConfigFileExtension());
}
@ -150,7 +156,8 @@ public abstract class ConfigHolder {
}
@Override
public void reload() {}// Nothing to do
public void reload() {
}// Nothing to do
}
@ -158,6 +165,7 @@ public abstract class ConfigHolder {
public abstract static class ResourceConfigHolder extends ConfigHolder {
String resourceName;
private ResourceConfigHolder(String resourceName) {
this.resourceName = resourceName;
}
@ -184,6 +192,7 @@ public abstract class ConfigHolder {
private final static String FILE_NAME = "item_groups";
ItemGroupManager itemGroupsManager;
private ItemGroupConfigHolder() {
super(FILE_NAME);
}
@ -210,6 +219,7 @@ public abstract class ConfigHolder {
private final static String FILE_NAME = "enchant_conflict";
EnchantConflictManager conflictManager;
private ConflictConfigHolder() {
super(FILE_NAME);
}
@ -235,8 +245,10 @@ public abstract class ConfigHolder {
private UnitRepairHolder() {
super(ITEM_GROUP_FILE_NAME);
}
@Override
public void reload() {} // Do nothing
public void reload() {
} // Do nothing
}

View file

@ -41,11 +41,10 @@ public enum EnchantmentProperties {
SWEEPING(EnchantmentRarity.RARE),
THORNS(EnchantmentRarity.VERY_RARE),
UNBREAKING(EnchantmentRarity.UNCOMMON),
VANISHING_CURSE(EnchantmentRarity.VERY_RARE)
;
VANISHING_CURSE(EnchantmentRarity.VERY_RARE);
private final EnchantmentRarity rarity;
EnchantmentProperties(EnchantmentRarity rarity) {
this.rarity = rarity;
}

View file

@ -7,9 +7,7 @@ public enum EnchantmentRarity {
COMMON(1),
UNCOMMON(2),
RARE(4),
VERY_RARE(8)
;
VERY_RARE(8);
private final int itemValue;
private final int bookValue;
@ -18,6 +16,7 @@ public enum EnchantmentRarity {
this.itemValue = itemValue;
this.bookValue = bookValue;
}
EnchantmentRarity(int itemValue) {
this(itemValue, Math.max(1, itemValue / 2));
}

View file

@ -1,76 +1,41 @@
package xyz.alexcrea.cuanvil.gui.config;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
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.enchantments.Enchantment;
import xyz.alexcrea.cuanvil.gui.MainConfigGui;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.settings.AbstractSettingGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* Abstract Global Config gui for enchantment setting configuration.
*
* @param <T> Type of the factory of the type of setting the gui should edit.
*/
public abstract class AbstractEnchantConfigGui<T extends AbstractSettingGui.SettingGuiFactory> extends ValueUpdatableGui {
private final static Material SECONDARY_BACKGROUND_MATERIAL = Material.BLACK_STAINED_GLASS_PANE;
private final Gui backGui;
/**
* Constructor for a gui displaying available enchantment to edit a enchantment setting.
* @param title Title of the gui.
* @param backGui Gui to go back on click on the "back" button.
*/
protected AbstractEnchantConfigGui(String title, Gui backGui){
super(6, title, CustomAnvil.instance);
this.backGui = backGui;
}
/**
* Constructor for a gui displaying available enchantment to edit a enchantment setting.
*
* @param title Title of the gui.
*/
protected AbstractEnchantConfigGui(String title) {
this(title, MainConfigGui.INSTANCE);
super(6, title, CustomAnvil.instance);
}
PatternPane backgroundItems;
OutlinePane filledEnchant;
private OutlinePane filledEnchant;
// Why is called like it is rn
/**
* Initialise value updatable gui pattern
*/
protected void init() {
// Back item panel
Pattern pattern = new Pattern(
"000000000",
"000000000",
"000000000",
"000000000",
"000000000",
"B11111111"
);
this.backgroundItems = new PatternPane(0, 0, 9, 6, Pane.Priority.LOW, pattern);
addPane(this.backgroundItems);
GuiGlobalItems.addBackItem(this.backgroundItems, this.backGui);
GuiGlobalItems.addBackgroundItem(this.backgroundItems);
this.backgroundItems.bindItem('1', GuiGlobalItems.backgroundItem(SECONDARY_BACKGROUND_MATERIAL));
addPane(GuiSharedConstant.BACK_TO_MAIN_MENU_BIG_LIST_DISPLAY_BACKGROUND_PANE);
// enchant item panel
this.filledEnchant = new OutlinePane(0, 0, 9, 5);
@ -90,10 +55,7 @@ public abstract class AbstractEnchantConfigGui<T extends AbstractSettingGui.Sett
protected void prepareValues() {
bookItemFactoryList = new ArrayList<>();
List<Enchantment> enchantments = Arrays.asList(Enchantment.values());
enchantments.sort(Comparator.comparing(ench -> ench.getKey().getKey()));
for (Enchantment enchant : enchantments) {
for (Enchantment enchant : GuiSharedConstant.SORTED_ENCHANTMENT_LIST) {
T factory = getFactoryFromEnchant(enchant);
bookItemFactoryList.add(factory);
@ -102,6 +64,7 @@ public abstract class AbstractEnchantConfigGui<T extends AbstractSettingGui.Sett
@Override
public void updateGuiValues() {
// probably not the most efficient but hey ! it do the work
// TODO optimise one day.. maybe

View file

@ -0,0 +1,94 @@
package xyz.alexcrea.cuanvil.gui.config;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.gui.type.ChestGui;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
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.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Supplier;
import java.util.logging.Level;
public class ConfirmActionGui extends ChestGui {
private static final ItemStack CANCEL_ITEM;
private static final ItemStack CONFIRM_ITEM;
static {
CANCEL_ITEM = new ItemStack(Material.RED_TERRACOTTA);
ItemMeta meta = CANCEL_ITEM.getItemMeta();
meta.setDisplayName("\u00A7cCancel");
meta.setLore(Collections.singletonList("\u00A77Cancel current action and return to previous menu."));
CANCEL_ITEM.setItemMeta(meta);
CONFIRM_ITEM = new ItemStack(Material.GREEN_TERRACOTTA);
meta = CONFIRM_ITEM.getItemMeta();
meta.setDisplayName("\u00A7aConfirm");
meta.setLore(Arrays.asList("\u00A77Confirm current action.",
"\u00A74Cation: This action can't be canceled."));
CONFIRM_ITEM.setItemMeta(meta);
}
public ConfirmActionGui(@NotNull String title, String actionDescription,
Gui backOnCancel, Gui backOnConfirm, Supplier<Boolean> onConfirm) {
super(3, title, CustomAnvil.instance);
Pattern pattern = new Pattern(
GuiSharedConstant.EMPTY_GUI_FULL_LINE,
"00B0I0S00",
GuiSharedConstant.EMPTY_GUI_FULL_LINE
);
PatternPane pane = new PatternPane(0, 0, pattern.getLength(), pattern.getHeight(), pattern);
addPane(pane);
pane.bindItem('0', GuiGlobalItems.backgroundItem());
pane.bindItem('B', new GuiItem(CANCEL_ITEM, GuiGlobalActions.openGuiAction(backOnCancel), CustomAnvil.instance));
pane.bindItem('S', new GuiItem(CONFIRM_ITEM, event -> {
event.setCancelled(true);
HumanEntity player = event.getWhoClicked();
if (!player.hasPermission(CustomAnvil.editConfigPermission)) {
player.closeInventory();
player.sendMessage(GuiGlobalActions.NO_EDIT_PERM);
return;
}
boolean success;
try {
success = onConfirm.get();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not process confirmation supplier.", e);
success = false;
}
if (!success) {
event.getWhoClicked().sendMessage("\u00A7cAction could not be completed. ");
}
backOnConfirm.show(player);
}, CustomAnvil.instance));
ItemStack infoItem = new ItemStack(Material.PAPER);
ItemMeta infoMeta = infoItem.getItemMeta();
infoMeta.setDisplayName("\u00A7eAre you sure ?");
infoMeta.setLore(Arrays.asList(actionDescription.split("\n")));
infoItem.setItemMeta(infoMeta);
pane.bindItem('I', new GuiItem(infoItem, GuiGlobalActions.stayInPlace, CustomAnvil.instance));
}
}

View file

@ -0,0 +1,15 @@
package xyz.alexcrea.cuanvil.gui.config;
import org.bukkit.enchantments.Enchantment;
import java.util.Set;
public interface SelectEnchantmentContainer {
Set<Enchantment> getSelectedEnchantments();
boolean setSelectedEnchantments(Set<Enchantment> enchantments);
Set<Enchantment> illegalEnchantments();
}

View file

@ -0,0 +1,15 @@
package xyz.alexcrea.cuanvil.gui.config;
import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup;
import java.util.Set;
public interface SelectGroupContainer {
Set<AbstractMaterialGroup> getSelectedGroups();
boolean setSelectedGroups(Set<AbstractMaterialGroup> groups);
Set<AbstractMaterialGroup> illegalGroups();
}

View file

@ -0,0 +1,15 @@
package xyz.alexcrea.cuanvil.gui.config;
import org.bukkit.Material;
import java.util.EnumSet;
public interface SelectMaterialContainer {
EnumSet<Material> getSelectedMaterials();
boolean setSelectedMaterials(EnumSet<Material> materials);
EnumSet<Material> illegalMaterials();
}

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.pane.PatternPane;
@ -10,12 +10,12 @@ import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.gui.MainConfigGui;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.settings.BoolSettingsGui;
import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import java.util.Collections;
@ -44,7 +44,7 @@ public class BasicConfigGui extends ValueUpdatableGui {
*/
private void init() {
Pattern pattern = new Pattern(
"000000000",
GuiSharedConstant.EMPTY_GUI_FULL_LINE,
"012345670",
"B00000000"
);

View file

@ -0,0 +1,407 @@
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;
public class EnchantConflictGui extends ChestGui {
public final static EnchantConflictGui INSTANCE = new EnchantConflictGui();
static {
INSTANCE.init();
}
private final HashMap<EnchantConflictGroup, EnchantConflictSubSettingGui> conflictGuiMap;
private EnchantConflictGui() {
super(6, "Conflict Config", CustomAnvil.instance);
this.conflictGuiMap = new HashMap<>();
}
private OutlinePane firstPage;
private ArrayList<OutlinePane> pages;
private HashMap<UUID, Integer> 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));
}
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();
}
public static ItemStack createItemForConflict(EnchantConflictGroup conflict) {
ItemStack item = new ItemStack(conflict.getRepresentativeMaterial());
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(conflict.getName()) + " \u00A7fConflict");
meta.setLore(Arrays.asList(
"\u00A77Enchantment count: \u00A7e" + conflict.getEnchants().size(),
"\u00A77Group count: \u00A7e" + conflict.getCantConflictGroup().getGroups().size(),
"\u00A77Min enchantments count: \u00A7e" + conflict.getMinBeforeBlock()
));
item.setItemMeta(meta);
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;
}
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

@ -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 org.bukkit.Material;
@ -8,6 +8,7 @@ import org.bukkit.inventory.meta.ItemMeta;
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;
@ -45,7 +46,8 @@ public class EnchantCostConfigGui extends AbstractEnchantConfigGui<EnchantCostSe
EnchantmentRarity rarity = EnchantmentRarity.NO_RARITY;
try {
rarity = EnchantmentProperties.valueOf(key.toUpperCase(Locale.ENGLISH)).getRarity();
}catch (IllegalArgumentException ignored){}
} catch (IllegalArgumentException ignored) {
}
return EnchantCostSettingsGui.enchantCostFactory(prettyKey + " Level Cost", this,
SECTION_NAME + '.' + key, ConfigHolder.DEFAULT_CONFIG, 0, 255,
@ -58,13 +60,13 @@ public class EnchantCostConfigGui extends AbstractEnchantConfigGui<EnchantCostSe
// Get item properties
int itemCost = factory.getConfiguredValue();
int bookCost = factory.getConfiguredBookValue();
StringBuilder itemName = new StringBuilder("\u00A7a").append(factory.getTitle());
String itemName = "\u00A7a" + factory.getTitle();
// Create item
ItemStack item = new ItemStack(Material.ENCHANTED_BOOK);
ItemMeta itemMeta = item.getItemMeta();
// Edit name and lore
itemMeta.setDisplayName(itemName.toString());
itemMeta.setDisplayName(itemName);
itemMeta.setLore(Arrays.asList(
"\u00A77Item Cost: " + itemCost,
"\u00A77Book Cost: " + bookCost));

View file

@ -1,9 +1,10 @@
package xyz.alexcrea.cuanvil.gui.config;
package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
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;

View file

@ -1,4 +1,4 @@
package xyz.alexcrea.cuanvil.gui;
package xyz.alexcrea.cuanvil.gui.config.global;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.gui.type.ChestGui;
@ -8,9 +8,6 @@ import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import xyz.alexcrea.cuanvil.gui.config.BasicConfigGui;
import xyz.alexcrea.cuanvil.gui.config.EnchantCostConfigGui;
import xyz.alexcrea.cuanvil.gui.config.EnchantLimitConfigGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
@ -23,6 +20,7 @@ public class MainConfigGui extends ChestGui {
static {
INSTANCE.init();
}
private MainConfigGui() {
super(3, "\u00A78Anvil Config", CustomAnvil.instance);
@ -72,17 +70,26 @@ public class MainConfigGui extends ChestGui {
GuiItem enchantCostItem = GuiGlobalItems.goToGuiItem(enchantCostItemstack, EnchantCostConfigGui.INSTANCE);
pane.bindItem('3', enchantCostItem);
// Enchantment Conflicts
ItemStack EnchantConflictItemstack = new ItemStack(Material.OAK_FENCE);
ItemMeta enchantConflictMeta = EnchantConflictItemstack.getItemMeta();
enchantConflictMeta.setDisplayName("\u00A7aEnchantment Conflict");
enchantConflictMeta.setLore(Collections.singletonList("\u00A77Click here to open enchantment conflict menu"));
EnchantConflictItemstack.setItemMeta(enchantConflictMeta);
GuiItem enchantConflictItem = GuiGlobalItems.goToGuiItem(EnchantConflictItemstack, EnchantConflictGui.INSTANCE);
pane.bindItem('4', enchantConflictItem);
// WIP configuration items
ItemStack wipItemstack = new ItemStack(Material.BARRIER);
ItemMeta wipMeta = wipItemstack.getItemMeta();
wipMeta.setDisplayName("\u00A7cWIP");
wipItemstack.setItemMeta(wipMeta);
GuiItem wip4 = new GuiItem(wipItemstack, GuiGlobalActions.stayInPlace, CustomAnvil.instance);
GuiItem wip5 = new GuiItem(wipItemstack, GuiGlobalActions.stayInPlace, CustomAnvil.instance);
GuiItem wip6 = new GuiItem(wipItemstack, GuiGlobalActions.stayInPlace, CustomAnvil.instance);
pane.bindItem('4', wip4);
pane.bindItem('5', wip5);
pane.bindItem('6', wip6);

View file

@ -20,16 +20,13 @@ import java.util.List;
*/
public abstract class AbstractSettingGui extends ChestGui {
// Temporary values, until I get something better.
public static final boolean TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE = true;
public static final boolean TEMPORARY_DO_BACKUP_EVERY_SAVE = true;
protected final static List<String> CLICK_LORE = Collections.singletonList("\u00A77Click Here to change the value");
private PatternPane pane;
/**
* Prepare necessary object for a setting gui.
*
* @param rows Number of row for this gui.
* @param title Title of this gui.
* @param parent Parent gui to go back when completed.
@ -41,6 +38,7 @@ public abstract class AbstractSettingGui extends ChestGui {
/**
* Prepare necessary object for a setting gui.
*
* @param rows Number of row for this gui.
* @param title Title of this gui.
* @param parent Parent gui to go back when completed.
@ -54,6 +52,7 @@ public abstract class AbstractSettingGui extends ChestGui {
/**
* Initialise and prepare value for this gui.
*
* @param parent Parent gui to go back when completed.
*/
private void initBase(ValueUpdatableGui parent) {
@ -79,6 +78,7 @@ public abstract class AbstractSettingGui extends ChestGui {
/**
* Get main pane for this setting gui.
*
* @return Main pattern pain of this gui.
*/
protected PatternPane getPane() {
@ -93,12 +93,14 @@ public abstract class AbstractSettingGui extends ChestGui {
* <li><b>B</b>: "back to previous gui" button.</li>
* <li><b>0</b>: default background item.</li>
* </ul>
*
* @return The gui's pattern.
*/
protected abstract Pattern getGuiPattern();
/**
* Called when the associated setting need to be saved.
*
* @return true if the save was successful. false otherwise
*/
public abstract boolean onSave();
@ -106,6 +108,7 @@ public abstract class AbstractSettingGui extends ChestGui {
/**
* If this function return true
* the gui assume the associated setting can be saved.
*
* @return true if there is a change to the setting. false otherwise
*/
public abstract boolean hadChange();
@ -121,6 +124,7 @@ public abstract class AbstractSettingGui extends ChestGui {
/**
* Constructor for settings gui factory
*
* @param configPath Configuration path of this setting.
* @param config Configuration holder of this setting.
*/
@ -145,6 +149,7 @@ public abstract class AbstractSettingGui extends ChestGui {
/**
* Create a gui using setting parameters and current setting value.
*
* @return A new instance of the implemented setting gui.
*/
public abstract AbstractSettingGui create();

View file

@ -12,6 +12,7 @@ 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;
@ -28,6 +29,7 @@ public class BoolSettingsGui extends AbstractSettingGui{
/**
* Create a boolean setting config gui.
*
* @param holder Configuration factory of this setting.
* @param now The defined value of this setting.
*/
@ -45,7 +47,7 @@ public class BoolSettingsGui extends AbstractSettingGui{
@Override
public Pattern getGuiPattern() {
return new Pattern(
"000000000",
GuiSharedConstant.EMPTY_GUI_FULL_LINE,
"D0-0v0+00",
"B0000000S"
);
@ -125,8 +127,8 @@ public class BoolSettingsGui extends AbstractSettingGui{
holder.config.getConfig().set(holder.configPath, now);
MetricsUtil.INSTANCE.notifyChange(this.holder.config, this.holder.configPath);
if(TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE){
return holder.config.saveToDisk(TEMPORARY_DO_BACKUP_EVERY_SAVE);
if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
return holder.config.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
}
return true;
}
@ -138,6 +140,7 @@ public class BoolSettingsGui extends AbstractSettingGui{
/**
* Create a bool 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.
@ -158,11 +161,14 @@ public class BoolSettingsGui extends AbstractSettingGui{
* A factory for a boolean setting gui that hold setting's information.
*/
public static class BoolSettingFactory extends SettingGuiFactory {
@NotNull String title; ValueUpdatableGui parent;
@NotNull
String title;
ValueUpdatableGui parent;
boolean defaultVal;
/**
* Constructor for a boolean 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.

View file

@ -14,6 +14,7 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
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.MetricsUtil;
import java.util.Arrays;
@ -33,6 +34,7 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
/**
* Create an enchantment cost setting config gui.
*
* @param holder Configuration factory of this setting.
* @param nowItem The defined value of this setting item's value.
*/
@ -80,7 +82,8 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
bookMeta.setDisplayName("\u00A7aCost of an Enchantment by Book");
bookMeta.setLore(Arrays.asList(
"\u00A77Cost per result item level of an sacrifice enchantment",
"\u00A77Only apply if sacrificed item \u00A7cis \u00A77a book"));bookItemstack.setItemMeta(bookMeta);
"\u00A77Only apply if sacrificed item \u00A7cis \u00A77a book"));
bookItemstack.setItemMeta(bookMeta);
// sword display
ItemStack swordItemstack = new ItemStack(Material.WOODEN_SWORD);
@ -207,8 +210,8 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
holder.config.getConfig().set(holder.configPath + BOOK_PATH, nowBook);
MetricsUtil.INSTANCE.notifyChange(this.holder.config, this.holder.configPath);
if(TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE){
return holder.config.saveToDisk(TEMPORARY_DO_BACKUP_EVERY_SAVE);
if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
return holder.config.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
}
return true;
}
@ -220,6 +223,7 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
/**
* Create an int 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.
@ -254,6 +258,7 @@ public class EnchantCostSettingsGui extends IntSettingsGui {
/**
* Constructor for an enchantment cost 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.

View file

@ -0,0 +1,159 @@
package xyz.alexcrea.cuanvil.gui.config.settings;
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.util.Pattern;
import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
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.config.SelectEnchantmentContainer;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
public class EnchantSelectSettingGui extends AbstractSettingGui {
SelectEnchantmentContainer enchantContainer;
int page;
Set<Enchantment> selectedEnchant;
public EnchantSelectSettingGui(@NotNull String title, ValueUpdatableGui parent, SelectEnchantmentContainer enchantContainer, int page) {
super(6, title, parent);
this.enchantContainer = enchantContainer;
// Not used and not planned rn
this.page = page;
this.selectedEnchant = new HashSet<>(enchantContainer.getSelectedEnchantments());
// Add secondary background item
this.getPane().bindItem('1', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM);
initGroups();
}
@Override
protected Pattern getGuiPattern() {
return 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,
"B1111111S"
);
}
protected void initGroups() {
// Add enchantment gui item
OutlinePane filledEnchant = new OutlinePane(0, 0, 9, 5);
filledEnchant.setPriority(Pane.Priority.HIGH);
filledEnchant.align(OutlinePane.Alignment.BEGIN);
filledEnchant.setOrientation(Orientable.Orientation.HORIZONTAL);
Set<Enchantment> illegalEnchant = this.enchantContainer.illegalEnchantments();
for (Enchantment enchant : GuiSharedConstant.SORTED_ENCHANTMENT_LIST) {
if (illegalEnchant.contains(enchant)) {
return;
}
filledEnchant.addItem(getGuiItemFromEnchant(enchant));
}
addPane(filledEnchant);
}
private GuiItem getGuiItemFromEnchant(Enchantment enchantment) {
boolean isIn = this.selectedEnchant.contains(enchantment);
Material usedMaterial;
if (isIn) {
usedMaterial = Material.ENCHANTED_BOOK;
} else {
usedMaterial = Material.BOOK;
}
ItemStack item = new ItemStack(usedMaterial);
setEnchantItemMeta(item, enchantment.getKey().getKey(), isIn);
GuiItem guiItem = new GuiItem(item, CustomAnvil.instance);
guiItem.setAction(getEnchantItemConsumer(enchantment, guiItem));
return guiItem;
}
private static final List<String> TRUE_LORE = Collections.singletonList("\u00A77Value: \u00A7aSelected");
private static final List<String> FALSE_LORE = Collections.singletonList("\u00A77Value: \u00A7cNot Selected");
public void setEnchantItemMeta(ItemStack item, String name, boolean isIn) {
ItemMeta meta = item.getItemMeta();
if (meta == null) {
CustomAnvil.instance.getLogger().warning("Could not create item for enchantment: " + name + ":\n" +
"Item do not gave item meta: " + item + ". Using placeholder instead");
item.setType(Material.PAPER);
meta = item.getItemMeta();
assert meta != null;
}
meta.setDisplayName("\u00A7" + (isIn ? 'a' : 'c') + CasedStringUtil.snakeToUpperSpacedCase(name));
if (isIn) {
meta.addEnchant(Enchantment.DAMAGE_UNDEAD, 1, true);
meta.setLore(TRUE_LORE);
} else {
meta.removeEnchant(Enchantment.DAMAGE_UNDEAD);
meta.setLore(FALSE_LORE);
}
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS);
item.setItemMeta(meta);
}
private Consumer<InventoryClickEvent> getEnchantItemConsumer(Enchantment enchant, GuiItem guiItem) {
return event -> {
event.setCancelled(true);
ItemStack item = guiItem.getItem();
boolean isIn = this.selectedEnchant.contains(enchant);
if (isIn) {
this.selectedEnchant.remove(enchant);
item.setType(Material.BOOK);
} else {
this.selectedEnchant.add(enchant);
item.setType(Material.ENCHANTED_BOOK);
}
setEnchantItemMeta(item, enchant.getKey().getKey(), !isIn);
guiItem.setItem(item);// Just in case
update();
};
}
@Override
public boolean onSave() {
return this.enchantContainer.setSelectedEnchantments(this.selectedEnchant);
}
@Override
public boolean hadChange() {
Set<Enchantment> baseGroup = this.enchantContainer.getSelectedEnchantments();
return baseGroup.size() != this.selectedEnchant.size() ||
!baseGroup.containsAll(this.selectedEnchant);
}
}

View file

@ -0,0 +1,152 @@
package xyz.alexcrea.cuanvil.gui.config.settings;
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.util.Pattern;
import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
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.AbstractMaterialGroup;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.SelectGroupContainer;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
public class GroupSelectSettingGui extends AbstractSettingGui {
SelectGroupContainer groupContainer;
int page;
Set<AbstractMaterialGroup> selectedGroups;
public GroupSelectSettingGui(@NotNull String title, ValueUpdatableGui parent, SelectGroupContainer groupContainer, int page) {
super(6, title, parent);
this.groupContainer = groupContainer;
//Not used but planned
this.page = page;
this.selectedGroups = new HashSet<>(groupContainer.getSelectedGroups());
// Add secondary background item
this.getPane().bindItem('1', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM);
initGroups();
}
@Override
protected Pattern getGuiPattern() {
return 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,
"B1111111S"
);
}
protected void initGroups() {
// Add enchantment gui item
OutlinePane filledEnchant = new OutlinePane(0, 0, 9, 5);
filledEnchant.setPriority(Pane.Priority.HIGH);
filledEnchant.align(OutlinePane.Alignment.BEGIN);
filledEnchant.setOrientation(Orientable.Orientation.HORIZONTAL);
Set<AbstractMaterialGroup> illegalGroup = this.groupContainer.illegalGroups();
for (AbstractMaterialGroup group : ConfigHolder.ITEM_GROUP_HOLDER.getItemGroupsManager().getGroupMap().values()) {
if (illegalGroup.contains(group)) {
return;
}
filledEnchant.addItem(getGuiItemFromGroup(group));
}
addPane(filledEnchant);
}
private GuiItem getGuiItemFromGroup(AbstractMaterialGroup group) {
boolean isIn = this.selectedGroups.contains(group);
Material usedMaterial = group.getRepresentativeMaterial();
ItemStack item = new ItemStack(usedMaterial);
setGroupItemMeta(item, group.getName(), isIn);
GuiItem guiItem = new GuiItem(item, CustomAnvil.instance);
guiItem.setAction(getGroupItemConsumer(group, guiItem));
return guiItem;
}
private static final List<String> TRUE_LORE = Collections.singletonList("\u00A77Value: \u00A7aSelected");
private static final List<String> FALSE_LORE = Collections.singletonList("\u00A77Value: \u00A7cNot Selected");
public void setGroupItemMeta(ItemStack item, String name, boolean isIn) {
ItemMeta meta = item.getItemMeta();
if (meta == null) {
CustomAnvil.instance.getLogger().warning("Could not create item for group: " + name + ":\n" +
"Item do not gave item meta: " + item + ". Using placeholder instead");
item.setType(Material.PAPER);
meta = item.getItemMeta();
assert meta != null;
}
meta.setDisplayName("\u00A7" + (isIn ? 'a' : 'c') + CasedStringUtil.snakeToUpperSpacedCase(name));
if (isIn) {
meta.addEnchant(Enchantment.DAMAGE_UNDEAD, 1, true);
meta.setLore(TRUE_LORE);
} else {
meta.removeEnchant(Enchantment.DAMAGE_UNDEAD);
meta.setLore(FALSE_LORE);
}
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS);
item.setItemMeta(meta);
}
private Consumer<InventoryClickEvent> getGroupItemConsumer(AbstractMaterialGroup group, GuiItem guiItem) {
return event -> {
event.setCancelled(true);
boolean isIn = this.selectedGroups.contains(group);
if (isIn) {
this.selectedGroups.remove(group);
} else {
this.selectedGroups.add(group);
}
ItemStack item = guiItem.getItem();
setGroupItemMeta(item, group.getName(), !isIn);
guiItem.setItem(item);// Just in case
update();
};
}
@Override
public boolean onSave() {
return this.groupContainer.setSelectedGroups(this.selectedGroups);
}
@Override
public boolean hadChange() {
Set<AbstractMaterialGroup> baseGroup = this.groupContainer.getSelectedGroups();
return baseGroup.size() != this.selectedGroups.size() ||
!baseGroup.containsAll(this.selectedGroups);
}
}

View file

@ -13,6 +13,7 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
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.MetricsUtil;
import java.util.Collections;
@ -31,6 +32,7 @@ public class IntSettingsGui extends AbstractSettingGui{
/**
* Create an int setting config gui.
*
* @param holder Configuration factory of this setting.
* @param now The defined value of this setting.
*/
@ -58,6 +60,7 @@ public class IntSettingsGui extends AbstractSettingGui{
}
protected GuiItem returnToDefault;
/**
* Prepare "return to default value" gui item.
*/
@ -185,6 +188,7 @@ public class IntSettingsGui extends AbstractSettingGui{
/**
* Step use lower case character from 'a' to a certain char.
*
* @return The middle value of the character set for steps.
*/
protected char getMidStepChar() {
@ -193,6 +197,7 @@ public class IntSettingsGui extends AbstractSettingGui{
/**
* Create a step item from a step value.
*
* @param stepIndex the index of the step item.
* @return A step item corresponding to its index value.
*/
@ -247,8 +252,8 @@ public class IntSettingsGui extends AbstractSettingGui{
holder.config.getConfig().set(holder.configPath, now);
MetricsUtil.INSTANCE.notifyChange(this.holder.config, this.holder.configPath);
if(TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE){
return holder.config.saveToDisk(TEMPORARY_DO_BACKUP_EVERY_SAVE);
if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
return holder.config.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
}
return true;
}
@ -260,6 +265,7 @@ public class IntSettingsGui extends AbstractSettingGui{
/**
* Create an int 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.
@ -286,11 +292,17 @@ public class IntSettingsGui extends AbstractSettingGui{
* A factory for an int setting gui that hold setting's information.
*/
public static class IntSettingFactory extends SettingGuiFactory {
@NotNull String title; ValueUpdatableGui parent;
int min; int max; int defaultVal; int[] steps;
@NotNull
String title;
ValueUpdatableGui parent;
int min;
int max;
int defaultVal;
int[] steps;
/**
* Constructor for an int 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.

View file

@ -0,0 +1,350 @@
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 org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
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.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;
import xyz.alexcrea.cuanvil.gui.config.global.EnchantConflictGui;
import xyz.alexcrea.cuanvil.gui.config.settings.EnchantSelectSettingGui;
import xyz.alexcrea.cuanvil.gui.config.settings.GroupSelectSettingGui;
import xyz.alexcrea.cuanvil.gui.config.settings.IntSettingsGui;
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.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Level;
public class EnchantConflictSubSettingGui extends ValueUpdatableGui implements SelectEnchantmentContainer, SelectGroupContainer {
private final EnchantConflictGui parent;
private final EnchantConflictGroup enchantConflict;
private final GuiItem parentItemForThisGui;
private final PatternPane pane;
private boolean shouldWorld = true;
public EnchantConflictSubSettingGui(
@NotNull EnchantConflictGui parent,
@NotNull EnchantConflictGroup enchantConflict,
@NotNull GuiItem parentItemForThisGui) {
super(3,
"\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + " \u00A78Config",
CustomAnvil.instance);
this.parent = parent;
this.enchantConflict = enchantConflict;
this.parentItemForThisGui = parentItemForThisGui;
Pattern pattern = new Pattern(
GuiSharedConstant.EMPTY_GUI_FULL_LINE,
"00EGM000D",
"B00000000"
);
this.pane = new PatternPane(0, 0, 9, 3, pattern);
addPane(this.pane);
prepareStaticValues();
}
private GuiItem enchantSettingItem;
private GuiItem groupSettingItem;
private IntSettingsGui.IntSettingFactory minBeforeActiveSettingFactory;
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 CONFLICT");
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
this.enchantSettingItem = new GuiItem(new ItemStack(Material.ENCHANTED_BOOK), (event) -> {
event.setCancelled(true);
EnchantSelectSettingGui enchantGui = new EnchantSelectSettingGui(
"\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(enchantConflict.getName()) + " \u00A75Enchantments",
this, this, 0);
enchantGui.show(event.getWhoClicked());
}, CustomAnvil.instance);
this.groupSettingItem = new GuiItem(new ItemStack(Material.PAPER), (event) -> {
event.setCancelled(true);
GroupSelectSettingGui enchantGui = new GroupSelectSettingGui(
"\u00A7e" + CasedStringUtil.snakeToUpperSpacedCase(this.enchantConflict.getName()) + " \u00A73Groups",
this, this, 0);
enchantGui.show(event.getWhoClicked());
}, CustomAnvil.instance);
this.minBeforeActiveSettingFactory = IntSettingsGui.intFactory(
"\u00A78Minimum enchantment count",
this, this.enchantConflict.getName() + ".maxEnchantmentBeforeConflict", ConfigHolder.CONFLICT_HOLDER,
0, 255, 0, 1
);
this.pane.bindItem('E', this.enchantSettingItem);
this.pane.bindItem('G', this.groupSettingItem);
// Now we update the items
updateLocal();
}
private ConfirmActionGui createDeleteGui() {
Supplier<Boolean> deleteSupplier = () -> {
EnchantConflictManager manager = ConfigHolder.CONFLICT_HOLDER.getConflictManager();
// Remove from manager
for (Enchantment enchantment : this.enchantConflict.getEnchants()) {
manager.removeConflictFromMap(enchantment, this.enchantConflict);
}
manager.conflictList.remove(this.enchantConflict);
// Remove from parent
this.parent.removeConflict(this.enchantConflict);
// Remove self
cleanUnused();
// Update config file storage
ConfigHolder.CONFLICT_HOLDER.getConfig().set(this.enchantConflict.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(enchantConflict.getName()) + "\u00A7c?",
"\u00A77Confirm that you want to delete this conflict.",
this, this.parent, deleteSupplier
);
}
@Override
public void updateGuiValues() {
this.parent.updateValueForConflict(this.enchantConflict, true);
// Parent should call updateLocal
}
public void updateLocal() {
if (!this.shouldWorld) return;
// Prepare enchantment lore
ArrayList<String> enchantLore = new ArrayList<>();
enchantLore.add("\u00A77Allow you to select a list of \u00A75Enchantments \u00A77that this conflict should include");
Set<Enchantment> enchants = getSelectedEnchantments();
if (enchants.isEmpty()) {
enchantLore.add("\u00A77There is no included enchantment for this conflict.");
} else {
enchantLore.add("\u00A77List of included enchantment for this conflict:");
Iterator<Enchantment> enchantIterator = enchants.iterator();
boolean greaterThanMax = enchants.size() > 5;
int maxindex = (greaterThanMax ? 4 : enchants.size());
for (int i = 0; i < maxindex; i++) {
// format string like "- Fire Protection"
String formattedName = CasedStringUtil.snakeToUpperSpacedCase(enchantIterator.next().getKey().getKey());
enchantLore.add("\u00A77- \u00A75" + formattedName);
}
if (greaterThanMax) {
enchantLore.add("\u00A77And " + (enchants.size() - 4) + " more...");
}
}
// Prepare group lore
ArrayList<String> groupLore = new ArrayList<>();
groupLore.add("\u00A77Allow you to select a list of \u00A73Groups \u00A77that this conflict should include");
Set<AbstractMaterialGroup> grouos = getSelectedGroups();
if (grouos.isEmpty()) {
groupLore.add("\u00A77There is no excluded groups for this conflict.");
} else {
groupLore.add("\u00A77List of excluded groups for this conflict:");
Iterator<AbstractMaterialGroup> groupIterator = grouos.iterator();
boolean greaterThanMax = grouos.size() > 5;
int maxindex = (greaterThanMax ? 4 : grouos.size());
for (int i = 0; i < maxindex; i++) {
// format string like "- Melee Weapons"
String formattedName = CasedStringUtil.snakeToUpperSpacedCase(groupIterator.next().getName());
groupLore.add("\u00A77- \u00A73" + formattedName);
}
if (greaterThanMax) {
groupLore.add("\u00A77And " + (grouos.size() - 4) + " more...");
}
}
// Configure enchant setting item
ItemStack enchantItem = this.enchantSettingItem.getItem();
ItemMeta enchantMeta = enchantItem.getItemMeta();
enchantMeta.setDisplayName("\u00A7aSelect included \u00A75Enchantments \u00A7aSettings");
enchantMeta.setLore(enchantLore);
enchantItem.setItemMeta(enchantMeta);
this.enchantSettingItem.setItem(enchantItem); // Just in case
// Configure group setting item
ItemStack groupItem = this.groupSettingItem.getItem();
ItemMeta groupMeta = groupItem.getItemMeta();
groupMeta.setDisplayName("\u00A7aSelect excluded \u00A73Groups \u00A7aSettings");
groupMeta.setLore(groupLore);
groupItem.setItemMeta(groupMeta);
this.groupSettingItem.setItem(groupItem); // Just in case
this.pane.bindItem('M', GuiGlobalItems.intSettingGuiItem(this.minBeforeActiveSettingFactory, Material.COMMAND_BLOCK));
update();
}
public void cleanUnused() {
for (HumanEntity viewer : getViewers()) {
this.parent.show(viewer);
}
this.shouldWorld = false;
// Just in case something is extremely wrong
GuiItem background = GuiGlobalItems.backgroundItem();
this.pane.bindItem('E', background);
this.pane.bindItem('G', background);
this.pane.bindItem('M', background);
this.pane.bindItem('D', background);
}
@Override
public void show(@NotNull HumanEntity humanEntity) {
if (this.shouldWorld) {
super.show(humanEntity);
} else {
this.parent.show(humanEntity);
}
}
public GuiItem getParentItemForThisGui() {
return parentItemForThisGui;
}
// Select enchantment container methods
@Override
public Set<Enchantment> getSelectedEnchantments() {
return this.enchantConflict.getEnchants();
}
@Override
public boolean setSelectedEnchantments(Set<Enchantment> enchantments) {
if (!this.shouldWorld) {
CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " enchants but sub config is destroyed");
return false;
}
// Set live configuration
this.enchantConflict.setEnchants(enchantments);
// Save on file configuration
String[] enchantKeys = new String[enchantments.size()];
int index = 0;
for (Enchantment enchantment : enchantments) {
enchantKeys[index++] = enchantment.getKey().getKey();
}
ConfigHolder.CONFLICT_HOLDER.getConfig().set(enchantConflict.getName() + ".enchantments", enchantKeys);
try {
updateGuiValues();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "An error occurred while updating enchants for " + this.enchantConflict.getName(), e);
}
// Save file configuration to disk
if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
return ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
}
return true;
}
@Override
public Set<Enchantment> illegalEnchantments() {
return Collections.emptySet();
}
// Select group container methods
@Override
public Set<AbstractMaterialGroup> getSelectedGroups() {
return this.enchantConflict.getCantConflictGroup().getGroups();
}
@Override
public boolean setSelectedGroups(Set<AbstractMaterialGroup> groups) {
if (!this.shouldWorld) {
CustomAnvil.instance.getLogger().info("Trying to save " + enchantConflict.getName() + " groups but sub config is destroyed");
return false;
}
// Set live configuration
this.enchantConflict.getCantConflictGroup().setGroups(groups);
// Save on file configuration
String[] groupsNames = new String[groups.size()];
int index = 0;
for (AbstractMaterialGroup group : groups) {
groupsNames[index++] = group.getName();
}
ConfigHolder.CONFLICT_HOLDER.getConfig().set(this.enchantConflict.getName() + ".notAffectedGroups", groupsNames);
try {
updateGuiValues();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "An error occurred while updating group for " + this.enchantConflict.getName(), e);
}
// Save file configuration to disk
if (GuiSharedConstant.TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE) {
return ConfigHolder.CONFLICT_HOLDER.saveToDisk(GuiSharedConstant.TEMPORARY_DO_BACKUP_EVERY_SAVE);
}
return true;
}
@Override
public Set<AbstractMaterialGroup> illegalGroups() {
return Collections.emptySet();
}
}

View file

@ -27,6 +27,7 @@ public class GuiGlobalActions {
/**
* Create a consumer to create and open a new GUI.
* Used with InventoryClickEvent as the consumer argument as it is planned to be used on click on an GuiItem.
*
* @param clazz The class of the gui to open.
* It is assumed this class contain a constructor requiring arguments of argClass in the same order as argClass array.
* @param argClass Classes of the argument that will be passed to the constructor of the GUI class.
@ -62,6 +63,7 @@ public class GuiGlobalActions {
/**
* Create a consumer to create and open a new GUI.
* Used with InventoryClickEvent as the consumer argument as it is planned to be used on click on an GuiItem.
*
* @param clazz The class of the gui to open.
* It is assumed this class contain a constructor with no argument.
* @return A consumer to create a new gui and open it.
@ -74,6 +76,7 @@ public class GuiGlobalActions {
/**
* Create a consumer to open a setting gui from a setting GUI factory.
* Used with InventoryClickEvent as the consumer argument as it is planned to be used on click on an GuiItem.
*
* @param factory The setting gui factory.
* @return A consumer to create and open a new setting GUI.
*/
@ -88,6 +91,7 @@ public class GuiGlobalActions {
/**
* Create a consumer to open a global GUI.
* Used with InventoryClickEvent as the consumer argument as it is planned to be used on click on an GuiItem.
*
* @param goal The gui to open when consumer is run.
* @return A consumer to open a global GUI.
*/
@ -109,6 +113,7 @@ public class GuiGlobalActions {
* Create a consumer to update and open an updatable GUI.
* Used with InventoryClickEvent as the consumer argument as it is planned to be used on click on an GuiItem.
* This consumer check if the player who interacted with the item have the permission to save before saving.
*
* @param setting The gui that contain the modified setting.
* @param goal The gui to update and open when consumer is run.
* @return A consumer to open a global GUI.

View file

@ -23,6 +23,7 @@ public class GuiGlobalItems {
// statically create default back itemstack
private static final ItemStack BACK_ITEM;
static {
BACK_ITEM = new ItemStack(Material.BARRIER);
ItemMeta meta = BACK_ITEM.getItemMeta();
@ -32,6 +33,7 @@ public class GuiGlobalItems {
/**
* Create a GuiItem that open the given GUi.
*
* @param item The item to display in the GUI.
* @param goal The GUI to open on click.
* @return An GuiItem that open goal on click.
@ -43,6 +45,7 @@ public class GuiGlobalItems {
/**
* Create back button item from default back GuiItem.
* The back item will open the goal inventory when clicked.
*
* @param goal The GUI to go back to.
* @return An GuiItem that go back to goal on click.
*/
@ -53,6 +56,7 @@ public class GuiGlobalItems {
/**
* Add default back item to a GUI pattern with the reserved character key <strong>B</strong>.
* The back item will open the target inventory when clicked.
*
* @param target The pattern to add the back item.
* @param goal The GUI to go back to.
*/
@ -66,6 +70,7 @@ public class GuiGlobalItems {
/**
* Get a background item with backgroundMat as the displayed material.
* A background item is a GuiItem that do nothing when interacted with and have an empty name.
*
* @param backgroundMat The material to which the background item should be made of.
* @return A background item with backgroundMat as material.
*/
@ -76,9 +81,11 @@ public class GuiGlobalItems {
item.setItemMeta(meta);
return new GuiItem(item, GuiGlobalActions.stayInPlace, CustomAnvil.instance);
}
/**
* Get default background GuiItem.
* A background item is a GuiItem that do nothing when interacted with and have an empty name.
*
* @return A new instance of the default background item.
*/
public static GuiItem backgroundItem() {
@ -88,6 +95,7 @@ public class GuiGlobalItems {
/**
* Add default background item to a GUI pattern with the reserved character key <strong>0</strong>.
* A background item is a GuiItem that do nothing when interacted with and have an empty name.
*
* @param target The pattern to add the background item.
* @param backgroundMat The material of the background item.
*/
@ -99,6 +107,7 @@ public class GuiGlobalItems {
/**
* Add default background item to a GUI pattern with the reserved character key <strong>0</strong>.
* A background item is a GuiItem that do nothing when interacted with and have an empty name.
*
* @param target The pattern to add the background item.
*/
public static void addBackgroundItem(@NotNull PatternPane target) {
@ -112,6 +121,7 @@ public class GuiGlobalItems {
* Create a new save setting GuiItem.
* A save setting item is a GuiItem that save a changed setting when clicked.
* This item also check if the player who interacted with the item have the permission to save before saving.
*
* @param setting The setting to change.
* @param goal Parent GUI of this setting GUI. as setting will be change the display of goal GUI will be updated.
* @return A save setting item.
@ -131,6 +141,7 @@ public class GuiGlobalItems {
// Create static non change item
private static final GuiItem NO_CHANGE_ITEM;
static {
ItemStack item = new ItemStack(DEFAULT_NO_CHANGE_ITEM);
ItemMeta meta = item.getItemMeta();
@ -142,6 +153,7 @@ public class GuiGlobalItems {
/**
* Get the global "no change" GuiItem.
* The no change item do nothing when interacted, only the title is change to show there is no change.
*
* @return The global "no change" item.
*/
public static GuiItem noChangeItem() {
@ -151,6 +163,7 @@ public class GuiGlobalItems {
/**
* Create a new "create and go to the setting GUI" GuiItem.
* This item will create and open a setting GUI from the factory.
*
* @param item The item that will be displayed.
* @param factory The setting's GUI factory.
* @return A formatted GuiItem that will create and open a GUI for the setting.
@ -169,6 +182,7 @@ public class GuiGlobalItems {
* Create a new Boolean setting GuiItem.
* This item will create and open a boolean setting GUI from the factory.
* The item will have its value written in the lore part of the item.
*
* @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 boolean setting.
@ -199,6 +213,7 @@ public class GuiGlobalItems {
* This item will create and open a boolean setting GUI from the factory.
* The item will have its value written in the lore part of the item.
* 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 boolean setting.
*/
@ -213,6 +228,7 @@ public class GuiGlobalItems {
* Create a new int setting GuiItem.
* This item will create and open an int setting GUI from the factory.
* The item will have its value written in the lore part of the item.
*
* @param factory The setting's GUI factory.
* @param itemMat Displayed material of the item.
* @param name Name of the item.
@ -235,6 +251,7 @@ public class GuiGlobalItems {
* This item will create and open an int setting GUI from the factory.
* The item will have its value written in the lore part of the item.
* Item's name will be the factory set title.
*
* @param factory The setting's GUI factory.
* @param itemMat Displayed material of the item.
* @return A formatted GuiItem that will create and open a GUI for the int setting.
@ -244,11 +261,12 @@ public class GuiGlobalItems {
@NotNull Material itemMat
) {
String configPath = getConfigNameFromPath(factory.getConfigPath());
return intSettingGuiItem(factory, itemMat, CasedStringUtil.snakeToUpperSpacedCase(configPath));
return intSettingGuiItem(factory, itemMat, CasedStringUtil.detectToUpperSpacedCase(configPath));
}
/**
* Create an arbitrary GuiItem from a unique setting and item's property.
*
* @param factory The setting's GUI factory.
* @param itemMat Displayed material of the item.
* @param itemName Name of the item.
@ -277,6 +295,7 @@ public class GuiGlobalItems {
/**
* Get the setting name from the setting path.
* For example: "gui.command.name" will return "name".
*
* @param path The setting's path.
* @return The setting's name.
*/

View file

@ -0,0 +1,53 @@
package xyz.alexcrea.cuanvil.gui.util;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.pane.Pane;
import com.github.stefvanschie.inventoryframework.pane.PatternPane;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import xyz.alexcrea.cuanvil.gui.config.global.MainConfigGui;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class GuiSharedConstant {
public static final List<Enchantment> SORTED_ENCHANTMENT_LIST;
static {
SORTED_ENCHANTMENT_LIST = Arrays.asList(Enchantment.values());
SORTED_ENCHANTMENT_LIST.sort(Comparator.comparing(ench -> ench.getKey().getKey()));
}
public static final Material SECONDARY_BACKGROUND_MATERIAL = Material.BLACK_STAINED_GLASS_PANE;
public static final GuiItem SECONDARY_BACKGROUND_ITEM = GuiGlobalItems.backgroundItem(GuiSharedConstant.SECONDARY_BACKGROUND_MATERIAL);
public static final String EMPTY_GUI_FULL_LINE = "000000000";
// Temporary values, until I get something better.
public static final boolean TEMPORARY_DO_SAVE_TO_DISK_EVERY_CHANGE = true;
public static final boolean TEMPORARY_DO_BACKUP_EVERY_SAVE = true;
public static final PatternPane BACK_TO_MAIN_MENU_BIG_LIST_DISPLAY_BACKGROUND_PANE;
static {
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,
"B11111111"
);
BACK_TO_MAIN_MENU_BIG_LIST_DISPLAY_BACKGROUND_PANE = new PatternPane(0, 0, 9, 6, Pane.Priority.LOW, pattern);
GuiGlobalItems.addBackItem(BACK_TO_MAIN_MENU_BIG_LIST_DISPLAY_BACKGROUND_PANE, MainConfigGui.INSTANCE);
GuiGlobalItems.addBackgroundItem(BACK_TO_MAIN_MENU_BIG_LIST_DISPLAY_BACKGROUND_PANE);
BACK_TO_MAIN_MENU_BIG_LIST_DISPLAY_BACKGROUND_PANE.bindItem('1', GuiSharedConstant.SECONDARY_BACKGROUND_ITEM);
}
}

View file

@ -9,6 +9,7 @@ public class CasedStringUtil {
* Transform a snake cased string to an upper-cased spaced string.
* <p>
* for example: if we use "hello_world" as an input this function will return "Hello World".
*
* @param snake_cased_string The input string.
* This argument NEED to be a snake cased string, or it will not work
* @return The input as an upper-cased string with space separator.
@ -28,4 +29,30 @@ public class CasedStringUtil {
return result.substring(1);
}
public static String camelCaseToUpperSpaceCase(String camelCasedString) {
if (camelCasedString.isEmpty()) return camelCasedString;
StringBuilder stb = new StringBuilder();
char[] chars = camelCasedString.toCharArray();
stb.append(chars[0]);
for (int i = 1; i < chars.length; i++) {
char chr = chars[i];
if (Character.isUpperCase(chr)) {
stb.append(" ");
}
stb.append(chr);
}
return stb.toString();
}
public static String detectToUpperSpacedCase(String toDetect) {
//not advanced detection
if (toDetect.contains("_")) {
return snakeToUpperSpacedCase(toDetect);
} else {
return camelCaseToUpperSpaceCase(toDetect);
}
}
}

View file

@ -39,6 +39,7 @@ class AnvilEventListener : Listener {
private const val ANVIL_INPUT_LEFT = 0
private const val ANVIL_INPUT_RIGHT = 1
private const val ANVIL_OUTPUT_SLOT = 2
// static slot container
private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0)
private val CURSOR_SLOT = SlotContainer(SlotType.CURSOR, 0)
@ -64,6 +65,7 @@ class AnvilEventListener : Listener {
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("no right item, But input is same as output")
event.result = null
return
}
@ -95,6 +97,7 @@ class AnvilEventListener : Listener {
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("Mergable with second, But input is same as output")
event.result = null
return
}
@ -125,6 +128,7 @@ class AnvilEventListener : Listener {
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("unit repair, But input is same as output")
event.result = null
return
}
@ -132,6 +136,7 @@ class AnvilEventListener : Listener {
handleAnvilXp(inventory, event, anvilCost)
} else {
CustomAnvil.log("no anvil fuse type found")
event.result = null
}
}
@ -156,7 +161,9 @@ class AnvilEventListener : Listener {
val player = event.whoClicked as? Player ?: return
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
val inventory = event.inventory as? AnvilInventory ?: return
if (event.rawSlot != ANVIL_OUTPUT_SLOT) { return }
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
return
}
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
@ -169,7 +176,8 @@ class AnvilEventListener : Listener {
// True if there was no change or not allowed
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))
|| !allowed){
|| !allowed
) {
event.result = Event.Result.DENY
return
@ -181,23 +189,28 @@ class AnvilEventListener : Listener {
if (canMerge) {
event.result = Event.Result.ALLOW
} else if (unitRepairResult != null) {
onUnitRepairExtract(leftItem, rightItem, output,
unitRepairResult, event, player, inventory)
onUnitRepairExtract(
leftItem, rightItem, output,
unitRepairResult, event, player, inventory
)
return
}
}
private fun onUnitRepairExtract(leftItem: ItemStack,
private fun onUnitRepairExtract(
leftItem: ItemStack,
rightItem: ItemStack,
output: ItemStack,
unitRepairResult: Double,
event: InventoryClickEvent,
player: Player,
inventory: AnvilInventory){
inventory: AnvilInventory
) {
val resultCopy = leftItem.clone()
val resultAmount = resultCopy.unitRepair(
rightItem.amount, unitRepairResult)
rightItem.amount, unitRepairResult
)
// To avoid vanilla, we cancel the event for unit repair
event.result = Event.Result.DENY
@ -224,7 +237,8 @@ class AnvilEventListener : Listener {
repairCost += resultAmount * ConfigOptions.unitRepairCost
if ((inventory.maximumRepairCost < repairCost)
|| (player.level < repairCost)) return
|| (player.level < repairCost)
) return
}
// If not creative middle click...
if (event.click != ClickType.MIDDLE) {
@ -287,8 +301,11 @@ class AnvilEventListener : Listener {
// Calculate work penalty
val leftPenalty = (left.itemMeta as? Repairable)?.repairCost ?: 0
val rightPenalty =
if(right == null){ 0 }
else{ (right.itemMeta as? Repairable)?.repairCost ?: 0 }
if (right == null) {
0
} else {
(right.itemMeta as? Repairable)?.repairCost ?: 0
}
// Try to set work penalty for the result item
result.itemMeta?.let {
@ -296,10 +313,12 @@ class AnvilEventListener : Listener {
result.itemMeta = it
}
CustomAnvil.log("Calculated penalty: " +
CustomAnvil.log(
"Calculated penalty: " +
"leftPenalty: $leftPenalty, " +
"rightPenalty: $rightPenalty, " +
"result penalty: ${(result.itemMeta as? Repairable)?.repairCost ?: "none"}")
"result penalty: ${(result.itemMeta as? Repairable)?.repairCost ?: "none"}"
)
return leftPenalty + rightPenalty
}
@ -321,7 +340,11 @@ class AnvilEventListener : Listener {
// count enchant as illegal enchant if it conflicts with another enchant or not in result
if ((enchantment.key !in resultEnchsKeys)) {
resultEnchsKeys.add(enchantment.key)
val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(resultEnchsKeys,result.type,enchantment.key)
val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(
resultEnchsKeys,
result.type,
enchantment.key
)
resultEnchsKeys.remove(enchantment.key)
if (ConflictType.BIG_CONFLICT == conflictType) {
@ -338,9 +361,11 @@ class AnvilEventListener : Listener {
rightValue += value
}
CustomAnvil.log("Calculated right values: " +
CustomAnvil.log(
"Calculated right values: " +
"rightValue: $rightValue, " +
"illegalPenalty: $illegalPenalty")
"illegalPenalty: $illegalPenalty"
)
return rightValue + illegalPenalty
}
@ -348,15 +373,16 @@ class AnvilEventListener : Listener {
/**
* Display xp needed for the work on the anvil inventory
*/
private fun handleAnvilXp(inventory: AnvilInventory,
private fun handleAnvilXp(
inventory: AnvilInventory,
event: PrepareAnvilEvent,
anvilCost: Int){
anvilCost: Int
) {
// Test repair cost limit
val finalAnvilCost: Int
if (ConfigOptions.limitRepairCost) {
finalAnvilCost = min(anvilCost, ConfigOptions.limitRepairValue)
val finalAnvilCost = if (ConfigOptions.limitRepairCost) {
min(anvilCost, ConfigOptions.limitRepairValue)
} else {
finalAnvilCost = anvilCost
anvilCost
}
/* Because Minecraft likes to have the final say in the repair cost displayed

View file

@ -7,14 +7,14 @@ import org.bukkit.plugin.java.JavaPlugin
import xyz.alexcrea.cuanvil.command.EditConfigExecutor
import xyz.alexcrea.cuanvil.command.ReloadExecutor
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.listener.ChatEventListener
import xyz.alexcrea.cuanvil.util.Metrics
import xyz.alexcrea.cuanvil.util.MetricsUtil
import java.io.File
import java.io.FileReader
/**
* Bukkit/Spigot/Paper plugin to alter enchantment max
* levels and allow unsafe enchantment combinations
* Bukkit/Spigot/Paper plugin to alter anvil feature
*/
class CustomAnvil : JavaPlugin() {
@ -24,23 +24,31 @@ class CustomAnvil : JavaPlugin() {
// Permission string required to use the plugin's features
const val affectedByPluginPermission = "ca.affected"
// Permission string required to bypass enchantment conflicts test
const val bypassFusePermission = "ca.bypass.fuse"
// Permission string required to bypass enchantment conflicts test
const val bypassLevelPermission = "ca.bypass.level"
// Permission string required to reload the config
const val commandReloadPermission = "ca.command.reload"
// Permission string required to edit the plugin's config
const val editConfigPermission = "ca.config.edit"
// Command Name to reload the config
const val commandReloadName = "anvilconfigreload"
// Test command name
const val commandTestName = "customanvilconfig"
// Current plugin instance
lateinit var instance: CustomAnvil
// Chat message listener
lateinit var chatListener: ChatEventListener
/**
* Logging handler
*/
@ -49,6 +57,15 @@ class CustomAnvil : JavaPlugin() {
instance.logger.info(message)
}
}
/**
* Vebose Logging handler
*/
fun verboseLog(message: String) {
if (ConfigOptions.verboseDebugLog) {
instance.logger.info(message)
}
}
}
/**
@ -65,8 +82,11 @@ class CustomAnvil : JavaPlugin() {
logger.warning("Please note CustomAnvil is a more recent version of UnsafeEnchantsPlus")
}
// Load config
// Load chat listener
chatListener = ChatEventListener()
Bukkit.getPluginManager().registerEvents(chatListener, this)
// Load config
val success = ConfigHolder.loadConfig()
if (!success) return
@ -83,8 +103,10 @@ class CustomAnvil : JavaPlugin() {
)
}
fun reloadResource(resourceName: String,
hardFailSafe:Boolean = true): YamlConfiguration?{
fun reloadResource(
resourceName: String,
hardFailSafe: Boolean = true
): YamlConfiguration? {
// Save default resource
val file = File(dataFolder, resourceName)
if (!file.exists()) {

View file

@ -12,66 +12,97 @@ object ConfigOptions {
// Path for default enchantment limits
private const val DEFAULT_LIMIT_PATH = "default_limit"
// Path for limiting repair cost
const val LIMIT_REPAIR_COST = "limit_repair_cost"
// Path for repair value limit
const val LIMIT_REPAIR_VALUE = "limit_repair_value"
// Path for level cost on item repair
const val ITEM_REPAIR_COST = "item_repair_cost"
// Path for level cost on unit repair
const val UNIT_REPAIR_COST = "unit_repair_cost"
// Path for level cost on item renaming
const val ITEM_RENAME_COST = "item_rename_cost"
// Path for level cost on illegal enchantment on sacrifice
const val SACRIFICE_ILLEGAL_COST = "sacrifice_illegal_enchant_cost"
// Path for removing repair cost limits
const val REMOVE_REPAIR_LIMIT = "remove_repair_limit"
// Root path for enchantment limits
const val ENCHANT_LIMIT_ROOT = "enchant_limits"
// Root path for enchantment values
const val ENCHANT_VALUES_ROOT = "enchant_values"
// Keys for specific enchantment values
private const val KEY_BOOK = "book"
private const val KEY_ITEM = "item"
// Debug logging toggle path
private const val DEBUG_LOGGING = "debug_log"
// Debug verbose logging toggle path
private const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose"
// Default value for enchantment limits
private const val DEFAULT_ENCHANT_LIMIT = 5
// Default value for limiting repair cost
const val DEFAULT_LIMIT_REPAIR = false
// Default value for repair cost limit
const val DEFAULT_LIMIT_REPAIR_VALUE = 39
// Default value for level cost on item repair
const val DEFAULT_ITEM_REPAIR_COST = 2
// Default value for level cost per unit repair
const val DEFAULT_UNIT_REPAIR_COST = 1
// Default value for level cost on item renaming
const val DEFAULT_ITEM_RENAME_COST = 1
// Default value for level cost on illegal enchantment on sacrifice
const val DEFAULT_SACRIFICE_ILLEGAL_COST = 1
// Valid range for repair cost limit
@JvmField
val REPAIR_LIMIT_RANGE = 1..39
// Valid range for repair cost
@JvmField
val REPAIR_COST_RANGE = 0..255
// Valid range for rename cost
@JvmField
val ITEM_RENAME_COST_RANGE = 0..255
// Valid range for illegal enchantment conflict cost
@JvmField
val SACRIFICE_ILLEGAL_COST_RANGE = 0..255
// Default for removing repair cost limits
const val DEFAULT_REMOVE_LIMIT = false
// Valid range for an enchantment limit
@JvmField
val ENCHANT_LIMIT_RANGE = 1..255
// Default value for an enchantment multiplier
private const val DEFAULT_ENCHANT_VALUE = 0
// Default value for debug logging
private const val DEFAULT_DEBUG_LOG = false
// Default value for debug logging
private const val DEFAULT_VERBOSE_DEBUG_LOG = false
/**
* Default enchantment limit
*/
@ -151,6 +182,7 @@ object ConfigOptions {
.takeIf { it in SACRIFICE_ILLEGAL_COST_RANGE }
?: DEFAULT_SACRIFICE_ILLEGAL_COST
}
/**
* Whether to remove repair cost limit
*/
@ -171,6 +203,16 @@ object ConfigOptions {
.getBoolean(DEBUG_LOGGING, DEFAULT_DEBUG_LOG)
}
/**
* Whether to show verbose debug logging
*/
val verboseDebugLog: Boolean
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(VERBOSE_DEBUG_LOGGING, DEFAULT_VERBOSE_DEBUG_LOG)
}
/**
* Get the given [enchantment]'s limit
*/
@ -204,7 +246,8 @@ object ConfigOptions {
* Get an array of key of basic config options
*/
fun getBasicConfigKeys(): Array<String> {
return arrayOf(DEFAULT_LIMIT_PATH,
return arrayOf(
DEFAULT_LIMIT_PATH,
LIMIT_REPAIR_COST,
LIMIT_REPAIR_VALUE,
ITEM_REPAIR_COST,

View file

@ -32,10 +32,14 @@ object EnchantmentUtil {
other.forEach { (enchantment, level) ->
// Enchantment not yet in result list
if (!containsKey(enchantment)) {
// Add the enchantment if it doesn't have conflicts, or, if player is allowed to bypass enchantment restrictions
// Add the enchantment if it doesn't have conflicts, or if player is allowed to bypass enchantment restrictions
this[enchantment] = level
val conflictType =
ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(this.keys, mat, enchantment)
if (!player.hasPermission(CustomAnvil.bypassFusePermission) &&
(ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(this.keys,mat,enchantment) != ConflictType.NO_CONFLICT)){
(conflictType != ConflictType.NO_CONFLICT)
) {
CustomAnvil.verboseLog("Enchantment not yet in result list, but there is conflict (${enchantment.key}, conflict: $conflictType)")
this.remove(enchantment)
}
@ -43,8 +47,12 @@ object EnchantmentUtil {
// Enchantment already in result list
else {
// ... and they are conflicting
if((ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(this.keys,mat,enchantment) != ConflictType.NO_CONFLICT)
&& !player.hasPermission(CustomAnvil.bypassFusePermission)){
val conflictType =
ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(this.keys, mat, enchantment)
if ((conflictType != ConflictType.NO_CONFLICT)
&& !player.hasPermission(CustomAnvil.bypassFusePermission)
) {
CustomAnvil.verboseLog("Enchantment already in result list, and they are conflicting (${enchantment.key}, conflict: $conflictType)")
return@forEach
}
@ -52,7 +60,10 @@ object EnchantmentUtil {
if (this[enchantment] != other[enchantment]) {
val newLevel = max(this[enchantment] ?: 0, other[enchantment] ?: 0)
// apply the greater of the two if non-zero
if (newLevel > 0) { this[enchantment] = newLevel }
if (newLevel > 0) {
this[enchantment] = newLevel
}
}
// ... and they're the same level
else {
@ -65,7 +76,9 @@ object EnchantmentUtil {
ConfigOptions.enchantLimit(enchantment)
}
newLevel = min(newLevel, maxLevel)
if (newLevel > 0) { this[enchantment] = newLevel }
if (newLevel > 0) {
this[enchantment] = newLevel
}
}
}
}

View file

@ -5,7 +5,7 @@ import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.entity.HumanEntity
import xyz.alexcrea.cuanvil.gui.MainConfigGui
import xyz.alexcrea.cuanvil.gui.config.global.MainConfigGui
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions
class EditConfigExecutor : CommandExecutor {

View file

@ -5,9 +5,10 @@ import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.gui.config.BasicConfigGui
import xyz.alexcrea.cuanvil.gui.config.EnchantCostConfigGui
import xyz.alexcrea.cuanvil.gui.config.EnchantLimitConfigGui
import xyz.alexcrea.cuanvil.gui.config.global.BasicConfigGui
import xyz.alexcrea.cuanvil.gui.config.global.EnchantConflictGui
import xyz.alexcrea.cuanvil.gui.config.global.EnchantCostConfigGui
import xyz.alexcrea.cuanvil.gui.config.global.EnchantLimitConfigGui
import xyz.alexcrea.cuanvil.util.MetricsUtil
class ReloadExecutor : CommandExecutor {
@ -41,6 +42,7 @@ class ReloadExecutor : CommandExecutor {
BasicConfigGui.INSTANCE.updateGuiValues()
EnchantCostConfigGui.INSTANCE.updateGuiValues()
EnchantLimitConfigGui.INSTANCE.updateGuiValues()
EnchantConflictGui.INSTANCE.reloadValues()
// & update metric
MetricsUtil.testIfConfigIsDefault()

View file

@ -14,8 +14,8 @@ abstract class AbstractMaterialGroup(private val name: String) {
/**
* Get if a material is allowed following the group policy
*/
fun contain(mat : Material): Boolean {
return mat in includedMaterial
open fun contain(mat: Material): Boolean {
return mat in getMaterials()
}
/**
@ -33,18 +33,59 @@ abstract class AbstractMaterialGroup(private val name: String) {
*/
abstract fun addToPolicy(other: AbstractMaterialGroup)
/**
* Get the group contained material as a set
*/
abstract fun getMaterials(): EnumSet<Material>
/**
* Get the group non-inherited material as a set
*/
open fun getNonGroupInheritedMaterials(): EnumSet<Material> {
return includedMaterial
}
/**
* Get the group non-inherited material as a set
*/
open fun setNonGroupInheritedMaterials(materials: EnumSet<Material>) {
this.includedMaterial.clear()
this.includedMaterial.addAll(materials)
}
/**
* Get the group name in case something is wrong
*/
fun getName(): String {
open fun getName(): String {
return name
}
/**
* Get the group as a set
* Update the contained groups of this group
*/
fun getSet(): Set<Material> {
return includedMaterial
abstract fun setGroups(groups: MutableSet<AbstractMaterialGroup>)
/**
* Get the contained group of this material group
*/
abstract fun getGroups(): MutableSet<AbstractMaterialGroup>
open fun getRepresentativeMaterial(): Material {
// Test inner material
val matIterator = includedMaterial.iterator()
while (matIterator.hasNext()) {
val material = matIterator.next()
if (material.isAir) continue
return material
}
// Test included group representative material
val groupIterator = getGroups().iterator()
while (groupIterator.hasNext()) {
val groupMat = groupIterator.next().getRepresentativeMaterial()
if (groupMat.isAir) continue
return groupMat
}
return Material.PAPER
}
}

View file

@ -1,15 +1,21 @@
package xyz.alexcrea.cuanvil.group
import io.delilaheve.CustomAnvil
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, private val minBeforeBlock: Int){
class EnchantConflictGroup(
val name: String,
private val cantConflict: AbstractMaterialGroup,
val minBeforeBlock: Int
) {
private val enchantments = HashSet<Enchantment>()
fun addEnchantment(ench: Enchantment){
enchantments.add(ench)
fun addEnchantment(enchant: Enchantment) {
enchantments.add(enchant)
}
fun allowed(enchants: Set<Enchantment>, mat: Material): Boolean {
if (enchantments.size < minBeforeBlock) {
return true
@ -23,6 +29,7 @@ class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, priv
var enchantAmount = 0
for (enchantment in enchants) {
if (enchantment !in enchantments) continue
CustomAnvil.verboseLog("Enchant ${enchantment.key} is in: ${enchantAmount + 1}/$minBeforeBlock ")
if (++enchantAmount > minBeforeBlock) {
return false
}
@ -31,8 +38,28 @@ class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, priv
return true
}
fun getCantConflictGroup(): AbstractMaterialGroup {
return this.cantConflict
}
fun getEnchants(): HashSet<Enchantment> {
return enchantments
}
fun setEnchants(enchants: Set<Enchantment>) {
enchantments.clear()
enchantments.addAll(enchants)
}
fun getRepresentativeMaterial(): Material {
val groups = getCantConflictGroup().getGroups()
val groupIterator = groups.iterator()
while (groupIterator.hasNext()) {
val mat = groupIterator.next().getRepresentativeMaterial()
if (mat != Material.ENCHANTED_BOOK) return mat
}
return Material.ENCHANTED_BOOK
}
}

View file

@ -10,34 +10,37 @@ class EnchantConflictManager {
companion object {
// Path for the enchantments list
private const val ENCH_LIST_PATH = "enchantments"
const val ENCH_LIST_PATH = "enchantments"
// Path for group list related to the conflict
private const val CONFLICT_GROUP_PATH = "notAffectedGroups"
const val CONFLICT_GROUP_PATH = "notAffectedGroups"
// Path for the maximum number of enchantment before validating the conflict
private const val ENCH_MAX_PATH = "maxEnchantmentBeforeConflict"
const val ENCH_MAX_PATH = "maxEnchantmentBeforeConflict"
// Path for a flag: if the enchantment will be used in the last supported version
// TODO maybe replace this system by a list of "future" enchantment.
private const val FUTURE_USE_PATH = "useInFuture"
// Default name for an empty Material group
private val DEFAULT_EMPTY_GROUP = IncludeGroup("empty")
// Default name for a joining group
private const val DEFAULT_GROUP_NAME = "joinedGroup"
}
private lateinit var conflictMap: HashMap<Enchantment, ArrayList<EnchantConflictGroup>>
lateinit var conflictList: ArrayList<EnchantConflictGroup>
// Read and prepare all conflict
fun prepareConflicts(config: ConfigurationSection, itemManager: ItemGroupManager) {
conflictMap = HashMap()
conflictList = ArrayList()
val keys = config.getKeys(false)
for (key in keys) {
val section = config.getConfigurationSection(key)!!
val conflict = createConflict(section, itemManager, key)
if(conflict != null){
addToMap(conflict)
}
addToMap(conflict)
conflictList.add(conflict)
}
}
@ -45,17 +48,27 @@ class EnchantConflictManager {
// Add the conflict to the map
private fun addToMap(conflict: EnchantConflictGroup) {
conflict.getEnchants().forEach { enchant ->
addConflictToConflictMap(enchant, conflict)
}
}
fun addConflictToConflictMap(enchant: Enchantment, conflict: EnchantConflictGroup) {
if (!conflictMap.containsKey(enchant)) {
conflictMap[enchant] = ArrayList()
}
conflictMap[enchant]!!.add(conflict)
}
fun removeConflictFromMap(enchant: Enchantment, conflict: EnchantConflictGroup): Boolean {
return conflictMap[enchant]!!.remove(conflict)
}
// create and read a conflict from a yaml section
private fun createConflict(section: ConfigurationSection,
private fun createConflict(
section: ConfigurationSection,
itemManager: ItemGroupManager,
conflictName: String): EnchantConflictGroup? {
conflictName: String
): EnchantConflictGroup {
// Is it planed for the future
val futureUse = section.getBoolean(FUTURE_USE_PATH, false)
// Create conflict
@ -75,17 +88,18 @@ class EnchantConflictManager {
}
if (conflict.getEnchants().size == 0) {
if (!futureUse) {
CustomAnvil.instance.logger.warning("Conflict $conflictName do not have valid enchantment, it will not work")
CustomAnvil.instance.logger.warning("Conflict $conflictName do not have valid enchantment, it will not do anything")
}
return null
}
return conflict
}
private fun createConflictObject(section: ConfigurationSection,
private fun createConflictObject(
section: ConfigurationSection,
itemManager: ItemGroupManager,
conflictName: String): EnchantConflictGroup {
conflictName: String
): EnchantConflictGroup {
// Get the maximum number of enchantment before validating the conflict
var minBeforeBlock = section.getInt(ENCH_MAX_PATH, 0)
if (minBeforeBlock < 0) {
@ -95,39 +109,43 @@ class EnchantConflictManager {
}
// Find or create the selected group for the conflict
val groupList = section.getStringList(CONFLICT_GROUP_PATH)
val finalGroup: AbstractMaterialGroup
if(groupList.size < 1){
finalGroup = DEFAULT_EMPTY_GROUP
}else if(groupList.size == 1){
finalGroup = findGroup(groupList[0], itemManager, conflictName)
}else{
finalGroup = IncludeGroup(DEFAULT_GROUP_NAME)
val finalGroup = IncludeGroup(DEFAULT_GROUP_NAME)
for (groupName in groupList) {
finalGroup.addToPolicy(findGroup(groupName, itemManager, conflictName))
}
}
// Return conflict
return EnchantConflictGroup(finalGroup, minBeforeBlock)
return EnchantConflictGroup(conflictName, finalGroup, minBeforeBlock)
}
private fun findGroup(groupName: String, itemManager: ItemGroupManager, conflictName: String): AbstractMaterialGroup {
private fun findGroup(
groupName: String,
itemManager: ItemGroupManager,
conflictName: String
): AbstractMaterialGroup {
val group = itemManager.get(groupName)
if (group == null) {
CustomAnvil.instance.logger.warning("Group $groupName do not exist but is ask by conflict $conflictName")
return DEFAULT_EMPTY_GROUP
return IncludeGroup("error_placeholder")
}
return group
}
fun isConflicting(base: Set<Enchantment>, mat: Material, newEnchant: Enchantment): ConflictType {
CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${mat.key}")
val conflictList = conflictMap[newEnchant] ?: return ConflictType.NO_CONFLICT
CustomAnvil.verboseLog("Did not get skipped")
var result = ConflictType.NO_CONFLICT
for (conflict in conflictList) {
if(!conflict.allowed(base,mat)) {
CustomAnvil.verboseLog("Is against ${conflict.name}")
val conflicting = conflict.allowed(base, mat)
CustomAnvil.verboseLog("Was against ${conflict.name} and conflicting: $conflicting ")
if (!conflicting) {
if (conflict.getEnchants().size <= 1) {
result = ConflictType.SMALL_CONFLICT
CustomAnvil.verboseLog("Small conflict, continuing")
} else {
return ConflictType.BIG_CONFLICT
}

View file

@ -8,7 +8,8 @@ class ExcludeGroup(name: String): AbstractMaterialGroup(name) {
return EnumSet.allOf(Material::class.java)
}
private val includedGroup = HashSet<AbstractMaterialGroup>()
private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet()
private val groupItems by lazy { createDefaultSet() }
override fun isReferencing(other: AbstractMaterialGroup): Boolean {
for (materialGroup in includedGroup.iterator()) {
@ -21,11 +22,33 @@ class ExcludeGroup(name: String): AbstractMaterialGroup(name) {
override fun addToPolicy(mat: Material) {
includedMaterial.remove(mat)
groupItems.remove(mat)
}
override fun addToPolicy(other: AbstractMaterialGroup) {
includedGroup.add(other)
includedMaterial.removeAll(other.getSet())
groupItems.removeAll(other.getMaterials())
}
override fun setGroups(groups: MutableSet<AbstractMaterialGroup>) {
groupItems.clear()
groupItems.addAll(includedMaterial)
includedGroup.clear()
groups.forEach { group ->
if (!group.isReferencing(this)) {
includedGroup.add(group)
groupItems.removeAll(group.getMaterials())
}
}
}
override fun getGroups(): MutableSet<AbstractMaterialGroup> {
return includedGroup
}
override fun getMaterials(): EnumSet<Material> {
return groupItems
}

View file

@ -8,7 +8,8 @@ class IncludeGroup(name: String): AbstractMaterialGroup(name) {
return EnumSet.noneOf(Material::class.java)
}
private val includedGroup = HashSet<AbstractMaterialGroup>()
private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet()
private val groupItems by lazy { createDefaultSet() }
override fun isReferencing(other: AbstractMaterialGroup): Boolean {
for (materialGroup in includedGroup.iterator()) {
@ -21,11 +22,45 @@ class IncludeGroup(name: String): AbstractMaterialGroup(name) {
override fun addToPolicy(mat: Material) {
includedMaterial.add(mat)
groupItems.add(mat)
}
override fun addToPolicy(other: AbstractMaterialGroup) {
includedGroup.add(other)
includedMaterial.addAll(other.getSet())
groupItems.addAll(other.getMaterials())
}
override fun setGroups(groups: MutableSet<AbstractMaterialGroup>) {
groupItems.clear()
groupItems.addAll(includedMaterial)
includedGroup.clear()
groups.forEach { group ->
if (!group.isReferencing(this)) {
includedGroup.add(group)
groupItems.addAll(group.getMaterials())
}
}
}
override fun setNonGroupInheritedMaterials(materials: EnumSet<Material>) {
super.setNonGroupInheritedMaterials(materials)
// Update group items
groupItems.clear()
groupItems.addAll(includedMaterial)
includedGroup.forEach { group ->
groupItems.addAll(group.getMaterials())
}
}
override fun getGroups(): MutableSet<AbstractMaterialGroup> {
return includedGroup
}
override fun getMaterials(): EnumSet<Material> {
return groupItems
}
}

View file

@ -10,19 +10,22 @@ class ItemGroupManager {
companion object {
// Path for group type
private const val GROUP_TYPE_PATH = "type"
// Path for included items list
private const val MATERIAL_LIST_PATH = "items"
// Path for included groups list
private const val GROUP_LIST_PATH = "groups"
// Temporary list of elements in default config that are use in future
private val FUTURE_MATERIAL = setOf("PIGLIN_HEAD", "BRUSH")
}
private lateinit var groupMap : HashMap<String, AbstractMaterialGroup>
lateinit var groupMap: LinkedHashMap<String, AbstractMaterialGroup>
// Read and create material groups
fun prepareGroups(config: ConfigurationSection) {
groupMap = HashMap()
groupMap = LinkedHashMap()
val keys = config.getKeys(false)
for (key in keys) {
@ -33,9 +36,11 @@ class ItemGroupManager {
}
// Create group by key
private fun createGroup(config: ConfigurationSection,
private fun createGroup(
config: ConfigurationSection,
keys: Set<String>,
key: String): AbstractMaterialGroup {
key: String
): AbstractMaterialGroup {
val groupSection = config.getConfigurationSection(key)!!
val groupType = groupSection.getString(GROUP_TYPE_PATH, null)
@ -56,10 +61,12 @@ class ItemGroupManager {
}
// Read Group elements
private fun readGroup(group: AbstractMaterialGroup,
private fun readGroup(
group: AbstractMaterialGroup,
groupSection: ConfigurationSection,
config: ConfigurationSection,
keys: Set<String>){
keys: Set<String>
) {
// Read material to include in this group policy
val materialList = groupSection.getStringList(MATERIAL_LIST_PATH)
for (materialTemp in materialList) {
@ -69,7 +76,8 @@ class ItemGroupManager {
// Check if we should warn the user
if (materialName !in FUTURE_MATERIAL) {
CustomAnvil.instance.logger.warning(
"Unknown material $materialTemp on group ${group.getName()}")
"Unknown material $materialTemp on group ${group.getName()}"
)
}
continue
@ -83,7 +91,8 @@ class ItemGroupManager {
for (groupName in groupList) {
if (groupName !in keys) {
CustomAnvil.instance.logger.warning(
"Group $groupName do not exist but is included in group ${group.getName()}")
"Group $groupName do not exist but is included in group ${group.getName()}"
)
continue
}
// Get other group or create it if not yet created
@ -95,9 +104,11 @@ class ItemGroupManager {
// Avoid self reference or it will create an infinite loop
if (otherGroup.isReferencing(group)) {
CustomAnvil.instance.logger.warning(
"Group $groupName is on a reference loop with group ${group.getName()} !")
"Group $groupName is on a reference loop with group ${group.getName()} !"
)
CustomAnvil.instance.logger.warning(
"Please fix it in your item_groups config or the plugin will probably not work as expected.")
"Please fix it in your item_groups config or the plugin will probably not work as expected."
)
continue
}

View file

@ -0,0 +1,47 @@
package xyz.alexcrea.cuanvil.listener
import io.delilaheve.CustomAnvil
import org.bukkit.Bukkit
import org.bukkit.entity.HumanEntity
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.AsyncPlayerChatEvent
import org.bukkit.event.player.PlayerQuitEvent
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.function.Consumer
class ChatEventListener : Listener {
private val playerListenMap: ConcurrentHashMap<UUID, Consumer<String?>> = ConcurrentHashMap()
fun setListenedCallback(playeruuid: UUID, callback: Consumer<String?>) {
playerListenMap[playeruuid] = callback
}
fun setListenedCallback(player: HumanEntity, callback: Consumer<String?>) {
setListenedCallback(player.uniqueId, callback)
}
@EventHandler
fun onQuit(event: PlayerQuitEvent) {
val eventCallback = playerListenMap.remove(event.player.uniqueId) ?: return
eventCallback.accept(null)
}
@EventHandler
fun onChat(event: AsyncPlayerChatEvent) {
if (event.isCancelled) return
val player = event.player
val eventCallback = playerListenMap.remove(player.uniqueId) ?: return
event.isCancelled = true
// sync callback with default server thread
Bukkit.getScheduler().runTask(CustomAnvil.instance, Runnable {
eventCallback.accept(event.message)
})
}
}

View file

@ -31,12 +31,11 @@ object MetricsUtil {
*/
private fun getHashFromKey(section: ConfigurationSection, key: String): Int {
// Key is assumend to exist
val resultHash: Int
if(section.isConfigurationSection(key)){
val resultHash = if (section.isConfigurationSection(key)) {
val sectionResult = getConfigurationHash(section.getConfigurationSection(key)!!)
resultHash = key.hashCode() xor sectionResult
key.hashCode() xor sectionResult
} else {
resultHash = key.hashCode() xor section.getString(key).hashCode()
key.hashCode() xor section.getString(key).hashCode()
}
return resultHash.hashCode()
}
@ -83,12 +82,24 @@ object MetricsUtil {
isDefaultUnitRepairItemConfig = unitRepairItemConfigHash == unitRepairConfig
// If not default and debug flag active, print the hash.
if (ConfigOptions.debugLog) {
if(!isDefaultBaseConfig){CustomAnvil.log("baseConfig: $baseConfig")}
if(!isDefaultEnchantLimitsConfig){CustomAnvil.log("limitEnchantConfig: $limitEnchantConfig")}
if(!isDefaultEnchantValuesConfig){CustomAnvil.log("enchantValueConfig: $enchantValueConfig")}
if(!isDefaultEnchantConflictConfig){CustomAnvil.log("enchantConflictConfig: $enchantConflictConfig")}
if(!isDefaultItemGroupsConfig){CustomAnvil.log("itemGroupConfig: $itemGroupConfig")}
if(!isDefaultUnitRepairItemConfig){CustomAnvil.log("unitRepairConfig: $unitRepairConfig")}
if (!isDefaultBaseConfig) {
CustomAnvil.log("baseConfig: $baseConfig")
}
if (!isDefaultEnchantLimitsConfig) {
CustomAnvil.log("limitEnchantConfig: $limitEnchantConfig")
}
if (!isDefaultEnchantValuesConfig) {
CustomAnvil.log("enchantValueConfig: $enchantValueConfig")
}
if (!isDefaultEnchantConflictConfig) {
CustomAnvil.log("enchantConflictConfig: $enchantConflictConfig")
}
if (!isDefaultItemGroupsConfig) {
CustomAnvil.log("itemGroupConfig: $itemGroupConfig")
}
if (!isDefaultUnitRepairItemConfig) {
CustomAnvil.log("unitRepairConfig: $unitRepairConfig")
}
}
}
@ -96,18 +107,18 @@ object MetricsUtil {
fun notifyChange(holder: ConfigHolder, path: String) {
if (ConfigHolder.DEFAULT_CONFIG.equals(holder)) {
if (path.startsWith(ConfigOptions.ENCHANT_LIMIT_ROOT + ".")) {
isDefaultEnchantLimitsConfig = false;
isDefaultEnchantLimitsConfig = false
} else if (path.startsWith(ConfigOptions.ENCHANT_VALUES_ROOT + ".")) {
isDefaultEnchantValuesConfig = false;
isDefaultEnchantValuesConfig = false
} else {
isDefaultBaseConfig = false;
isDefaultBaseConfig = false
}
} else if (ConfigHolder.CONFLICT_HOLDER.equals(holder)) {
isDefaultEnchantConflictConfig = false;
isDefaultEnchantConflictConfig = false
} else if (ConfigHolder.ITEM_GROUP_HOLDER.equals(holder)) {
isDefaultItemGroupsConfig = false;
isDefaultItemGroupsConfig = false
} else if (ConfigHolder.UNIT_REPAIR_HOLDER.equals(holder)) {
isDefaultUnitRepairItemConfig = false;
isDefaultUnitRepairItemConfig = false
}
}

View file

@ -8,6 +8,7 @@ object UnitRepairUtil {
// Default value for user set default unit repair %
private const val DEFAULT_DEFAULT_UNIT_REPAIR = 0.25
// Path to user default unit repair value
private const val UNIT_REPAIR_DEFAULT_PATH = "default_repair_amount"

View file

@ -220,3 +220,6 @@ enchant_values:
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false

View file

@ -232,3 +232,7 @@ crossbow_conflict:
notAffectedGroups: [ ]
maxEnchantmentBeforeConflict: 1
# ----------------------------------------------------
# Bellow is for custom conflicts.
# This is also where conflict create from the gui will be placed.
# ----------------------------------------------------

View file

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