9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-19 15:09:25 +00:00

Cancel the lambda implementation of the actions

This commit is contained in:
XiaoMoMi
2024-09-05 16:24:57 +08:00
committed by GitHub
22 changed files with 1985 additions and 696 deletions

View File

@@ -18,47 +18,9 @@
package net.momirealms.customcrops.api.action; package net.momirealms.customcrops.api.action;
import dev.dejvokep.boostedyaml.block.implementation.Section; 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.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.context.Context; import net.momirealms.customcrops.api.action.builtin.*;
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.common.util.ClassUtils; 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -66,10 +28,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import static java.util.Objects.requireNonNull;
public abstract class AbstractActionManager<T> implements ActionManager<T> { public abstract class AbstractActionManager<T> implements ActionManager<T> {
@@ -194,55 +152,13 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
} }
protected void registerBroadcastAction() { protected void registerBroadcastAction() {
registerAction((args, chance) -> { registerAction((args, chance) -> new ActionBroadcast<>(plugin, args, chance), "broadcast");
List<String> 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<String> 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");
} }
protected void registerNearbyMessage() { protected void registerNearbyMessage() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
List<String> messages = ListUtils.toList(section.get("message")); return new ActionMessageNearby<>(plugin, section, chance);
MathValue<T> 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<String> replaced = BukkitPlaceholderManager.getInstance().parse(
owner,
messages,
context.placeholderMap()
);
Audience audience = plugin.getSenderFactory().getAudience(player);
for (String text : replaced) {
audience.sendMessage(AdventureHelper.miniMessage(text));
}
}
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at message-nearby action which should be Section"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at message-nearby action which should be Section");
return Action.empty(); return Action.empty();
@@ -253,26 +169,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerNearbyActionBar() { protected void registerNearbyActionBar() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
String actionbar = section.getString("actionbar"); return new ActionActionbarNearby<>(plugin, section, chance);
MathValue<T> 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));
}
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at actionbar-nearby action which should be Section"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at actionbar-nearby action which should be Section");
return Action.empty(); return Action.empty();
@@ -281,63 +178,11 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
} }
protected void registerCommandAction() { protected void registerCommandAction() {
registerAction((args, chance) -> { registerAction((args, chance) -> new ActionCommand<>(plugin, args, chance), "command");
List<String> commands = ListUtils.toList(args); registerAction((args, chance) -> new ActionRandomCommand<>(plugin, args, chance), "random-command");
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<String> 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<String> 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) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
List<String> cmd = ListUtils.toList(section.get("command")); return new ActionCommandNearby<>(plugin, section, chance);
MathValue<T> 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<String> replaced = BukkitPlaceholderManager.getInstance().parse(owner, cmd, context.placeholderMap());
for (String text : replaced) {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text);
}
}
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at command-nearby action which should be Section"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at command-nearby action which should be Section");
return Action.empty(); return Action.empty();
@@ -346,109 +191,12 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
} }
protected void registerBundleAction(Class<T> tClass) { protected void registerBundleAction(Class<T> tClass) {
registerAction((args, chance) -> { registerAction((args, chance) -> new ActionBundle<>(plugin, this, args, chance), "chain");
List<Action<T>> actions = new ArrayList<>(); registerAction((args, chance) -> new ActionDelay<>(plugin, this, args, chance), "delay");
if (args instanceof Section section) { registerAction((args, chance) -> new ActionTimer<>(plugin, this, args, chance), "timer");
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section innerSection) {
actions.add(parseAction(innerSection));
}
}
}
return context -> {
if (Math.random() > chance) return;
for (Action<T> action : actions) {
action.trigger(context);
}
};
}, "chain");
registerAction((args, chance) -> {
List<Action<T>> 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<String, Object> 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<T> action : actions)
action.trigger(context);
}, delay * 50L, TimeUnit.MILLISECONDS);
} else {
plugin.getScheduler().sync().runLater(() -> {
for (Action<T> action : actions)
action.trigger(context);
}, delay, location);
}
};
}, "delay");
registerAction((args, chance) -> {
List<Action<T>> 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<String, Object> 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<T> action : actions) {
action.trigger(context);
}
}, delay * 50L, period * 50L, TimeUnit.MILLISECONDS);
} else {
task = plugin.getScheduler().sync().runRepeating(() -> {
for (Action<T> action : actions) {
action.trigger(context);
}
}, delay, period, location);
}
plugin.getScheduler().asyncLater(task::cancel, duration * 50L, TimeUnit.MILLISECONDS);
};
}, "timer");
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
Action<T>[] actions = parseActions(section.getSection("actions")); return new ActionConditional<>(plugin, this, tClass, section, chance);
Requirement<T>[] requirements = plugin.getRequirementManager(tClass).parseRequirements(section.getSection("conditions"), true);
return condition -> {
if (Math.random() > chance) return;
for (Requirement<T> requirement : requirements) {
if (!requirement.isSatisfied(condition)) {
return;
}
}
for (Action<T> action : actions) {
action.trigger(condition);
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at conditional action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at conditional action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -456,31 +204,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
}, "conditional"); }, "conditional");
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
List<Pair<Requirement<T>[], Action<T>[]>> conditionActionPairList = new ArrayList<>(); return new ActionPriority<>(plugin, this, tClass, section, chance);
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section inner) {
Action<T>[] actions = parseActions(inner.getSection("actions"));
Requirement<T>[] 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<Requirement<T>[], Action<T>[]> pair : conditionActionPairList) {
if (pair.left() != null)
for (Requirement<T> requirement : pair.left()) {
if (!requirement.isSatisfied(context)) {
continue outer;
}
}
if (pair.right() != null)
for (Action<T> action : pair.right()) {
action.trigger(context);
}
return;
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at priority action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at priority action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -491,28 +215,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerNearbyTitle() { protected void registerNearbyTitle() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
TextValue<T> title = TextValue.auto(section.getString("title")); return new ActionTitleNearby<>(plugin, section, chance);
TextValue<T> 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
);
}
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title-nearby action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at title-nearby action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -523,54 +226,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerParticleAction() { protected void registerParticleAction() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
Particle particleType = ParticleUtils.getParticle(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); return new ActionParticle<>(plugin, section, chance);
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))
);
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at particle action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at particle action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -581,71 +237,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerQualityCropsAction() { protected void registerQualityCropsAction() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
MathValue<T> min = MathValue.auto(section.get("min")); return new ActionQualityCrops<>(plugin, section, chance);
MathValue<T> 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<CustomCropsWorld<?>> 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<CustomCropsChunk> chunk = world.get().getChunk(potLocation.toChunkPos());
if (chunk.isPresent()) {
Optional<CustomCropsBlockState> state = chunk.get().getBlockState(potLocation);
if (state.isPresent()) {
if (state.get().type() instanceof PotBlock potBlock) {
fertilizers = potBlock.fertilizers(state.get());
}
}
}
ArrayList<FertilizerConfig> 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;
}
}
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at quality-crops action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at quality-crops action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -656,58 +248,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerDropItemsAction() { protected void registerDropItemsAction() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
boolean ignoreFertilizer = section.getBoolean("ignore-fertilizer", true); return new ActionDropItem<>(plugin, section, chance);
String item = section.getString("item");
MathValue<T> min = MathValue.auto(section.get("min"));
MathValue<T> 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<CustomCropsWorld<?>> 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<CustomCropsChunk> chunk = world.get().getChunk(potLocation.toChunkPos());
if (chunk.isPresent()) {
Optional<CustomCropsBlockState> state = chunk.get().getBlockState(potLocation);
if (state.isPresent()) {
if (state.get().type() instanceof PotBlock potBlock) {
fertilizers = potBlock.fertilizers(state.get());
}
}
}
ArrayList<FertilizerConfig> 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");
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-item action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-item action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -718,25 +259,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerLegacyDropItemsAction() { protected void registerLegacyDropItemsAction() {
registerAction((args, chance) -> { registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
List<Action<T>> actions = new ArrayList<>(); return new ActionDropItemLegacy<>(plugin, this, section, chance);
Section otherItemSection = section.getSection("other-items");
if (otherItemSection != null) {
for (Map.Entry<String, Object> 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<T> action : actions) {
action.trigger(context);
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-items action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at drop-items action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -747,51 +270,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerHologramAction() { protected void registerHologramAction() {
registerAction(((args, chance) -> { registerAction(((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
TextValue<T> text = TextValue.auto(section.getString("text", "")); return new ActionHologram<>(plugin, section, chance);
MathValue<T> duration = MathValue.auto(section.get("duration", 20));
boolean other = section.getString("position", "other").equals("other");
MathValue<T> x = MathValue.auto(section.get("x", 0));
MathValue<T> y = MathValue.auto(section.get("y", 0));
MathValue<T> 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<CustomCropsWorld<?>> 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<Player> 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));
}
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at hologram action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -802,66 +281,7 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
protected void registerFakeItemAction() { protected void registerFakeItemAction() {
registerAction(((args, chance) -> { registerAction(((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
String itemID = section.getString("item", ""); return new ActionFakeItem<>(plugin, section, chance);
String[] split = itemID.split(":");
if (split.length >= 2) itemID = split[split.length - 1];
MathValue<T> duration = MathValue.auto(section.get("duration", 20));
boolean other = section.getString("position", "other").equals("other");
MathValue<T> x = MathValue.auto(section.get("x", 0));
MathValue<T> y = MathValue.auto(section.get("y", 0));
MathValue<T> z = MathValue.auto(section.get("z", 0));
MathValue<T> 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<Player> 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);
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fake-item action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at fake-item action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -869,62 +289,10 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
}), "fake-item"); }), "fake-item");
} }
@SuppressWarnings("unchecked")
protected void registerPlantAction() { protected void registerPlantAction() {
this.registerAction((args, chance) -> { this.registerAction((args, chance) -> {
if (args instanceof Section section) { if (args instanceof Section section) {
int point = section.getInt("point", 0); return new ActionPlant<>(plugin, section, chance);
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<CustomCropsWorld<?>> 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<Player>) 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);
};
} else { } else {
plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plant action which is expected to be `Section`"); plugin.getPluginLogger().warn("Invalid value type: " + args.getClass().getSimpleName() + " found at plant action which is expected to be `Section`");
return Action.empty(); return Action.empty();
@@ -933,49 +301,6 @@ public abstract class AbstractActionManager<T> implements ActionManager<T> {
} }
protected void registerBreakAction() { protected void registerBreakAction() {
this.registerAction((args, chance) -> { this.registerAction((args, chance) -> new ActionBreak<>(plugin, args, chance), "break");
boolean triggerEvent = (boolean) args;
return context -> {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
Pos3 pos3 = Pos3.from(location);
CustomCropsWorld<?> world = optionalWorld.get();
Optional<CustomCropsBlockState> 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");
} }
} }

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.api.action.builtin;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.action.Action;
public abstract class AbstractBuiltInAction<T> implements Action<T> {
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);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final String actionbar;
final MathValue<T> 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<T> 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<T> getRange() {
return range;
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final boolean triggerEvent;
public ActionBreak(
BukkitCustomCropsPlugin plugin,
Object args,
double chance
) {
super(plugin, chance);
this.triggerEvent = (boolean) args;
}
@Override
public void trigger(Context<T> context) {
Location location = requireNonNull(context.arg(ContextKeys.LOCATION));
Optional<CustomCropsWorld<?>> optionalWorld = plugin.getWorldManager().getWorld(location.getWorld());
if (optionalWorld.isEmpty()) {
return;
}
Pos3 pos3 = Pos3.from(location);
CustomCropsWorld<?> world = optionalWorld.get();
Optional<CustomCropsBlockState> 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;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<String> messages;
public ActionBroadcast(
BukkitCustomCropsPlugin plugin,
Object args,
double chance
) {
super(plugin, chance);
this.messages = ListUtils.toList(args);
}
@Override
public void trigger(Context<T> 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<String> 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<String> getMessages() {
return messages;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<Action<T>> actions;
public ActionBundle(
BukkitCustomCropsPlugin plugin,
AbstractActionManager<T> manager,
Object args,
double chance
) {
super(plugin, chance);
this.actions = new ArrayList<>();
if (args instanceof Section section) {
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section innerSection) {
actions.add(manager.parseAction(innerSection));
}
}
}
}
@Override
public void trigger(Context<T> context) {
if (!checkChance()) return;
for (Action<T> action : actions) {
action.trigger(context);
}
}
public List<Action<T>> getActions() {
return actions;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<String> commands;
public ActionCommand(
BukkitCustomCropsPlugin plugin,
Object args,
double chance
) {
super(plugin, chance);
this.commands = ListUtils.toList(args);
}
@Override
public void trigger(Context<T> 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<String> 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<String> getCommands() {
return commands;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<String> cmd;
final MathValue<T> 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<T> 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<String> replaced = BukkitPlaceholderManager.getInstance().parse(owner, cmd, context.placeholderMap());
for (String text : replaced) {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text);
}
}
}
}
public List<String> getCmd() {
return cmd;
}
public MathValue<T> getRange() {
return range;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final Action<T>[] actions;
final Requirement<T>[] requirements;
public ActionConditional(
BukkitCustomCropsPlugin plugin,
AbstractActionManager<T> manager,
Class<T> 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<T> condition) {
if (!checkChance()) return;
for (Requirement<T> requirement : requirements) {
if (!requirement.isSatisfied(condition)) {
return;
}
}
for (Action<T> action : actions) {
action.trigger(condition);
}
}
public Action<T>[] getActions() {
return actions;
}
public Requirement<T>[] getRequirements() {
return requirements;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<Action<T>> actions;
final int delay;
final boolean async;
public ActionDelay(
BukkitCustomCropsPlugin plugin,
AbstractActionManager<T> 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<String, Object> 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<T> context) {
if (!checkChance()) return;
Location location = context.arg(ContextKeys.LOCATION);
if (async) {
plugin.getScheduler().asyncLater(() -> {
for (Action<T> action : actions)
action.trigger(context);
}, delay * 50L, TimeUnit.MILLISECONDS);
} else {
plugin.getScheduler().sync().runLater(() -> {
for (Action<T> action : actions)
action.trigger(context);
}, delay, location);
}
}
public List<Action<T>> getActions() {
return actions;
}
public int getDelay() {
return delay;
}
public boolean isAsync() {
return async;
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final boolean ignoreFertilizer;
final String item;
final MathValue<T> min;
final MathValue<T> 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<T> 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<CustomCropsWorld<?>> 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<CustomCropsChunk> chunk = world.get().getChunk(potLocation.toChunkPos());
if (chunk.isPresent()) {
Optional<CustomCropsBlockState> state = chunk.get().getBlockState(potLocation);
if (state.isPresent()) {
if (state.get().type() instanceof PotBlock potBlock) {
fertilizers = potBlock.fertilizers(state.get());
}
}
}
ArrayList<FertilizerConfig> 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<T> getMin() {
return min;
}
public MathValue<T> getMax() {
return max;
}
public boolean isToInv() {
return toInv;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<Action<T>> actions;
public ActionDropItemLegacy(
BukkitCustomCropsPlugin plugin,
ActionManager<T> manager,
Section section,
double chance
) {
super(plugin, chance);
this.actions = new ArrayList<>();
Section otherItemSection = section.getSection("other-items");
if (otherItemSection != null) {
for (Map.Entry<String, Object> 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<T> context) {
if (!checkChance()) return;
for (Action<T> action : actions) {
action.trigger(context);
}
}
public List<Action<T>> getActions() {
return actions;
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final String itemID;
final MathValue<T> duration;
final boolean other;
final MathValue<T> x;
final MathValue<T> y;
final MathValue<T> z;
final MathValue<T> 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<T> 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<Player> 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<T> getDuration() {
return duration;
}
public boolean isOther() {
return other;
}
public MathValue<T> getX() {
return x;
}
public MathValue<T> getY() {
return y;
}
public MathValue<T> getZ() {
return z;
}
public MathValue<T> getYaw() {
return yaw;
}
public int getRange() {
return range;
}
public boolean isVisibleToAll() {
return visibleToAll;
}
public boolean isUseItemDisplay() {
return useItemDisplay;
}
}

View File

@@ -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> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
public class ActionHologram<T> extends AbstractBuiltInAction<T> {
final TextValue<T> text;
final MathValue<T> duration;
final boolean other;
final MathValue<T> x;
final MathValue<T> y;
final MathValue<T> 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<T> 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<CustomCropsWorld<?>> 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<Player> 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<T> getText() {
return text;
}
public MathValue<T> getDuration() {
return duration;
}
public boolean isOther() {
return other;
}
public MathValue<T> getX() {
return x;
}
public MathValue<T> getY() {
return y;
}
public MathValue<T> getZ() {
return z;
}
public boolean isApplyCorrection() {
return applyCorrection;
}
public boolean isOnlyShowToOne() {
return onlyShowToOne;
}
public int getRange() {
return range;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<String> messages;
final MathValue<T> 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<T> 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<String> 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<String> getMessages() {
return messages;
}
public MathValue<T> getRange() {
return range;
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
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<T> 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;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
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<T> 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<CustomCropsWorld<?>> 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<Player>) 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;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<Pair<Requirement<T>[], Action<T>[]>> conditionActionPairList;
public ActionPriority(
BukkitCustomCropsPlugin plugin,
AbstractActionManager<T> manager,
Class<T> tClass,
Section section,
double chance
) {
super(plugin, chance);
this.conditionActionPairList = new ArrayList<>();
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
if (entry.getValue() instanceof Section inner) {
Action<T>[] actions = manager.parseActions(inner.getSection("actions"));
Requirement<T>[] requirements = plugin.getRequirementManager(tClass).parseRequirements(inner.getSection("conditions"), false);
conditionActionPairList.add(Pair.of(requirements, actions));
}
}
}
@Override
public void trigger(Context<T> context) {
if (!checkChance()) return;
outer:
for (Pair<Requirement<T>[], Action<T>[]> pair : conditionActionPairList) {
if (pair.left() != null)
for (Requirement<T> requirement : pair.left()) {
if (!requirement.isSatisfied(context)) {
continue outer;
}
}
if (pair.right() != null)
for (Action<T> action : pair.right()) {
action.trigger(context);
}
return;
}
}
public List<Pair<Requirement<T>[], Action<T>[]>> getConditionActionPairList() {
return conditionActionPairList;
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final MathValue<T> min;
final MathValue<T> 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<T> 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<CustomCropsWorld<?>> 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<CustomCropsChunk> chunk = world.get().getChunk(potLocation.toChunkPos());
if (chunk.isPresent()) {
Optional<CustomCropsBlockState> state = chunk.get().getBlockState(potLocation);
if (state.isPresent()) {
if (state.get().type() instanceof PotBlock potBlock) {
fertilizers = potBlock.fertilizers(state.get());
}
}
}
ArrayList<FertilizerConfig> 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<T> getMin() {
return min;
}
public MathValue<T> getMax() {
return max;
}
public boolean isToInv() {
return toInv;
}
public String[] getQualityLoots() {
return qualityLoots;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<String> commands;
public ActionRandomCommand(
BukkitCustomCropsPlugin plugin,
Object args,
double chance
) {
super(plugin, chance);
this.commands = ListUtils.toList(args);
}
@Override
public void trigger(Context<T> 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<String> getCommands() {
return commands;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final List<Action<T>> actions;
final int delay, duration, period;
final boolean async;
public ActionTimer(
BukkitCustomCropsPlugin plugin,
AbstractActionManager<T> 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<String, Object> 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<T> context) {
if (!checkChance()) return;
Location location = context.arg(ContextKeys.LOCATION);
SchedulerTask task;
if (async) {
task = plugin.getScheduler().asyncRepeating(() -> {
for (Action<T> action : actions) {
action.trigger(context);
}
}, delay * 50L, period * 50L, TimeUnit.MILLISECONDS);
} else {
task = plugin.getScheduler().sync().runRepeating(() -> {
for (Action<T> action : actions) {
action.trigger(context);
}
}, delay, period, location);
}
plugin.getScheduler().asyncLater(task::cancel, duration * 50L, TimeUnit.MILLISECONDS);
}
public List<Action<T>> getActions() {
return actions;
}
public int getDelay() {
return delay;
}
public int getDuration() {
return duration;
}
public int getPeriod() {
return period;
}
public boolean isAsync() {
return async;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<T> extends AbstractBuiltInAction<T> {
final TextValue<T> title;
final TextValue<T> 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<T> 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<T> getTitle() {
return title;
}
public TextValue<T> getSubtitle() {
return subtitle;
}
public int getFadeIn() {
return fadeIn;
}
public int getStay() {
return stay;
}
public int getFadeOut() {
return fadeOut;
}
public int getRange() {
return range;
}
}