diff --git a/api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java b/api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java index bc49a85d..53df007c 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java +++ b/api/src/main/java/net/momirealms/customfishing/api/data/user/OnlineUser.java @@ -8,20 +8,6 @@ import org.bukkit.entity.Player; import java.util.UUID; -public interface OnlineUser { +public interface OnlineUser extends OfflineUser { Player getPlayer(); - - String getName(); - - UUID getUUID(); - - FishingBagHolder getHolder(); - - EarningData getEarningData(); - - Statistics getStatistics(); - - boolean isOnline(); - - PlayerData getPlayerData(); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java index 6ee01a97..3faf2135 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/MarketManager.java @@ -1,8 +1,13 @@ package net.momirealms.customfishing.api.manager; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import java.util.UUID; + public interface MarketManager { + void openMarketGUI(Player player); + int getDate(); double getItemPrice(ItemStack itemStack); diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java index 170a4383..b0650bde 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/condition/Condition.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.Map; public class Condition { @@ -14,7 +15,7 @@ public class Condition { @Nullable protected final Player player; @NotNull - protected final HashMap args; + protected final Map args; public Condition() { this(null, null, new HashMap<>()); @@ -28,21 +29,21 @@ public class Condition { this(player.getLocation(), player, new HashMap<>()); } - public Condition(Player player, HashMap args) { + public Condition(Player player, Map args) { this(player.getLocation(), player, args); } - public Condition(@Nullable Location location, @Nullable Player player, @NotNull HashMap args) { + public Condition(@Nullable Location location, @Nullable Player player, @NotNull Map args) { this.location = location; this.player = player; this.args = args; if (player != null) - this.args.put("player", player.getName()); + this.args.put("{player}", player.getName()); if (location != null) { - this.args.put("x", String.valueOf(location.getX())); - this.args.put("y", String.valueOf(location.getY())); - this.args.put("z", String.valueOf(location.getZ())); - this.args.put("world", location.getWorld().getName()); + this.args.put("{x}", String.valueOf(location.getX())); + this.args.put("{y}", String.valueOf(location.getY())); + this.args.put("{z}", String.valueOf(location.getZ())); + this.args.put("{world}", location.getWorld().getName()); } } @@ -57,7 +58,7 @@ public class Condition { } @NotNull - public HashMap getArgs() { + public Map getArgs() { return args; } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java index 535f867f..16d13c2a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java @@ -5,6 +5,7 @@ import net.momirealms.customfishing.api.manager.FishingManager; import net.momirealms.customfishing.api.mechanic.effect.Effect; import net.momirealms.customfishing.api.scheduler.CancellableTask; import org.bukkit.Material; +import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; import org.bukkit.inventory.PlayerInventory; import org.bukkit.potion.PotionEffectType; @@ -13,23 +14,26 @@ import java.util.concurrent.TimeUnit; public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { - protected boolean succeeded; + private final FishingManager manager; + private final long deadline; + + protected boolean success; protected CancellableTask task; protected Player player; protected GameSettings settings; - protected FishingManager manager; - private final long deadline; + protected FishHook fishHook; - public AbstractGamingPlayer(Player player, GameSettings settings, FishingManager manager) { + public AbstractGamingPlayer(Player player, FishHook hook, GameSettings settings) { this.player = player; + this.fishHook = hook; this.settings = settings; - this.manager = manager; + this.manager = CustomFishingPlugin.get().getFishingManager(); this.deadline = System.currentTimeMillis() + settings.getTime() * 1000L; this.arrangeTask(); } public void arrangeTask() { - this.task = CustomFishingPlugin.get().getScheduler().runTaskAsyncTimer(this, 50, 50, TimeUnit.MILLISECONDS); + this.task = CustomFishingPlugin.get().getScheduler().runTaskSyncTimer(this, fishHook.getLocation(), 1, 1); } @Override @@ -39,13 +43,13 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { } @Override - public boolean isSucceeded() { - return succeeded; + public boolean isSuccessful() { + return success; } @Override public boolean onRightClick() { - manager.processGameResult(this); + endGame(); return true; } @@ -85,10 +89,18 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { switchItemCheck(); } + protected void endGame() { + this.manager.processGameResult(this); + } + + protected void setGameResult(boolean success) { + this.success = success; + } + protected void timeOutCheck() { if (System.currentTimeMillis() > deadline) { cancel(); - manager.processGameResult(this); + endGame(); } } @@ -97,8 +109,7 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { if (playerInventory.getItemInMainHand().getType() != Material.FISHING_ROD && playerInventory.getItemInOffHand().getType() != Material.FISHING_ROD) { cancel(); - manager.processGameResult(this); - player.removePotionEffect(PotionEffectType.SLOW); + endGame(); } } } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java index 0de14d0f..1d0afe8b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/Game.java @@ -1,9 +1,9 @@ package net.momirealms.customfishing.api.mechanic.game; -import net.momirealms.customfishing.api.manager.FishingManager; +import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; public interface Game { - GamingPlayer start(Player player, GameSettings settings, FishingManager manager); + GamingPlayer start(Player player, FishHook hook, GameSettings settings); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java index f100ccc3..25fc68ae 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java @@ -1,6 +1,5 @@ package net.momirealms.customfishing.api.mechanic.game; -import net.kyori.adventure.text.Component; import net.momirealms.customfishing.api.mechanic.effect.Effect; import org.bukkit.entity.Player; @@ -8,7 +7,7 @@ public interface GamingPlayer { void cancel(); - boolean isSucceeded(); + boolean isSuccessful(); boolean onRightClick(); 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 9d2ace7f..e0f3e481 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 @@ -11,7 +11,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -public interface ItemBuilder { +public interface ItemBuilder extends BuildableItem { ItemBuilder customModelData(int value); diff --git a/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java index 21c1ccd2..8ffc9c68 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/command/CommandManagerImpl.java @@ -34,17 +34,23 @@ public class CommandManagerImpl implements CommandManager { ) .register(); - if (CustomFishingPlugin.get().getBagManager().isBagEnabled()) { + new CommandAPICommand("sellfish") + .withPermission("customfishing.sellfish") + .executesPlayer((player, args) -> { + plugin.getMarketManager().openMarketGUI(player); + }) + .register(); + + if (plugin.getBagManager().isBagEnabled()) { FishingBagCommand.INSTANCE.getBagCommand().register(); } } private CommandAPICommand getReloadCommand() { return new CommandAPICommand("reload") - .withPermission("customfishing.command.reload") .executes((sender, args) -> { long time = System.currentTimeMillis(); - CustomFishingPlugin.get().reload(); + plugin.reload(); AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, Locale.MSG_Reload.replace("{time}", String.valueOf(System.currentTimeMillis()-time))); }); } 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 c05f4508..e6e28b84 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 @@ -313,6 +313,7 @@ public class ActionManagerImpl implements ActionManager { condition.getPlayer().addPotionEffect(potionEffect); }; } + LogUtils.warn("Illegal value format found at action: potion-effect"); return null; }); } @@ -332,6 +333,7 @@ public class ActionManagerImpl implements ActionManager { AdventureManagerImpl.getInstance().sendSound(condition.getPlayer(), sound); }; } + LogUtils.warn("Illegal value format found at action: sound"); return null; }); } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java index eebc24b1..5bced975 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/block/BlockManagerImpl.java @@ -223,17 +223,15 @@ public class BlockManagerImpl implements BlockManager, Listener { } else { blockData = blockLibraryMap.get("vanilla").getBlockData(player, blockID, config.getDataModifier()); } - plugin.getScheduler().runTaskSync(() -> { - FallingBlock fallingBlock = hookLocation.getWorld().spawnFallingBlock(hookLocation, blockData); - fallingBlock.getPersistentDataContainer().set( - Objects.requireNonNull(NamespacedKey.fromString("block", CustomFishingPlugin.get())), - PersistentDataType.STRING, - loot.getID() + ";" + player.getName() - ); - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1); - vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector()); - fallingBlock.setVelocity(vector); - }, hookLocation); + FallingBlock fallingBlock = hookLocation.getWorld().spawnFallingBlock(hookLocation, blockData); + fallingBlock.getPersistentDataContainer().set( + Objects.requireNonNull(NamespacedKey.fromString("block", CustomFishingPlugin.get())), + PersistentDataType.STRING, + loot.getID() + ";" + player.getName() + ); + Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1); + vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector()); + fallingBlock.setVelocity(vector); } private void registerDirectional() { 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 130957cb..65ffaff9 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 @@ -2,7 +2,6 @@ package net.momirealms.customfishing.mechanic.fishing; import com.destroystokyo.paper.event.player.PlayerJumpEvent; import de.tr7zw.changeme.nbtapi.NBTItem; -import io.papermc.paper.event.player.AsyncChatEvent; import net.momirealms.customfishing.CustomFishingPluginImpl; import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.event.LavaFishingEvent; @@ -434,7 +433,7 @@ public class FishingManagerImpl implements Listener, FishingManager { if (bonus != null) tempFishingState.getEffect().merge(bonus); - if (gamingPlayer.isSucceeded()) + if (gamingPlayer.isSuccessful()) success(tempFishingState, fishHook); else fail(tempFishingState); @@ -472,10 +471,6 @@ public class FishingManagerImpl implements Listener, FishingManager { var fishingPreparation = state.getPreparation(); var player = fishingPreparation.getPlayer(); - int amount = (int) effect.getMultipleLootChance(); - amount += Math.random() < (effect.getMultipleLootChance() - amount) ? 2 : 1; - - fishingPreparation.insertArg("{amount}", String.valueOf(amount)); fishingPreparation.insertArg("{score}", String.format("%.2f", loot.getScore() * effect.getScoreMultiplier())); fishingPreparation.insertArg("{size-multiplier}", String.format("%.2f", effect.getSizeMultiplier())); fishingPreparation.insertArg("{x}", String.valueOf(hook.getLocation().getBlockX())); @@ -483,35 +478,39 @@ public class FishingManagerImpl implements Listener, FishingManager { fishingPreparation.insertArg("{z}", String.valueOf(hook.getLocation().getBlockZ())); fishingPreparation.insertArg("{loot}", loot.getID()); fishingPreparation.insertArg("{nick}", loot.getNick()); - fishingPreparation.insertArg("{score}", String.format("%.2f", loot.getScore())); - switch (loot.getType()) { - case LOOT -> { - // build the items for multiple times instead of using setAmount() to make sure that each item is unique - if (loot.getID().equals("vanilla")) { - ItemStack itemStack = vanillaLootMap.remove(player.getUniqueId()); - if (itemStack != null) { - fishingPreparation.insertArg("{loot}", ""); + plugin.getScheduler().runTaskSync(() -> { + switch (loot.getType()) { + case LOOT -> { + int amount = (int) effect.getMultipleLootChance(); + amount += Math.random() < (effect.getMultipleLootChance() - amount) ? 2 : 1; + fishingPreparation.insertArg("{amount}", String.valueOf(amount)); + // build the items for multiple times instead of using setAmount() to make sure that each item is unique + if (loot.getID().equals("vanilla")) { + ItemStack itemStack = vanillaLootMap.remove(player.getUniqueId()); + if (itemStack != null) { + fishingPreparation.insertArg("{loot}", ""); + for (int i = 0; i < amount; i++) { + plugin.getItemManager().dropItem(hook.getLocation(), player.getLocation(), itemStack.clone()); + doSuccessActions(loot, fishingPreparation, player); + } + } + } else { for (int i = 0; i < amount; i++) { - plugin.getItemManager().dropItem(hook.getLocation(), player.getLocation(), itemStack.clone()); - doActions(loot, fishingPreparation, player); + plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot, fishingPreparation.getArgs()); + doSuccessActions(loot, fishingPreparation, player); } } - } else { - for (int i = 0; i < amount; i++) { - plugin.getItemManager().dropItem(player, hook.getLocation(), player.getLocation(), loot, fishingPreparation.getArgs()); - doActions(loot, fishingPreparation, player); - } + return; } - return; + case MOB -> plugin.getMobManager().summonMob(hook.getLocation(), player.getLocation(), loot); + case BLOCK -> plugin.getBlockManager().summonBlock(player, hook.getLocation(), player.getLocation(), loot); } - case MOB -> plugin.getMobManager().summonMob(hook.getLocation(), player.getLocation(), loot); - case BLOCK -> plugin.getBlockManager().summonBlock(player, hook.getLocation(), player.getLocation(), loot); - } - doActions(loot, fishingPreparation, player); + doSuccessActions(loot, fishingPreparation, player); + }, hook.getLocation()); } - private void doActions(Loot loot, FishingPreparation fishingPreparation, Player player) { + private void doSuccessActions(Loot loot, FishingPreparation fishingPreparation, Player player) { Action[] globalActions = LootManagerImpl.globalLootProperties.getActions(ActionTrigger.SUCCESS); if (globalActions != null) for (Action action : globalActions) @@ -565,7 +564,12 @@ public class FishingManagerImpl implements Listener, FishingManager { @Override public void startFishingGame(Player player, GameSettings settings, Game game) { - this.gamingPlayerMap.put(player.getUniqueId(), game.start(player, settings, this)); + Optional hook = getHook(player.getUniqueId()); + if (hook.isPresent()) { + this.gamingPlayerMap.put(player.getUniqueId(), game.start(player, hook.get(), settings)); + } else { + LogUtils.warn("It seems that player " + player.getName() + " is not fishing. Fishing game failed to start."); + } } @Override diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java index 967d39f3..178e14d1 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java @@ -165,7 +165,7 @@ public class GameManagerImpl implements GameManager { var barImage = section.getString("subtitle.bar"); var pointerImage = section.getString("subtitle.pointer"); - return (player, settings, manager) -> new AbstractGamingPlayer(player, settings, manager) { + return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { private int progress; private boolean face; @@ -205,7 +205,7 @@ public class GameManagerImpl implements GameManager { } @Override - public boolean isSucceeded() { + public boolean isSuccessful() { int last = progress / widthPerSection; return (Math.random() < successRate[last]); } @@ -234,7 +234,7 @@ public class GameManagerImpl implements GameManager { var barImage = section.getString("subtitle.bar"); var tip = section.getString("tip"); - return (player, settings, manager) -> new AbstractGamingPlayer(player, settings, manager) { + return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { private double hold_time; private double judgement_position; private double fish_position; @@ -278,8 +278,8 @@ public class GameManagerImpl implements GameManager { hold_time -= punishment * 33; } if (hold_time >= time_requirement) { - succeeded = true; - manager.processGameResult(this); + setGameResult(true); + endGame(); return; } hold_time = Math.max(0, Math.min(hold_time, time_requirement)); @@ -376,7 +376,7 @@ public class GameManagerImpl implements GameManager { var barImage = section.getString("subtitle.bar"); var tip = section.getString("tip"); - return (player, settings, manager) -> new AbstractGamingPlayer(player, settings, manager) { + return (player, fishHook, settings) -> new AbstractGamingPlayer(player, fishHook, settings) { private int fish_position = fishStartPosition; private double strain; @@ -401,13 +401,13 @@ public class GameManagerImpl implements GameManager { if (player.isSneaking()) pull(); else loosen(); if (fish_position < successPosition - fishIconWidth - 1) { - super.succeeded = true; - manager.processGameResult(this); + setGameResult(true); + endGame(); return; } if (fish_position + fishIconWidth > barEffectiveWidth || strain >= ultimateTension) { - super.succeeded = false; - manager.processGameResult(this); + setGameResult(false); + endGame(); return; } showUI(); 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 fd0eba96..3175186f 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 @@ -266,12 +266,10 @@ public class ItemManagerImpl implements ItemManager { @Override public void dropItem(Location hookLocation, Location playerLocation, ItemStack itemStack) { - plugin.getScheduler().runTaskSync(() -> { - Entity itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack); - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105); - vector = vector.setY((vector.getY() + 0.2) * 1.18); - itemEntity.setVelocity(vector); - }, hookLocation); + Entity itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack); + Vector vector = playerLocation.subtract(hookLocation).toVector().multiply(0.105); + vector = vector.setY((vector.getY() + 0.2) * 1.18); + itemEntity.setVelocity(vector); } @NotNull @@ -291,7 +289,7 @@ public class ItemManagerImpl implements ItemManager { return Pair.of(Float.parseFloat(split[0]), Float.parseFloat(split[1])); } - public static class CFBuilder implements ItemBuilder, BuildableItem { + public static class CFBuilder implements ItemBuilder { private final String library; private final String id; @@ -438,6 +436,7 @@ public class ItemManagerImpl implements ItemManager { @Override public ItemBuilder price(float base, float bonus) { + if (base == 0 && bonus == 0) return this; editors.put("price", (player, nbtItem, placeholders) -> { if (base != 0) { placeholders.put("{base}", String.format("%.2f", base)); @@ -463,7 +462,7 @@ public class ItemManagerImpl implements ItemManager { editors.put("size", (player, nbtItem, placeholders) -> { NBTCompound cfCompound = nbtItem.getOrCreateCompound("CustomFishing"); float random = size.left() + ThreadLocalRandom.current().nextFloat(size.right() - size.left()); - float bonus = Float.parseFloat(placeholders.getOrDefault("size-multiplier", "1.0")); + float bonus = Float.parseFloat(placeholders.getOrDefault("{size-multiplier}", "1.0")); random *= bonus; cfCompound.setFloat("size", random); placeholders.put("{size}", String.format("%.2f", random)); 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 93fb1eea..26ed607d 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 @@ -1,6 +1,7 @@ package net.momirealms.customfishing.mechanic.market; import net.momirealms.customfishing.adventure.AdventureManagerImpl; +import net.momirealms.customfishing.api.data.EarningData; import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; import net.momirealms.customfishing.api.util.InventoryUtils; import net.momirealms.customfishing.api.util.LogUtils; @@ -20,10 +21,12 @@ public class MarketGUI { private final Inventory inventory; private final MarketManagerImpl manager; private final Player owner; + private final EarningData earningData; - public MarketGUI(MarketManagerImpl manager, Player player) { + public MarketGUI(MarketManagerImpl manager, Player player, EarningData earningData) { this.manager = manager; this.owner = player; + this.earningData = earningData; this.itemsCharMap = new HashMap<>(); this.itemsSlotMap = new HashMap<>(); var holder = new MarketGUIHolder(); @@ -45,8 +48,10 @@ public class MarketGUI { for (int index = 0; index < 9; index++) { char symbol = content.charAt(index); MarketGUIElement element = itemsCharMap.get(symbol); - element.addSlot(index + line * 9); - itemsSlotMap.put(index + line * 9, element); + if (element != null) { + element.addSlot(index + line * 9); + itemsSlotMap.put(index + line * 9, element); + } } line++; } @@ -82,35 +87,48 @@ public class MarketGUI { return itemsCharMap.get(slot); } - public void refresh() { + public MarketGUI refresh() { double totalWorth = getTotalWorth(); + MarketDynamicGUIElement functionElement = (MarketDynamicGUIElement) getElement(manager.getFunctionSlot()); + if (functionElement == null) { + return this; + } if (totalWorth <= 0) { - addElement(new MarketDynamicGUIElement( - manager.getFunctionSlot(), + functionElement.setItemStack( manager.getFunctionIconDenyBuilder().build(owner, - Map.of("{worth}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName()) + Map.of("{money}", String.format("%.2f", totalWorth) + ,"{player}", owner.getName() + ,"{rest}", String.format("%.2f", manager.getEarningLimit() - earningData.earnings)) ) - )); + ); + } else if (manager.getEarningLimit() != -1 && (manager.getEarningLimit() - earningData.earnings < totalWorth)) { + functionElement.setItemStack( + manager.getFunctionIconLimitBuilder().build(owner, + Map.of("{money}", String.format("%.2f", totalWorth) + ,"{player}", owner.getName() + ,"{rest}", String.format("%.2f", manager.getEarningLimit() - earningData.earnings)) + ) + ); } else { - addElement(new MarketDynamicGUIElement( - manager.getFunctionSlot(), + functionElement.setItemStack( manager.getFunctionIconAllowBuilder().build(owner, - Map.of("{worth}", String.format("%.2f", totalWorth) - ,"{player}", owner.getName()) + Map.of("{money}", String.format("%.2f", totalWorth) + ,"{player}", owner.getName() + ,"{rest}", String.format("%.2f", manager.getEarningLimit() - earningData.earnings)) ) - )); + ); } for (Map.Entry entry : itemsSlotMap.entrySet()) { if (entry.getValue() instanceof MarketDynamicGUIElement dynamicGUIElement) { this.inventory.setItem(entry.getKey(), dynamicGUIElement.getItemStack().clone()); } } + return this; } public double getTotalWorth() { double money = 0d; - MarketGUIElement itemElement = getElement(manager.getItemSlot()); + MarketGUIElement itemElement = getElement(manager.getItemSlot());; if (itemElement == null) { LogUtils.warn("No item slot available. Please check if GUI layout contains the item slot symbol."); return money; @@ -137,4 +155,36 @@ public class MarketGUI { } } } + + public int getEmptyItemSlot() { + MarketGUIElement itemElement = getElement(manager.getItemSlot()); + if (itemElement == null) { + return -1; + } + for (int slot : itemElement.getSlots()) { + ItemStack itemStack = inventory.getItem(slot); + if (itemStack == null || itemStack.getType() == Material.AIR) { + return slot; + } + } + return -1; + } + + public void returnItems() { + MarketGUIElement itemElement = getElement(manager.getItemSlot()); + if (itemElement == null) { + return; + } + for (int slot : itemElement.getSlots()) { + ItemStack itemStack = inventory.getItem(slot); + if (itemStack != null && itemStack.getType() != Material.AIR) { + owner.getInventory().addItem(itemStack); + inventory.setItem(slot, new ItemStack(Material.AIR)); + } + } + } + + public EarningData getEarningData() { + return earningData; + } } 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 a06265fe..fbc95bc9 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 @@ -1,12 +1,17 @@ package net.momirealms.customfishing.mechanic.market; +import com.willfp.eco.core.map.MutableListMap; import de.tr7zw.changeme.nbtapi.NBTItem; import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.data.EarningData; +import net.momirealms.customfishing.api.data.InventoryData; import net.momirealms.customfishing.api.data.user.OnlineUser; import net.momirealms.customfishing.api.manager.MarketManager; +import net.momirealms.customfishing.api.mechanic.action.Action; +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.market.MarketGUIHolder; +import net.momirealms.customfishing.api.util.LogUtils; import net.momirealms.customfishing.util.ConfigUtils; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; @@ -14,12 +19,12 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.*; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -34,13 +39,18 @@ public class MarketManagerImpl implements MarketManager, Listener { private String[] layout; private String title; private String formula; - private final HashMap decorativeIcons; + private final HashMap decorativeIcons; private char itemSlot; private char functionSlot; private BuildableItem functionIconAllowBuilder; private BuildableItem functionIconDenyBuilder; + private BuildableItem functionIconLimitBuilder; + private Action[] denyActions; + private Action[] allowActions; + private Action[] limitActions; private double earningLimit; - private ConcurrentHashMap marketGUIMap; + private boolean allowItemWithNoPrice; + private final ConcurrentHashMap marketGUIMap; public MarketManagerImpl(CustomFishingPlugin plugin) { this.plugin = plugin; @@ -54,7 +64,6 @@ public class MarketManagerImpl implements MarketManager, Listener { Bukkit.getPluginManager().registerEvents(this, plugin); } - public void unload() { HandlerList.unregisterAll(this); this.priceMap.clear(); @@ -70,6 +79,17 @@ public class MarketManagerImpl implements MarketManager, Listener { this.layout = config.getStringList("layout").toArray(new String[0]); this.title = config.getString("title", "market.title"); this.formula = config.getString("price-formula", "{base} + {bonus} * {size}"); + this.itemSlot = config.getString("item-slot.symbol", "I").charAt(0); + this.functionSlot = config.getString("functional-icons.symbol", "B").charAt(0); + this.functionIconAllowBuilder = plugin.getItemManager().getItemBuilder(config.getConfigurationSection("functional-icons.allow-icon"), "gui", "allow"); + this.functionIconDenyBuilder = plugin.getItemManager().getItemBuilder(config.getConfigurationSection("functional-icons.deny-icon"), "gui", "deny"); + this.functionIconLimitBuilder = plugin.getItemManager().getItemBuilder(config.getConfigurationSection("functional-icons.limit-icon"), "gui", "limit"); + this.allowActions = plugin.getActionManager().getActions(config.getConfigurationSection("functional-icons.allow-icon.action")); + this.denyActions = plugin.getActionManager().getActions(config.getConfigurationSection("functional-icons.deny-icon.action")); + this.limitActions = plugin.getActionManager().getActions(config.getConfigurationSection("functional-icons.limit-icon.action")); + this.earningLimit = config.getBoolean("limitation.enable", true) ? config.getDouble("limitation.earnings", 100) : -1; + this.allowItemWithNoPrice = config.getBoolean("item-slot.allow-items-with-no-price", true); + ConfigurationSection priceSection = config.getConfigurationSection("item-price"); if (priceSection != null) { for (Map.Entry entry : priceSection.getValues(false).entrySet()) { @@ -88,9 +108,70 @@ public class MarketManagerImpl implements MarketManager, Listener { } } + @Override public void openMarketGUI(Player player) { - MarketGUI gui = new MarketGUI(this, player); + OnlineUser user = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + if (user == null) { + LogUtils.warn("Player " + player.getName() + "'s market data is not loaded yet."); + return; + } + MarketGUI gui = new MarketGUI(this, player, user.getEarningData()); + gui.addElement(new MarketGUIElement(getItemSlot(), new ItemStack(Material.AIR))); + gui.addElement(new MarketDynamicGUIElement(getFunctionSlot(), new ItemStack(Material.AIR))); + for (Map.Entry entry : decorativeIcons.entrySet()) { + gui.addElement(new MarketGUIElement(entry.getKey(), entry.getValue().build(player))); + } + gui.build().refresh().show(player); + marketGUIMap.put(player.getUniqueId(), gui); + } + + @EventHandler + public void onCloseInv(InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player player)) + return; + if (!(event.getInventory().getHolder() instanceof MarketGUIHolder)) + return; + MarketGUI gui = marketGUIMap.remove(player.getUniqueId()); + if (gui != null) + gui.returnItems(); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + MarketGUI gui = marketGUIMap.remove(event.getPlayer().getUniqueId()); + if (gui != null) + gui.returnItems(); + } + + @EventHandler + public void onDragInv(InventoryDragEvent event) { + if (event.isCancelled()) + return; + Inventory inventory = event.getInventory(); + if (!(inventory.getHolder() instanceof MarketGUIHolder holder)) + return; + Player player = (Player) event.getWhoClicked(); + MarketGUI gui = marketGUIMap.get(player.getUniqueId()); + if (gui == null) { + event.setCancelled(true); + player.closeInventory(); + return; + } + + MarketGUIElement element = gui.getElement(itemSlot); + if (element == null) { + event.setCancelled(true); + return; + } + + List slots = element.getSlots(); + for (int dragSlot : event.getRawSlots()) { + if (!slots.contains(dragSlot)) { + event.setCancelled(true); + return; + } + } } @EventHandler @@ -100,62 +181,91 @@ public class MarketManagerImpl implements MarketManager, Listener { Inventory clickedInv = event.getClickedInventory(); if (clickedInv == null) return; - HumanEntity human = event.getWhoClicked(); - if (!(clickedInv.getHolder() instanceof MarketGUIHolder holder)) + Player player = (Player) event.getWhoClicked(); + if (!(event.getInventory().getHolder() instanceof MarketGUIHolder holder)) return; - MarketGUI gui = marketGUIMap.get(human.getUniqueId()); + MarketGUI gui = marketGUIMap.get(player.getUniqueId()); if (gui == null) { event.setCancelled(true); - human.closeInventory(); + player.closeInventory(); return; } - int slot = event.getSlot(); - MarketGUIElement element = gui.getElement(slot); - if (element == null) { - event.setCancelled(true); - return; - } + if (clickedInv != player.getInventory()) { + EarningData data = gui.getEarningData(); + if (data.date != getDate()) { + data.date = getDate(); + data.earnings = 0; + } - if (element.getSymbol() == itemSlot) { - plugin.getScheduler().runTaskSyncLater(gui::refresh, human.getLocation(), 50, TimeUnit.MILLISECONDS); - return; - } + int slot = event.getSlot(); + MarketGUIElement element = gui.getElement(slot); + if (element == null) { + event.setCancelled(true); + return; + } - if (element.getSymbol() == functionSlot) { - event.setCancelled(true); - double worth = gui.getTotalWorth(); - if (worth > 0) { - double remainingToEarn = getRemainingMoneyToEarn(human.getUniqueId()); - if (remainingToEarn < worth) { + if (element.getSymbol() != itemSlot) { + event.setCancelled(true); + } + if (element.getSymbol() == functionSlot) { + double worth = gui.getTotalWorth(); + Condition condition = new Condition(player, new HashMap<>(Map.of( + "{money}", String.format("%.2f", worth) + ,"{rest}", String.format("%.2f", (earningLimit - data.earnings)) + ))); + if (worth > 0) { + if (earningLimit != -1 && (earningLimit - data.earnings) < worth) { + // can't earn more money + if (limitActions != null) { + for (Action action : limitActions) { + action.trigger(condition); + } + } + } else { + // clear items + gui.clearWorthyItems(); + data.earnings += worth; + condition.insertArg("{rest}", String.format("%.2f", (earningLimit - data.earnings))); + if (allowActions != null) { + for (Action action : allowActions) { + action.trigger(condition); + } + } + } } else { - gui.clearWorthyItems(); - this.setRemainMoneyToEarn(human.getUniqueId(), remainingToEarn + worth); + // nothing to sell + if (denyActions != null) { + for (Action action : denyActions) { + action.trigger(condition); + } + } + } + } + } else { + ItemStack current = event.getCurrentItem(); + if (!allowItemWithNoPrice) { + double price = getItemPrice(current); + if (price <= 0) { + event.setCancelled(true); + return; + } + } + + if ((event.getClick() == ClickType.SHIFT_LEFT || event.getClick() == ClickType.SHIFT_RIGHT) + && (current != null && current.getType() != Material.AIR)) { + event.setCancelled(true); + int slot = gui.getEmptyItemSlot(); + if (slot != -1) { + gui.getInventory().setItem(slot, current.clone()); + current.setAmount(0); } } - plugin.getScheduler().runTaskSyncLater(gui::refresh, human.getLocation(), 50, TimeUnit.MILLISECONDS); - return; } - event.setCancelled(true); - } - - public double getRemainingMoneyToEarn(UUID uuid) { - OnlineUser user = plugin.getStorageManager().getOnlineUser(uuid); - if (user == null) { - return -1; - } - return earningLimit - user.getEarningData().earnings; - } - - public void setRemainMoneyToEarn(UUID uuid, double remaining) { - OnlineUser user = plugin.getStorageManager().getOnlineUser(uuid); - if (user == null) { - return; - } - user.getEarningData().earnings = remaining; + plugin.getScheduler().runTaskSyncLater(gui::refresh, player.getLocation(), 50, TimeUnit.MILLISECONDS); } @Override @@ -171,13 +281,13 @@ public class MarketManagerImpl implements MarketManager, Listener { NBTItem nbtItem = new NBTItem(itemStack); Double price = nbtItem.getDouble("Price"); if (price != null && price != 0) { - return price; + return price * itemStack.getAmount(); } String itemID = itemStack.getType().name(); if (nbtItem.hasTag("CustomModelData")) { itemID = itemID + ":" + nbtItem.getInteger("CustomModelData"); } - return priceMap.getOrDefault(itemID, 0d); + return priceMap.getOrDefault(itemID, 0d) * itemStack.getAmount(); } @Override @@ -212,6 +322,14 @@ public class MarketManagerImpl implements MarketManager, Listener { return title; } + public double getEarningLimit() { + return earningLimit; + } + + public BuildableItem getFunctionIconLimitBuilder() { + return functionIconLimitBuilder; + } + public BuildableItem getFunctionIconAllowBuilder() { return functionIconAllowBuilder; } diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java index 3506150d..c6e2000e 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/mob/MobManagerImpl.java @@ -125,20 +125,18 @@ public class MobManagerImpl implements MobManager { return; } String mobID = config.getMobID(); - plugin.getScheduler().runTaskSync(() -> { - Entity entity; - if (mobID.contains(":")) { - String[] split = mobID.split(":", 2); - String identification = split[0]; - String id = split[1]; - MobLibrary library = mobLibraryMap.get(identification); - entity = library.spawn(hookLocation, id, config.getPropertyMap()); - } else { - entity = mobLibraryMap.get("vanilla").spawn(hookLocation, mobID, config.getPropertyMap()); - } - Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1); - vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector()); - entity.setVelocity(vector); - }, hookLocation); + Entity entity; + if (mobID.contains(":")) { + String[] split = mobID.split(":", 2); + String identification = split[0]; + String id = split[1]; + MobLibrary library = mobLibraryMap.get(identification); + entity = library.spawn(hookLocation, id, config.getPropertyMap()); + } else { + entity = mobLibraryMap.get("vanilla").spawn(hookLocation, mobID, config.getPropertyMap()); + } + Vector vector = playerLocation.subtract(hookLocation).toVector().multiply((config.getHorizontalVector()) - 1); + vector = vector.setY((vector.getY() + 0.2) * config.getVerticalVector()); + entity.setVelocity(vector); } } diff --git a/plugin/src/main/resources/market.yml b/plugin/src/main/resources/market.yml index ac95ee05..1c9e8166 100644 --- a/plugin/src/main/resources/market.yml +++ b/plugin/src/main/resources/market.yml @@ -1,6 +1,10 @@ # Container title title: 'Fish Market' +limitation: + enable: true + earnings: 10000 + # Market menu layout layout: - 'AAAAAAAAA' @@ -25,22 +29,29 @@ item-price: # Slots to put items in item-slot: symbol: 'I' + allow-items-with-no-price: true # Functional icon functional-icons: symbol: 'B' - sell-icon: + allow-icon: material: IRON_BLOCK display: name: '<#00CED1>Ship the fish' lore: - 'You will get {money}$ by selling the fish' + - 'You can earn {rest}' action: sound_action: type: sound + value: + key: 'minecraft:block.amethyst_block.place' + source: 'player' + volume: 1 + pitch: 1 message_action: type: message - value: 'You earned {money}$ by selling the fish! You can still get {remains}$ from market today' + value: 'You earned {money}$ by selling the fish! You can still get {rest}$ from market today' command_action: type: command value: 'money give {player} {money}' @@ -53,6 +64,25 @@ functional-icons: action: sound_action: type: sound + value: + key: 'minecraft:entity.villager.no' + source: 'player' + volume: 1 + pitch: 1 + limit-icon: + material: REDSTONE_BLOCK + display: + name: 'Denied trade' + lore: + - 'The worth of items exceeds the money that can be earned for the rest of today!' + action: + sound_action: + type: sound + value: + key: 'minecraft:block.anvil.land' + source: 'player' + volume: 1 + pitch: 1 # Decorative icons decorative-icons: