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 309f3d3..f32005d 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 @@ -27,7 +27,10 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public abstract class AbstractActionManager implements ActionManager { @@ -54,6 +57,7 @@ public abstract class AbstractActionManager implements ActionManager { this.registerHologramAction(); this.registerPlantAction(); this.registerBreakAction(); + this.registerSpawnEntity(); } @Override @@ -303,4 +307,15 @@ public abstract class AbstractActionManager implements ActionManager { protected void registerBreakAction() { this.registerAction((args, chance) -> new ActionBreak<>(plugin, args, chance), "break"); } + + protected void registerSpawnEntity() { + this.registerAction((args, chance) -> { + if (args instanceof Section section) { + return new ActionSpawnEntity<>(plugin, section, chance); + } else { + plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at spawn-entity action which is expected to be `Section`"); + return Action.empty(); + } + }, "spawn-entity", "spawn-mob"); + } } 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 index 9af5eb9..76d34ea 100644 --- 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 @@ -12,23 +12,6 @@ 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; diff --git a/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionSpawnEntity.java b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionSpawnEntity.java new file mode 100644 index 0000000..2efdf9d --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/action/builtin/ActionSpawnEntity.java @@ -0,0 +1,76 @@ +/* + * 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.integration.EntityProvider; +import org.bukkit.Location; + +import java.util.HashMap; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +public class ActionSpawnEntity extends AbstractBuiltInAction { + + private final String id; + private final Map properties; + + public ActionSpawnEntity( + BukkitCustomCropsPlugin plugin, + Section section, + double chance + ) { + super(plugin, chance); + this.id = section.getString("id"); + Section proeprtySection = section.getSection("properties"); + this.properties = proeprtySection == null ? new HashMap<>() : proeprtySection.getStringRouteMappedValues(false); + } + + @Override + protected void triggerAction(Context context) { + Location location = requireNonNull(context.arg(ContextKeys.LOCATION)); + String finalID; + EntityProvider provider; + if (id.contains(":")) { + String[] split = id.split(":", 2); + String providerID = split[0]; + finalID = split[1]; + provider = BukkitCustomCropsPlugin.getInstance().getIntegrationManager().getEntityProvider(providerID); + } else { + finalID = id; + provider = BukkitCustomCropsPlugin.getInstance().getIntegrationManager().getEntityProvider("vanilla"); + } + if (provider == null) { + plugin.getPluginLogger().warn("Failed to spawn entity: " + id); + return; + } + provider.spawn(location, finalID, properties()); + } + + public String id() { + return id; + } + + public Map properties() { + return properties; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java b/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java index 5d28e18..a74804d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/BlockContextImpl.java @@ -18,12 +18,14 @@ package net.momirealms.customcrops.api.context; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.bukkit.Location; import org.jetbrains.annotations.NotNull; public class BlockContextImpl extends AbstractContext { - public BlockContextImpl(@NotNull CustomCropsBlockState block, boolean sync) { + public BlockContextImpl(@NotNull CustomCropsBlockState block, Location location, boolean sync) { super(block, sync); + updateLocation(location); } @Override diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/Context.java b/api/src/main/java/net/momirealms/customcrops/api/context/Context.java index 1cb4999..f2c09bd 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/Context.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/Context.java @@ -18,6 +18,7 @@ package net.momirealms.customcrops.api.context; import net.momirealms.customcrops.api.core.world.CustomCropsBlockState; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -122,10 +123,11 @@ public interface Context { * Creates a block-specific context. * * @param block the block to be used as the holder of the context. + * @param location the location of the block * @return a new Context instance with the specified block as the holder. */ - static Context block(@NotNull CustomCropsBlockState block) { - return new BlockContextImpl(block, false); + static Context block(@NotNull CustomCropsBlockState block, @NotNull Location location) { + return new BlockContextImpl(block, location, false); } /** @@ -143,10 +145,24 @@ public interface Context { * Creates a block-specific context. * * @param block the block to be used as the holder of the context. + * @param location the location of the block * @param threadSafe is the created map thread safe * @return a new Context instance with the specified block as the holder. */ - static Context block(@NotNull CustomCropsBlockState block, boolean threadSafe) { - return new BlockContextImpl(block, threadSafe); + static Context block(@NotNull CustomCropsBlockState block, @NotNull Location location, boolean threadSafe) { + return new BlockContextImpl(block, location, threadSafe); + } + + /** + * Updates location for the context + * + * @param location location + */ + default void updateLocation(Location location) { + arg(ContextKeys.LOCATION, location) + .arg(ContextKeys.X, location.getBlockX()) + .arg(ContextKeys.Y, location.getBlockY()) + .arg(ContextKeys.Z, location.getBlockZ()) + .arg(ContextKeys.WORLD, location.getWorld().getName()); } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java index 8a846b0..57e9ef2 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/ContextKeys.java @@ -18,7 +18,6 @@ package net.momirealms.customcrops.api.context; import org.bukkit.Location; -import org.bukkit.entity.Player; import org.bukkit.inventory.EquipmentSlot; import java.util.Objects; @@ -45,7 +44,6 @@ public class ContextKeys { public static final ContextKeys ICON = of("icon", String.class); public static final ContextKeys MAX_TIMES = of("max_times", Integer.class); public static final ContextKeys LEFT_TIMES = of("left_times", Integer.class); - public static final ContextKeys PLAYER_INSTANCE = of("player_instance", Player.class); private final String key; private final Class type; diff --git a/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java b/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java index 679563b..f6a7d30 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/context/PlayerContextImpl.java @@ -27,12 +27,8 @@ public final class PlayerContextImpl extends AbstractContext { super(player, sync); if (player == null) return; final Location location = player.getLocation(); - arg(ContextKeys.PLAYER, player.getName()) - .arg(ContextKeys.LOCATION, location) - .arg(ContextKeys.X, location.getBlockX()) - .arg(ContextKeys.Y, location.getBlockY()) - .arg(ContextKeys.Z, location.getBlockZ()) - .arg(ContextKeys.WORLD, location.getWorld().getName()); + arg(ContextKeys.PLAYER, player.getName()); + updateLocation(location); } @Override diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java index 82a3526..66239a3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/AbstractCustomEventListener.java @@ -20,7 +20,6 @@ package net.momirealms.customcrops.api.core; 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.block.CropBlock; import net.momirealms.customcrops.api.core.mechanic.crop.BoneMeal; import net.momirealms.customcrops.api.core.mechanic.crop.CropConfig; @@ -32,7 +31,6 @@ import net.momirealms.customcrops.api.core.world.CustomCropsWorld; import net.momirealms.customcrops.api.core.world.Pos3; import net.momirealms.customcrops.api.event.BoneMealDispenseEvent; import net.momirealms.customcrops.api.util.EventUtils; -import net.momirealms.customcrops.api.util.LocationUtils; import net.momirealms.customcrops.common.helper.VersionHelper; import org.bukkit.Location; import org.bukkit.Material; @@ -383,13 +381,12 @@ public abstract class AbstractCustomEventListener implements Listener { String id = itemManager.id(storage); if (id.equals(itemID)) { storage.setAmount(storage.getAmount() - 1); - Context context = Context.player(null); - context.arg(ContextKeys.LOCATION, location); - boneMeal.triggerActions(context); + Context playerContext = Context.player(null); + playerContext.updateLocation(location); + boneMeal.triggerActions(playerContext); int afterPoints = Math.min(point + boneMeal.rollPoint(), cropConfig.maxPoints()); cropBlock.point(state, afterPoints); - Context blockContext = Context.block(state); - blockContext.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); + Context blockContext = Context.block(state, location); for (int i = point + 1; i <= afterPoints; i++) { CropStageConfig stage = cropConfig.stageByPoint(i); if (stage != null) { @@ -399,12 +396,11 @@ public abstract class AbstractCustomEventListener implements Listener { CropStageConfig currentStage = cropConfig.stageWithModelByPoint(point); CropStageConfig afterStage = cropConfig.stageWithModelByPoint(afterPoints); if (currentStage == afterStage) return; - Location bukkitLocation = location.toLocation(world.bukkitWorld()); - FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(location, ExistenceForm.ANY); if (rotation == FurnitureRotation.NONE && cropConfig.rotation()) { rotation = FurnitureRotation.random(); } - BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, afterStage.existenceForm(), Objects.requireNonNull(afterStage.stageID()), rotation); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(location, afterStage.existenceForm(), Objects.requireNonNull(afterStage.stageID()), rotation); } } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java index c5cef05..97a060f 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/CropBlock.java @@ -76,7 +76,8 @@ public class CropBlock extends AbstractCustomCropsBlock { public void onBreak(WrappedBreakEvent event) { List configs = Registries.STAGE_TO_CROP_UNSAFE.get(event.brokenID()); CustomCropsWorld world = event.world(); - Pos3 pos3 = Pos3.from(event.location()); + Location location = LocationUtils.toBlockLocation(event.location()); + Pos3 pos3 = Pos3.from(location); if (configs == null || configs.isEmpty()) { world.removeBlockState(pos3); return; @@ -102,7 +103,8 @@ public class CropBlock extends AbstractCustomCropsBlock { final Player player = event.playerBreaker(); Context context = Context.player(player); - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(event.location())); + + context.updateLocation(location); // check requirements if (!RequirementManager.isSatisfied(context, cropConfig.breakRequirements())) { @@ -169,7 +171,7 @@ public class CropBlock extends AbstractCustomCropsBlock { // data first CustomCropsWorld world = event.world(); - Location location = event.location(); + Location location = LocationUtils.toBlockLocation(event.location()); Pos3 pos3 = Pos3.from(location); // fix if possible CustomCropsBlockState state = fixOrGetState(world, pos3, event.relatedID()); @@ -198,7 +200,7 @@ public class CropBlock extends AbstractCustomCropsBlock { String blockBelowID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(potLocation.getBlock()); PotConfig potConfig = Registries.ITEM_TO_POT.get(blockBelowID); if (potConfig != null) { - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(potLocation)); + context.updateLocation(potLocation); PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); assert potBlock != null; // fix or get data @@ -207,7 +209,7 @@ public class CropBlock extends AbstractCustomCropsBlock { return; } - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); + context.updateLocation(location); if (point < cropConfig.maxPoints()) { for (BoneMeal boneMeal : cropConfig.boneMeals()) { if (boneMeal.requiredItem().equals(event.itemID()) && boneMeal.amountOfRequiredItem() <= itemInHand.getAmount()) { @@ -230,8 +232,7 @@ public class CropBlock extends AbstractCustomCropsBlock { CropStageConfig nextStage = cropConfig.stageWithModelByPoint(afterPoints); - Context blockContext = Context.block(state); - blockContext.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); + Context blockContext = Context.block(state, location); for (int i = point + 1; i <= afterPoints; i++) { CropStageConfig stage = cropConfig.stageByPoint(i); if (stage != null) { @@ -240,12 +241,11 @@ public class CropBlock extends AbstractCustomCropsBlock { } if (Objects.equals(nextStage.stageID(), event.relatedID())) return; - Location bukkitLocation = location.toLocation(world.bukkitWorld()); - FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(bukkitLocation, ExistenceForm.ANY); + FurnitureRotation rotation = BukkitCustomCropsPlugin.getInstance().getItemManager().remove(location, ExistenceForm.ANY); if (rotation == FurnitureRotation.NONE && cropConfig.rotation()) { rotation = FurnitureRotation.random(); } - BukkitCustomCropsPlugin.getInstance().getItemManager().place(bukkitLocation, nextStage.existenceForm(), Objects.requireNonNull(nextStage.stageID()), rotation); + BukkitCustomCropsPlugin.getInstance().getItemManager().place(location, nextStage.existenceForm(), Objects.requireNonNull(nextStage.stageID()), rotation); return; } } @@ -307,9 +307,8 @@ public class CropBlock extends AbstractCustomCropsBlock { } } - Context context = Context.block(state).arg(ContextKeys.OFFLINE, offline); Location bukkitLocation = location.toLocation(bukkitWorld); - context.arg(ContextKeys.LOCATION, bukkitLocation); + Context context = Context.block(state, bukkitLocation).arg(ContextKeys.OFFLINE, offline); for (DeathCondition deathCondition : config.deathConditions()) { if (deathCondition.isMet(context)) { BukkitCustomCropsPlugin.getInstance().getScheduler().sync().runLater(() -> { diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java index ff36325..b1ba078 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/DeadCrop.java @@ -48,14 +48,14 @@ public class DeadCrop extends AbstractCustomCropsBlock { Context context = Context.player(player); // data first CustomCropsWorld world = event.world(); - Location location = event.location(); + Location location = LocationUtils.toBlockLocation(event.location()); context.arg(ContextKeys.SLOT, event.hand()); // check pot below Location potLocation = location.clone().subtract(0,1,0); String blockBelowID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(potLocation.getBlock()); PotConfig potConfig = Registries.ITEM_TO_POT.get(blockBelowID); if (potConfig != null) { - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(potLocation)); + context.updateLocation(potLocation); PotBlock potBlock = (PotBlock) BuiltInBlockMechanics.POT.mechanic(); assert potBlock != null; // fix or get data diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java index 2fcf908..81bcc7d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/PotBlock.java @@ -84,7 +84,7 @@ public class PotBlock extends AbstractCustomCropsBlock { @Override public void onBreak(WrappedBreakEvent event) { CustomCropsWorld world = event.world(); - Location location = event.location(); + Location location = LocationUtils.toBlockLocation(event.location()); Pos3 pos3 = Pos3.from(location); PotConfig config = Registries.ITEM_TO_POT.get(event.brokenID()); if (config == null) { @@ -95,7 +95,7 @@ public class PotBlock extends AbstractCustomCropsBlock { final Player player = event.playerBreaker(); Context context = Context.player(player); - context.arg(ContextKeys.LOCATION, location); + context.updateLocation(location); if (!RequirementManager.isSatisfied(context, config.breakRequirements())) { event.setCancelled(true); return; @@ -130,7 +130,7 @@ public class PotBlock extends AbstractCustomCropsBlock { cropConfig = cropConfigs.get(0); } - context.arg(ContextKeys.LOCATION, upperLocation); + context.updateLocation(upperLocation); if (!RequirementManager.isSatisfied(context, cropConfig.breakRequirements())) { event.setCancelled(true); return; @@ -171,7 +171,7 @@ public class PotBlock extends AbstractCustomCropsBlock { BukkitCustomCropsPlugin.getInstance().getItemManager().remove(upperLocation, ExistenceForm.ANY); } - context.arg(ContextKeys.LOCATION, location); + context.updateLocation(location); ActionManager.trigger(context, config.breakActions()); world.removeBlockState(pos3); @@ -231,7 +231,7 @@ public class PotBlock extends AbstractCustomCropsBlock { return; } - Location location = event.location(); + Location location = LocationUtils.toBlockLocation(event.location()); Pos3 pos3 = Pos3.from(location); CustomCropsWorld world = event.world(); @@ -241,7 +241,7 @@ public class PotBlock extends AbstractCustomCropsBlock { final Player player = event.player(); Context context = Context.player(player); context.arg(ContextKeys.SLOT, event.hand()); - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(location)); + context.updateLocation(location); // check use requirements if (!RequirementManager.isSatisfied(context, potConfig.useRequirements())) { @@ -407,17 +407,17 @@ public class PotBlock extends AbstractCustomCropsBlock { boolean fertilizerChanged = tickFertilizer(state); + Location bukkitLocation = location.toLocation(bukkitWorld); if (fertilizerChanged || waterChanged) { boolean finalHasNaturalWater = hasNaturalWater; - Location bukkitLocation = location.toLocation(bukkitWorld); + BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(() -> { updateBlockAppearance(bukkitLocation, config, finalHasNaturalWater, fertilizers(state)); }, bukkitLocation); } - ActionManager.trigger(Context.block(state) - .arg(ContextKeys.OFFLINE, offline) - .arg(ContextKeys.LOCATION, location.toLocation(bukkitWorld)), + ActionManager.trigger(Context.block(state, bukkitLocation) + .arg(ContextKeys.OFFLINE, offline), config.tickActions() ); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java index fe51a4e..31d752a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/block/SprinklerBlock.java @@ -74,7 +74,8 @@ public class SprinklerBlock extends AbstractCustomCropsBlock { @Override public void onBreak(WrappedBreakEvent event) { CustomCropsWorld world = event.world(); - Pos3 pos3 = Pos3.from(event.location()); + Location location = LocationUtils.toBlockLocation(event.location()); + Pos3 pos3 = Pos3.from(location); SprinklerConfig config = Registries.ITEM_TO_SPRINKLER.get(event.brokenID()); if (config == null) { world.removeBlockState(pos3); @@ -83,14 +84,14 @@ public class SprinklerBlock extends AbstractCustomCropsBlock { final Player player = event.playerBreaker(); Context context = Context.player(player); - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(event.location())); + context.updateLocation(location); CustomCropsBlockState state = fixOrGetState(world, pos3, config, event.brokenID()); if (!RequirementManager.isSatisfied(context, config.breakRequirements())) { event.setCancelled(true); return; } - SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(event.entityBreaker(), event.blockBreaker(), event.location(), state, config, event.reason()); + SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(event.entityBreaker(), event.blockBreaker(), location, state, config, event.reason()); if (EventUtils.fireAndCheckCancel(breakEvent)) { event.setCancelled(true); return; @@ -110,7 +111,8 @@ public class SprinklerBlock extends AbstractCustomCropsBlock { final Player player = event.player(); Context context = Context.player(player); - context.arg(ContextKeys.LOCATION, LocationUtils.toBlockLocation(event.location())); + Location location = LocationUtils.toBlockLocation(event.location()); + context.updateLocation(location); if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { event.setCancelled(true); return; @@ -156,7 +158,7 @@ public class SprinklerBlock extends AbstractCustomCropsBlock { Location location = LocationUtils.toBlockLocation(event.location()); Context context = Context.player(player); context.arg(ContextKeys.SLOT, event.hand()); - context.arg(ContextKeys.LOCATION, location); + context.updateLocation(location); CustomCropsBlockState state = fixOrGetState(event.world(), Pos3.from(location), config, event.relatedID()); if (!RequirementManager.isSatisfied(context, config.useRequirements())) { return; @@ -257,10 +259,9 @@ public class SprinklerBlock extends AbstractCustomCropsBlock { updateState = false; } - Context context = Context.block(state).arg(ContextKeys.OFFLINE, offline); World bukkitWorld = world.bukkitWorld(); Location bukkitLocation = location.toLocation(bukkitWorld); - context.arg(ContextKeys.LOCATION, bukkitLocation); + Context context = Context.block(state, bukkitLocation).arg(ContextKeys.OFFLINE, offline); CompletableFuture syncCheck = new CompletableFuture<>(); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java index d267945..b50ff6e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/FertilizerItem.java @@ -77,7 +77,7 @@ public class FertilizerItem extends AbstractCustomCropsItem { targetBlockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetLocation); } - context.arg(ContextKeys.LOCATION, targetLocation); + context.updateLocation(targetLocation); // if the clicked block is a pot PotConfig potConfig = Registries.ITEM_TO_POT.get(targetBlockID); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java index 2a5e187..cd99223 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SeedItem.java @@ -67,7 +67,7 @@ public class SeedItem extends AbstractCustomCropsItem { Location seedLocation = LocationUtils.toBlockLocation(event.location().add(0, 1, 0)); Context context = Context.player(player); context.arg(ContextKeys.SLOT, event.hand()); - context.arg(ContextKeys.LOCATION, seedLocation); + context.updateLocation(seedLocation); // check pot whitelist if (!cropConfig.potWhitelist().contains(potConfig.id())) { ActionManager.trigger(context, cropConfig.wrongPotActions()); diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java index c78c334..eddcab1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/SprinklerItem.java @@ -20,7 +20,6 @@ package net.momirealms.customcrops.api.core.item; 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.*; import net.momirealms.customcrops.api.core.block.SprinklerBlock; import net.momirealms.customcrops.api.core.mechanic.sprinkler.SprinklerConfig; @@ -78,7 +77,7 @@ public class SprinklerItem extends AbstractCustomCropsItem { final Player player = event.player(); final ItemStack itemInHand = event.itemInHand(); Context context = Context.player(player); - context.arg(ContextKeys.LOCATION, targetLocation); + context.updateLocation(targetLocation); // check requirements if (!RequirementManager.isSatisfied(context, config.placeRequirements())) { return InteractionResult.COMPLETE; diff --git a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java index 6e240bd..32fcf86 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java +++ b/api/src/main/java/net/momirealms/customcrops/api/core/item/WateringCanItem.java @@ -133,7 +133,7 @@ public class WateringCanItem extends AbstractCustomCropsItem { return; final Vector vector = result.getHitPosition(); // for old config compatibility - context.arg(ContextKeys.LOCATION, new Location(player.getWorld(), vector.getX() - 0.5,vector.getY() - 1, vector.getZ() - 0.5)); + context.updateLocation(new Location(player.getWorld(), vector.getX() - 0.5,vector.getY() - 1, vector.getZ() - 0.5)); final ItemStack itemInHand = event.itemInHand(); int water = getCurrentWater(itemInHand); @@ -202,7 +202,7 @@ public class WateringCanItem extends AbstractCustomCropsItem { Location targetLocation = LocationUtils.toBlockLocation(event.location()); final Context context = Context.player(player); context.arg(ContextKeys.SLOT, event.hand()); - context.arg(ContextKeys.LOCATION, targetLocation); + context.updateLocation(targetLocation); // check watering can requirements if (!RequirementManager.isSatisfied(context, wateringCanConfig.requirements())) { @@ -315,7 +315,7 @@ public class WateringCanItem extends AbstractCustomCropsItem { if (potBlock.addWater(potState, potConfig, wateringCanConfig.wateringAmount())) { potBlock.updateBlockAppearance(temp, potConfig, true, potBlock.fertilizers(potState)); } - context.arg(ContextKeys.LOCATION, temp); + context.updateLocation(temp); ActionManager.trigger(context, potConfig.addWaterActions()); } @@ -332,7 +332,7 @@ public class WateringCanItem extends AbstractCustomCropsItem { return InteractionResult.COMPLETE; final Vector vector = result.getHitPosition(); // for old config compatibility - context.arg(ContextKeys.LOCATION, new Location(player.getWorld(), vector.getX() - 0.5,vector.getY() - 1, vector.getZ() - 0.5)); + context.updateLocation(new Location(player.getWorld(), vector.getX() - 0.5,vector.getY() - 1, vector.getZ() - 0.5)); String blockID = BukkitCustomCropsPlugin.getInstance().getItemManager().blockID(targetBlock); BukkitCustomCropsPlugin.getInstance().debug(blockID); diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/EntityProvider.java b/api/src/main/java/net/momirealms/customcrops/api/integration/EntityProvider.java new file mode 100644 index 0000000..5027622 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/EntityProvider.java @@ -0,0 +1,49 @@ +/* + * 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.integration; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * The EntityProvider interface defines methods to interact with external entity + * spawning systems, allowing the spawning of entities at specified locations with + * given properties. Implementations of this interface should provide the logic + * for spawning entities and managing their properties. + */ +public interface EntityProvider extends ExternalProvider { + + /** + * Spawns an entity at the specified location with the given properties. + * + * @param location The location where the entity will be spawned. + * @param id The identifier of the entity to be spawned. + * @param propertyMap A map containing additional properties for the entity. + * @return The spawned entity. + */ + @NotNull + Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap); + + default Entity spawn(@NotNull Location location, @NotNull String id) { + return spawn(location, id, new HashMap<>()); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java b/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java index 6605814..afc890b 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/IntegrationManager.java @@ -75,4 +75,29 @@ public interface IntegrationManager extends Reloadable { * @return true if unregistration is successful, false otherwise. */ boolean unregisterItemProvider(@NotNull String id); + + /** + * Registers an EntityProvider. + * + * @param entityProvider the EntityProvider to register + * @return true if registration is successful, false otherwise. + */ + boolean registerEntityProvider(@NotNull EntityProvider entityProvider); + + /** + * Unregisters an EntityProvider by its ID. + * + * @param id the ID of the EntityProvider to unregister + * @return true if unregistration is successful, false otherwise. + */ + boolean unregisterEntityProvider(@NotNull String id); + + /** + * Retrieves a registered EntityProvider by its ID. + * + * @param id the ID of the EntityProvider to retrieve + * @return the EntityProvider if found, or null if not found + */ + @Nullable + EntityProvider getEntityProvider(String id); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java b/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java index 09e497c..837f020 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java +++ b/api/src/main/java/net/momirealms/customcrops/api/requirement/AbstractRequirementManager.java @@ -875,7 +875,7 @@ public abstract class AbstractRequirementManager implements RequirementManage if (advanced) ActionManager.trigger(context, actions); return false; }; - }, "natural-light"); + }, "natural-light", "skylight"); registerRequirement((args, actions, advanced) -> { int value = (int) args; return context -> { @@ -885,7 +885,7 @@ public abstract class AbstractRequirementManager implements RequirementManage if (advanced) ActionManager.trigger(context, actions); return false; }; - }, "skylight_more_than", "skylight-more-than"); + }, "skylight_more_than", "skylight-more-than", "natural_light_more_than", "natural-light-more-than"); registerRequirement((args, actions, advanced) -> { int value = (int) args; return context -> { @@ -895,7 +895,7 @@ public abstract class AbstractRequirementManager implements RequirementManage if (advanced) ActionManager.trigger(context, actions); return false; }; - }, "skylight_less_than", "skylight-less-than"); + }, "skylight_less_than", "skylight-less-than", "natural_light_less_than", "natural-light-less-than"); registerRequirement((args, actions, advanced) -> { int value = (int) args; return context -> { diff --git a/compatibility/build.gradle.kts b/compatibility/build.gradle.kts index f2bea9b..0e61b34 100644 --- a/compatibility/build.gradle.kts +++ b/compatibility/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { compileOnly("pers.neige.neigeitems:NeigeItems:1.17.13") compileOnly("io.lumine:Mythic-Dist:5.6.2") compileOnly("com.github.Xiao-MoMi:Custom-Fishing:2.2.19") + compileOnly("com.github.LoneDev6:api-itemsadder:3.6.3-beta-14") // eco compileOnly("com.willfp:eco:6.70.1") compileOnly("com.willfp:EcoJobs:3.56.1") diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/ItemsAdderEntityProvider.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/ItemsAdderEntityProvider.java new file mode 100644 index 0000000..c4dad9b --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/ItemsAdderEntityProvider.java @@ -0,0 +1,47 @@ +/* + * 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.bukkit.integration.entity; + +import dev.lone.itemsadder.api.CustomEntity; +import net.momirealms.customcrops.api.integration.EntityProvider; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class ItemsAdderEntityProvider implements EntityProvider { + + @Override + public String identifier() { + return "ItemsAdder"; + } + + @NotNull + @Override + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { + CustomEntity customEntity = CustomEntity.spawn( + id, + location, + (Boolean) propertyMap.getOrDefault("frustumCulling", true), + (Boolean) propertyMap.getOrDefault("noBase", false), + (Boolean) propertyMap.getOrDefault("noHitbox", false) + ); + return customEntity.getEntity(); + } +} \ No newline at end of file diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/MythicEntityProvider.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/MythicEntityProvider.java new file mode 100644 index 0000000..662d890 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/MythicEntityProvider.java @@ -0,0 +1,58 @@ +/* + * 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.bukkit.integration.entity; + +import io.lumine.mythic.api.adapters.AbstractLocation; +import io.lumine.mythic.api.mobs.MythicMob; +import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.bukkit.utils.serialize.Position; +import io.lumine.mythic.core.mobs.ActiveMob; +import net.momirealms.customcrops.api.integration.EntityProvider; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Optional; + +public class MythicEntityProvider implements EntityProvider { + + private MythicBukkit mythicBukkit; + + @Override + public String identifier() { + return "MythicMobs"; + } + + @NotNull + @Override + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { + if (this.mythicBukkit == null || mythicBukkit.isClosed()) { + this.mythicBukkit = MythicBukkit.inst(); + } + Optional mythicMob = mythicBukkit.getMobManager().getMythicMob(id); + if (mythicMob.isPresent()) { + MythicMob theMob = mythicMob.get(); + Position position = Position.of(location); + AbstractLocation abstractLocation = new AbstractLocation(position); + ActiveMob activeMob = theMob.spawn(abstractLocation, (double) propertyMap.getOrDefault("level", 0d)); + return activeMob.getEntity().getBukkitEntity(); + } + throw new NullPointerException("MythicMobs: " + id + " doesn't exist."); + } +} diff --git a/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/VanillaEntityProvider.java b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/VanillaEntityProvider.java new file mode 100644 index 0000000..8d9a080 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customcrops/bukkit/integration/entity/VanillaEntityProvider.java @@ -0,0 +1,41 @@ +/* + * 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.bukkit.integration.entity; + +import net.momirealms.customcrops.api.integration.EntityProvider; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.Map; + +public class VanillaEntityProvider implements EntityProvider { + + @Override + public String identifier() { + return "vanilla"; + } + + @NotNull + @Override + public Entity spawn(@NotNull Location location, @NotNull String id, @NotNull Map propertyMap) { + return location.getWorld().spawnEntity(location, EntityType.valueOf(id.toUpperCase(Locale.ENGLISH))); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java index ee23935..69c7537 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/BukkitIntegrationManager.java @@ -18,10 +18,10 @@ package net.momirealms.customcrops.bukkit.integration; import net.momirealms.customcrops.api.BukkitCustomCropsPlugin; -import net.momirealms.customcrops.api.integration.IntegrationManager; -import net.momirealms.customcrops.api.integration.ItemProvider; -import net.momirealms.customcrops.api.integration.LevelerProvider; -import net.momirealms.customcrops.api.integration.SeasonProvider; +import net.momirealms.customcrops.api.integration.*; +import net.momirealms.customcrops.bukkit.integration.entity.ItemsAdderEntityProvider; +import net.momirealms.customcrops.bukkit.integration.entity.MythicEntityProvider; +import net.momirealms.customcrops.bukkit.integration.entity.VanillaEntityProvider; import net.momirealms.customcrops.bukkit.integration.item.*; import net.momirealms.customcrops.bukkit.integration.level.*; import net.momirealms.customcrops.bukkit.integration.papi.CustomCropsPapi; @@ -43,6 +43,7 @@ public class BukkitIntegrationManager implements IntegrationManager { private final BukkitCustomCropsPlugin plugin; private final HashMap levelerProviders = new HashMap<>(); + private final HashMap entityProviders = new HashMap<>(); public BukkitIntegrationManager(BukkitCustomCropsPlugin plugin) { this.plugin = plugin; @@ -60,6 +61,7 @@ public class BukkitIntegrationManager implements IntegrationManager { @Override public void load() { + registerEntityProvider(new VanillaEntityProvider()); if (isHooked("MMOItems")) { registerItemProvider(new MMOItemsItemProvider()); } @@ -69,11 +71,15 @@ public class BukkitIntegrationManager implements IntegrationManager { if (isHooked("NeigeItems")) { registerItemProvider(new NeigeItemsItemProvider()); } + if (isHooked("ItemsAdder")) { + registerEntityProvider(new ItemsAdderEntityProvider()); + } if (isHooked("CustomFishing", "2.2", "2.3", "2.4")) { registerItemProvider(new CustomFishingItemProvider()); } if (isHooked("MythicMobs", "5")) { registerItemProvider(new MythicMobsItemProvider()); + registerEntityProvider(new MythicEntityProvider()); } if (isHooked("EcoJobs")) { registerLevelerProvider(new EcoJobsLevelerProvider()); @@ -157,8 +163,8 @@ public class BukkitIntegrationManager implements IntegrationManager { @Override @Nullable - public LevelerProvider getLevelerProvider(String plugin) { - return levelerProviders.get(plugin); + public LevelerProvider getLevelerProvider(String id) { + return levelerProviders.get(id); } @Override @@ -175,4 +181,22 @@ public class BukkitIntegrationManager implements IntegrationManager { public boolean unregisterItemProvider(@NotNull String id) { return ((BukkitItemManager) plugin.getItemManager()).unregisterItemProvider(id); } + + @Override + public boolean registerEntityProvider(@NotNull EntityProvider entity) { + if (entityProviders.containsKey(entity.identifier())) return false; + entityProviders.put(entity.identifier(), entity); + return true; + } + + @Override + public boolean unregisterEntityProvider(@NotNull String id) { + return entityProviders.remove(id) != null; + } + + @Nullable + @Override + public EntityProvider getEntityProvider(String id) { + return entityProviders.get(id); + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java index d3bf622..b555d9d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java +++ b/plugin/src/main/java/net/momirealms/customcrops/bukkit/integration/adaptor/BukkitWorldAdaptor.java @@ -30,7 +30,6 @@ import net.momirealms.customcrops.api.core.world.adaptor.AbstractWorldAdaptor; import net.momirealms.customcrops.api.util.StringUtils; import net.momirealms.customcrops.common.helper.GsonHelper; import net.momirealms.customcrops.common.helper.VersionHelper; -import net.momirealms.customcrops.common.util.FileUtils; import net.momirealms.customcrops.common.util.Key; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey;