diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java index b4050037..8c419cda 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/ActionManager.java @@ -121,5 +121,9 @@ public interface ActionManager { * @param actions The list of actions to trigger. * @param condition The condition associated with the actions. */ - void triggerActions(List actions, Condition condition); + static void triggerActions(Condition condition, Action... actions) { + if (actions != null) + for (Action action : actions) + action.trigger(condition); + } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java index 52a10981..9665352e 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/BagManager.java @@ -18,9 +18,12 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.data.user.OfflineUser; +import net.momirealms.customfishing.api.mechanic.action.Action; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; +import java.util.List; import java.util.UUID; public interface BagManager { @@ -44,4 +47,14 @@ public interface BagManager { * @param userData The OfflineUser data of the player whose bag is being edited. */ void editOfflinePlayerBag(Player admin, OfflineUser userData); + + Action[] getCollectLootActions(); + + Action[] getBagFullActions(); + + boolean doesBagStoreLoots(); + + String getBagTitle(); + + List getBagWhiteListItems(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java index 47e62c7b..8536e7e0 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/ItemManager.java @@ -18,6 +18,7 @@ package net.momirealms.customfishing.api.manager; import net.momirealms.customfishing.api.common.Key; +import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.mechanic.item.BuildableItem; import net.momirealms.customfishing.api.mechanic.item.ItemBuilder; import net.momirealms.customfishing.api.mechanic.item.ItemLibrary; @@ -157,19 +158,10 @@ public interface ItemManager { * @param player The player for whom the item is intended. * @param hookLocation The location where the item will initially drop. * @param playerLocation The target location towards which the item's velocity is applied. - * @param id The loot object representing the item to be dropped. - * @param args A map of placeholders for item customization. + * @param itemStack The loot to drop + * @param condition A map of placeholders for item customization. */ - void dropItem(Player player, Location hookLocation, Location playerLocation, String id, Map args); - - /** - * Drops an item entity at the specified location and applies velocity towards another location. - * - * @param hookLocation The location where the item will initially drop. - * @param playerLocation The target location towards which the item's velocity is applied. - * @param itemStack The item stack to be dropped as an entity. - */ - void dropItem(Location hookLocation, Location playerLocation, ItemStack itemStack); + void dropItem(Player player, Location hookLocation, Location playerLocation, ItemStack itemStack, Condition condition); /** * Checks if the provided ItemStack is a custom fishing item diff --git a/build.gradle.kts b/build.gradle.kts index cac05f19..19c055c4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { allprojects { - version = "2.0.9" + version = "2.0.10" apply() apply(plugin = "java") diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java b/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java index 27583bd3..3dd6a99b 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java +++ b/plugin/src/main/java/net/momirealms/customfishing/command/sub/ItemCommand.java @@ -125,7 +125,7 @@ public class ItemCommand { int amount = (int) args.getOrDefault("amount", 1); ItemStack item = CustomFishingPlugin.get().getItemManager().build(player, namespace, id, new Condition(player).getArgs()); if (item != null) { - int actual = ItemUtils.giveCertainAmountOfItem(player, item, amount); + int actual = ItemUtils.putLootsToBag(player, item, amount); AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Get_Item.replace("{item}", id).replace("{amount}", String.valueOf(actual))); } else { AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CFLocale.MSG_Item_Not_Exists); @@ -151,7 +151,7 @@ public class ItemCommand { assert players != null; for (Player player : players) { ItemStack item = CustomFishingPlugin.get().getItemManager().build(player, namespace, id, new Condition(player).getArgs()); - int actual = ItemUtils.giveCertainAmountOfItem(player, item, amount); + int actual = ItemUtils.putLootsToBag(player, item, amount); AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CFLocale.MSG_Give_Item.replace("{item}", id).replace("{amount}", String.valueOf(actual)).replace("{player}", player.getName())); } } else { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java index 985ed71a..d5346848 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java @@ -29,7 +29,6 @@ import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.action.ActionExpansion; import net.momirealms.customfishing.api.mechanic.action.ActionFactory; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; -import net.momirealms.customfishing.api.mechanic.condition.Condition; import net.momirealms.customfishing.api.mechanic.loot.Loot; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; import net.momirealms.customfishing.api.scheduler.CancellableTask; @@ -296,20 +295,6 @@ public class ActionManagerImpl implements ActionManager { return actionMap; } - /** - * Triggers a list of actions with the given condition. - * If the list of actions is not null, each action in the list is triggered. - * - * @param actions The list of actions to trigger. - * @param condition The condition associated with the actions. - */ - @Override - public void triggerActions(List actions, Condition condition) { - if (actions != null) - for (Action action : actions) - action.trigger(condition); - } - private void registerMessageAction() { registerAction("message", (args, chance) -> { ArrayList msg = ConfigUtils.stringListArgs(args); @@ -558,22 +543,33 @@ public class ActionManagerImpl implements ActionManager { if (Math.random() > chance) return; Player owner = condition.getPlayer(); Location location = position ? condition.getLocation() : owner.getLocation(); - plugin.getScheduler().runTaskSync(() -> { - for (Entity player : condition.getLocation().getWorld().getNearbyEntities(condition.getLocation(), range, range, range, entity -> entity instanceof Player)) { - double distance = LocationUtils.getDistance(player.getLocation(), condition.getLocation()); - if (distance <= range) { - ArmorStandUtils.sendHologram( - (Player) player, - location.clone().add(x, y, z), - AdventureManagerImpl.getInstance().getComponentFromMiniMessage( - PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs()) - ), - duration - ); - } - } - }, condition.getLocation() - ); + if (range > 0) { + plugin.getScheduler().runTaskSync(() -> { + for (Entity player : location.getWorld().getNearbyEntities(location, range, range, range, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), location); + if (distance <= range) { + ArmorStandUtils.sendHologram( + (Player) player, + location.clone().add(x, y, z), + AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs()) + ), + duration + ); + } + } + }, location + ); + } else { + ArmorStandUtils.sendHologram( + owner, + location.clone().add(x, y, z), + AdventureManagerImpl.getInstance().getComponentFromMiniMessage( + PlaceholderManagerImpl.getInstance().parse(owner, text, condition.getArgs()) + ), + duration + ); + } }; } else { LogUtils.warn("Illegal value format found at action: hologram"); @@ -630,7 +626,7 @@ public class ActionManagerImpl implements ActionManager { return condition -> { if (Math.random() > chance) return; Player player = condition.getPlayer(); - ItemUtils.giveCertainAmountOfItem(player, Objects.requireNonNull(CustomFishingPlugin.get().getItemManager().buildAnyPluginItemByID(player, id)), amount); + ItemUtils.putLootsToBag(player, Objects.requireNonNull(CustomFishingPlugin.get().getItemManager().buildAnyPluginItemByID(player, id)), amount); }; } else { LogUtils.warn("Illegal value format found at action: give-item"); @@ -644,20 +640,56 @@ public class ActionManagerImpl implements ActionManager { if (args instanceof ConfigurationSection section) { String[] itemSplit = section.getString("item", "").split(":", 2); int duration = section.getInt("duration", 20); - boolean position = section.getString("position", "hook").equals("hook"); - double x = section.getDouble("x"); - double y = section.getDouble("y"); - double z = section.getDouble("z"); + boolean position = !section.getString("position", "player").equals("player"); + String x = ConfigUtils.getString(section.get("x", "0")); + String y = ConfigUtils.getString(section.get("y", "0")); + String z = ConfigUtils.getString(section.get("z", "0")); + String yaw = ConfigUtils.getString(section.get("yaw", "0")); + int range = section.getInt("range", 0); + boolean opposite = section.getBoolean("opposite-yaw", false); return condition -> { if (Math.random() > chance) return; - Player player = condition.getPlayer(); - Location location = position ? condition.getLocation() : player.getLocation(); - ArmorStandUtils.sendFakeItem( - condition.getPlayer(), - location.clone().add(x, y, z), - plugin.getItemManager().build(player, itemSplit[0], itemSplit[1], condition.getArgs()), - duration + Player owner = condition.getPlayer(); + Location location = position ? condition.getLocation() : owner.getLocation(); + location = location.clone().add( + plugin.getPlaceholderManager().getExpressionValue(owner, x, condition.getArgs()), + plugin.getPlaceholderManager().getExpressionValue(owner, y, condition.getArgs()), + plugin.getPlaceholderManager().getExpressionValue(owner, z, condition.getArgs()) ); + Location finalLocation = location; + ItemStack itemStack = plugin.getItemManager().build( + owner, itemSplit[0], + plugin.getPlaceholderManager().parse(owner, itemSplit[1], condition.getArgs()), + condition.getArgs() + ); + if (range > 0) { + plugin.getScheduler().runTaskSync(() -> { + for (Entity player : finalLocation.getWorld().getNearbyEntities(finalLocation, range, range, range, entity -> entity instanceof Player)) { + double distance = LocationUtils.getDistance(player.getLocation(), finalLocation); + if (distance <= range) { + Location locationTemp = finalLocation.clone(); + if (opposite) locationTemp.setYaw(-player.getLocation().getYaw()); + else locationTemp.setYaw((float) plugin.getPlaceholderManager().getExpressionValue((Player) player, yaw, condition.getArgs())); + ArmorStandUtils.sendFakeItem( + condition.getPlayer(), + locationTemp, + itemStack, + duration + ); + } + } + }, condition.getLocation() + ); + } else { + if (opposite) finalLocation.setYaw(-owner.getLocation().getYaw()); + else finalLocation.setYaw((float) plugin.getPlaceholderManager().getExpressionValue(owner, yaw, condition.getArgs())); + ArmorStandUtils.sendFakeItem( + condition.getPlayer(), + finalLocation, + itemStack, + duration + ); + } }; } else { LogUtils.warn("Illegal value format found at action: fake-item"); @@ -819,7 +851,7 @@ public class ActionManagerImpl implements ActionManager { int fadeIn = section.getInt("fade-in", 20); int stay = section.getInt("stay", 30); int fadeOut = section.getInt("fade-out", 10); - int range = section.getInt("range", 32); + int range = section.getInt("range", 0); return condition -> { if (Math.random() > chance) return; plugin.getScheduler().runTaskSync(() -> { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java index cd9fdf79..b20ffe46 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/bag/BagManagerImpl.java @@ -23,12 +23,15 @@ import net.momirealms.customfishing.api.CustomFishingPlugin; import net.momirealms.customfishing.api.data.user.OfflineUser; import net.momirealms.customfishing.api.manager.BagManager; import net.momirealms.customfishing.api.manager.EffectManager; +import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; import net.momirealms.customfishing.api.util.InventoryUtils; import net.momirealms.customfishing.compatibility.papi.PlaceholderManagerImpl; import net.momirealms.customfishing.setting.CFConfig; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; @@ -40,14 +43,17 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class BagManagerImpl implements BagManager, Listener { private final CustomFishingPlugin plugin; private final HashMap tempEditMap; + private Action[] collectLootActions; + private Action[] bagFullActions; + private boolean bagStoreLoots; + private String bagTitle; + private List bagWhiteListItems; public BagManagerImpl(CustomFishingPluginImpl plugin) { this.plugin = plugin; @@ -61,6 +67,19 @@ public class BagManagerImpl implements BagManager, Listener { public void load() { Bukkit.getPluginManager().registerEvents(this, plugin); + if (isEnabled()) { + YamlConfiguration config = plugin.getConfig("config.yml"); + ConfigurationSection bagSection = config.getConfigurationSection("mechanics.fishing-bag"); + if (bagSection != null) { + bagTitle = bagSection.getString("bag-title"); + bagStoreLoots = bagSection.getBoolean("can-store-loot", false); + bagWhiteListItems = bagSection.getStringList("whitelist-items").stream().map(it -> Material.valueOf(it.toUpperCase(Locale.ENGLISH))).toList(); + if (bagStoreLoots) { + collectLootActions = plugin.getActionManager().getActions(bagSection.getConfigurationSection("collect-actions")); + bagFullActions = plugin.getActionManager().getActions(bagSection.getConfigurationSection("full-actions")); + } + } + } } public void unload() { @@ -92,7 +111,7 @@ public class BagManagerImpl implements BagManager, Listener { Inventory newBag = InventoryUtils.createInventory(onlinePlayer.getHolder(), rows * 9, AdventureManagerImpl.getInstance().getComponentFromMiniMessage( PlaceholderManagerImpl.getInstance().parse( - player, CFConfig.bagTitle, Map.of("{player}", player.getName()) + player, bagTitle, Map.of("{player}", player.getName()) ) )); onlinePlayer.getHolder().setInventory(newBag); @@ -165,7 +184,7 @@ public class BagManagerImpl implements BagManager, Listener { ItemStack clickedItem = event.getCurrentItem(); if (clickedItem == null || clickedItem.getType() == Material.AIR) return; - if (CFConfig.bagWhiteListItems.contains(clickedItem.getType())) + if (bagWhiteListItems.contains(clickedItem.getType())) return; String id = plugin.getItemManager().getAnyPluginItemID(clickedItem); EffectManager effectManager = plugin.getEffectManager(); @@ -176,7 +195,7 @@ public class BagManagerImpl implements BagManager, Listener { ) { return; } - if (CFConfig.bagStoreLoots && plugin.getLootManager().getLoot(id) != null) + if (bagStoreLoots && plugin.getLootManager().getLoot(id) != null) return; event.setCancelled(true); } @@ -196,4 +215,29 @@ public class BagManagerImpl implements BagManager, Listener { return; plugin.getStorageManager().saveUserData(offlineUser, true); } + + @Override + public Action[] getCollectLootActions() { + return collectLootActions; + } + + @Override + public Action[] getBagFullActions() { + return bagFullActions; + } + + @Override + public boolean doesBagStoreLoots() { + return bagStoreLoots; + } + + @Override + public String getBagTitle() { + return bagTitle; + } + + @Override + public List getBagWhiteListItems() { + return bagWhiteListItems; + } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java index 8d262725..c7509bdc 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java @@ -416,12 +416,12 @@ public class FishingManagerImpl implements Listener, FishingManager { // put vanilla loot in map this.vanillaLootMap.put(uuid, Pair.of(item.getItemStack(), event.getExpToDrop())); } - loot.triggerActions(ActionTrigger.HOOK, temp.getPreparation()); - temp.getPreparation().triggerActions(ActionTrigger.HOOK); + var fishingPreparation = temp.getPreparation(); + loot.triggerActions(ActionTrigger.HOOK, fishingPreparation); + fishingPreparation.triggerActions(ActionTrigger.HOOK); if (!loot.disableGame()) { // start the game if the loot has a game - - if (startFishingGame(player, temp.getPreparation(), temp.getEffect())) { + if (startFishingGame(player, fishingPreparation, temp.getEffect())) { event.setCancelled(true); } } else { @@ -455,14 +455,16 @@ public class FishingManagerImpl implements Listener, FishingManager { TempFishingState temp = getTempFishingState(uuid); if (temp != null) { var loot = temp.getLoot(); + var fishingPreparation = temp.getPreparation(); + fishingPreparation.setLocation(event.getHook().getLocation()); - loot.triggerActions(ActionTrigger.BITE, temp.getPreparation()); - temp.getPreparation().triggerActions(ActionTrigger.BITE); + loot.triggerActions(ActionTrigger.BITE, fishingPreparation); + fishingPreparation.triggerActions(ActionTrigger.BITE); if (loot.instanceGame() && !loot.disableGame()) { - loot.triggerActions(ActionTrigger.HOOK, temp.getPreparation()); - temp.getPreparation().triggerActions(ActionTrigger.HOOK); - startFishingGame(player, temp.getPreparation(), temp.getEffect()); + loot.triggerActions(ActionTrigger.HOOK, fishingPreparation); + fishingPreparation.triggerActions(ActionTrigger.HOOK); + startFishingGame(player, fishingPreparation, temp.getEffect()); } } } @@ -641,18 +643,27 @@ public class FishingManagerImpl implements Listener, FishingManager { if (pair != null) { fishingPreparation.insertArg("{nick}", ""); for (int i = 0; i < amount; i++) { - plugin.getItemManager().dropItem(hook.getLocation(), player.getLocation(), pair.left().clone()); - doSuccessActions(loot, effect, fishingPreparation, player); - if (pair.right() > 0) { - player.giveExp(pair.right(), true); - AdventureManagerImpl.getInstance().sendSound(player, Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); - } + plugin.getScheduler().runTaskSyncLater(() -> { + plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), pair.left().clone(), fishingPreparation); + doSuccessActions(loot, effect, fishingPreparation, player); + if (pair.right() > 0) { + player.giveExp(pair.right(), true); + AdventureManagerImpl.getInstance().sendSound(player, Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); + } + }, hook.getLocation(), (long) CFConfig.multipleLootSpawnDelay * i); } } } else { for (int i = 0; i < amount; i++) { - plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot.getID(), fishingPreparation.getArgs()); - doSuccessActions(loot, effect, fishingPreparation, player); + plugin.getScheduler().runTaskSyncLater(() -> { + ItemStack item = plugin.getItemManager().build(player, "item", loot.getID(), fishingPreparation.getArgs()); + if (item == null) { + LogUtils.warn(String.format("Item %s not exists", loot.getID())); + return; + } + plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), item, fishingPreparation); + doSuccessActions(loot, effect, fishingPreparation, player); + }, hook.getLocation(), (long) CFConfig.multipleLootSpawnDelay * i); } } return; diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java index 1e1be169..e138be75 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/HookCheckTimerTask.java @@ -185,9 +185,6 @@ public class HookCheckTimerTask implements Runnable { this.loot = nextLoot; fishingPreparation.insertArg("{nick}", nextLoot.getNick()); fishingPreparation.insertArg("{loot}", nextLoot.getID()); - fishingPreparation.insertArg("{x}", String.valueOf(fishHook.getLocation().getBlockX())); - fishingPreparation.insertArg("{y}", String.valueOf(fishHook.getLocation().getBlockY())); - fishingPreparation.insertArg("{z}", String.valueOf(fishHook.getLocation().getBlockZ())); if (!nextLoot.disableStats()) { fishingPreparation.insertArg("{statistics_size}", nextLoot.getStatisticKey().getSizeKey()); fishingPreparation.insertArg("{statistics_amount}", nextLoot.getStatisticKey().getAmountKey()); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java index af0afde5..4b926a28 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/hook/HookManagerImpl.java @@ -322,7 +322,7 @@ public class HookManagerImpl implements Listener, HookManager { if (cursor.getAmount() == 0) { event.setCursor(previousItemStack); } else { - ItemUtils.giveCertainAmountOfItem(player, previousItemStack, 1); + ItemUtils.putLootsToBag(player, previousItemStack, 1); } } 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 fe0ed915..bd54710b 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 @@ -24,6 +24,7 @@ import net.momirealms.customfishing.api.common.Key; import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.common.Tuple; import net.momirealms.customfishing.api.event.FishingLootSpawnEvent; +import net.momirealms.customfishing.api.manager.ActionManager; import net.momirealms.customfishing.api.manager.ItemManager; import net.momirealms.customfishing.api.manager.RequirementManager; import net.momirealms.customfishing.api.mechanic.GlobalSettings; @@ -374,7 +375,9 @@ public class ItemManagerImpl implements ItemManager, Listener { if (temp.getType() == Material.AIR) { return temp; } - temp.setAmount(builder.getAmount()); + int amount = builder.getAmount(); + temp.setAmount(amount); + placeholders.put("{amount}", String.valueOf(amount)); NBTItem nbtItem = new NBTItem(temp); for (ItemBuilder.ItemPropertyEditor editor : builder.getEditors()) { editor.edit(player, nbtItem, placeholders); @@ -423,27 +426,28 @@ public class ItemManagerImpl implements ItemManager, Listener { return itemLibraryMap.remove(identification) != null; } - /** - * Drops an item based on the provided loot, applying velocity from a hook location to a player location. - * This method would also trigger FishingLootSpawnEvent - * - * @param player The player for whom the item is intended. - * @param hookLocation The location where the item will initially drop. - * @param playerLocation The target location towards which the item's velocity is applied. - * @param id The loot object representing the item to be dropped. - * @param args A map of placeholders for item customization. - */ @Override - public void dropItem(Player player, Location hookLocation, Location playerLocation, String id, Map args) { - ItemStack item = build(player, "item", id, args); - if (item == null) { - LogUtils.warn(String.format("Item %s not exists", id)); - return; - } + public void dropItem(Player player, Location hookLocation, Location playerLocation, ItemStack item, Condition condition) { if (item.getType() == Material.AIR) { return; } + if (CFConfig.enableFishingBag && plugin.getBagManager().doesBagStoreLoots() && player.hasPermission("fishingbag.collectloot")) { + var bag = plugin.getBagManager().getOnlineBagInventory(player.getUniqueId()); + int cannotPut = ItemUtils.putLootsToBag(bag, item, item.getAmount()); + // some are put into bag + if (cannotPut != item.getAmount()) { + ActionManager.triggerActions(condition, plugin.getBagManager().getCollectLootActions()); + } + // all are put + if (cannotPut == 0) { + return; + } + // bag is full + item.setAmount(cannotPut); + ActionManager.triggerActions(condition, plugin.getBagManager().getBagFullActions()); + } + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(player, hookLocation, item); Bukkit.getPluginManager().callEvent(spawnEvent); if (spawnEvent.isCancelled()) { @@ -456,21 +460,6 @@ public class ItemManagerImpl implements ItemManager, Listener { itemEntity.setVelocity(vector); } - /** - * Drops an item entity at the specified location and applies velocity towards another location. - * - * @param hookLocation The location where the item will initially drop. - * @param playerLocation The target location towards which the item's velocity is applied. - * @param itemStack The item stack to be dropped as an entity. - */ - @Override - public void dropItem(Location hookLocation, Location playerLocation, ItemStack itemStack) { - Entity itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack); - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105); - vector = vector.setY((vector.getY() + 0.22) * 1.18); - itemEntity.setVelocity(vector); - } - /** * Decreases the durability of an ItemStack by a specified amount and optionally updates its lore. * @@ -731,7 +720,8 @@ public class ItemManagerImpl implements ItemManager, Listener { placeholders.put("{BASE}", String.valueOf(base)); placeholders.put("{bonus}", String.format("%.2f", bonus)); placeholders.put("{BONUS}", String.valueOf(bonus)); - double price = CustomFishingPlugin.get().getMarketManager().getFishPrice( + double price; + price = CustomFishingPlugin.get().getMarketManager().getFishPrice( player, placeholders ); diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java index 36e0eb3b..53985a10 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java @@ -255,7 +255,7 @@ public class MarketGUI { for (int slot : itemElement.getSlots()) { ItemStack itemStack = inventory.getItem(slot); if (itemStack != null && itemStack.getType() != Material.AIR) { - ItemUtils.giveCertainAmountOfItem(owner, itemStack, itemStack.getAmount()); + ItemUtils.putLootsToBag(owner, itemStack, itemStack.getAmount()); inventory.setItem(slot, new ItemStack(Material.AIR)); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java index f70beb66..ca427a62 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java @@ -433,7 +433,12 @@ public class MarketManagerImpl implements MarketManager, Listener { */ @Override public double getFishPrice(Player player, Map vars) { - return ConfigUtils.getExpressionValue(player, formula, vars); + String temp = PlaceholderManagerImpl.getInstance().parse(player, formula, vars); + var placeholders = PlaceholderManagerImpl.getInstance().detectPlaceholders(temp); + for (String placeholder : placeholders) { + temp = temp.replace(placeholder, "0"); + } + return new ExpressionBuilder(temp).build().evaluate(); } /** diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java index d7bf6514..1a430b26 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/requirement/RequirementManagerImpl.java @@ -215,6 +215,7 @@ public class RequirementManagerImpl implements RequirementManager { this.registerPotionEffectRequirement(); this.registerSizeRequirement(); this.registerHasStatsRequirement(); + this.registerLootTypeRequirement(); } public HashMap getLootWithWeight(Condition condition) { @@ -1075,6 +1076,33 @@ public class RequirementManagerImpl implements RequirementManager { }); } + private void registerLootTypeRequirement() { + registerRequirement("loot-type", (args, actions, advanced) -> { + List types = ConfigUtils.stringListArgs(args); + return condition -> { + String loot = condition.getArg("{loot}"); + Loot lootInstance = plugin.getLootManager().getLoot(loot); + if (lootInstance != null) { + if (types.contains(lootInstance.getType().name().toLowerCase(Locale.ENGLISH))) return true; + } + if (advanced) triggerActions(actions, condition); + return false; + }; + }); + registerRequirement("!loot-type", (args, actions, advanced) -> { + List types = ConfigUtils.stringListArgs(args); + return condition -> { + String loot = condition.getArg("{loot}"); + Loot lootInstance = plugin.getLootManager().getLoot(loot); + if (lootInstance != null) { + if (!types.contains(lootInstance.getType().name().toLowerCase(Locale.ENGLISH))) return true; + } + if (advanced) triggerActions(actions, condition); + return false; + }; + }); + } + private void registerEnvironmentRequirement() { registerRequirement("environment", (args, actions, advanced) -> { List environments = ConfigUtils.stringListArgs(args); diff --git a/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java index f8cf674a..b3de4a61 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/scheduler/BukkitSchedulerImpl.java @@ -73,6 +73,11 @@ public class BukkitSchedulerImpl implements SyncScheduler { */ @Override public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { + if (delay == 0) { + if (Bukkit.isPrimaryThread()) runnable.run(); + else Bukkit.getScheduler().runTask(plugin, runnable); + return new BukkitCancellableTask(null); + } return new BukkitCancellableTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); } @@ -89,11 +94,13 @@ public class BukkitSchedulerImpl implements SyncScheduler { @Override public void cancel() { - this.bukkitTask.cancel(); + if (this.bukkitTask != null) + this.bukkitTask.cancel(); } @Override public boolean isCancelled() { + if (this.bukkitTask == null) return true; return this.bukkitTask.isCancelled(); } } diff --git a/plugin/src/main/java/net/momirealms/customfishing/setting/CFConfig.java b/plugin/src/main/java/net/momirealms/customfishing/setting/CFConfig.java index 5a980022..25c616e5 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/setting/CFConfig.java +++ b/plugin/src/main/java/net/momirealms/customfishing/setting/CFConfig.java @@ -24,6 +24,7 @@ import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.mechanic.action.Action; import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.api.util.OffsetUtils; import net.momirealms.customfishing.util.ConfigUtils; @@ -40,7 +41,7 @@ import java.util.Objects; public class CFConfig { // config version - public static String configVersion = "31"; + public static String configVersion = "32"; // Debug mode public static boolean debug; // language @@ -66,9 +67,6 @@ public class CFConfig { // fishing bag public static boolean enableFishingBag; - public static boolean bagStoreLoots; - public static String bagTitle; - public static List bagWhiteListItems; // Fishing wait time public static boolean overrideVanilla; @@ -101,6 +99,8 @@ public class CFConfig { public static boolean globalDisableGame; public static boolean globalInstantGame; + public static int multipleLootSpawnDelay; + public static void load() { try { YamlDocument.create( @@ -118,6 +118,8 @@ public class CFConfig { .addIgnoredRoute(configVersion, "mechanics.mechanic-requirements", '.') .addIgnoredRoute(configVersion, "mechanics.global-events", '.') .addIgnoredRoute(configVersion, "mechanics.global-effects", '.') + .addIgnoredRoute(configVersion, "mechanics.fishing-bag.collect-actions", '.') + .addIgnoredRoute(configVersion, "mechanics.fishing-bag.full-actions", '.') .addIgnoredRoute(configVersion, "other-settings.placeholder-register", '.') .build() ); @@ -143,9 +145,6 @@ public class CFConfig { blockDetectOrder = config.getStringList("other-settings.block-detection-order"); enableFishingBag = config.getBoolean("mechanics.fishing-bag.enable", true); - bagTitle = config.getString("mechanics.fishing-bag.bag-title"); - bagStoreLoots = config.getBoolean("mechanics.fishing-bag.can-store-loot", false); - bagWhiteListItems = config.getStringList("mechanics.fishing-bag.whitelist-items").stream().map(it -> Material.valueOf(it.toUpperCase(Locale.ENGLISH))).toList(); overrideVanilla = config.getBoolean("mechanics.fishing-wait-time.override-vanilla", false); waterMinTime = config.getInt("mechanics.fishing-wait-time.min-wait-time", 100); @@ -165,6 +164,8 @@ public class CFConfig { placeholderLimit = config.getInt("mechanics.competition.placeholder-limit", 3); serverGroup = config.getString("mechanics.competition.server-group","default"); + multipleLootSpawnDelay = config.getInt("mechanics.multiple-loot-spawn-delay", 0); + dataSaveInterval = config.getInt("other-settings.data-saving-interval", 600); logDataSaving = config.getBoolean("other-settings.log-data-saving", true); lockData = config.getBoolean("other-settings.lock-data", true); diff --git a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java b/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java index 0380e204..5ba1d3de 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/storage/user/OfflineUserImpl.java @@ -66,7 +66,7 @@ public class OfflineUserImpl implements OfflineUser { this.holder.setInventory(InventoryUtils.createInventory(this.holder, playerData.getBagData().size, AdventureManagerImpl.getInstance().getComponentFromMiniMessage( PlaceholderManagerImpl.getInstance().parse( - offlinePlayer, CFConfig.bagTitle, Map.of("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(String.valueOf(uuid))) + offlinePlayer, CustomFishingPlugin.get().getBagManager().getBagTitle(), Map.of("{player}", Optional.ofNullable(offlinePlayer.getName()).orElse(String.valueOf(uuid))) ) ))); this.holder.setItems(InventoryUtils.getInventoryItems(playerData.getBagData().serialized)); diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java index 3c1fa7a3..bd5c3748 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ArmorStandUtils.java @@ -67,6 +67,7 @@ public class ArmorStandUtils { entityPacket.getDoubles().write(0, location.getX()); entityPacket.getDoubles().write(1, location.getY()); entityPacket.getDoubles().write(2, location.getZ()); + entityPacket.getBytes().write(0, (byte) ((location.getYaw() % 360) * 128 / 180)); return entityPacket; } 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 0cab3ded..516450d7 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ConfigUtils.java @@ -258,6 +258,17 @@ public class ConfigUtils { return list; } + public static String getString(Object o) { + if (o instanceof String s) { + return s; + } else if (o instanceof Integer i) { + return String.valueOf(i); + } else if (o instanceof Double d) { + return String.valueOf(d); + } + throw new IllegalArgumentException("Illegal string format: " + o); + } + /** * Reads data from a YAML configuration file and creates it if it doesn't exist. * diff --git a/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java index 142e8ca6..c6b27e1f 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java +++ b/plugin/src/main/java/net/momirealms/customfishing/util/ItemUtils.java @@ -34,6 +34,7 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.Damageable; @@ -341,7 +342,7 @@ public class ItemUtils { * @param amount The amount of items to give * @return The actual amount of items given */ - public static int giveCertainAmountOfItem(Player player, ItemStack itemStack, int amount) { + public static int putLootsToBag(Player player, ItemStack itemStack, int amount) { PlayerInventory inventory = player.getInventory(); ItemMeta meta = itemStack.getItemMeta(); int maxStackSize = itemStack.getMaxStackSize(); @@ -404,4 +405,59 @@ public class ItemUtils { return actualAmount; } + + public static ItemStack removeOwner(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) return itemStack; + NBTItem nbtItem = new NBTItem(itemStack); + if (nbtItem.hasTag("owner")) { + nbtItem.removeKey("owner"); + return nbtItem.getItem(); + } + return itemStack; + } + + /** + * @return the amount of items that can't be put in the inventory + */ + public static int putLootsToBag(Inventory inventory, ItemStack itemStack, int amount) { + itemStack = removeOwner(itemStack.clone()); + ItemMeta meta = itemStack.getItemMeta(); + int maxStackSize = itemStack.getMaxStackSize(); + for (ItemStack other : inventory.getStorageContents()) { + if (other != null) { + if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { + if (other.getAmount() < maxStackSize) { + int delta = maxStackSize - other.getAmount(); + if (amount > delta) { + other.setAmount(maxStackSize); + amount -= delta; + } else { + other.setAmount(amount + other.getAmount()); + return 0; + } + } + } + } + } + + if (amount > 0) { + for (ItemStack other : inventory.getStorageContents()) { + if (other == null) { + if (amount > maxStackSize) { + amount -= maxStackSize; + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + inventory.addItem(cloned); + } else { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(amount); + inventory.addItem(cloned); + return 0; + } + } + } + } + + return amount; + } } diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 8531c0ae..d285da65 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,6 +1,6 @@ # Developer: @Xiao-MoMi # Wiki: https://mo-mi.gitbook.io/xiaomomi-plugins/ -config-version: '31' +config-version: '32' # Debug debug: false @@ -130,9 +130,6 @@ mechanics: duration: 35 position: hook item: util:lava_effect - y: 0 - x: 0 - z: 0 priority_2: conditions: lava-fishing: false @@ -143,9 +140,6 @@ mechanics: duration: 35 position: hook item: util:water_effect - y: 0 - x: 0 - z: 0 # Global properties which would help you reduce duplicated lines global-loot-property: @@ -165,6 +159,37 @@ mechanics: # Other whitelist-items whitelist-items: - fishing_rod + # Actions to do if fishing loots are automatically collected into bag + collect-actions: + sound_action: + type: sound + value: + key: "minecraft:item.armor.equip_leather" + source: 'player' + volume: 1 + pitch: 1 + hologram_action: + type: hologram + value: + duration: 40 + text: '{nick} <#B0E0E6>has been stored into bag' + position: other + y: 1 + # Actions to do if the fishing bag is full + full-actions: + conditional_action: + type: conditional + value: + conditions: + condition_1: + type: cooldown + value: + key: fishing_bag_full_notice + time: 60000 + actions: + message_action: + type: message + value: "<#EEE8AA>[Fishing Bag] Your fishing bag has been full." # Fishing wait time # This section would take effect if you set "override-vanilla" to true @@ -199,6 +224,9 @@ mechanics: # Increase this value would allow you to use more placeholders like {4_player} {5_score} in sacrifice of some performance placeholder-limit: 3 + # If a player could get multiple loots from fishing, should the loots spawn at the same time or have delays for each (tick) + multiple-loot-spawn-delay: 4 + # Other settings other-settings: # It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below. @@ -233,6 +261,8 @@ other-settings: '{record}': '%fishingstats_size-record_{loot}%' # Requires server expansion '{date}': '%server_time_yyyy-MM-dd-HH:mm:ss%' + # Requires player expansion + '{yaw}': '%player_yaw%' # CustomFishing supports using items/blocks from other plugins # If items share the same id, they would inherit the effects diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index c90e8cdd..fb35c448 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -27,4 +27,8 @@ softdepend: - Zaphkiel permissions: fishingbag.user: + default: true + fishingbag.collectloot: + default: false + customfishing.sellfish: default: true \ No newline at end of file