9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-25 18:09:28 +00:00
This commit is contained in:
XiaoMoMi
2024-03-16 11:17:01 +08:00
parent 559a46da65
commit c6652372f6
31 changed files with 953 additions and 142 deletions

View File

@@ -32,7 +32,6 @@ import net.momirealms.customcrops.compatibility.season.AdvancedSeasonsImpl;
import net.momirealms.customcrops.compatibility.season.InBuiltSeason;
import net.momirealms.customcrops.compatibility.season.RealisticSeasonsImpl;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
@@ -161,9 +160,4 @@ public class IntegrationManagerImpl implements IntegrationManager {
public int getDate(World world) {
return seasonInterface.getDate(world);
}
@Override
public ItemStack build(String itemID) {
return null;
}
}

View File

@@ -45,4 +45,20 @@ public class AdvancedSeasonsImpl implements SeasonInterface {
public int getDate(World world) {
return 0;
}
@Override
public void setSeason(World world, Season season) {
String seasonName = switch (season) {
case AUTUMN -> "FALL";
case WINTER -> "WINTER";
case SUMMER -> "SUMMER";
case SPRING -> "SPRING";
};
api.setSeason(seasonName, world);
}
@Override
public void setDate(World world, int date) {
}
}

View File

@@ -21,6 +21,7 @@ import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
@@ -52,4 +53,22 @@ public class InBuiltSeason implements SeasonInterface {
.map(cropsWorld -> cropsWorld.getInfoData().getDate())
.orElse(0);
}
@Override
public void setSeason(World world, Season season) {
worldManager.getCustomCropsWorld(world)
.ifPresent(customWorld -> {
WorldInfoData infoData = customWorld.getInfoData();
infoData.setSeason(season);
});
}
@Override
public void setDate(World world, int date) {
worldManager.getCustomCropsWorld(world)
.ifPresent(customWorld -> {
WorldInfoData infoData = customWorld.getInfoData();
infoData.setDate(date);
});
}
}

View File

@@ -18,6 +18,7 @@
package net.momirealms.customcrops.compatibility.season;
import me.casperge.realisticseasons.api.SeasonsAPI;
import me.casperge.realisticseasons.calendar.Date;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import org.bukkit.World;
@@ -25,9 +26,15 @@ import org.jetbrains.annotations.Nullable;
public class RealisticSeasonsImpl implements SeasonInterface {
private final SeasonsAPI api;
public RealisticSeasonsImpl() {
this.api = SeasonsAPI.getInstance();
}
@Override
public @Nullable Season getSeason(World world) {
return switch (SeasonsAPI.getInstance().getSeason(world)) {
return switch (api.getSeason(world)) {
case WINTER -> Season.WINTER;
case SPRING -> Season.SPRING;
case SUMMER -> Season.SUMMER;
@@ -38,6 +45,23 @@ public class RealisticSeasonsImpl implements SeasonInterface {
@Override
public int getDate(World world) {
return SeasonsAPI.getInstance().getDate(world).getDay();
return api.getDate(world).getDay();
}
@Override
public void setSeason(World world, Season season) {
me.casperge.realisticseasons.season.Season rsSeason = switch (season) {
case AUTUMN -> me.casperge.realisticseasons.season.Season.FALL;
case SUMMER -> me.casperge.realisticseasons.season.Season.SUMMER;
case WINTER -> me.casperge.realisticseasons.season.Season.WINTER;
case SPRING -> me.casperge.realisticseasons.season.Season.SPRING;
};
api.setSeason(world, rsSeason);
}
@Override
public void setDate(World world, int date) {
Date rsDate = api.getDate(world);
api.setDate(world, new Date(date, rsDate.getMonth(), rsDate.getYear()));
}
}

View File

@@ -26,6 +26,10 @@ import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.common.Initable;
import net.momirealms.customcrops.api.integration.SeasonInterface;
import net.momirealms.customcrops.api.manager.MessageManager;
import net.momirealms.customcrops.api.mechanic.item.ItemType;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import net.momirealms.customcrops.compatibility.season.InBuiltSeason;
@@ -54,8 +58,8 @@ public class CommandManager implements Initable {
getReloadCommand(),
getAboutCommand(),
getSeasonCommand(),
getDateCommand()
//getStressTest()
getDateCommand(),
getForceTickCommand()
)
.register();
}
@@ -85,6 +89,32 @@ public class CommandManager implements Initable {
});
}
private CommandAPICommand getForceTickCommand() {
return new CommandAPICommand("force-tick")
.withArguments(new WorldArgument("world"))
.withArguments(new StringArgument("type").replaceSuggestions(ArgumentSuggestions.strings("sprinkler", "crop", "pot", "scarecrow", "greenhouse")))
.executes((sender, args) -> {
World world = (World) args.get("world");
ItemType itemType = ItemType.valueOf(((String) args.get("type")).toUpperCase(Locale.ENGLISH));
Optional<CustomCropsWorld> customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world);
if (customCropsWorld.isEmpty()) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
plugin.getScheduler().runTaskAsync(() -> {
for (CustomCropsChunk chunk : customCropsWorld.get().getChunkStorage()) {
for (CustomCropsSection section : chunk.getSections()) {
for (CustomCropsBlock block : section.getBlocks()) {
if (block.getType() == itemType) {
block.tick(1);
}
}
}
}
});
});
}
private CommandAPICommand getDateCommand() {
return new CommandAPICommand("date")
.withSubcommands(
@@ -176,45 +206,4 @@ public class CommandManager implements Initable {
})
);
}
// private CommandAPICommand getStressTest() {
// return new CommandAPICommand("test").executes((sender, args) -> {
// for (int i = 0; i < 16; i++) {
// for (int j = 0; j < 16; j++) {
// for (int k = -64; k < 0; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 0), location);
// }
// for (int k = 1; k < 64; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 1), location);
// }
// for (int k = 65; k < 128; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 2), location);
// }
// for (int k = 129; k < 165; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addPotAt(new MemoryPot(location, "default"), location);
// }
// for (int k = 166; k < 190; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addPotAt(new MemoryPot(location, "sprinkler"), location);
// }
// for (int k = 191; k < 250; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 3), location);
// }
// for (int k = 251; k < 300; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "sbsbssbsb", 3), location);
// }
// for (int k = 301; k < 320; k++) {
// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j);
// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "sbsbssbsb", 2), location);
// }
// }
// }
// });
// }
}

View File

@@ -133,7 +133,7 @@ public class ConfigManagerImpl extends ConfigManager {
syncSeasons = mechanics.getBoolean("sync-season.enable", true);
if (syncSeasons) {
referenceWorld = new WeakReference<>(Bukkit.getWorld(Objects.requireNonNull(mechanics.getString("sync-season.reference"))));
referenceWorld = new WeakReference<>(Bukkit.getWorld(mechanics.getString("sync-season.reference", "world")));
}
}

View File

@@ -38,6 +38,7 @@ import net.momirealms.customcrops.api.mechanic.item.fertilizer.QualityCrop;
import net.momirealms.customcrops.api.mechanic.item.fertilizer.Variation;
import net.momirealms.customcrops.api.mechanic.item.fertilizer.YieldIncrease;
import net.momirealms.customcrops.api.mechanic.misc.CRotation;
import net.momirealms.customcrops.api.mechanic.misc.Reason;
import net.momirealms.customcrops.api.mechanic.misc.Value;
import net.momirealms.customcrops.api.mechanic.requirement.Requirement;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
@@ -696,7 +697,7 @@ public class ActionManagerImpl implements ActionManager {
switch (removed.get().getType()) {
case SPRINKLER -> {
WorldSprinkler sprinkler = (WorldSprinkler) removed.get();
SprinklerBreakEvent event = new SprinklerBreakEvent(state.getPlayer(), state.getLocation(), sprinkler);
SprinklerBreakEvent event = new SprinklerBreakEvent(state.getPlayer(), state.getLocation(), sprinkler, Reason.ACTION);
if (EventUtils.fireAndCheckCancel(event))
return;
if (arg) sprinkler.getConfig().trigger(ActionTrigger.BREAK, state);
@@ -705,7 +706,7 @@ public class ActionManagerImpl implements ActionManager {
}
case CROP -> {
WorldCrop crop = (WorldCrop) removed.get();
CropBreakEvent event = new CropBreakEvent(state.getPlayer(), state.getLocation(), crop);
CropBreakEvent event = new CropBreakEvent(state.getPlayer(), state.getLocation(), crop, Reason.ACTION);
if (EventUtils.fireAndCheckCancel(event))
return;
Crop cropConfig = crop.getConfig();
@@ -718,7 +719,7 @@ public class ActionManagerImpl implements ActionManager {
}
case POT -> {
WorldPot pot = (WorldPot) removed.get();
PotBreakEvent event = new PotBreakEvent(state.getPlayer(), state.getLocation(), pot);
PotBreakEvent event = new PotBreakEvent(state.getPlayer(), state.getLocation(), pot, Reason.ACTION);
if (EventUtils.fireAndCheckCancel(event))
return;
if (arg) pot.getConfig().trigger(ActionTrigger.BREAK, state);

View File

@@ -32,12 +32,12 @@ import net.momirealms.customcrops.api.mechanic.item.*;
import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod;
import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod;
import net.momirealms.customcrops.api.mechanic.misc.CRotation;
import net.momirealms.customcrops.api.mechanic.misc.Reason;
import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar;
import net.momirealms.customcrops.api.mechanic.requirement.State;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop;
import net.momirealms.customcrops.api.mechanic.world.level.WorldPot;
import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler;
import net.momirealms.customcrops.api.mechanic.world.level.*;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener;
import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener;
@@ -70,6 +70,7 @@ import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -629,12 +630,23 @@ public class ItemManagerImpl implements ItemManager {
if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) {
return FunctionResult.PASS;
}
// get or fix
Location location = breakWrapper.getLocation();
SimpleLocation simpleLocation = SimpleLocation.of(location);
Optional<WorldGlass> optionalWorldGlass = plugin.getWorldManager().getGlassAt(simpleLocation);
if (optionalWorldGlass.isEmpty()) {
WorldGlass glass = new MemoryGlass(simpleLocation);
optionalWorldGlass = Optional.of(glass);
plugin.getWorldManager().addGlassAt(glass, simpleLocation);
}
// fire event
GreenhouseGlassBreakEvent event = new GreenhouseGlassBreakEvent(breakWrapper.getPlayer(), breakWrapper.getLocation());
GreenhouseGlassBreakEvent event = new GreenhouseGlassBreakEvent(breakWrapper.getPlayer(), location, optionalWorldGlass.get(), Reason.BREAK);
if (EventUtils.fireAndCheckCancel(event))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
plugin.getWorldManager().removeGlassAt(SimpleLocation.of(breakWrapper.getLocation()));
plugin.getWorldManager().removeGlassAt(simpleLocation);
return FunctionResult.RETURN;
}, CFunction.FunctionPriority.NORMAL)
);
@@ -661,12 +673,23 @@ public class ItemManagerImpl implements ItemManager {
if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) {
return FunctionResult.PASS;
}
// get or fix
Location location = breakWrapper.getLocation();
SimpleLocation simpleLocation = SimpleLocation.of(location);
Optional<WorldScarecrow> optionalWorldScarecrow = plugin.getWorldManager().getScarecrowAt(simpleLocation);
if (optionalWorldScarecrow.isEmpty()) {
WorldScarecrow scarecrow = new MemoryScarecrow(simpleLocation);
optionalWorldScarecrow = Optional.of(scarecrow);
plugin.getWorldManager().addScarecrowAt(scarecrow, simpleLocation);
}
// fire event
ScarecrowBreakEvent event = new ScarecrowBreakEvent(breakWrapper.getPlayer(), breakWrapper.getLocation());
ScarecrowBreakEvent event = new ScarecrowBreakEvent(breakWrapper.getPlayer(), location, optionalWorldScarecrow.get(), Reason.BREAK);
if (EventUtils.fireAndCheckCancel(event))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
plugin.getWorldManager().removeScarecrowAt(SimpleLocation.of(breakWrapper.getLocation()));
plugin.getWorldManager().removeScarecrowAt(simpleLocation);
return FunctionResult.RETURN;
}, CFunction.FunctionPriority.NORMAL)
);
@@ -740,8 +763,20 @@ public class ItemManagerImpl implements ItemManager {
int waterInCan = wateringCan.getCurrentWater(itemStack);
if (waterInCan > 0 || wateringCan.isInfinite()) {
Collection<Location> pots = getPotInRange(clicked, wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey());
// get or fix pot
SimpleLocation simpleLocation = SimpleLocation.of(clicked);
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(simpleLocation);
if (worldPot.isEmpty()) {
plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it.");
MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey());
plugin.getWorldManager().addPotAt(memoryPot, simpleLocation);
worldPot = Optional.of(memoryPot);
}
// fire the event
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, wateringCan, clicked, pot);
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, new HashSet<>(pots), wateringCan, worldPot.get());
if (EventUtils.fireAndCheckCancel(waterEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -750,8 +785,8 @@ public class ItemManagerImpl implements ItemManager {
state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage()));
wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs());
wateringCan.trigger(ActionTrigger.CONSUME_WATER, state);
Collection<Location> pots = getPotInRange(clicked, wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey());
for (Location location : pots) {
for (Location location : waterEvent.getLocation()) {
plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater());
pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location));
}
@@ -809,8 +844,21 @@ public class ItemManagerImpl implements ItemManager {
int waterInCan = wateringCan.getCurrentWater(itemStack);
if (waterInCan > 0 || wateringCan.isInfinite()) {
Collection<Location> pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey());
// get or fix pot
SimpleLocation simpleLocation = SimpleLocation.of(potBlock.getLocation());
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(simpleLocation);
if (worldPot.isEmpty()) {
plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it.");
MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey());
plugin.getWorldManager().addPotAt(memoryPot, simpleLocation);
worldPot = Optional.of(memoryPot);
}
// fire the event
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, wateringCan, potBlock.getLocation(), pot);
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, new HashSet<>(pots), wateringCan, worldPot.get());
if (EventUtils.fireAndCheckCancel(waterEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -819,8 +867,8 @@ public class ItemManagerImpl implements ItemManager {
state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage()));
wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs());
wateringCan.trigger(ActionTrigger.CONSUME_WATER, state);
Collection<Location> pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey());
for (Location location : pots) {
for (Location location : waterEvent.getLocation()) {
plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater());
pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location));
}
@@ -875,8 +923,21 @@ public class ItemManagerImpl implements ItemManager {
int waterInCan = wateringCan.getCurrentWater(itemStack);
if (waterInCan > 0 || wateringCan.isInfinite()) {
Collection<Location> pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey());
// get or fix pot
SimpleLocation simpleLocation = SimpleLocation.of(potBlock.getLocation());
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(simpleLocation);
if (worldPot.isEmpty()) {
plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it.");
MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey());
plugin.getWorldManager().addPotAt(memoryPot, simpleLocation);
worldPot = Optional.of(memoryPot);
}
// fire the event
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, wateringCan, clicked, pot);
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemStack, new HashSet<>(pots), wateringCan, worldPot.get());
if (EventUtils.fireAndCheckCancel(waterEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -885,8 +946,8 @@ public class ItemManagerImpl implements ItemManager {
state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage()));
wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs());
wateringCan.trigger(ActionTrigger.CONSUME_WATER, state);
Collection<Location> pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey());
for (Location location : pots) {
for (Location location : waterEvent.getLocation()) {
plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater());
pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location));
}
@@ -936,6 +997,9 @@ public class ItemManagerImpl implements ItemManager {
Optional<WorldSprinkler> worldSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation);
if (worldSprinkler.isEmpty()) {
plugin.debug("Player " + player.getName() + " tried to interact a sprinkler which not exists in memory. Fixing the data...");
WorldSprinkler sp = new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0);
plugin.getWorldManager().addSprinklerAt(sp, simpleLocation);
worldSprinkler = Optional.of(sp);
} else {
if (sprinkler.getStorage() <= worldSprinkler.get().getWater()) {
return FunctionResult.RETURN;
@@ -943,7 +1007,7 @@ public class ItemManagerImpl implements ItemManager {
}
// fire the event
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemInHand, wateringCan, location, sprinkler);
WateringCanWaterEvent waterEvent = new WateringCanWaterEvent(player, itemInHand, new HashSet<>(Set.of(location)), wateringCan, worldSprinkler.get());
if (EventUtils.fireAndCheckCancel(waterEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -969,7 +1033,7 @@ public class ItemManagerImpl implements ItemManager {
if (method.getId().equals(clickedFurnitureID)) {
if (method.canFill(state)) {
// fire the event
WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, wateringCan, location, method);
WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, location, wateringCan, method);
if (EventUtils.fireAndCheckCancel(fillEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -1029,7 +1093,7 @@ public class ItemManagerImpl implements ItemManager {
if (method.canFill(state)) {
if (water < wateringCan.getStorage()) {
// fire the event
WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, wateringCan, state.getLocation(), method);
WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, state.getLocation(), wateringCan, method);
if (EventUtils.fireAndCheckCancel(fillEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -1083,7 +1147,7 @@ public class ItemManagerImpl implements ItemManager {
if (method.canFill(state)) {
if (water < wateringCan.getStorage()) {
// fire the event
WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, wateringCan, state.getLocation(), method);
WateringCanFillEvent fillEvent = new WateringCanFillEvent(player, itemInHand, state.getLocation(), wateringCan, method);
if (EventUtils.fireAndCheckCancel(fillEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
@@ -1367,7 +1431,7 @@ public class ItemManagerImpl implements ItemManager {
return FunctionResult.RETURN;
}
// fire event
SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(breakFurnitureWrapper.getPlayer(), location, optionalSprinkler.get());
SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(breakFurnitureWrapper.getPlayer(), location, optionalSprinkler.get(), Reason.BREAK);
if (EventUtils.fireAndCheckCancel(breakEvent)) {
return FunctionResult.CANCEL_EVENT_AND_RETURN;
}
@@ -1487,10 +1551,17 @@ public class ItemManagerImpl implements ItemManager {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(simpleLocation);
boolean hasWater = false;
if (worldPot.isEmpty()) {
plugin.debug("Found pot data not exists at " + simpleLocation);
plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it.");
MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey());
plugin.getWorldManager().addPotAt(memoryPot, simpleLocation);
worldPot = Optional.of(memoryPot);
} else {
hasWater = worldPot.get().getWater() > 0;
}
// fire the event
FertilizerUseEvent useEvent = new FertilizerUseEvent(state.getPlayer(), itemInHand, fertilizer, location, worldPot.get());
if (EventUtils.fireAndCheckCancel(useEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
// add data
plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation);
@@ -1548,10 +1619,17 @@ public class ItemManagerImpl implements ItemManager {
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(simpleLocation);
boolean hasWater = false;
if (worldPot.isEmpty()) {
plugin.debug("Found pot data not exists at " + potLocation);
plugin.debug("Found pot data not exists at " + simpleLocation + ". Fixing it.");
MemoryPot memoryPot = new MemoryPot(simpleLocation, pot.getKey());
plugin.getWorldManager().addPotAt(memoryPot, simpleLocation);
worldPot = Optional.of(memoryPot);
} else {
hasWater = worldPot.get().getWater() > 0;
}
// fire the event
FertilizerUseEvent useEvent = new FertilizerUseEvent(state.getPlayer(), itemInHand, fertilizer, location, worldPot.get());
if (EventUtils.fireAndCheckCancel(useEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
// add data
plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation);
@@ -1808,6 +1886,11 @@ public class ItemManagerImpl implements ItemManager {
if (optionalCrop.get().getPoint() < crop.getMaxPoints()) {
for (BoneMeal boneMeal : crop.getBoneMeals()) {
if (boneMeal.getItem().equals(itemID)) {
// fire the event
BoneMealUseEvent useEvent = new BoneMealUseEvent(player, itemInHand, cropLocation, boneMeal, optionalCrop.get());
if (EventUtils.fireAndCheckCancel(useEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
if (player.getGameMode() != GameMode.CREATIVE) {
itemInHand.setAmount(itemAmount - boneMeal.getUsedAmount());
if (boneMeal.getReturned() != null) {
@@ -1852,6 +1935,9 @@ public class ItemManagerImpl implements ItemManager {
SimpleLocation simpleLocation = SimpleLocation.of(cropLocation);
Optional<WorldCrop> optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation);
if (optionalWorldCrop.isEmpty()) {
WorldCrop worldCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint());
plugin.getWorldManager().addCropAt(worldCrop, simpleLocation);
optionalWorldCrop = Optional.of(worldCrop);
plugin.debug("Found a crop without data broken by " + player.getName() + " at " + cropLocation + ". " +
"You can safely ignore this if the crop is spawned in the wild.");
} else {
@@ -1862,7 +1948,7 @@ public class ItemManagerImpl implements ItemManager {
}
}
// fire event
CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.orElse(null));
CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.get(), Reason.BREAK);
if (EventUtils.fireAndCheckCancel(breakEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
// trigger actions
@@ -2036,23 +2122,27 @@ public class ItemManagerImpl implements ItemManager {
if (!RequirementManager.isRequirementMet(cropState, stage.getBreakRequirements())) {
return FunctionResult.CANCEL_EVENT_AND_RETURN;
}
Optional<WorldCrop> optionalWorldCrop = plugin.getWorldManager().getCropAt(SimpleLocation.of(cropLocation));
SimpleLocation simpleLocation = SimpleLocation.of(cropLocation);
Optional<WorldCrop> optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation);
if (optionalWorldCrop.isPresent()) {
if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) {
LogUtils.warn("Found a crop having inconsistent data broken by " + player.getName() + " at " + cropLocation + ".");
}
} else {
WorldCrop worldCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint());
optionalWorldCrop = Optional.of(worldCrop);
plugin.getWorldManager().addCropAt(worldCrop, simpleLocation);
plugin.debug("Found a crop without data broken by " + player.getName() + " at " + cropLocation + ". " +
"You can safely ignore this if the crop is spawned in the wild.");
}
// fire event
CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.orElse(null));
CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.get(), Reason.BREAK);
if (EventUtils.fireAndCheckCancel(breakEvent))
return FunctionResult.CANCEL_EVENT_AND_RETURN;
// trigger actions
stage.trigger(ActionTrigger.BREAK, cropState);
crop.trigger(ActionTrigger.BREAK, cropState);
plugin.getWorldManager().removeCropAt(SimpleLocation.of(cropLocation));
plugin.getWorldManager().removeCropAt(simpleLocation);
customProvider.removeAnythingAt(cropLocation);
} else {
LogUtils.warn("Invalid crop stage: " + cropStageID);
@@ -2076,7 +2166,7 @@ public class ItemManagerImpl implements ItemManager {
return FunctionResult.RETURN;
}
// fire event
PotBreakEvent breakEvent = new PotBreakEvent(blockWrapper.getPlayer(), location, optionalPot.get());
PotBreakEvent breakEvent = new PotBreakEvent(blockWrapper.getPlayer(), location, optionalPot.get(), Reason.BREAK);
if (EventUtils.fireAndCheckCancel(breakEvent)) {
return FunctionResult.CANCEL_EVENT_AND_RETURN;
}
@@ -2297,47 +2387,139 @@ public class ItemManagerImpl implements ItemManager {
if (entity instanceof Player player) {
handlePlayerBreakBlock(player, block, event);
} else {
// if the block is a pot
Pot pot = getPotByBlock(block);
if (pot != null) {
// prevent entities from breaking pots with requirements
if (pot.getBreakRequirements().length != 0) {
Location potLocation = block.getLocation();
// get or fix pot
SimpleLocation potSimpleLocation = SimpleLocation.of(potLocation);
Optional<WorldPot> worldPot = plugin.getWorldManager().getPotAt(potSimpleLocation);
if (worldPot.isEmpty()) {
plugin.debug("Found pot data not exists at " + potSimpleLocation + ". Fixing it.");
MemoryPot memoryPot = new MemoryPot(potSimpleLocation, pot.getKey());
plugin.getWorldManager().addPotAt(memoryPot, potSimpleLocation);
worldPot = Optional.of(memoryPot);
}
// fire the event
PotBreakEvent potBreakEvent = new PotBreakEvent(entity, potLocation, worldPot.get(), Reason.TRAMPLE);
if (EventUtils.fireAndCheckCancel(potBreakEvent)) {
event.setCancelled(true);
return;
}
plugin.getWorldManager().removePotAt(SimpleLocation.of(block.getLocation()));
pot.trigger(ActionTrigger.BREAK, new State(null, new ItemStack(Material.AIR), block.getLocation()));
Location cropLocation = block.getLocation().clone().add(0,1,0);
String cropStageID = customProvider.getSomethingAt(cropLocation);
Crop.Stage stage = stage2CropStageMap.get(cropStageID);
if (stage != null) {
//State state = new State(null, new ItemStack(Material.AIR), cropLocation);
// if crops are above, check the break requirements for crops
Crop crop = getCropByStageID(cropStageID);
if (crop.getBreakRequirements().length != 0 || stage.getBreakRequirements().length != 0) {
event.setCancelled(true);
return;
}
Optional<WorldCrop> optionalWorldCrop = plugin.getWorldManager().getCropAt(SimpleLocation.of(cropLocation));
SimpleLocation simpleLocation = SimpleLocation.of(cropLocation);
Optional<WorldCrop> optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation);
if (optionalWorldCrop.isPresent()) {
if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) {
LogUtils.warn("Found a crop having inconsistent data broken by " + entity.getType() + " at " + cropLocation + ".");
}
} else {
WorldCrop worldCrop = new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint());
plugin.getWorldManager().addCropAt(worldCrop, simpleLocation);
optionalWorldCrop = Optional.of(worldCrop);
plugin.debug("Found a crop without data broken by " + entity.getType() + " at " + cropLocation + ". " +
"You can safely ignore this if the crop is spawned in the wild.");
}
// fire the event
CropBreakEvent breakEvent = new CropBreakEvent(entity, cropLocation, optionalWorldCrop.get(), Reason.TRAMPLE);
if (EventUtils.fireAndCheckCancel(breakEvent)) {
event.setCancelled(true);
return;
}
State state = new State(null, new ItemStack(Material.AIR), cropLocation);
// trigger actions
//stage.trigger(ActionTrigger.BREAK, state);
//crop.trigger(ActionTrigger.BREAK, state);
plugin.getWorldManager().removeCropAt(SimpleLocation.of(cropLocation));
stage.trigger(ActionTrigger.BREAK, state);
crop.trigger(ActionTrigger.BREAK, state);
plugin.getWorldManager().removeCropAt(simpleLocation);
customProvider.removeAnythingAt(cropLocation);
}
if (deadCrops.contains(cropStageID)) {
customProvider.removeAnythingAt(cropLocation);
}
}
}
}
plugin.getWorldManager().removePotAt(SimpleLocation.of(block.getLocation()));
//pot.trigger(ActionTrigger.BREAK, new State(null, new ItemStack(Material.AIR), block.getLocation()));
public void handleExplosion(Entity entity, List<Block> blocks, Cancellable event) {
List<Location> locationsToRemove = new ArrayList<>();
List<Location> locationsToRemoveBlock = new ArrayList<>();
HashSet<Location> blockLocations = new HashSet<>(blocks.stream().map(Block::getLocation).toList());
List<Location> aboveLocations = new ArrayList<>();
for (Location location : blockLocations) {
Optional<CustomCropsBlock> optionalCustomCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(location));
if (optionalCustomCropsBlock.isPresent()) {
Event customEvent = null;
CustomCropsBlock customCropsBlock = optionalCustomCropsBlock.get();
switch (customCropsBlock.getType()) {
case POT -> {
customEvent = new PotBreakEvent(entity, location, (WorldPot) customCropsBlock, Reason.EXPLODE);
Location above = location.clone().add(0,1,0);
if (!blockLocations.contains(above)) {
aboveLocations.add(above);
}
}
case SPRINKLER -> customEvent = new SprinklerBreakEvent(entity, location, (WorldSprinkler) customCropsBlock, Reason.EXPLODE);
case CROP -> customEvent = new CropBreakEvent(entity, location, (WorldCrop) customCropsBlock, Reason.EXPLODE);
case GREENHOUSE -> customEvent = new GreenhouseGlassBreakEvent(entity, location, (WorldGlass) customCropsBlock, Reason.EXPLODE);
case SCARECROW -> customEvent = new ScarecrowBreakEvent(entity, location, (WorldScarecrow) customCropsBlock, Reason.EXPLODE);
}
if (customEvent != null && EventUtils.fireAndCheckCancel(customEvent)) {
event.setCancelled(true);
return;
}
locationsToRemove.add(location);
}
}
for (Location location : aboveLocations) {
Optional<CustomCropsBlock> optionalCustomCropsBlock = plugin.getWorldManager().getBlockAt(SimpleLocation.of(location));
if (optionalCustomCropsBlock.isPresent()) {
CustomCropsBlock customCropsBlock = optionalCustomCropsBlock.get();
if (customCropsBlock.getType() == ItemType.CROP) {
if (EventUtils.fireAndCheckCancel(new CropBreakEvent(entity, location, (WorldCrop) customCropsBlock, Reason.EXPLODE))) {
event.setCancelled(true);
return;
}
locationsToRemove.add(location);
locationsToRemoveBlock.add(location);
}
}
}
for (Location location : locationsToRemoveBlock) {
removeAnythingAt(location);
}
for (Location location : locationsToRemove) {
CustomCropsBlock customCropsBlock = plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(location));
if (customCropsBlock != null) {
State state = new State(null, new ItemStack(Material.AIR), location);
switch (customCropsBlock.getType()) {
case POT -> {
Pot pot = ((WorldPot) customCropsBlock).getConfig();
pot.trigger(ActionTrigger.BREAK, state);
}
case CROP -> {
Crop crop = ((WorldCrop) customCropsBlock).getConfig();
Crop.Stage stage = crop.getStageByItemID(crop.getStageItemByPoint(((WorldCrop) customCropsBlock).getPoint()));
crop.trigger(ActionTrigger.BREAK, state);
stage.trigger(ActionTrigger.BREAK, state);
}
case SPRINKLER -> {
Sprinkler sprinkler = ((WorldSprinkler) customCropsBlock).getConfig();
sprinkler.trigger(ActionTrigger.BREAK, state);
}
}
}
}
}

View File

@@ -18,17 +18,19 @@
package net.momirealms.customcrops.mechanic.item.custom;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.event.BoneMealDispenseEvent;
import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.item.Crop;
import net.momirealms.customcrops.api.mechanic.item.Pot;
import net.momirealms.customcrops.api.mechanic.item.Sprinkler;
import net.momirealms.customcrops.api.mechanic.item.WateringCan;
import net.momirealms.customcrops.api.mechanic.item.*;
import net.momirealms.customcrops.api.mechanic.requirement.State;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop;
import net.momirealms.customcrops.mechanic.item.ItemManagerImpl;
import net.momirealms.customcrops.utils.EventUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Dispenser;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
@@ -38,13 +40,17 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.*;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemDamageEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public abstract class AbstractCustomListener implements Listener {
protected ItemManagerImpl itemManager;
@@ -195,19 +201,76 @@ public abstract class AbstractCustomListener implements Listener {
}
}
@EventHandler (ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent event) {
this.itemManager.handleExplosion(event.getEntity(), event.blockList(), event);
}
@EventHandler (ignoreCancelled = true)
public void onExplosion(BlockExplodeEvent event) {
this.itemManager.handleExplosion(null, event.blockList(), event);
}
@EventHandler (ignoreCancelled = true)
public void onDispenser(BlockDispenseEvent event) {
Block block = event.getBlock();
if (block.getBlockData() instanceof org.bukkit.block.data.type.Dispenser directional) {
Block relative = block.getRelative(directional.getFacing());
Location location = relative.getLocation();
SimpleLocation simpleLocation = SimpleLocation.of(location);
Optional<WorldCrop> worldCropOptional = CustomCropsPlugin.get().getWorldManager().getCropAt(simpleLocation);
if (worldCropOptional.isPresent()) {
WorldCrop crop = worldCropOptional.get();
Crop config = crop.getConfig();
ItemStack itemStack = event.getItem();
String itemID = itemManager.getItemID(itemStack);
if (crop.getPoint() < config.getMaxPoints()) {
for (BoneMeal boneMeal : config.getBoneMeals()) {
if (boneMeal.getItem().equals(itemID)) {
// fire the event
if (EventUtils.fireAndCheckCancel(new BoneMealDispenseEvent(block, itemStack, location, boneMeal, crop))) {
event.setCancelled(true);
return;
}
if (block.getState() instanceof Dispenser dispenser) {
event.setCancelled(true);
Inventory inventory = dispenser.getInventory();
for (ItemStack storage : inventory.getStorageContents()) {
if (storage == null) continue;
String id = itemManager.getItemID(storage);
if (id.equals(itemID)) {
storage.setAmount(storage.getAmount() - 1);
boneMeal.trigger(new State(null, itemStack, location));
CustomCropsPlugin.get().getWorldManager().addPointToCrop(config, simpleLocation, boneMeal.getPoint());
}
}
}
return;
}
}
}
}
}
}
public void onPlaceBlock(Player player, Block block, String blockID, Cancellable event) {
if (player == null) return;
this.itemManager.handlePlayerPlaceBlock(player, block, blockID, event);
}
public void onBreakFurniture(Player player, Location location, String id, Cancellable event) {
if (player == null) return;
this.itemManager.handlePlayerBreakFurniture(player, location, id, event);
}
public void onPlaceFurniture(Player player, Location location, String id, Cancellable event) {
if (player == null) return;
this.itemManager.handlePlayerPlaceFurniture(player, location, id, event);
}
public void onInteractFurniture(Player player, Location location, String id, @Nullable Entity baseEntity, Cancellable event) {
if (player == null) return;
this.itemManager.handlePlayerInteractFurniture(player, location, id, baseEntity, event);
}
}

View File

@@ -125,6 +125,7 @@ public class RequirementManagerImpl implements RequirementManager {
this.registerTemperatureRequirement();
this.registerFertilizerRequirement();
this.registerLightRequirement();
this.registerGameModeRequirement();
}
@NotNull
@@ -228,6 +229,21 @@ public class RequirementManagerImpl implements RequirementManager {
});
}
private void registerGameModeRequirement() {
registerRequirement("gamemode", (args, actions, advanced) -> {
List<String> modes = ConfigUtils.stringListArgs(args);
return condition -> {
if (condition.getPlayer() == null) return true;
var name = condition.getPlayer().getGameMode().name().toLowerCase(Locale.ENGLISH);
if (modes.contains(name)) {
return true;
}
if (advanced) triggerActions(actions, condition);
return false;
};
});
}
private void registerTemperatureRequirement() {
registerRequirement("temperature", (args, actions, advanced) -> {
List<Pair<Integer, Integer>> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList();
@@ -317,6 +333,7 @@ public class RequirementManagerImpl implements RequirementManager {
registerRequirement("level", (args, actions, advanced) -> {
int level = (int) args;
return state -> {
if (state.getPlayer() == null) return true;
int current = state.getPlayer().getLevel();
if (current >= level)
return true;
@@ -330,6 +347,7 @@ public class RequirementManagerImpl implements RequirementManager {
registerRequirement("money", (args, actions, advanced) -> {
double money = ConfigUtils.getDoubleValue(args);
return state -> {
if (state.getPlayer() == null) return true;
double current = VaultHook.getEconomy().getBalance(state.getPlayer());
if (current >= money)
return true;
@@ -420,6 +438,7 @@ public class RequirementManagerImpl implements RequirementManager {
String key = section.getString("key");
int time = section.getInt("time");
return state -> {
if (state.getPlayer() == null) return true;
if (!plugin.getCoolDownManager().isCoolDown(state.getPlayer().getUniqueId(), key, time)) {
return true;
}
@@ -451,6 +470,7 @@ public class RequirementManagerImpl implements RequirementManager {
registerRequirement("sneak", (args, actions, advanced) -> {
boolean sneak = (boolean) args;
return state -> {
if (state.getPlayer() == null) return true;
if (sneak) {
if (state.getPlayer().isSneaking())
return true;
@@ -468,6 +488,7 @@ public class RequirementManagerImpl implements RequirementManager {
registerRequirement("permission", (args, actions, advanced) -> {
List<String> perms = ConfigUtils.stringListArgs(args);
return state -> {
if (state.getPlayer() == null) return true;
for (String perm : perms)
if (state.getPlayer().hasPermission(perm))
return true;
@@ -478,6 +499,7 @@ public class RequirementManagerImpl implements RequirementManager {
registerRequirement("!permission", (args, actions, advanced) -> {
List<String> perms = ConfigUtils.stringListArgs(args);
return state -> {
if (state.getPlayer() == null) return true;
for (String perm : perms)
if (state.getPlayer().hasPermission(perm)) {
if (advanced) triggerActions(actions, state);
@@ -934,6 +956,7 @@ public class RequirementManagerImpl implements RequirementManager {
int required = Integer.parseInt(split[1]);
String operator = potions.substring(split[0].length(), potions.length() - split[1].length());
return state -> {
if (state.getPlayer() == null) return true;
int level = -1;
PotionEffect potionEffect = state.getPlayer().getPotionEffect(type);
if (potionEffect != null) {

View File

@@ -88,9 +88,7 @@ public class CChunk implements CustomCropsChunk {
@Override
public void notifyOfflineUpdates() {
long delta = this.lastLoadedTime - System.currentTimeMillis();
int seconds = (int) (delta / 1000);
this.lastLoadedTime = System.currentTimeMillis();
}
public void setWorld(CWorld cWorld) {

View File

@@ -94,8 +94,10 @@ public class CWorld implements CustomCropsWorld {
if (setting.isAutoSeasonChange()) {
this.updateSeasonAndDate();
}
for (CChunk chunk : loadedChunks.values()) {
chunk.secondTimer();
if (setting.isSchedulerEnabled()) {
for (CChunk chunk : loadedChunks.values()) {
chunk.secondTimer();
}
}
}

View File

@@ -328,6 +328,7 @@ public class ConfigUtils {
innerSection.getInt("item-amount",1),
innerSection.getString("return"),
innerSection.getInt("return-amount",1),
innerSection.getBoolean("dispenser",true),
getIntChancePair(innerSection.getConfigurationSection("chance")),
getActions(innerSection.getConfigurationSection("actions"))
);

View File

@@ -259,6 +259,8 @@ tomato:
custom-bone-meal:
bone_meal_1:
item: BONE_MEAL
# Allow to be used with dispenser
dispenser: true
chance:
2: 0.2
1: 0.6