From cf948b9904fef42d9bece4a6e78347df73b079d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=BA=E9=96=93=E5=B7=A5=E4=BD=9C?= Date: Thu, 5 Sep 2024 16:04:21 +0800 Subject: [PATCH] move all built in action impl out of AbstractActionManager --- .../api/action/AbstractActionManager.java | 717 +----------------- .../action/builtin/AbstractBuiltInAction.java | 42 + .../action/builtin/ActionActionbarNearby.java | 73 ++ .../api/action/builtin/ActionBreak.java | 98 +++ .../api/action/builtin/ActionBroadcast.java | 62 ++ .../api/action/builtin/ActionBundle.java | 59 ++ .../api/action/builtin/ActionCommand.java | 60 ++ .../action/builtin/ActionCommandNearby.java | 77 ++ .../api/action/builtin/ActionConditional.java | 61 ++ .../api/action/builtin/ActionDelay.java | 86 +++ .../api/action/builtin/ActionDropItem.java | 139 ++++ .../action/builtin/ActionDropItemLegacy.java | 66 ++ .../api/action/builtin/ActionFakeItem.java | 159 ++++ .../api/action/builtin/ActionHologram.java | 139 ++++ .../action/builtin/ActionMessageNearby.java | 83 ++ .../api/action/builtin/ActionParticle.java | 152 ++++ .../api/action/builtin/ActionPlant.java | 129 ++++ .../api/action/builtin/ActionPriority.java | 73 ++ .../action/builtin/ActionQualityCrops.java | 148 ++++ .../action/builtin/ActionRandomCommand.java | 61 ++ .../api/action/builtin/ActionTimer.java | 103 +++ .../api/action/builtin/ActionTitleNearby.java | 94 +++ 22 files changed, 1985 insertions(+), 696 deletions(-) create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/AbstractBuiltInAction.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionActionbarNearby.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBreak.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBroadcast.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBundle.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommand.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommandNearby.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionConditional.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDelay.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItemLegacy.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionFakeItem.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionHologram.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionMessageNearby.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionParticle.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPlant.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPriority.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionQualityCrops.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionRandomCommand.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTimer.java create mode 100644 api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTitleNearby.java diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java index b09d1a6..e8cae29 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/action/AbstractActionManager.java @@ -18,47 +18,9 @@ package net.momirealms.customcrops.api.action; import dev.dejvokep.boostedyaml.block.implementation.Section; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.Component; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.api.context.Context; -import net.momirealms.customcrops.api.context.ContextKeys; -import net.momirealms.customcrops.api.core.*; -import net.momirealms.customcrops.api.core.block.BreakReason; -import net.momirealms.customcrops.api.core.block.CropBlock; -import net.momirealms.customcrops.api.core.block.PotBlock; -import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; -import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; -import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; -import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; -import net.momirealms.customcrops.api.core.world.CustomCropsChunk; -import net.momirealms.customcrops.api.core.world.CustomCropsWorld; -import net.momirealms.customcrops.api.core.world.Pos3; -import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import net.momirealms.customcrops.api.misc.HologramManager; -import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; -import net.momirealms.customcrops.api.misc.value.MathValue; -import net.momirealms.customcrops.api.misc.value.TextValue; -import net.momirealms.customcrops.api.requirement.Requirement; -import net.momirealms.customcrops.api.util.*; -import net.momirealms.customcrops.common.helper.AdventureHelper; -import net.momirealms.customcrops.common.helper.VersionHelper; -import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import net.momirealms.customcrops.api.action.builtin.*; import net.momirealms.customcrops.common.util.ClassUtils; -import net.momirealms.customcrops.common.util.ListUtils; -import net.momirealms.customcrops.common.util.Pair; -import net.momirealms.customcrops.common.util.RandomUtils; -import net.momirealms.sparrow.heart.SparrowHeart; -import net.momirealms.sparrow.heart.feature.entity.FakeEntity; -import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; -import net.momirealms.sparrow.heart.feature.entity.display.FakeItemDisplay; -import org.bukkit.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -66,10 +28,6 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -import static java.util.Objects.requireNonNull; public abstract class AbstractActionManager implements ActionManager { @@ -194,55 +152,13 @@ public abstract class AbstractActionManager implements ActionManager { } protected void registerBroadcastAction() { - registerAction((args, chance) -> { - List messages = ListUtils.toList(args); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - OfflinePlayer offlinePlayer = null; - if (context.holder() instanceof Player player) { - offlinePlayer = player; - } - List replaced = plugin.getPlaceholderManager().parse(offlinePlayer, messages, context.placeholderMap()); - for (Player player : Bukkit.getOnlinePlayers()) { - Audience audience = plugin.getSenderFactory().getAudience(player); - for (String text : replaced) { - audience.sendMessage(AdventureHelper.miniMessage(text)); - } - } - }; - }, "broadcast"); + registerAction((args, chance) -> new ActionBroadcast<>(plugin, args, chance), "broadcast"); } protected void registerNearbyMessage() { registerAction((args, chance) -> { if (args instanceof Section section) { - List messages = ListUtils.toList(section.get("message")); - MathValue range = MathValue.auto(section.get("range")); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - double realRange = range.evaluate(context); - OfflinePlayer owner = null; - if (context.holder() instanceof Player player) { - owner = player; - } - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { - context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); - List replaced = BukkitPlaceholderManager.getInstance().parse( - owner, - messages, - context.placeholderMap() - ); - Audience audience = plugin.getSenderFactory().getAudience(player); - for (String text : replaced) { - audience.sendMessage(AdventureHelper.miniMessage(text)); - } - } - } - }; + return new ActionMessageNearby<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at message-nearby action which should be Section"); return Action.empty(); @@ -253,26 +169,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerNearbyActionBar() { registerAction((args, chance) -> { if (args instanceof Section section) { - String actionbar = section.getString("actionbar"); - MathValue range = MathValue.auto(section.get("range")); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - OfflinePlayer owner = null; - if (context.holder() instanceof Player player) { - owner = player; - } - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - double realRange = range.evaluate(context); - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { - context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); - String replaced = plugin.getPlaceholderManager().parse(owner, actionbar, context.placeholderMap()); - Audience audience = plugin.getSenderFactory().getAudience(player); - audience.sendActionBar(AdventureHelper.miniMessage(replaced)); - } - } - }; + return new ActionActionbarNearby<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at actionbar-nearby action which should be Section"); return Action.empty(); @@ -281,63 +178,11 @@ public abstract class AbstractActionManager implements ActionManager { } protected void registerCommandAction() { - registerAction((args, chance) -> { - List commands = ListUtils.toList(args); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - OfflinePlayer owner = null; - if (context.holder() instanceof Player player) { - owner = player; - } - List replaced = BukkitPlaceholderManager.getInstance().parse(owner, commands, context.placeholderMap()); - plugin.getScheduler().sync().run(() -> { - for (String text : replaced) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); - } - }, null); - }; - }, "command"); - registerAction((args, chance) -> { - List commands = ListUtils.toList(args); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - OfflinePlayer owner = null; - if (context.holder() instanceof Player player) { - owner = player; - } - String random = commands.get(ThreadLocalRandom.current().nextInt(commands.size())); - random = BukkitPlaceholderManager.getInstance().parse(owner, random, context.placeholderMap()); - String finalRandom = random; - plugin.getScheduler().sync().run(() -> { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); - }, null); - }; - }, "random-command"); + registerAction((args, chance) -> new ActionCommand<>(plugin, args, chance), "command"); + registerAction((args, chance) -> new ActionRandomCommand<>(plugin, args, chance), "random-command"); registerAction((args, chance) -> { if (args instanceof Section section) { - List cmd = ListUtils.toList(section.get("command")); - MathValue range = MathValue.auto(section.get("range")); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - OfflinePlayer owner = null; - if (context.holder() instanceof Player player) { - owner = player; - } - double realRange = range.evaluate(context); - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { - context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); - List replaced = BukkitPlaceholderManager.getInstance().parse(owner, cmd, context.placeholderMap()); - for (String text : replaced) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); - } - } - } - }; + return new ActionCommandNearby<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at command-nearby action which should be Section"); return Action.empty(); @@ -346,109 +191,12 @@ public abstract class AbstractActionManager implements ActionManager { } protected void registerBundleAction(Class tClass) { - registerAction((args, chance) -> { - List> actions = new ArrayList<>(); - if (args instanceof Section section) { - for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { - if (entry.getValue() instanceof Section innerSection) { - actions.add(parseAction(innerSection)); - } - } - } - return context -> { - if (Math.random() > chance) return; - for (Action action : actions) { - action.trigger(context); - } - }; - }, "chain"); - registerAction((args, chance) -> { - List> actions = new ArrayList<>(); - int delay; - boolean async; - if (args instanceof Section section) { - delay = section.getInt("delay", 1); - async = section.getBoolean("async", false); - Section actionSection = section.getSection("actions"); - if (actionSection != null) - for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) - if (entry.getValue() instanceof Section innerSection) - actions.add(parseAction(innerSection)); - } else { - delay = 1; - async = false; - } - return context -> { - if (Math.random() > chance) return; - Location location = context.arg(ContextKeys.LOCATION); - if (async) { - plugin.getScheduler().asyncLater(() -> { - for (Action action : actions) - action.trigger(context); - }, delay * 50L, TimeUnit.MILLISECONDS); - } else { - plugin.getScheduler().sync().runLater(() -> { - for (Action action : actions) - action.trigger(context); - }, delay, location); - } - }; - }, "delay"); - registerAction((args, chance) -> { - List> actions = new ArrayList<>(); - int delay, duration, period; - boolean async; - if (args instanceof Section section) { - delay = section.getInt("delay", 2); - duration = section.getInt("duration", 20); - period = section.getInt("period", 2); - async = section.getBoolean("async", false); - Section actionSection = section.getSection("actions"); - if (actionSection != null) - for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) - if (entry.getValue() instanceof Section innerSection) - actions.add(parseAction(innerSection)); - } else { - delay = 1; - period = 1; - async = false; - duration = 20; - } - return context -> { - if (Math.random() > chance) return; - Location location = context.arg(ContextKeys.LOCATION); - SchedulerTask task; - if (async) { - task = plugin.getScheduler().asyncRepeating(() -> { - for (Action action : actions) { - action.trigger(context); - } - }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); - } else { - task = plugin.getScheduler().sync().runRepeating(() -> { - for (Action action : actions) { - action.trigger(context); - } - }, delay, period, location); - } - plugin.getScheduler().asyncLater(task::cancel, duration * 50L, TimeUnit.MILLISECONDS); - }; - }, "timer"); + registerAction((args, chance) -> new ActionBundle<>(plugin, this, args, chance), "chain"); + registerAction((args, chance) -> new ActionDelay<>(plugin, this, args, chance), "delay"); + registerAction((args, chance) -> new ActionTimer<>(plugin, this, args, chance), "timer"); registerAction((args, chance) -> { if (args instanceof Section section) { - Action[] actions = parseActions(section.getSection("actions")); - Requirement[] requirements = plugin.getRequirementManager(tClass).parseRequirements(section.getSection("conditions"), true); - return condition -> { - if (Math.random() > chance) return; - for (Requirement requirement : requirements) { - if (!requirement.isSatisfied(condition)) { - return; - } - } - for (Action action : actions) { - action.trigger(condition); - } - }; + return new ActionConditional<>(plugin, this, tClass, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at conditional action which is expected to be `Section`"); return Action.empty(); @@ -456,31 +204,7 @@ public abstract class AbstractActionManager implements ActionManager { }, "conditional"); registerAction((args, chance) -> { if (args instanceof Section section) { - List[], Action[]>> conditionActionPairList = new ArrayList<>(); - for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { - if (entry.getValue() instanceof Section inner) { - Action[] actions = parseActions(inner.getSection("actions")); - Requirement[] requirements = plugin.getRequirementManager(tClass).parseRequirements(inner.getSection("conditions"), false); - conditionActionPairList.add(Pair.of(requirements, actions)); - } - } - return context -> { - if (Math.random() > chance) return; - outer: - for (Pair[], Action[]> pair : conditionActionPairList) { - if (pair.left() != null) - for (Requirement requirement : pair.left()) { - if (!requirement.isSatisfied(context)) { - continue outer; - } - } - if (pair.right() != null) - for (Action action : pair.right()) { - action.trigger(context); - } - return; - } - }; + return new ActionPriority<>(plugin, this, tClass, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at priority action which is expected to be `Section`"); return Action.empty(); @@ -491,28 +215,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerNearbyTitle() { registerAction((args, chance) -> { if (args instanceof Section section) { - TextValue title = TextValue.auto(section.getString("title")); - TextValue subtitle = TextValue.auto(section.getString("subtitle")); - 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", 0); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= range) { - context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); - Audience audience = plugin.getSenderFactory().getAudience(player); - AdventureHelper.sendTitle(audience, - AdventureHelper.miniMessage(title.render(context)), - AdventureHelper.miniMessage(subtitle.render(context)), - fadeIn, stay, fadeOut - ); - } - } - }; + return new ActionTitleNearby<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title-nearby action which is expected to be `Section`"); return Action.empty(); @@ -523,54 +226,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerParticleAction() { registerAction((args, chance) -> { if (args instanceof Section section) { - Particle particleType = ParticleUtils.getParticle(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); - double x = section.getDouble("x",0.0); - double y = section.getDouble("y",0.0); - double z = section.getDouble("z",0.0); - double offSetX = section.getDouble("offset-x",0.0); - double offSetY = section.getDouble("offset-y",0.0); - double offSetZ = section.getDouble("offset-z",0.0); - int count = section.getInt("count", 1); - double extra = section.getDouble("extra", 0.0); - float scale = section.getDouble("scale", 1d).floatValue(); - - ItemStack itemStack; - if (section.contains("itemStack")) - itemStack = BukkitCustomCropsPlugin.getInstance() - .getItemManager() - .build(null, section.getString("itemStack")); - else - itemStack = null; - - Color color; - if (section.contains("color")) { - String[] rgb = section.getString("color","255,255,255").split(","); - color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); - } else { - color = null; - } - - Color toColor; - if (section.contains("color")) { - String[] rgb = section.getString("to-color","255,255,255").split(","); - toColor = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); - } else { - toColor = null; - } - - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (Math.random() > chance) return; - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - location.getWorld().spawnParticle( - particleType, - location.getX() + x, location.getY() + y, location.getZ() + z, - count, - offSetX, offSetY, offSetZ, - extra, - itemStack != null ? itemStack : (color != null && toColor != null ? new Particle.DustTransition(color, toColor, scale) : (color != null ? new Particle.DustOptions(color, scale) : null)) - ); - }; + return new ActionParticle<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at particle action which is expected to be `Section`"); return Action.empty(); @@ -581,71 +237,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerQualityCropsAction() { registerAction((args, chance) -> { if (args instanceof Section section) { - MathValue min = MathValue.auto(section.get("min")); - MathValue max = MathValue.auto(section.get("max")); - boolean toInv = section.getBoolean("to-inventory", false); - String[] qualityLoots = new String[ConfigManager.defaultQualityRatio().length]; - for (int i = 1; i <= ConfigManager.defaultQualityRatio().length; i++) { - qualityLoots[i-1] = section.getString("items." + i); - if (qualityLoots[i-1] == null) { - plugin.getPluginLogger().warn("items." + i + " should not be null"); - qualityLoots[i-1] = ""; - } - } - return context -> { - if (Math.random() > chance) return; - double[] ratio = ConfigManager.defaultQualityRatio(); - int random = RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); - if (world.isEmpty()) { - return; - } - Pos3 pos3 = Pos3.from(location); - Fertilizer[] fertilizers = null; - Player player = null; - if (context.holder() instanceof Player p) { - player = p; - } - Pos3 potLocation = pos3.add(0, -1, 0); - Optional chunk = world.get().getChunk(potLocation.toChunkPos()); - if (chunk.isPresent()) { - Optional state = chunk.get().getBlockState(potLocation); - if (state.isPresent()) { - if (state.get().type() instanceof PotBlock potBlock) { - fertilizers = potBlock.fertilizers(state.get()); - } - } - } - ArrayList configs = new ArrayList<>(); - if (fertilizers != null) { - for (Fertilizer fertilizer : fertilizers) { - Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); - } - } - for (FertilizerConfig config : configs) { - random = config.processDroppedItemAmount(random); - double[] newRatio = config.overrideQualityRatio(); - if (newRatio != null) { - ratio = newRatio; - } - } - for (int i = 0; i < random; i++) { - double r1 = Math.random(); - for (int j = 0; j < ratio.length; j++) { - if (r1 < ratio[j]) { - ItemStack drop = plugin.getItemManager().build(player, qualityLoots[j]); - if (drop == null || drop.getType() == Material.AIR) return; - if (toInv && player != null) { - PlayerUtils.giveItem(player, drop, 1); - } else { - location.getWorld().dropItemNaturally(location, drop); - } - break; - } - } - } - }; + return new ActionQualityCrops<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at quality-crops action which is expected to be `Section`"); return Action.empty(); @@ -656,58 +248,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerDropItemsAction() { registerAction((args, chance) -> { if (args instanceof Section section) { - boolean ignoreFertilizer = section.getBoolean("ignore-fertilizer", true); - String item = section.getString("item"); - MathValue min = MathValue.auto(section.get("min")); - MathValue max = MathValue.auto(section.get("max")); - boolean toInv = section.getBoolean("to-inventory", false); - return context -> { - if (Math.random() > chance) return; - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); - if (world.isEmpty()) { - return; - } - Player player = null; - if (context.holder() instanceof Player p) { - player = p; - } - ItemStack itemStack = plugin.getItemManager().build(player, item); - if (itemStack != null) { - int random = RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); - if (!ignoreFertilizer) { - Pos3 pos3 = Pos3.from(location); - Fertilizer[] fertilizers = null; - Pos3 potLocation = pos3.add(0, -1, 0); - Optional chunk = world.get().getChunk(potLocation.toChunkPos()); - if (chunk.isPresent()) { - Optional state = chunk.get().getBlockState(potLocation); - if (state.isPresent()) { - if (state.get().type() instanceof PotBlock potBlock) { - fertilizers = potBlock.fertilizers(state.get()); - } - } - } - ArrayList configs = new ArrayList<>(); - if (fertilizers != null) { - for (Fertilizer fertilizer : fertilizers) { - Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); - } - } - for (FertilizerConfig config : configs) { - random = config.processDroppedItemAmount(random); - } - } - itemStack.setAmount(random); - if (toInv && player != null) { - PlayerUtils.giveItem(player, itemStack, random); - } else { - location.getWorld().dropItemNaturally(location, itemStack); - } - } else { - plugin.getPluginLogger().warn("Item: " + item + " doesn't exist"); - } - }; + return new ActionDropItem<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-item action which is expected to be `Section`"); return Action.empty(); @@ -718,25 +259,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerLegacyDropItemsAction() { registerAction((args, chance) -> { if (args instanceof Section section) { - List> actions = new ArrayList<>(); - Section otherItemSection = section.getSection("other-items"); - if (otherItemSection != null) { - for (Map.Entry entry : otherItemSection.getStringRouteMappedValues(false).entrySet()) { - if (entry.getValue() instanceof Section inner) { - actions.add(requireNonNull(getActionFactory("drop-item")).process(inner, inner.getDouble("chance", 1D))); - } - } - } - Section qualitySection = section.getSection("quality-crops"); - if (qualitySection != null) { - actions.add(requireNonNull(getActionFactory("quality-crops")).process(qualitySection, 1)); - } - return context -> { - if (Math.random() > chance) return; - for (Action action : actions) { - action.trigger(context); - } - }; + return new ActionDropItemLegacy<>(plugin, this, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-items action which is expected to be `Section`"); return Action.empty(); @@ -747,51 +270,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerHologramAction() { registerAction(((args, chance) -> { if (args instanceof Section section) { - TextValue text = TextValue.auto(section.getString("text", "")); - MathValue duration = MathValue.auto(section.get("duration", 20)); - boolean other = section.getString("position", "other").equals("other"); - MathValue x = MathValue.auto(section.get("x", 0)); - MathValue y = MathValue.auto(section.get("y", 0)); - MathValue z = MathValue.auto(section.get("z", 0)); - boolean applyCorrection = section.getBoolean("apply-correction", false); - boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); - int range = section.getInt("range", 32); - return context -> { - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - if (context.holder() == null) return; - if (Math.random() > chance) return; - Player owner = null; - if (context.holder() instanceof Player p) { - owner = p; - } - Location location = other ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : owner.getLocation().clone(); - Pos3 pos3 = Pos3.from(location).add(0,1,0); - location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); - Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); - if (optionalWorld.isEmpty()) { - return; - } - if (applyCorrection) { - String itemID = plugin.getItemManager().anyID(location.clone().add(0,1,0)); - location.add(0,ConfigManager.getOffset(itemID),0); - } - ArrayList viewers = new ArrayList<>(); - if (onlyShowToOne) { - if (owner == null) return; - viewers.add(owner); - } else { - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= range) { - viewers.add(player); - } - } - } - if (viewers.isEmpty()) return; - Component component = AdventureHelper.miniMessage(text.render(context)); - for (Player viewer : viewers) { - HologramManager.getInstance().showHologram(viewer, location, AdventureHelper.componentToJson(component), (int) (duration.evaluate(context) * 50)); - } - }; + return new ActionHologram<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`"); return Action.empty(); @@ -802,66 +281,7 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerFakeItemAction() { registerAction(((args, chance) -> { if (args instanceof Section section) { - String itemID = section.getString("item", ""); - String[] split = itemID.split(":"); - if (split.length >= 2) itemID = split[split.length - 1]; - MathValue duration = MathValue.auto(section.get("duration", 20)); - boolean other = section.getString("position", "other").equals("other"); - MathValue x = MathValue.auto(section.get("x", 0)); - MathValue y = MathValue.auto(section.get("y", 0)); - MathValue z = MathValue.auto(section.get("z", 0)); - MathValue yaw = MathValue.auto(section.get("yaw", 0)); - int range = section.getInt("range", 32); - boolean visibleToAll = section.getBoolean("visible-to-all", true); - boolean useItemDisplay = section.getBoolean("use-item-display", false); - String finalItemID = itemID; - return context -> { - if (Math.random() > chance) return; - if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; - Player owner = null; - if (context.holder() instanceof Player p) { - owner = p; - } - Location location = other ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : requireNonNull(owner).getLocation().clone(); - location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); - location.setPitch(0); - location.setYaw((float) yaw.evaluate(context)); - FakeEntity fakeEntity; - if (useItemDisplay && VersionHelper.isVersionNewerThan1_19_4()) { - location.add(0,1.5,0); - FakeItemDisplay itemDisplay = SparrowHeart.getInstance().createFakeItemDisplay(location); - itemDisplay.item(plugin.getItemManager().build(owner, finalItemID)); - fakeEntity = itemDisplay; - } else { - FakeArmorStand armorStand = SparrowHeart.getInstance().createFakeArmorStand(location); - armorStand.invisible(true); - armorStand.equipment(EquipmentSlot.HEAD, plugin.getItemManager().build(owner, finalItemID)); - fakeEntity = armorStand; - } - ArrayList viewers = new ArrayList<>(); - if (range > 0 && visibleToAll) { - for (Player player : location.getWorld().getPlayers()) { - if (LocationUtils.getDistance(player.getLocation(), location) <= range) { - viewers.add(player); - } - } - } else { - if (owner != null) { - viewers.add(owner); - } - } - if (viewers.isEmpty()) return; - for (Player player : viewers) { - fakeEntity.spawn(player); - } - plugin.getScheduler().asyncLater(() -> { - for (Player player : viewers) { - if (player.isOnline() && player.isValid()) { - fakeEntity.destroy(player); - } - } - }, (long) (duration.evaluate(context) * 50), TimeUnit.MILLISECONDS); - }; + return new ActionFakeItem<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fake-item action which is expected to be `Section`"); return Action.empty(); @@ -869,62 +289,10 @@ public abstract class AbstractActionManager implements ActionManager { }), "fake-item"); } - @SuppressWarnings("unchecked") protected void registerPlantAction() { this.registerAction((args, chance) -> { if (args instanceof Section section) { - int point = section.getInt("point", 0); - String key = requireNonNull(section.getString("crop")); - int y = section.getInt("y", 0); - boolean triggerAction = section.getBoolean("trigger-event", false); - return context -> { - if (Math.random() > chance) return; - CropConfig cropConfig = Registries.CROP.get(key); - if (cropConfig == null) { - plugin.getPluginLogger().warn("`plant` action is not executed due to crop[" + key + "] not exists"); - return; - } - Location cropLocation = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0,y,0); - Location potLocation = cropLocation.clone().subtract(0,1,0); - Optional> optionalWorld = plugin.getWorldManager().getWorld(cropLocation.getWorld()); - if (optionalWorld.isEmpty()) { - return; - } - CustomCropsWorld world = optionalWorld.get(); - PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); - Pos3 potPos3 = Pos3.from(potLocation); - String potItemID = plugin.getItemManager().blockID(potLocation); - PotConfig potConfig = Registries.ITEM_TO_POT.get(potItemID); - CustomCropsBlockState potState = potBlock.fixOrGetState(world, potPos3, potConfig, potItemID); - if (potState == null) { - plugin.getPluginLogger().warn("Pot doesn't exist below the crop when executing `plant` action at location[" + world.worldName() + "," + potPos3 + "]"); - return; - } - - CropBlock cropBlock = (CropBlock) BuiltInBlockMechanics.CROP.mechanic(); - CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); - cropBlock.id(state, key); - cropBlock.point(state, point); - - if (context.holder() instanceof Player player) { - EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); - CropPlantEvent plantEvent = new CropPlantEvent(player, player.getInventory().getItem(slot), slot, cropLocation, cropConfig, state, point); - if (EventUtils.fireAndCheckCancel(plantEvent)) { - return; - } - cropBlock.point(state, plantEvent.point()); - if (triggerAction) { - ActionManager.trigger((Context) context, cropConfig.plantActions()); - } - } - - CropStageConfig stageConfigWithModel = cropConfig.stageWithModelByPoint(cropBlock.point(state)); - world.addBlockState(Pos3.from(cropLocation), state); - plugin.getScheduler().sync().run(() -> { - plugin.getItemManager().remove(cropLocation, ExistenceForm.ANY); - plugin.getItemManager().place(cropLocation, stageConfigWithModel.existenceForm(), requireNonNull(stageConfigWithModel.stageID()), cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); - }, cropLocation); - }; + return new ActionPlant<>(plugin, section, chance); } else { plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plant action which is expected to be `Section`"); return Action.empty(); @@ -933,49 +301,6 @@ public abstract class AbstractActionManager implements ActionManager { } protected void registerBreakAction() { - this.registerAction((args, chance) -> { - boolean triggerEvent = (boolean) args; - return context -> { - Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); - Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); - if (optionalWorld.isEmpty()) { - return; - } - Pos3 pos3 = Pos3.from(location); - CustomCropsWorld world = optionalWorld.get(); - Optional optionalState = world.getBlockState(pos3); - if (optionalState.isEmpty()) { - return; - } - CustomCropsBlockState state = optionalState.get(); - if (!(state.type() instanceof CropBlock cropBlock)) { - return; - } - CropConfig config = cropBlock.config(state); - if (config == null) { - return; - } - if (triggerEvent) { - CropStageConfig stageConfig = config.stageWithModelByPoint(cropBlock.point(state)); - Player player = null; - if (context.holder() instanceof Player p) { - player = p; - } - DummyCancellable dummyCancellable = new DummyCancellable(); - if (player != null) { - EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); - ItemStack itemStack = player.getInventory().getItem(slot); - state.type().onBreak(new WrappedBreakEvent(player, null, world, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), BreakReason.ACTION, dummyCancellable)); - } else { - state.type().onBreak(new WrappedBreakEvent(null, null, world, location, stageConfig.stageID(), null, null, BreakReason.ACTION, dummyCancellable)); - } - if (dummyCancellable.isCancelled()) { - return; - } - } - world.removeBlockState(pos3); - plugin.getItemManager().remove(location, ExistenceForm.ANY); - }; - }, "break"); + this.registerAction((args, chance) -> new ActionBreak<>(plugin, args, chance), "break"); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/AbstractBuiltInAction.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/AbstractBuiltInAction.java new file mode 100644 index 0000000..ae5d90f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/AbstractBuiltInAction.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.Action; + +public abstract class AbstractBuiltInAction implements Action { + protected final BukkitCustomCropsPlugin plugin; + protected final double chance; + protected AbstractBuiltInAction(BukkitCustomCropsPlugin plugin, double chance) { + this.plugin = plugin; + this.chance = chance; + } + + public BukkitCustomCropsPlugin getPlugin() { + return plugin; + } + + public double getChance() { + return chance; + } + + public boolean checkChance() { + return !(Math.random() > chance); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionActionbarNearby.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionActionbarNearby.java new file mode 100644 index 0000000..1c3da2b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionActionbarNearby.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.audience.Audience; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import static java.util.Objects.requireNonNull; + +public class ActionActionbarNearby extends AbstractBuiltInAction { + final String actionbar; + final MathValue range; + public ActionActionbarNearby( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.actionbar = section.getString("actionbar"); + this.range = MathValue.auto(section.get("range")); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + double realRange = range.evaluate(context); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + String replaced = plugin.getPlaceholderManager().parse(owner, actionbar, context.placeholderMap()); + Audience audience = plugin.getSenderFactory().getAudience(player); + audience.sendActionBar(AdventureHelper.miniMessage(replaced)); + } + } + } + + public String getActionbar() { + return actionbar; + } + + public MathValue getRange() { + return range; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBreak.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBreak.java new file mode 100644 index 0000000..209f4c5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBreak.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.block.BreakReason; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent; +import net.momirealms.customcrops.api.util.DummyCancellable; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ActionBreak extends AbstractBuiltInAction { + final boolean triggerEvent; + public ActionBreak( + BukkitCustomCropsPlugin plugin, + Object args, + double chance + ) { + super(plugin, chance); + this.triggerEvent = (boolean) args; + } + @Override + public void trigger(Context context) { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + Pos3 pos3 = Pos3.from(location); + CustomCropsWorld world = optionalWorld.get(); + Optional optionalState = world.getBlockState(pos3); + if (optionalState.isEmpty()) { + return; + } + CustomCropsBlockState state = optionalState.get(); + if (!(state.type() instanceof CropBlock cropBlock)) { + return; + } + CropConfig config = cropBlock.config(state); + if (config == null) { + return; + } + if (triggerEvent) { + CropStageConfig stageConfig = config.stageWithModelByPoint(cropBlock.point(state)); + Player player = null; + if (context.holder() instanceof Player p) { + player = p; + } + DummyCancellable dummyCancellable = new DummyCancellable(); + if (player != null) { + EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); + ItemStack itemStack = player.getInventory().getItem(slot); + state.type().onBreak(new WrappedBreakEvent(player, null, world, location, stageConfig.stageID(), itemStack, plugin.getItemManager().id(itemStack), BreakReason.ACTION, dummyCancellable)); + } else { + state.type().onBreak(new WrappedBreakEvent(null, null, world, location, stageConfig.stageID(), null, null, BreakReason.ACTION, dummyCancellable)); + } + if (dummyCancellable.isCancelled()) { + return; + } + } + world.removeBlockState(pos3); + plugin.getItemManager().remove(location, ExistenceForm.ANY); + } + + public boolean isTriggerEvent() { + return triggerEvent; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBroadcast.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBroadcast.java new file mode 100644 index 0000000..3aae07f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBroadcast.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import net.kyori.adventure.audience.Audience; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.List; + +public class ActionBroadcast extends AbstractBuiltInAction { + final List messages; + public ActionBroadcast( + BukkitCustomCropsPlugin plugin, + Object args, + double chance + ) { + super(plugin, chance); + this.messages = ListUtils.toList(args); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + OfflinePlayer offlinePlayer = null; + if (context.holder() instanceof Player player) { + offlinePlayer = player; + } + List replaced = plugin.getPlaceholderManager().parse(offlinePlayer, messages, context.placeholderMap()); + for (Player player : Bukkit.getOnlinePlayers()) { + Audience audience = plugin.getSenderFactory().getAudience(player); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + } + } + + public List getMessages() { + return messages; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBundle.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBundle.java new file mode 100644 index 0000000..ae01edf --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionBundle.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.Context; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ActionBundle extends AbstractBuiltInAction { + final List> actions; + public ActionBundle( + BukkitCustomCropsPlugin plugin, + AbstractActionManager manager, + Object args, + double chance + ) { + super(plugin, chance); + this.actions = new ArrayList<>(); + if (args instanceof Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section innerSection) { + actions.add(manager.parseAction(innerSection)); + } + } + } + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + for (Action action : actions) { + action.trigger(context); + } + } + + public List> getActions() { + return actions; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommand.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommand.java new file mode 100644 index 0000000..be20f7e --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommand.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.List; + +public class ActionCommand extends AbstractBuiltInAction { + final List commands; + public ActionCommand( + BukkitCustomCropsPlugin plugin, + Object args, + double chance + ) { + super(plugin, chance); + this.commands = ListUtils.toList(args); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + List replaced = BukkitPlaceholderManager.getInstance().parse(owner, commands, context.placeholderMap()); + plugin.getScheduler().sync().run(() -> { + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + }, null); + } + + public List getCommands() { + return commands; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommandNearby.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommandNearby.java new file mode 100644 index 0000000..d8d9d45 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionCommandNearby.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class ActionCommandNearby extends AbstractBuiltInAction { + final List cmd; + final MathValue range; + public ActionCommandNearby( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.cmd = ListUtils.toList(section.get("command")); + this.range = MathValue.auto(section.get("range")); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + double realRange = range.evaluate(context); + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + List replaced = BukkitPlaceholderManager.getInstance().parse(owner, cmd, context.placeholderMap()); + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + } + } + } + + public List getCmd() { + return cmd; + } + + public MathValue getRange() { + return range; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionConditional.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionConditional.java new file mode 100644 index 0000000..37122c1 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionConditional.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.requirement.Requirement; + +public class ActionConditional extends AbstractBuiltInAction { + final Action[] actions; + final Requirement[] requirements; + public ActionConditional( + BukkitCustomCropsPlugin plugin, + AbstractActionManager manager, + Class tClass, + Section section, + double chance + ) { + super(plugin, chance); + this.actions = manager.parseActions(section.getSection("actions")); + this.requirements = plugin.getRequirementManager(tClass).parseRequirements(section.getSection("conditions"), true); + } + @Override + public void trigger(Context condition) { + if (!checkChance()) return; + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(condition)) { + return; + } + } + for (Action action : actions) { + action.trigger(condition); + } + } + + public Action[] getActions() { + return actions; + } + + public Requirement[] getRequirements() { + return requirements; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDelay.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDelay.java new file mode 100644 index 0000000..fe9705a --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDelay.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class ActionDelay extends AbstractBuiltInAction { + final List> actions; + final int delay; + final boolean async; + public ActionDelay( + BukkitCustomCropsPlugin plugin, + AbstractActionManager manager, + Object args, + double chance + ) { + super(plugin, chance); + this.actions = new ArrayList<>(); + if (args instanceof Section section) { + delay = section.getInt("delay", 1); + async = section.getBoolean("async", false); + Section actionSection = section.getSection("actions"); + if (actionSection != null) + for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section innerSection) + actions.add(manager.parseAction(innerSection)); + } else { + delay = 1; + async = false; + } + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + Location location = context.arg(ContextKeys.LOCATION); + if (async) { + plugin.getScheduler().asyncLater(() -> { + for (Action action : actions) + action.trigger(context); + }, delay * 50L, TimeUnit.MILLISECONDS); + } else { + plugin.getScheduler().sync().runLater(() -> { + for (Action action : actions) + action.trigger(context); + }, delay, location); + } + } + + public List> getActions() { + return actions; + } + + public int getDelay() { + return delay; + } + + public boolean isAsync() { + return async; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItem.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItem.java new file mode 100644 index 0000000..930f10d --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItem.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsChunk; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.util.PlayerUtils; +import net.momirealms.customcrops.common.util.RandomUtils; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ActionDropItem extends AbstractBuiltInAction { + final boolean ignoreFertilizer; + final String item; + final MathValue min; + final MathValue max; + final boolean toInv; + public ActionDropItem( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.ignoreFertilizer = section.getBoolean("ignore-fertilizer", true); + this.item = section.getString("item"); + this.min = MathValue.auto(section.get("min")); + this.max = MathValue.auto(section.get("max")); + this.toInv = section.getBoolean("to-inventory", false); + + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + Player player = null; + if (context.holder() instanceof Player p) { + player = p; + } + int random = RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); + ItemStack itemStack = generateItem(location, player, random); + if (itemStack != null) { + if (toInv && player != null) { + PlayerUtils.giveItem(player, itemStack, random); + } else { + location.getWorld().dropItemNaturally(location, itemStack); + } + } + } + + @Nullable + public ItemStack generateItem(Location location, @Nullable Player player, int amount) { + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) { + return null; + } + ItemStack itemStack = plugin.getItemManager().build(player, item); + if (itemStack != null) { + if (!ignoreFertilizer) { + Pos3 pos3 = Pos3.from(location); + Fertilizer[] fertilizers = null; + Pos3 potLocation = pos3.add(0, -1, 0); + Optional chunk = world.get().getChunk(potLocation.toChunkPos()); + if (chunk.isPresent()) { + Optional state = chunk.get().getBlockState(potLocation); + if (state.isPresent()) { + if (state.get().type() instanceof PotBlock potBlock) { + fertilizers = potBlock.fertilizers(state.get()); + } + } + } + ArrayList configs = new ArrayList<>(); + if (fertilizers != null) { + for (Fertilizer fertilizer : fertilizers) { + Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); + } + } + for (FertilizerConfig config : configs) { + amount = config.processDroppedItemAmount(amount); + } + } + itemStack.setAmount(amount); + } else { + plugin.getPluginLogger().warn("Item: " + item + " doesn't exist"); + } + return itemStack; + } + + public boolean isIgnoreFertilizer() { + return ignoreFertilizer; + } + + public String getItem() { + return item; + } + + public MathValue getMin() { + return min; + } + + public MathValue getMax() { + return max; + } + + public boolean isToInv() { + return toInv; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItemLegacy.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItemLegacy.java new file mode 100644 index 0000000..7c79f94 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionDropItemLegacy.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +public class ActionDropItemLegacy extends AbstractBuiltInAction { + final List> actions; + public ActionDropItemLegacy( + BukkitCustomCropsPlugin plugin, + ActionManager manager, + Section section, + double chance + ) { + super(plugin, chance); + this.actions = new ArrayList<>(); + Section otherItemSection = section.getSection("other-items"); + if (otherItemSection != null) { + for (Map.Entry entry : otherItemSection.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + actions.add(requireNonNull(manager.getActionFactory("drop-item")).process(inner, inner.getDouble("chance", 1D))); + } + } + } + Section qualitySection = section.getSection("quality-crops"); + if (qualitySection != null) { + actions.add(requireNonNull(manager.getActionFactory("quality-crops")).process(qualitySection, 1)); + } + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + for (Action action : actions) { + action.trigger(context); + } + } + + public List> getActions() { + return actions; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionFakeItem.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionFakeItem.java new file mode 100644 index 0000000..84ae5e6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionFakeItem.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.helper.VersionHelper; +import net.momirealms.sparrow.heart.SparrowHeart; +import net.momirealms.sparrow.heart.feature.entity.FakeEntity; +import net.momirealms.sparrow.heart.feature.entity.armorstand.FakeArmorStand; +import net.momirealms.sparrow.heart.feature.entity.display.FakeItemDisplay; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +import static java.util.Objects.requireNonNull; + +public class ActionFakeItem extends AbstractBuiltInAction { + final String itemID; + final MathValue duration; + final boolean other; + final MathValue x; + final MathValue y; + final MathValue z; + final MathValue yaw; + final int range; + final boolean visibleToAll; + final boolean useItemDisplay; + public ActionFakeItem( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + String itemID = section.getString("item", ""); + String[] split = itemID.split(":"); + if (split.length >= 2) itemID = split[split.length - 1]; + this.itemID = itemID; + this.duration = MathValue.auto(section.get("duration", 20)); + this.other = section.getString("position", "other").equals("other"); + this.x = MathValue.auto(section.get("x", 0)); + this.y = MathValue.auto(section.get("y", 0)); + this.z = MathValue.auto(section.get("z", 0)); + this.yaw = MathValue.auto(section.get("yaw", 0)); + this.range = section.getInt("range", 32); + this.visibleToAll = section.getBoolean("visible-to-all", true); + this.useItemDisplay = section.getBoolean("use-item-display", false); + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + Player owner = null; + if (context.holder() instanceof Player p) { + owner = p; + } + Location location = other ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : requireNonNull(owner).getLocation().clone(); + location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); + location.setPitch(0); + location.setYaw((float) yaw.evaluate(context)); + FakeEntity fakeEntity; + if (useItemDisplay && VersionHelper.isVersionNewerThan1_19_4()) { + location.add(0,1.5,0); + FakeItemDisplay itemDisplay = SparrowHeart.getInstance().createFakeItemDisplay(location); + itemDisplay.item(plugin.getItemManager().build(owner, itemID)); + fakeEntity = itemDisplay; + } else { + FakeArmorStand armorStand = SparrowHeart.getInstance().createFakeArmorStand(location); + armorStand.invisible(true); + armorStand.equipment(EquipmentSlot.HEAD, plugin.getItemManager().build(owner, itemID)); + fakeEntity = armorStand; + } + ArrayList viewers = new ArrayList<>(); + if (range > 0 && visibleToAll) { + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + viewers.add(player); + } + } + } else { + if (owner != null) { + viewers.add(owner); + } + } + if (viewers.isEmpty()) return; + for (Player player : viewers) { + fakeEntity.spawn(player); + } + plugin.getScheduler().asyncLater(() -> { + for (Player player : viewers) { + if (player.isOnline() && player.isValid()) { + fakeEntity.destroy(player); + } + } + }, (long) (duration.evaluate(context) * 50), TimeUnit.MILLISECONDS); + } + + public String getItemID() { + return itemID; + } + + public MathValue getDuration() { + return duration; + } + + public boolean isOther() { + return other; + } + + public MathValue getX() { + return x; + } + + public MathValue getY() { + return y; + } + + public MathValue getZ() { + return z; + } + + public MathValue getYaw() { + return yaw; + } + + public int getRange() { + return range; + } + + public boolean isVisibleToAll() { + return visibleToAll; + } + + public boolean isUseItemDisplay() { + return useItemDisplay; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionHologram.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionHologram.java new file mode 100644 index 0000000..b760a9c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionHologram.java @@ -0,0 +1,139 @@ +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.misc.HologramManager; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import org.bukkit.Location; +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ActionHologram extends AbstractBuiltInAction { + final TextValue text; + final MathValue duration; + final boolean other; + final MathValue x; + final MathValue y; + final MathValue z; + final boolean applyCorrection; + final boolean onlyShowToOne; + final int range; + public ActionHologram( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.text = TextValue.auto(section.getString("text", "")); + this.duration = MathValue.auto(section.get("duration", 20)); + this.other = section.getString("position", "other").equals("other"); + this.x = MathValue.auto(section.get("x", 0)); + this.y = MathValue.auto(section.get("y", 0)); + this.z = MathValue.auto(section.get("z", 0)); + this.applyCorrection = section.getBoolean("apply-correction", false); + this.onlyShowToOne = !section.getBoolean("visible-to-all", false); + this.range = section.getInt("range", 32); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (context.holder() == null) return; + if (Math.random() > chance) return; + Player owner = null; + if (context.holder() instanceof Player p) { + owner = p; + } + Location location = other ? requireNonNull(context.arg(ContextKeys.LOCATION)).clone() : owner.getLocation().clone(); + // Pos3 pos3 = Pos3.from(location).add(0,1,0); + location.add(x.evaluate(context), y.evaluate(context), z.evaluate(context)); + Optional> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + if (applyCorrection) { + String itemID = plugin.getItemManager().anyID(location.clone().add(0,1,0)); + location.add(0, ConfigManager.getOffset(itemID),0); + } + ArrayList viewers = new ArrayList<>(); + if (onlyShowToOne) { + if (owner == null) return; + viewers.add(owner); + } else { + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + viewers.add(player); + } + } + } + if (viewers.isEmpty()) return; + Component component = AdventureHelper.miniMessage(text.render(context)); + for (Player viewer : viewers) { + HologramManager.getInstance().showHologram(viewer, location, AdventureHelper.componentToJson(component), (int) (duration.evaluate(context) * 50)); + } + } + + public TextValue getText() { + return text; + } + + public MathValue getDuration() { + return duration; + } + + public boolean isOther() { + return other; + } + + public MathValue getX() { + return x; + } + + public MathValue getY() { + return y; + } + + public MathValue getZ() { + return z; + } + + public boolean isApplyCorrection() { + return applyCorrection; + } + + public boolean isOnlyShowToOne() { + return onlyShowToOne; + } + + public int getRange() { + return range; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionMessageNearby.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionMessageNearby.java new file mode 100644 index 0000000..3f8de0b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionMessageNearby.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.audience.Audience; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class ActionMessageNearby extends AbstractBuiltInAction { + final List messages; + final MathValue range; + public ActionMessageNearby( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.messages = ListUtils.toList(section.get("message")); + this.range = MathValue.auto(section.get("range")); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + double realRange = range.evaluate(context); + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= realRange) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + List replaced = BukkitPlaceholderManager.getInstance().parse( + owner, + messages, + context.placeholderMap() + ); + Audience audience = plugin.getSenderFactory().getAudience(player); + for (String text : replaced) { + audience.sendMessage(AdventureHelper.miniMessage(text)); + } + } + } + } + + public List getMessages() { + return messages; + } + + public MathValue getRange() { + return range; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionParticle.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionParticle.java new file mode 100644 index 0000000..cebb4ee --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionParticle.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.util.ParticleUtils; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.inventory.ItemStack; + +import java.util.Locale; + +import static java.util.Objects.requireNonNull; + +public class ActionParticle extends AbstractBuiltInAction { + final Particle particleType; + final double x; + final double y; + final double z; + final double offSetX; + final double offSetY; + final double offSetZ; + final int count; + final double extra; + final float scale; + final ItemStack itemStack; + final Color color; + final Color toColor; + public ActionParticle( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.particleType = ParticleUtils.getParticle(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); + this.x = section.getDouble("x",0.0); + this.y = section.getDouble("y",0.0); + this.z = section.getDouble("z",0.0); + this.offSetX = section.getDouble("offset-x",0.0); + this.offSetY = section.getDouble("offset-y",0.0); + this.offSetZ = section.getDouble("offset-z",0.0); + this.count = section.getInt("count", 1); + this.extra = section.getDouble("extra", 0.0); + this.scale = section.getDouble("scale", 1d).floatValue(); + + if (section.contains("itemStack")) + itemStack = BukkitCustomCropsPlugin.getInstance() + .getItemManager() + .build(null, section.getString("itemStack")); + else + itemStack = null; + + if (section.contains("color")) { + String[] rgb = section.getString("color","255,255,255").split(","); + color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + } else { + color = null; + } + + if (section.contains("color")) { + String[] rgb = section.getString("to-color","255,255,255").split(","); + toColor = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + } else { + toColor = null; + } + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + location.getWorld().spawnParticle( + particleType, + location.getX() + x, location.getY() + y, location.getZ() + z, + count, + offSetX, offSetY, offSetZ, + extra, + itemStack != null ? itemStack : (color != null && toColor != null ? new Particle.DustTransition(color, toColor, scale) : (color != null ? new Particle.DustOptions(color, scale) : null)) + ); + } + + public Particle getParticleType() { + return particleType; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public double getOffSetX() { + return offSetX; + } + + public double getOffSetY() { + return offSetY; + } + + public double getOffSetZ() { + return offSetZ; + } + + public int getCount() { + return count; + } + + public double getExtra() { + return extra; + } + + public float getScale() { + return scale; + } + + public ItemStack getItemStack() { + return itemStack; + } + + public Color getColor() { + return color; + } + + public Color getToColor() { + return toColor; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPlant.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPlant.java new file mode 100644 index 0000000..25c237f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPlant.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.ActionManager; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.BuiltInBlockMechanics; +import net.momirealms.customcrops.api.core.ExistenceForm; +import net.momirealms.customcrops.api.core.FurnitureRotation; +import net.momirealms.customcrops.api.core.Registries; +import net.momirealms.customcrops.api.core.block.CropBlock; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; +import net.momirealms.customcrops.api.core.mechanic.crop.CropStageConfig; +import net.momirealms.customcrops.api.core.mechanic.pot.PotConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.util.EventUtils; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ActionPlant extends AbstractBuiltInAction { + final int point; + final String key; + final int y; + final boolean triggerAction; + public ActionPlant( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.point = section.getInt("point", 0); + this.key = requireNonNull(section.getString("crop")); + this.y = section.getInt("y", 0); + this.triggerAction = section.getBoolean("trigger-event", false); + } + @Override + @SuppressWarnings("unchecked") + public void trigger(Context context) { + if (!checkChance()) return; + CropConfig cropConfig = Registries.CROP.get(key); + if (cropConfig == null) { + plugin.getPluginLogger().warn("`plant` action is not executed due to crop[" + key + "] not exists"); + return; + } + Location cropLocation = requireNonNull(context.arg(ContextKeys.LOCATION)).clone().add(0,y,0); + Location potLocation = cropLocation.clone().subtract(0,1,0); + Optional> optionalWorld = plugin.getWorldManager().getWorld(cropLocation.getWorld()); + if (optionalWorld.isEmpty()) { + return; + } + CustomCropsWorld world = optionalWorld.get(); + PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); + Pos3 potPos3 = Pos3.from(potLocation); + String potItemID = plugin.getItemManager().blockID(potLocation); + PotConfig potConfig = Registries.ITEM_TO_POT.get(potItemID); + CustomCropsBlockState potState = potBlock.fixOrGetState(world, potPos3, potConfig, potItemID); + if (potState == null) { + plugin.getPluginLogger().warn("Pot doesn't exist below the crop when executing `plant` action at location[" + world.worldName() + "," + potPos3 + "]"); + return; + } + + CropBlock cropBlock = (CropBlock) BuiltInBlockMechanics.CROP.mechanic(); + CustomCropsBlockState state = BuiltInBlockMechanics.CROP.createBlockState(); + cropBlock.id(state, key); + cropBlock.point(state, point); + + if (context.holder() instanceof Player player) { + EquipmentSlot slot = requireNonNull(context.arg(ContextKeys.SLOT)); + CropPlantEvent plantEvent = new CropPlantEvent(player, player.getInventory().getItem(slot), slot, cropLocation, cropConfig, state, point); + if (EventUtils.fireAndCheckCancel(plantEvent)) { + return; + } + cropBlock.point(state, plantEvent.point()); + if (triggerAction) { + ActionManager.trigger((Context) context, cropConfig.plantActions()); + } + } + + CropStageConfig stageConfigWithModel = cropConfig.stageWithModelByPoint(cropBlock.point(state)); + world.addBlockState(Pos3.from(cropLocation), state); + plugin.getScheduler().sync().run(() -> { + plugin.getItemManager().remove(cropLocation, ExistenceForm.ANY); + plugin.getItemManager().place(cropLocation, stageConfigWithModel.existenceForm(), requireNonNull(stageConfigWithModel.stageID()), cropConfig.rotation() ? FurnitureRotation.random() : FurnitureRotation.NONE); + }, cropLocation); + } + + public int getPoint() { + return point; + } + + public String getKey() { + return key; + } + + public int getY() { + return y; + } + + public boolean isTriggerAction() { + return triggerAction; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPriority.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPriority.java new file mode 100644 index 0000000..021c328 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionPriority.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.requirement.Requirement; +import net.momirealms.customcrops.common.util.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ActionPriority extends AbstractBuiltInAction { + final List[], Action[]>> conditionActionPairList; + public ActionPriority( + BukkitCustomCropsPlugin plugin, + AbstractActionManager manager, + Class tClass, + Section section, + double chance + ) { + super(plugin, chance); + this.conditionActionPairList = new ArrayList<>(); + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + Action[] actions = manager.parseActions(inner.getSection("actions")); + Requirement[] requirements = plugin.getRequirementManager(tClass).parseRequirements(inner.getSection("conditions"), false); + conditionActionPairList.add(Pair.of(requirements, actions)); + } + } + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + outer: + for (Pair[], Action[]> pair : conditionActionPairList) { + if (pair.left() != null) + for (Requirement requirement : pair.left()) { + if (!requirement.isSatisfied(context)) { + continue outer; + } + } + if (pair.right() != null) + for (Action action : pair.right()) { + action.trigger(context); + } + return; + } + } + + public List[], Action[]>> getConditionActionPairList() { + return conditionActionPairList; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionQualityCrops.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionQualityCrops.java new file mode 100644 index 0000000..24d9a32 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionQualityCrops.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.core.ConfigManager; +import net.momirealms.customcrops.api.core.block.PotBlock; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.Fertilizer; +import net.momirealms.customcrops.api.core.mechanic.fertilizer.FertilizerConfig; +import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import net.momirealms.customcrops.api.core.world.CustomCropsChunk; +import net.momirealms.customcrops.api.core.world.CustomCropsWorld; +import net.momirealms.customcrops.api.core.world.Pos3; +import net.momirealms.customcrops.api.misc.value.MathValue; +import net.momirealms.customcrops.api.util.PlayerUtils; +import net.momirealms.customcrops.common.util.RandomUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ActionQualityCrops extends AbstractBuiltInAction { + final MathValue min; + final MathValue max; + final boolean toInv; + final String[] qualityLoots; + public ActionQualityCrops( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.min = MathValue.auto(section.get("min")); + this.max = MathValue.auto(section.get("max")); + this.toInv = section.getBoolean("to-inventory", false); + this.qualityLoots = new String[ConfigManager.defaultQualityRatio().length]; + for (int i = 1; i <= ConfigManager.defaultQualityRatio().length; i++) { + qualityLoots[i-1] = section.getString("items." + i); + if (qualityLoots[i-1] == null) { + plugin.getPluginLogger().warn("items." + i + " should not be null"); + qualityLoots[i-1] = ""; + } + } + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + int random = RandomUtils.generateRandomInt((int) min.evaluate(context), (int) max.evaluate(context)); + Player player = null; + if (context.holder() instanceof Player p) { + player = p; + } + ItemStack drop = generateItem(location, player, random); + if (drop != null) { + if (toInv && player != null) { + PlayerUtils.giveItem(player, drop, 1); + } else { + location.getWorld().dropItemNaturally(location, drop); + } + } + } + + @Nullable + public ItemStack generateItem(Location location, @Nullable Player player, int randomAmount) { + double[] ratio = ConfigManager.defaultQualityRatio(); + Optional> world = plugin.getWorldManager().getWorld(location.getWorld()); + if (world.isEmpty()) { + return null; + } + Pos3 pos3 = Pos3.from(location); + Fertilizer[] fertilizers = null; + Pos3 potLocation = pos3.add(0, -1, 0); + Optional chunk = world.get().getChunk(potLocation.toChunkPos()); + if (chunk.isPresent()) { + Optional state = chunk.get().getBlockState(potLocation); + if (state.isPresent()) { + if (state.get().type() instanceof PotBlock potBlock) { + fertilizers = potBlock.fertilizers(state.get()); + } + } + } + ArrayList configs = new ArrayList<>(); + if (fertilizers != null) { + for (Fertilizer fertilizer : fertilizers) { + Optional.ofNullable(fertilizer.config()).ifPresent(configs::add); + } + } + for (FertilizerConfig config : configs) { + randomAmount = config.processDroppedItemAmount(randomAmount); + double[] newRatio = config.overrideQualityRatio(); + if (newRatio != null) { + ratio = newRatio; + } + } + for (int i = 0; i < randomAmount; i++) { + double r1 = Math.random(); + for (int j = 0; j < ratio.length; j++) { + if (r1 < ratio[j]) { + ItemStack drop = plugin.getItemManager().build(player, qualityLoots[j]); + if (drop == null || drop.getType() == Material.AIR) return null; + return drop; + } + } + } + return null; + } + + public MathValue getMin() { + return min; + } + + public MathValue getMax() { + return max; + } + + public boolean isToInv() { + return toInv; + } + + public String[] getQualityLoots() { + return qualityLoots; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionRandomCommand.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionRandomCommand.java new file mode 100644 index 0000000..a3245c0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionRandomCommand.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.placeholder.BukkitPlaceholderManager; +import net.momirealms.customcrops.common.util.ListUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class ActionRandomCommand extends AbstractBuiltInAction { + final List commands; + public ActionRandomCommand( + BukkitCustomCropsPlugin plugin, + Object args, + double chance + ) { + super(plugin, chance); + this.commands = ListUtils.toList(args); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + OfflinePlayer owner = null; + if (context.holder() instanceof Player player) { + owner = player; + } + String random = commands.get(ThreadLocalRandom.current().nextInt(commands.size())); + random = BukkitPlaceholderManager.getInstance().parse(owner, random, context.placeholderMap()); + String finalRandom = random; + plugin.getScheduler().sync().run(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); + }, null); + } + + public List getCommands() { + return commands; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTimer.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTimer.java new file mode 100644 index 0000000..04b01a5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTimer.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.action.AbstractActionManager; +import net.momirealms.customcrops.api.action.Action; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Location; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class ActionTimer extends AbstractBuiltInAction { + final List> actions; + final int delay, duration, period; + final boolean async; + public ActionTimer( + BukkitCustomCropsPlugin plugin, + AbstractActionManager manager, + Object args, + double chance + ) { + super(plugin, chance); + this.actions = new ArrayList<>(); + if (args instanceof Section section) { + delay = section.getInt("delay", 2); + duration = section.getInt("duration", 20); + period = section.getInt("period", 2); + async = section.getBoolean("async", false); + Section actionSection = section.getSection("actions"); + if (actionSection != null) + for (Map.Entry entry : actionSection.getStringRouteMappedValues(false).entrySet()) + if (entry.getValue() instanceof Section innerSection) + actions.add(manager.parseAction(innerSection)); + } else { + delay = 1; + period = 1; + async = false; + duration = 20; + } + } + @Override + public void trigger(Context context) { + if (!checkChance()) return; + Location location = context.arg(ContextKeys.LOCATION); + SchedulerTask task; + if (async) { + task = plugin.getScheduler().asyncRepeating(() -> { + for (Action action : actions) { + action.trigger(context); + } + }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); + } else { + task = plugin.getScheduler().sync().runRepeating(() -> { + for (Action action : actions) { + action.trigger(context); + } + }, delay, period, location); + } + plugin.getScheduler().asyncLater(task::cancel, duration * 50L, TimeUnit.MILLISECONDS); + } + + public List> getActions() { + return actions; + } + + public int getDelay() { + return delay; + } + + public int getDuration() { + return duration; + } + + public int getPeriod() { + return period; + } + + public boolean isAsync() { + return async; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTitleNearby.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTitleNearby.java new file mode 100644 index 0000000..fe8cdfe --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionTitleNearby.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customcrops.api.action.builtin; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.audience.Audience; +import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; +import net.momirealms.customcrops.api.context.Context; +import net.momirealms.customcrops.api.context.ContextKeys; +import net.momirealms.customcrops.api.misc.value.TextValue; +import net.momirealms.customcrops.api.util.LocationUtils; +import net.momirealms.customcrops.common.helper.AdventureHelper; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import static java.util.Objects.requireNonNull; + +public class ActionTitleNearby extends AbstractBuiltInAction { + final TextValue title; + final TextValue subtitle; + final int fadeIn; + final int stay; + final int fadeOut; + final int range; + public ActionTitleNearby( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.title = TextValue.auto(section.getString("title")); + this.subtitle = TextValue.auto(section.getString("subtitle")); + this.fadeIn = section.getInt("fade-in", 20); + this.stay = section.getInt("stay", 30); + this.fadeOut = section.getInt("fade-out", 10); + this.range = section.getInt("range", 0); + } + @Override + public void trigger(Context context) { + if (context.argOrDefault(ContextKeys.OFFLINE, false)) return; + if (Math.random() > chance) return; + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + for (Player player : location.getWorld().getPlayers()) { + if (LocationUtils.getDistance(player.getLocation(), location) <= range) { + context.arg(ContextKeys.TEMP_NEAR_PLAYER, player.getName()); + Audience audience = plugin.getSenderFactory().getAudience(player); + AdventureHelper.sendTitle(audience, + AdventureHelper.miniMessage(title.render(context)), + AdventureHelper.miniMessage(subtitle.render(context)), + fadeIn, stay, fadeOut + ); + } + } + } + + public TextValue getTitle() { + return title; + } + + public TextValue getSubtitle() { + return subtitle; + } + + public int getFadeIn() { + return fadeIn; + } + + public int getStay() { + return stay; + } + + public int getFadeOut() { + return fadeOut; + } + + public int getRange() { + return range; + } +}