diff --git a/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java b/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java index 7532b2f7..bf23b617 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java +++ b/api/src/main/java/net/momirealms/customfishing/api/event/FishingLootSpawnEvent.java @@ -17,53 +17,77 @@ package net.momirealms.customfishing.api.event; +import net.momirealms.customfishing.api.mechanic.context.Context; import net.momirealms.customfishing.api.mechanic.loot.Loot; import org.bukkit.Location; +import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class FishingLootSpawnEvent extends PlayerEvent implements Cancellable { +public class FishingLootSpawnEvent extends PlayerEvent { private static final HandlerList handlerList = new HandlerList(); private final Location location; - private final Item item; + private final Entity entity; private final Loot loot; - private boolean isCancelled; + private final Context context; + private boolean skipActions; + private boolean summonEntity; - public FishingLootSpawnEvent(@NotNull Player who, Location location, Loot loot, Item item) { - super(who); - this.item = item; + public FishingLootSpawnEvent(@NotNull Context context, Location location, Loot loot, @Nullable Entity entity) { + super(context.getHolder()); + this.entity = entity; this.loot = loot; this.location = location; - this.isCancelled = false; + this.skipActions = false; + this.summonEntity = true; + this.context = context; } - @Override - public boolean isCancelled() { - return isCancelled; - } - - @Override - public void setCancelled(boolean cancel) { - isCancelled = cancel; + public Context getContext() { + return context; } public Location getLocation() { return location; } - public Item getItem() { - return item; + /** + * Get the loot entity + * + * @return entity + */ + @Nullable + public Entity getEntity() { + return entity; } + @NotNull public Loot getLoot() { return loot; } + public boolean summonEntity() { + return summonEntity; + } + + public void summonEntity(boolean summonEntity) { + this.summonEntity = summonEntity; + } + + public boolean skipActions() { + return skipActions; + } + + public void skipActions(boolean skipActions) { + this.skipActions = skipActions; + } + @Override public @NotNull HandlerList getHandlers() { return handlerList; diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java index c89eb0c6..d949f64f 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/block/BlockManager.java @@ -29,7 +29,7 @@ public interface BlockManager extends Reloadable { boolean registerBlock(@NotNull BlockConfig block); - @Nullable + @NotNull FallingBlock summonBlockLoot(@NotNull Context context); @NotNull diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java index 741a91dd..17539883 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/context/ContextKeys.java @@ -34,11 +34,10 @@ public class ContextKeys { public static final ContextKeys LOOT = of("loot", LootType.class); public static final ContextKeys NICK = of("nick", String.class); public static final ContextKeys OPEN_WATER = of("open_water", Boolean.class); - public static final ContextKeys TYPE = of("type", String.class); - public static final ContextKeys SIZE = of("SIZE", Float.class); - public static final ContextKeys SIZE_FORMATTED = of("size", String.class); - public static final ContextKeys PRICE = of("PRICE", Double.class); - public static final ContextKeys PRICE_FORMATTED = of("price", String.class); + public static final ContextKeys SIZE = of("size", Float.class); + public static final ContextKeys SIZE_FORMATTED = of("size_formatted", String.class); + public static final ContextKeys PRICE = of("price", Double.class); + public static final ContextKeys PRICE_FORMATTED = of("price_formatted", String.class); public static final ContextKeys SURROUNDING = of("surrounding", String.class); public static final ContextKeys TEMP_NEAR_PLAYER = of("near", String.class); public static final ContextKeys ROD = of("rod", String.class); @@ -67,8 +66,9 @@ public class ContextKeys { public static final ContextKeys AMOUNT = of("amount", Integer.class); public static final ContextKeys WEIGHT = of("0", Double.class); public static final ContextKeys TIME_LEFT = of("time_left", String.class); - public static final ContextKeys REASON = of("reason", String.class); public static final ContextKeys PROGRESS = of("progress", String.class); + public static final ContextKeys RECORD = of("record", Float.class); + public static final ContextKeys RECORD_FORMATTED = of("record_formatted", String.class); public static final ContextKeys CLICKS_LEFT = of("left_clicks", Integer.class); public static final ContextKeys REQUIRED_TIMES = of("clicks", Integer.class); diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java index 3297b2d6..ab209d7a 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/entity/EntityManager.java @@ -21,6 +21,7 @@ import net.momirealms.customfishing.api.mechanic.context.Context; import net.momirealms.customfishing.common.plugin.feature.Reloadable; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Optional; @@ -40,6 +41,6 @@ public interface EntityManager extends Reloadable { boolean registerEntity(EntityConfig entity); - @Nullable + @NotNull Entity summonEntityLoot(Context context); } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java index f755cf78..7611a10b 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/event/EventManager.java @@ -51,7 +51,6 @@ public interface EventManager extends Reloadable { * * @param context The context in which the event is triggered. * @param id The unique identifier of the event carrier. - * @param type * @param trigger The trigger that initiates the event. */ default void trigger(Context context, String id, MechanicType type, ActionTrigger trigger) { @@ -63,7 +62,6 @@ public interface EventManager extends Reloadable { * * @param context The context in which the event is triggered. * @param id The unique identifier of the event carrier. - * @param type * @param trigger The trigger that initiates the event. * @param previousTimes The previous times count for the event. * @param afterTimes The after times count for the event. diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java index 87d5b518..ed0d9e85 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/fishing/CustomFishingHook.java @@ -18,6 +18,7 @@ package net.momirealms.customfishing.api.mechanic.fishing; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingLootSpawnEvent; import net.momirealms.customfishing.api.event.FishingResultEvent; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; @@ -44,13 +45,14 @@ import net.momirealms.customfishing.common.util.TriConsumer; import net.momirealms.customfishing.common.util.TriFunction; import net.momirealms.sparrow.heart.SparrowHeart; import net.momirealms.sparrow.heart.feature.inventory.HandSlot; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.Statistic; -import org.bukkit.entity.FishHook; -import org.bukkit.entity.Item; -import org.bukkit.entity.Player; +import org.bukkit.entity.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -218,7 +220,7 @@ public class CustomFishingHook { } } - private void end() { + public void end() { plugin.getFishingManager().destroy(context.getHolder().getUniqueId()); } @@ -242,18 +244,6 @@ public class CustomFishingHook { return gamingPlayer != null && gamingPlayer.isValid(); } - public void handleGameResult() { - if (gamingPlayer == null || !gamingPlayer.isValid()) { - throw new RuntimeException("You can't call this method if the player is not playing the game"); - } - if (gamingPlayer.isSuccessful()) { - handleSuccessfulFishing(); - } else { - handleFailedFishing(); - } - end(); - } - public void cancelCurrentGame() { if (gamingPlayer == null || !gamingPlayer.isValid()) { throw new RuntimeException("You can't call this method if the player is not playing the game"); @@ -270,11 +260,13 @@ public class CustomFishingHook { return; Game nextGame = plugin.getGameManager().getNextGame(tempFinalEffect, context); if (nextGame != null) { + plugin.debug("Next game: " + nextGame.id()); gamingPlayer = nextGame.start(this, tempFinalEffect); if (this.hookMechanic != null) { this.hookMechanic.freeze(); } } else { + plugin.debug("Next game: " + "`null`"); handleSuccessfulFishing(); end(); } @@ -302,16 +294,16 @@ public class CustomFishingHook { } public void onEscape() { - plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.getTypeByID(nextLoot.id()), ActionTrigger.ESCAPE); + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.ESCAPE); gears.trigger(ActionTrigger.ESCAPE, context); } public void onLure() { - plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.getTypeByID(nextLoot.id()), ActionTrigger.LURE); + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.LURE); gears.trigger(ActionTrigger.LURE, context); } - private void handleFailedFishing() { + public void handleFailedFishing() { // update the hook location context.arg(ContextKeys.HOOK_LOCATION, hook.getLocation()); @@ -320,10 +312,10 @@ public class CustomFishingHook { context.arg(ContextKeys.HOOK_Z, hook.getLocation().getBlockZ()); gears.trigger(ActionTrigger.FAILURE, context); - plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.getTypeByID(nextLoot.id()), ActionTrigger.FAILURE); + plugin.getEventManager().trigger(context, nextLoot.id(), MechanicType.LOOT, ActionTrigger.FAILURE); } - private void handleSuccessfulFishing() { + public void handleSuccessfulFishing() { // update the hook location context.arg(ContextKeys.HOOK_LOCATION, hook.getLocation()); @@ -350,6 +342,8 @@ public class CustomFishingHook { return; } + gears.trigger(ActionTrigger.SUCCESS, context); + switch (lootType) { case ITEM -> { for (int i = 0; i < amount; i++) { @@ -364,21 +358,41 @@ public class CustomFishingHook { context.arg(ContextKeys.NICK, ""); } } + + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, item); + Bukkit.getPluginManager().callEvent(spawnEvent); + if (item != null && !spawnEvent.summonEntity()) + item.remove(); + if (spawnEvent.skipActions()) + return; + if (item != null && item.isValid() && nextLoot.preventGrabbing()) { + item.getPersistentDataContainer().set(Objects.requireNonNull(NamespacedKey.fromString("owner", plugin.getBoostrap())), PersistentDataType.STRING, context.getHolder().getName()); + } doSuccessActions(); }, (long) ConfigManager.multipleLootSpawnDelay() * i, hook.getLocation()); } } case BLOCK -> { - plugin.getBlockManager().summonBlockLoot(context); + FallingBlock fallingBlock = plugin.getBlockManager().summonBlockLoot(context); + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, fallingBlock); + Bukkit.getPluginManager().callEvent(spawnEvent); + if (!spawnEvent.summonEntity()) + fallingBlock.remove(); + if (spawnEvent.skipActions()) + return; doSuccessActions(); } case ENTITY -> { - plugin.getEntityManager().summonEntityLoot(context); + Entity entity = plugin.getEntityManager().summonEntityLoot(context); + FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(context, hook.getLocation(), nextLoot, entity); + Bukkit.getPluginManager().callEvent(spawnEvent); + if (!spawnEvent.summonEntity()) + entity.remove(); + if (spawnEvent.skipActions()) + return; doSuccessActions(); } } - - gears.trigger(ActionTrigger.SUCCESS, context); } private void doSuccessActions() { @@ -416,13 +430,15 @@ public class CustomFishingHook { String id = context.arg(ContextKeys.ID); Player player = context.getHolder(); MechanicType type = MechanicType.getTypeByID(id); - plugin.getEventManager().trigger(context, id, type, ActionTrigger.SUCCESS); - player.setStatistic(Statistic.FISH_CAUGHT, player.getStatistic(Statistic.FISH_CAUGHT) + 1); + if (!nextLoot.disableStats()) { plugin.getStorageManager().getOnlineUser(player.getUniqueId()).ifPresent( userData -> { userData.statistics().addAmount(id, 1); Optional.ofNullable(context.arg(ContextKeys.SIZE)).ifPresent(size -> { + float max = Math.max(0, userData.statistics().getMaxSize(id)); + context.arg(ContextKeys.RECORD, max); + context.arg(ContextKeys.RECORD_FORMATTED, String.format("%.2f", max)); if (userData.statistics().updateSize(id, size)) { plugin.getEventManager().trigger(context, id, type, ActionTrigger.NEW_SIZE_RECORD); } @@ -430,5 +446,8 @@ public class CustomFishingHook { } ); } + + plugin.getEventManager().trigger(context, id, type, ActionTrigger.SUCCESS); + player.setStatistic(Statistic.FISH_CAUGHT, player.getStatistic(Statistic.FISH_CAUGHT) + 1); } } 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 910cb116..de4b258d 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 @@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit; public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { -// private final FishingManager manager; protected long deadline; protected boolean success; protected SchedulerTask task; @@ -130,8 +129,16 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { protected abstract void tick(); protected void endGame() { - hook.handleGameResult(); - valid = false; + destroy(); + boolean success = isSuccessful(); + BukkitCustomFishingPlugin.getInstance().getScheduler().sync().run(() -> { + if (success) { + hook.handleSuccessfulFishing(); + } else { + hook.handleFailedFishing(); + } + hook.end(); + }, hook.getHookEntity().getLocation()); } protected void setGameResult(boolean success) { diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java index b32fd6ee..3c54d6cd 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/Loot.java @@ -63,6 +63,8 @@ public interface Loot { */ boolean showInFinder(); + boolean preventGrabbing(); + /** * Get the unique identifier for this loot. * @@ -151,6 +153,8 @@ public interface Loot { */ Builder disableGame(boolean disableGame); + Builder preventGrabbing(boolean preventGrabbing); + /** * Specify whether statistics recording is disabled for this loot. * diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java index 6c8a6cbf..093d75f2 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/loot/LootImpl.java @@ -34,6 +34,7 @@ public class LootImpl implements Loot { private final boolean disableGame; private final boolean disableStatistics; private final boolean showInFinder; + private final boolean preventGrabbing; private final String id; private final String nick; private final StatisticsKeys statisticsKeys; @@ -41,7 +42,7 @@ public class LootImpl implements Loot { private final String[] groups; private final LootBaseEffect lootBaseEffect; - public LootImpl(LootType type, boolean instantGame, boolean disableGame, boolean disableStatistics, boolean showInFinder, String id, String nick, StatisticsKeys statisticsKeys, MathValue score, String[] groups, LootBaseEffect lootBaseEffect) { + public LootImpl(LootType type, boolean instantGame, boolean disableGame, boolean disableStatistics, boolean showInFinder, boolean preventGrabbing, String id, String nick, StatisticsKeys statisticsKeys, MathValue score, String[] groups, LootBaseEffect lootBaseEffect) { this.type = type; this.instantGame = instantGame; this.disableGame = disableGame; @@ -53,6 +54,7 @@ public class LootImpl implements Loot { this.score = score; this.groups = groups; this.lootBaseEffect = lootBaseEffect; + this.preventGrabbing = preventGrabbing; } @Override @@ -86,6 +88,11 @@ public class LootImpl implements Loot { return showInFinder; } + @Override + public boolean preventGrabbing() { + return preventGrabbing; + } + @Override public MathValue score() { return score; @@ -118,6 +125,7 @@ public class LootImpl implements Loot { private boolean disableGame = Loot.DefaultProperties.DEFAULT_DISABLE_GAME; private boolean disableStatistics = Loot.DefaultProperties.DEFAULT_DISABLE_STATS; private boolean showInFinder = Loot.DefaultProperties.DEFAULT_SHOW_IN_FINDER; + private boolean preventGrabbing = false; private String id = null; private String nick = "UNDEFINED"; private StatisticsKeys statisticsKeys = null; @@ -141,6 +149,11 @@ public class LootImpl implements Loot { return this; } @Override + public Builder preventGrabbing(boolean preventGrabbing) { + this.preventGrabbing = preventGrabbing; + return this; + } + @Override public Builder disableStatistics(boolean disableStatistics) { this.disableStatistics = disableStatistics; return this; @@ -188,6 +201,7 @@ public class LootImpl implements Loot { disableGame, disableStatistics, showInFinder, + preventGrabbing, requireNonNull(id), Optional.ofNullable(nick).orElse(id), Optional.ofNullable(statisticsKeys).orElse(new StatisticsKeys(id, id)), diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java index 5dcbc2ec..8af8e035 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/statistic/FishingStatisticsImpl.java @@ -85,6 +85,7 @@ public class FishingStatisticsImpl implements FishingStatistics { float previous = sizeMap.getOrDefault(id, 0f); if (previous >= newSize) return false; sizeMap.put(id, newSize); + System.out.println(sizeMap); return true; } diff --git a/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java index a310aae0..f59b3d56 100644 --- a/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java +++ b/common/src/main/java/net/momirealms/customfishing/common/plugin/scheduler/AbstractJavaScheduler.java @@ -48,7 +48,7 @@ public abstract class AbstractJavaScheduler implements SchedulerAdapter { public AbstractJavaScheduler(CustomFishingPlugin plugin) { this.plugin = plugin; - this.scheduler = new ScheduledThreadPoolExecutor(1, r -> { + this.scheduler = new ScheduledThreadPoolExecutor(4, r -> { Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setName("customfishing-scheduler"); return thread; diff --git a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java index cc85aca7..b3e6c801 100644 --- a/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java +++ b/compatibility/src/main/java/net/momirealms/customfishing/bukkit/integration/papi/StatisticsPapi.java @@ -84,7 +84,8 @@ public class StatisticsPapi extends PlaceholderExpansion { return String.valueOf(statistics.getAmount(split[1])); } case "size-record" -> { - return String.format("%.2f", statistics.getMaxSize(split[1])); + float size = statistics.getMaxSize(split[1]); + return String.format("%.2f", size < 0 ? 0 : size); } case "category" -> { if (split.length == 1) return "Invalid format"; diff --git a/core/build.gradle.kts b/core/build.gradle.kts index fc3da64a..727b9227 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}") // nms util implementation("com.github.Xiao-MoMi:Sparrow-Heart:${rootProject.properties["sparrow_heart_version"]}") - //implementation(files("libs/Sparrow-Heart-${rootProject.properties["sparrow_heart_version"]}.jar")) +// implementation(files("libs/Sparrow-Heart-${rootProject.properties["sparrow_heart_version"]}.jar")) // bstats compileOnly("org.bstats:bstats-bukkit:${rootProject.properties["bstats_version"]}") // config diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java index d5b014a4..4eebb4d1 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/bag/BukkitBagManager.java @@ -19,17 +19,26 @@ package net.momirealms.customfishing.bukkit.bag; import dev.dejvokep.boostedyaml.block.implementation.Section; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; +import net.momirealms.customfishing.api.event.FishingBagPreCollectEvent; +import net.momirealms.customfishing.api.event.FishingLootSpawnEvent; import net.momirealms.customfishing.api.mechanic.action.Action; +import net.momirealms.customfishing.api.mechanic.action.ActionManager; import net.momirealms.customfishing.api.mechanic.bag.BagManager; import net.momirealms.customfishing.api.mechanic.bag.FishingBagHolder; +import net.momirealms.customfishing.api.mechanic.context.Context; import net.momirealms.customfishing.api.mechanic.item.MechanicType; import net.momirealms.customfishing.api.mechanic.requirement.Requirement; +import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager; import net.momirealms.customfishing.api.storage.user.UserData; +import net.momirealms.customfishing.api.util.EventUtils; import net.momirealms.customfishing.bukkit.config.BukkitConfigManager; +import net.momirealms.customfishing.bukkit.util.ItemStackUtils; +import net.momirealms.customfishing.bukkit.util.PlayerUtils; import net.momirealms.customfishing.common.helper.AdventureHelper; import net.momirealms.sparrow.heart.SparrowHeart; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; @@ -51,6 +60,7 @@ public class BukkitBagManager implements BagManager, Listener { private Action[] collectLootActions; private Action[] bagFullActions; private boolean bagStoreLoots; + private boolean enable; private String bagTitle; private List bagWhiteListItems = new ArrayList<>(); private Requirement[] collectRequirements; @@ -77,9 +87,55 @@ public class BukkitBagManager implements BagManager, Listener { this.plugin.getStorageManager().getDataSource().updateManyPlayersData(tempEditMap.values(), true); } + @EventHandler + public void onLootSpawn(FishingLootSpawnEvent event) { + if (!enable || !bagStoreLoots) { + return; + } + if (!event.summonEntity()) { + return; + } + if (!(event.getEntity() instanceof Item itemEntity)) { + return; + } + + Player player = event.getPlayer(); + Context context = event.getContext(); + if (!RequirementManager.isSatisfied(context, collectRequirements)) { + return; + } + + Optional onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); + if (onlineUser.isEmpty()) { + return; + } + UserData userData = onlineUser.get(); + Inventory inventory = userData.holder().getInventory(); + ItemStack item = itemEntity.getItemStack(); + FishingBagPreCollectEvent preCollectEvent = new FishingBagPreCollectEvent(player, item, inventory); + if (EventUtils.fireAndCheckCancel(preCollectEvent)) { + return; + } + + int cannotPut = PlayerUtils.putItemsToInventory(inventory, item, item.getAmount()); + // some are put into bag + if (cannotPut != item.getAmount()) { + ActionManager.trigger(context, collectLootActions); + } + // all are put + if (cannotPut == 0) { + event.summonEntity(false); + return; + } + item.setAmount(cannotPut); + itemEntity.setItemStack(item); + ActionManager.trigger(context, bagFullActions); + } + private void loadConfig() { Section config = BukkitConfigManager.getMainConfig().getSection("mechanics.fishing-bag"); + enable = config.getBoolean("enable", true); bagTitle = config.getString("bag-title", ""); bagStoreLoots = config.getBoolean("can-store-loot", false); bagWhiteListItems = config.getStringList("whitelist-items").stream().map(it -> Material.valueOf(it.toUpperCase(Locale.ENGLISH))).toList(); @@ -91,21 +147,25 @@ public class BukkitBagManager implements BagManager, Listener { @Override public CompletableFuture openBag(Player viewer, UUID owner) { CompletableFuture future = new CompletableFuture<>(); - Optional onlineUser = plugin.getStorageManager().getOnlineUser(owner); - onlineUser.ifPresentOrElse(data -> { - viewer.openInventory(data.holder().getInventory()); - SparrowHeart.getInstance().updateInventoryTitle(viewer, AdventureHelper.componentToJson(AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(Bukkit.getOfflinePlayer(owner), bagTitle, Map.of("{uuid}", owner.toString(), "{player}", data.name()))))); - future.complete(true); - }, () -> plugin.getStorageManager().getOfflineUserData(owner, true).thenAccept(result -> result.ifPresentOrElse(data -> { - if (data.isLocked()) { - future.completeExceptionally(new RuntimeException("Data is locked")); - return; - } - this.tempEditMap.put(viewer.getUniqueId(), data); - viewer.openInventory(data.holder().getInventory()); - SparrowHeart.getInstance().updateInventoryTitle(viewer, AdventureHelper.componentToJson(AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(Bukkit.getOfflinePlayer(owner), bagTitle, Map.of("{uuid}", owner.toString(), "{player}", data.name()))))); - future.complete(true); - }, () -> future.complete(false)))); + if (enable) { + Optional onlineUser = plugin.getStorageManager().getOnlineUser(owner); + onlineUser.ifPresentOrElse(data -> { + viewer.openInventory(data.holder().getInventory()); + SparrowHeart.getInstance().updateInventoryTitle(viewer, AdventureHelper.componentToJson(AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(Bukkit.getOfflinePlayer(owner), bagTitle, Map.of("{uuid}", owner.toString(), "{player}", data.name()))))); + future.complete(true); + }, () -> plugin.getStorageManager().getOfflineUserData(owner, true).thenAccept(result -> result.ifPresentOrElse(data -> { + if (data.isLocked()) { + future.completeExceptionally(new RuntimeException("Data is locked")); + return; + } + this.tempEditMap.put(viewer.getUniqueId(), data); + viewer.openInventory(data.holder().getInventory()); + SparrowHeart.getInstance().updateInventoryTitle(viewer, AdventureHelper.componentToJson(AdventureHelper.miniMessage(plugin.getPlaceholderManager().parse(Bukkit.getOfflinePlayer(owner), bagTitle, Map.of("{uuid}", owner.toString(), "{player}", data.name()))))); + future.complete(true); + }, () -> future.complete(false)))); + } else { + future.complete(false); + } return future; } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java index 149017ac..42f0397f 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/block/BukkitBlockManager.java @@ -215,7 +215,7 @@ public class BukkitBlockManager implements BlockManager, Listener { } @Override - @Nullable + @NotNull public FallingBlock summonBlockLoot(@NotNull Context context) { String id = context.arg(ContextKeys.ID); BlockConfig config = requireNonNull(blocks.get(id), "Block " + id + " not found"); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java index 8cddfc27..4f58a36b 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/config/BukkitConfigManager.java @@ -779,6 +779,10 @@ public class BukkitConfigManager extends ConfigManager { } private void registerBuiltInLootParser() { + this.registerLootParser(object -> { + boolean value = (boolean) object; + return builder -> builder.preventGrabbing(value); + }, "prevent-grabbing"); this.registerLootParser(object -> { String string = (String) object; return builder -> builder.nick(string); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java index 26179261..ca4e2e21 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/entity/BukkitEntityManager.java @@ -100,7 +100,7 @@ public class BukkitEntityManager implements EntityManager { return entityProviders.remove(id) != null; } - @Nullable + @NotNull @Override public Entity summonEntityLoot(Context context) { String id = context.arg(ContextKeys.ID); diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java index 32318ec6..bcd82174 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/fishing/BukkitFishingManager.java @@ -52,6 +52,7 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class BukkitFishingManager implements FishingManager, Listener { @@ -257,12 +258,12 @@ public class BukkitFishingManager implements FishingManager, Listener { private void onReelIn(PlayerFishEvent event) { Player player = event.getPlayer(); getFishHook(player).ifPresent(hook -> { + event.setCancelled(true); Optional gamingPlayer = hook.getGamingPlayer(); if (gamingPlayer.isPresent()) { ((AbstractGamingPlayer) gamingPlayer.get()).internalRightClick(); return; } - event.setCancelled(true); hook.onReelIn(); }); } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java index 190b9f2e..16774b1a 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/game/BukkitGameManager.java @@ -832,8 +832,8 @@ public class BukkitGameManager implements GameManager { @Override public void arrangeTask() { - var period = ((double) 10*(200-settings.difficulty()))/((double) (1+4*settings.difficulty())); - this.task = plugin.getScheduler().asyncRepeating(this, 50, (long) period, TimeUnit.MILLISECONDS); + var period = Math.min(200, ((double) 10*(200-settings.difficulty()))/((double) (1+4*settings.difficulty()))); + this.task = plugin.getScheduler().asyncRepeating(this, 10, (long) 10, TimeUnit.MILLISECONDS); } @Override @@ -847,6 +847,7 @@ public class BukkitGameManager implements GameManager { face = !face; progress = -progress; } + if (!isValid()) return; showUI(); } @@ -862,7 +863,7 @@ public class BukkitGameManager implements GameManager { + OffsetUtils.getOffsetChars(pointerOffset + progress) + AdventureHelper.surroundWithMiniMessageFont(pointerImage, font) + OffsetUtils.getOffsetChars(totalWidth - progress - pointerWidth); - SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(sendTitle.render(hook.getContext())), AdventureHelper.miniMessageToJson(bar), 0, 20, 0); + SparrowHeart.getInstance().sendTitle(getPlayer(), AdventureHelper.miniMessageToJson(sendTitle.render(hook.getContext())), AdventureHelper.miniMessageToJson(bar), 0, 10, 0); } }; } diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java index 750a723f..3f445713 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/item/BukkitItemManager.java @@ -47,6 +47,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.*; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.event.player.PlayerAttemptPickupItemEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; @@ -196,12 +197,6 @@ public class BukkitItemManager implements ItemManager, Listener { Vector vector = new Vector(d0 * 0.1D, d1 * 0.1D + Math.sqrt(Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2)) * 0.08D, d2 * 0.1D); org.bukkit.entity.Item itemEntity = hookLocation.getWorld().dropItem(hookLocation, itemStack); - FishingLootSpawnEvent spawnEvent = new FishingLootSpawnEvent(player, hookLocation, plugin.getLootManager().getLoot(id).orElseThrow(), itemEntity); - Bukkit.getPluginManager().callEvent(spawnEvent); - if (spawnEvent.isCancelled()) { - itemEntity.remove(); - return itemEntity; - } itemEntity.setInvulnerable(true); // prevent from being killed by lava @@ -340,6 +335,16 @@ public class BukkitItemManager implements ItemManager, Listener { handleExplosion(event.blockList()); } + @EventHandler (ignoreCancelled = true) + public void onPickUpItem(PlayerAttemptPickupItemEvent event) { + String owner = event.getItem().getPersistentDataContainer().get(requireNonNull(NamespacedKey.fromString("owner", plugin.getBoostrap())), PersistentDataType.STRING); + if (owner != null) { + if (!owner.equals(event.getPlayer().getName())) { + event.setCancelled(true); + } + } + } + private void handleExplosion(List blocks) { ArrayList blockToRemove = new ArrayList<>(); for (Block block : blocks) { diff --git a/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java b/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java index 8c55a185..248bc47a 100644 --- a/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java +++ b/core/src/main/java/net/momirealms/customfishing/bukkit/util/PlayerUtils.java @@ -21,6 +21,7 @@ import net.momirealms.customfishing.common.util.RandomUtils; import org.bukkit.Location; import org.bukkit.entity.Item; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.ItemMeta; @@ -54,6 +55,47 @@ public class PlayerUtils { } } + public static int putItemsToInventory(Inventory inventory, ItemStack itemStack, int amount) { + 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; + } + public static int giveItem(Player player, ItemStack itemStack, int amount) { PlayerInventory inventory = player.getInventory(); ItemMeta meta = itemStack.getItemMeta(); diff --git a/core/src/main/resources/commands.yml b/core/src/main/resources/commands.yml index 68f3d3e2..f95a8dfb 100644 --- a/core/src/main/resources/commands.yml +++ b/core/src/main/resources/commands.yml @@ -6,6 +6,8 @@ config-version: "${config_version}" # # For safety reasons, editing this file requires a restart to apply # + +# A command to reload the plugin reload: enable: true permission: customfishing.command.reload @@ -13,18 +15,21 @@ reload: - /customfishing reload - /cfishing reload +# A command designed for players to sell fish sellfish: enable: true permission: customfishing.sellfish usage: - /sellfish +# A command designed for players to open the fishing bag fishingbag: enable: true permission: fishingbag.user usage: - /fishingbag +# A command to get items get_item: enable: true permission: customfishing.command.getitem @@ -32,6 +37,7 @@ get_item: - /customfishing items get - /cfishing items get +# A command to give items give_item: enable: true permission: customfishing.command.giveitem @@ -39,6 +45,7 @@ give_item: - /customfishing items give - /cfishing items give +# A command to stop the competition stop_competition: enable: true permission: customfishing.command.competition @@ -46,6 +53,7 @@ stop_competition: - /customfishing competition stop - /cfishing competition stop +# A command to end the competition end_competition: enable: true permission: customfishing.command.competition @@ -53,6 +61,7 @@ end_competition: - /customfishing competition end - /cfishing competition end +# A command to start competitions start_competition: enable: true permission: customfishing.command.competition @@ -60,6 +69,7 @@ start_competition: - /customfishing competition start - /cfishing competition start +# A command to open market for players open_market: enable: true permission: customfishing.command.open.market @@ -67,6 +77,7 @@ open_market: - /customfishing open market - /cfishing open market +# A command to open bag for players open_bag: enable: true permission: customfishing.command.open.bag @@ -74,6 +85,7 @@ open_bag: - /customfishing open bag - /cfishing open bag +# A command to edit bag contents edit_online_bag: enable: true permission: customfishing.command.edit.bag @@ -81,16 +93,10 @@ edit_online_bag: - /customfishing fishingbag edit-online - /cfishing fishingbag edit-online +# A command to edit bag contents edit_offline_bag: enable: true permission: customfishing.command.edit.bag usage: - /customfishing fishingbag edit-offline - - /cfishing fishingbag edit-offline - -browser: - enable: true - permission: customfishing.command.browser - usage: - - /customfishing browser - - /cfishing browser \ No newline at end of file + - /cfishing fishingbag edit-offline \ No newline at end of file diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index d92890be..29035fb4 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -57,7 +57,7 @@ mechanics: actions: actionbar_action: type: actionbar - value: '<#FFD700>[New Record] <#FFFFF0>You caught a(n) {nick} which is <#FFA500>{size}cm long!' + value: '<#FFD700>[New Record] <#FFFFF0>You caught a(n) {nick} which is <#FFA500>{size_formatted}cm long!' sound_action: type: sound value: @@ -87,7 +87,7 @@ mechanics: actions: actionbar_action: type: actionbar - value: 'You caught a(n) {nick} which is <#F5F5F5>{size}cm long! <#C0C0C0>(Best record: {record}cm)' + value: 'You caught a(n) {nick} which is <#F5F5F5>{size_formatted}cm long! <#C0C0C0>(Best record: {record_formatted}cm)' title_action: type: random-title value: @@ -407,7 +407,6 @@ other-settings: lock-data: true # Requires PlaceholderAPI to work placeholder-register: - '{record}': '%fishingstats_size-record_{loot}%' # Requires server expansion '{date}': '%server_time_yyyy-MM-dd-HH:mm:ss%' # Requires player expansion diff --git a/gradle.properties b/gradle.properties index b5a42c67..bc50d1e2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ h2_driver_version=2.2.224 sqlite_driver_version=3.46.0.0 adventure_bundle_version=4.17.0 adventure_platform_version=4.3.3 -sparrow_heart_version=0.23 +sparrow_heart_version=0.27 cloud_core_version=2.0.0-rc.2 cloud_services_version=2.0.0-rc.2 cloud_brigadier_version=2.0.0-beta.8