diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java index d7af4fd3..31502002 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/item/ItemBuilder.java @@ -20,6 +20,7 @@ package net.momirealms.customfishing.api.mechanic.item; import de.tr7zw.changeme.nbtapi.NBTItem; import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.common.Tuple; +import net.momirealms.customfishing.api.mechanic.misc.Value; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFlag; @@ -53,6 +54,8 @@ public interface ItemBuilder extends BuildableItem { ItemBuilder randomEnchantments(List> enchantments, boolean store); + ItemBuilder enchantmentPool(List> amountPairs, List, Value>> enchantments, boolean store); + ItemBuilder maxDurability(int max); ItemBuilder price(float base, float bonus); diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/Value.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/Value.java new file mode 100644 index 00000000..a15a3fc4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/misc/Value.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customfishing.api.mechanic.misc; + +import org.bukkit.entity.Player; + +public interface Value { + + double get(Player player); +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java index 7f883784..829ee0e0 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/CustomFishingPluginImpl.java @@ -229,7 +229,9 @@ public class CustomFishingPluginImpl extends CustomFishingPlugin { "xyz.xenondevs.invui:inventory-access-r12:1.23", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r13:1.23", "https://repo.xenondevs.xyz/releases/", "xyz.xenondevs.invui:inventory-access-r14:1.23", "https://repo.xenondevs.xyz/releases/", - "xyz.xenondevs.invui:inventory-access-r15:1.23", "https://repo.xenondevs.xyz/releases/" + "xyz.xenondevs.invui:inventory-access-r15:1.23", "https://repo.xenondevs.xyz/releases/", + "xyz.xenondevs.invui:inventory-access-r16:1.23", "https://repo.xenondevs.xyz/releases/", + "xyz.xenondevs.invui:inventory-access-r17:1.23", "https://repo.xenondevs.xyz/releases/" ); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java index 57991597..80b21604 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java +++ b/plugin/src/main/java/net/momirealms/customfishing/command/sub/DebugCommand.java @@ -32,6 +32,7 @@ import net.momirealms.customfishing.api.mechanic.condition.FishingPreparation; import net.momirealms.customfishing.api.mechanic.effect.EffectCarrier; import net.momirealms.customfishing.api.mechanic.effect.EffectModifier; import net.momirealms.customfishing.api.mechanic.effect.FishingEffect; +import org.bukkit.Material; import java.util.ArrayList; import java.util.List; @@ -110,6 +111,10 @@ public class DebugCommand { StringTooltip.ofString("false", "loots in water") }))) .executesPlayer((player, arg) -> { + if (player.getInventory().getItemInMainHand().getType() != Material.FISHING_ROD) { + AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, "Please hold a fishing rod before using this command."); + return; + } FishingEffect initialEffect = CustomFishingPlugin.get().getEffectManager().getInitialEffect(); FishingPreparation fishingPreparation = new FishingPreparation(player, CustomFishingPlugin.get()); boolean inLava = (boolean) arg.getOrDefault("lava fishing", false); diff --git a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java index 5cc2bd8f..06a812bf 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java +++ b/plugin/src/main/java/net/momirealms/customfishing/gui/page/item/ItemSelector.java @@ -42,7 +42,7 @@ public class ItemSelector implements YamlPage { private String prefix; private final File file; private long coolDown; - private String type; + private final String type; public ItemSelector(Player player, File file, String type) { this.yaml = YamlConfiguration.loadConfiguration(file); @@ -103,6 +103,54 @@ public class ItemSelector implements YamlPage { window.open(); } + public void reOpenWithNewKey() { + String tempKey = "New Key"; + prefix = tempKey; + var confirmIcon = new ConfirmIcon(); + Item border = new SimpleItem(new ItemBuilder(Material.AIR)); + Gui upperGui = Gui.normal() + .setStructure( + "a # b" + ) + .addIngredient('a', new SimpleItem(new ItemBuilder(Material.NAME_TAG).setDisplayName(tempKey))) + .addIngredient('b', confirmIcon) + .addIngredient('#', border) + .build(); + + var gui = PagedGui.items() + .setStructure( + "x x x x x x x x x", + "x x x x x x x x x", + "x x x x x x x x x", + "# # a # c # b # #" + ) + .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) + .addIngredient('#', new BackGroundItem()) + .addIngredient('a', new PreviousPageItem()) + .addIngredient('b', new NextPageItem()) + .addIngredient('c', new BackToFolderItem(file.getParentFile())) + .build(); + + var window = AnvilWindow.split() + .setViewer(player) + .setTitle(new ShadedAdventureComponentWrapper( + AdventureManagerImpl.getInstance().getComponentFromMiniMessage("Set key name") + )) + .addRenameHandler(s -> { + long current = System.currentTimeMillis(); + if (current - coolDown < 100) return; + if (s.equals(tempKey)) return; + prefix = s; + coolDown = current; + confirmIcon.notifyWindows(); + }) + .setUpperGui(upperGui) + .setLowerGui(gui) + .build(); + + window.open(); + } + public List getItemList() { List itemList = new ArrayList<>(); for (Map.Entry entry : this.yaml.getValues(false).entrySet()) { @@ -121,6 +169,7 @@ public class ItemSelector implements YamlPage { } itemList.add(new ItemInList(key, new ItemBuilder(Material.STRUCTURE_VOID), this)); } + itemList.add(new AddKey()); return itemList; } @@ -189,4 +238,57 @@ public class ItemSelector implements YamlPage { } } } + + public class AddKey extends AbstractItem { + + @Override + public ItemProvider getItemProvider() { + return new ItemBuilder(Material.ANVIL).setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "[+] Add a new key" + ))); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + reOpenWithNewKey(); + } + } + + public class ConfirmIcon extends AbstractItem { + + @Override + public ItemProvider getItemProvider() { + if (prefix != null && !yaml.contains(prefix) && prefix.matches("^[a-zA-Z0-9_]+$")) { + var builder = new ItemBuilder(Material.NAME_TAG) + .setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + prefix + ))); + builder.addLoreLines(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "<#00FF7F> -> Left click to confirm" + ))).addLoreLines(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "<#00CED1> -> Right click to cancel" + ))); + return builder; + } else { + return new ItemBuilder(Material.BARRIER) + .setDisplayName(new ShadedAdventureComponentWrapper(AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + "● Duplicated or invalid key" + ))); + } + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + if (clickType.isLeftClick()) { + if (prefix != null && !yaml.contains(prefix) && prefix.matches("^[a-zA-Z0-9_]+$")) { + yaml.createSection(prefix); + save(); + } else { + return; + } + } + prefix = SEARCH; + reOpenWithFilter(SEARCH); + } + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java index 45812693..34ad5f99 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/item/ItemManagerImpl.java @@ -35,9 +35,11 @@ import net.momirealms.customfishing.api.mechanic.item.ItemBuilder; import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; import net.momirealms.customfishing.api.mechanic.loot.Loot; import net.momirealms.customfishing.api.util.LogUtils; +import net.momirealms.customfishing.api.util.WeightUtils; import net.momirealms.customfishing.compatibility.item.CustomFishingItemImpl; import net.momirealms.customfishing.compatibility.item.VanillaItemImpl; import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; +import net.momirealms.customfishing.api.mechanic.misc.Value; import net.momirealms.customfishing.setting.CFConfig; import net.momirealms.customfishing.util.ConfigUtils; import net.momirealms.customfishing.util.ItemUtils; @@ -45,8 +47,10 @@ import net.momirealms.customfishing.util.NBTUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -263,7 +267,11 @@ public class ItemManagerImpl implements ItemManager, Listener { String[] split = id.split(":", 2); return itemLibraryMap.get(split[0]).buildItem(player, split[1]); } else { - return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); + try { + return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); + } catch (IllegalArgumentException e) { + return new ItemStack(Material.COD); + } } } @@ -327,6 +335,8 @@ public class ItemManagerImpl implements ItemManager, Listener { .itemFlag(section.getStringList("item-flags").stream().map(flag -> ItemFlag.valueOf(flag.toUpperCase())).toList()) .enchantment(ConfigUtils.getEnchantmentPair(section.getConfigurationSection("enchantments")), false) .enchantment(ConfigUtils.getEnchantmentPair(section.getConfigurationSection("stored-enchantments")), true) + .enchantmentPool(ConfigUtils.getEnchantAmountPair(section.getConfigurationSection("enchantment-pool.amount")), ConfigUtils.getEnchantPoolPair(section.getConfigurationSection("enchantment-pool.pool")), false) + .enchantmentPool(ConfigUtils.getEnchantAmountPair(section.getConfigurationSection("stored-enchantment-pool.amount")), ConfigUtils.getEnchantPoolPair(section.getConfigurationSection("stored-enchantment-pool.pool")), true) .randomEnchantments(ConfigUtils.getEnchantmentTuple(section.getConfigurationSection("random-enchantments")), false) .randomEnchantments(ConfigUtils.getEnchantmentTuple(section.getConfigurationSection("random-stored-enchantments")), true) .tag(section.getBoolean("tag", true), type, id) @@ -627,6 +637,51 @@ public class ItemManagerImpl implements ItemManager, Listener { return this; } + @Override + public ItemBuilder enchantmentPool(List> amountPairs, List, Value>> enchantments, boolean store) { + if (enchantments.size() == 0 || amountPairs.size() == 0) return this; + editors.put("enchantment-pool", (player, nbtItem, placeholders) -> { + List> parsedAmountPair = new ArrayList<>(amountPairs.size()); + for (Pair rawValue : amountPairs) { + parsedAmountPair.add(Pair.of(rawValue.left(), rawValue.right().get(player))); + } + + int amount = WeightUtils.getRandom(parsedAmountPair); + if (amount <= 0) return; + NBTCompoundList list = nbtItem.getCompoundList(store ? "StoredEnchantments" : "Enchantments"); + + HashSet addedEnchantments = new HashSet<>(); + + List, Double>> cloned = new ArrayList<>(enchantments.size()); + for (Pair, Value> rawValue : enchantments) { + cloned.add(Pair.of(rawValue.left(), rawValue.right().get(player))); + } + + int i = 0; + outer: + while (i < amount && cloned.size() != 0) { + Pair enchantPair = WeightUtils.getRandom(cloned); + Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(enchantPair.left())); + if (enchantment == null) { + throw new NullPointerException("Enchantment: " + enchantPair.left() + " doesn't exist on your server."); + } + for (Enchantment added : addedEnchantments) { + if (enchantment.conflictsWith(added)) { + cloned.removeIf(pair -> pair.left().left().equals(enchantPair.left())); + continue outer; + } + } + NBTCompound nbtCompound = list.addCompound(); + nbtCompound.setString("id", enchantPair.left()); + nbtCompound.setShort("lvl", enchantPair.right()); + addedEnchantments.add(enchantment); + cloned.removeIf(pair -> pair.left().left().equals(enchantPair.left())); + i++; + } + }); + return this; + } + @Override public ItemBuilder maxDurability(int max) { if (max == 0) return this; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java index 33ebc868..f1e564eb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/ExpressionValue.java @@ -17,6 +17,7 @@ package net.momirealms.customfishing.mechanic.misc.value; +import net.momirealms.customfishing.api.mechanic.misc.Value; import net.momirealms.customfishing.util.ConfigUtils; import org.bukkit.entity.Player; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java index 31ffb02d..31c37008 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/misc/value/PlainValue.java @@ -17,9 +17,10 @@ package net.momirealms.customfishing.mechanic.misc.value; +import net.momirealms.customfishing.api.mechanic.misc.Value; import org.bukkit.entity.Player; -public class PlainValue implements Value{ +public class PlainValue implements Value { private final double value; diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java index 9980431c..1450ca05 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/scheduler/FoliaSchedulerImpl.java @@ -20,9 +20,12 @@ package net.momirealms.customfishing.scheduler; import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.scheduler.CancellableTask; +import net.momirealms.customfishing.util.LocationUtils; import org.bukkit.Bukkit; import org.bukkit.Location; +import java.util.Optional; + /** * A scheduler implementation for "synchronous" tasks using Folia's RegionScheduler. */ @@ -42,7 +45,7 @@ public class FoliaSchedulerImpl implements SyncScheduler { */ @Override public void runSyncTask(Runnable runnable, Location location) { - Bukkit.getRegionScheduler().execute(plugin, location, runnable); + Bukkit.getRegionScheduler().execute(plugin, Optional.ofNullable(location).orElse(LocationUtils.getAnyLocationInstance()), runnable); } /** diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java index ee679e74..7f364c60 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java @@ -24,7 +24,7 @@ import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; import net.momirealms.customfishing.mechanic.misc.value.ExpressionValue; import net.momirealms.customfishing.mechanic.misc.value.PlainValue; -import net.momirealms.customfishing.mechanic.misc.value.Value; +import net.momirealms.customfishing.api.mechanic.misc.Value; import net.objecthunter.exp4j.ExpressionBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; @@ -201,12 +201,38 @@ public class ConfigUtils { if (section == null) return list; for (Map.Entry entry : section.getValues(false).entrySet()) { if (entry.getValue() instanceof Integer integer) { - list.add(Pair.of(entry.getKey(), Short.valueOf(String.valueOf(integer)))); + list.add(Pair.of(entry.getKey(), Short.parseShort(String.valueOf(Math.max(1, Math.min(Short.MAX_VALUE, integer)))))); } } return list; } + public static List> getEnchantAmountPair(ConfigurationSection section) { + List> list = new ArrayList<>(); + if (section == null) return list; + for (Map.Entry entry : section.getValues(false).entrySet()) { + list.add(Pair.of(Integer.parseInt(entry.getKey()), getValue(entry.getValue()))); + } + return list; + } + + public static List, Value>> getEnchantPoolPair(ConfigurationSection section) { + List, Value>> list = new ArrayList<>(); + if (section == null) return list; + for (Map.Entry entry : section.getValues(false).entrySet()) { + list.add(Pair.of(getEnchantmentPair(entry.getKey()), getValue(entry.getValue()))); + } + return list; + } + + public static Pair getEnchantmentPair(String value) { + int last = value.lastIndexOf(":"); + if (last == -1 || last == 0 || last == value.length() - 1) { + throw new IllegalArgumentException("Invalid format of the input enchantment"); + } + return Pair.of(value.substring(0, last), Short.parseShort(value.substring(last + 1))); + } + /** * Retrieves a list of enchantment tuples from a configuration section. * diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java index b3098602..e2580f4c 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/LocationUtils.java @@ -17,6 +17,7 @@ package net.momirealms.customfishing.util; +import org.bukkit.Bukkit; import org.bukkit.Location; public class LocationUtils { @@ -34,4 +35,8 @@ public class LocationUtils { Math.pow(location2.getZ() - location1.getZ(), 2) ); } + + public static Location getAnyLocationInstance() { + return new Location(Bukkit.getWorlds().get(0), 0, 64, 0); + } }