From 592f58a89b0c154cafc76a3223ef8c27a32c8b0c Mon Sep 17 00:00:00 2001 From: akastijn Date: Tue, 31 Mar 2026 22:51:37 +0200 Subject: [PATCH] Add April Fools' prank functionality with scheduled explosions and test command --- .../com/alttd/playerutils/PlayerUtils.java | 12 +++ .../playerutils_subcommands/AprilFools.java | 48 +++++++++++ .../playerutils/util/AprilFoolsPrank.java | 84 +++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/AprilFools.java create mode 100644 src/main/java/com/alttd/playerutils/util/AprilFoolsPrank.java diff --git a/src/main/java/com/alttd/playerutils/PlayerUtils.java b/src/main/java/com/alttd/playerutils/PlayerUtils.java index f75ef7d..d6dcb4d 100644 --- a/src/main/java/com/alttd/playerutils/PlayerUtils.java +++ b/src/main/java/com/alttd/playerutils/PlayerUtils.java @@ -1,6 +1,7 @@ package com.alttd.playerutils; import com.alttd.playerutils.commands.PlayerUtilsCommand; +import com.alttd.playerutils.commands.playerutils_subcommands.AprilFools; import com.alttd.playerutils.commands.playerutils_subcommands.GhastSpeed; import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock; import com.alttd.playerutils.config.Config; @@ -13,12 +14,17 @@ import org.bukkit.plugin.java.JavaPlugin; import java.util.concurrent.TimeUnit; +import com.alttd.playerutils.util.AprilFoolsPrank; + public final class PlayerUtils extends JavaPlugin { private PlayerUtilsCommand playerUtilsCommand; + private AprilFoolsPrank aprilFoolsPrank; @Override public void onEnable() { + // initialize prank utility + aprilFoolsPrank = new AprilFoolsPrank(this); registerCommands(); registerEvents(); reloadConfigs(); @@ -32,6 +38,8 @@ public final class PlayerUtils extends JavaPlugin { private void registerCommands() { playerUtilsCommand = new PlayerUtilsCommand(this); + // add april fools test command + playerUtilsCommand.addSubCommand(new AprilFools(aprilFoolsPrank)); } private void registerEvents() { @@ -61,7 +69,11 @@ public final class PlayerUtils extends JavaPlugin { } private void registerSchedulers() { + // periodic key storage save (async) Bukkit.getScheduler().runTaskTimerAsynchronously(this, KeyStorage.STORAGE::save, TimeUnit.MINUTES.toSeconds(5) * 20, TimeUnit.MINUTES.toSeconds(5) * 20); + + // April 1st prank scheduler + aprilFoolsPrank.schedule(); } } diff --git a/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/AprilFools.java b/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/AprilFools.java new file mode 100644 index 0000000..e358b61 --- /dev/null +++ b/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/AprilFools.java @@ -0,0 +1,48 @@ +package com.alttd.playerutils.commands.playerutils_subcommands; + +import com.alttd.playerutils.commands.SubCommand; +import com.alttd.playerutils.config.Messages; +import com.alttd.playerutils.util.AprilFoolsPrank; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class AprilFools extends SubCommand { + + private final AprilFoolsPrank prank; + + public AprilFools(AprilFoolsPrank prank) { + this.prank = prank; + } + + @Override + public boolean onCommand(CommandSender commandSender, String[] args) { + if (!(commandSender instanceof Player player)) { + commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY); + return true; + } + boolean ok = prank.playExplosionAround(player); + if (ok) { + commandSender.sendRichMessage("April Fools test triggered. Listen closely..."); + } else { + commandSender.sendRichMessage("Failed to trigger. You must be in the overworld named 'world'."); + } + return true; + } + + @Override + public String getName() { + return "aprilfools"; + } + + @Override + public List getTabComplete(CommandSender commandSender, String[] args) { + return List.of(); + } + + @Override + public String getHelpMessage() { + return "/playerutils aprilfools - Play a fake explosion near you (testing)."; + } +} diff --git a/src/main/java/com/alttd/playerutils/util/AprilFoolsPrank.java b/src/main/java/com/alttd/playerutils/util/AprilFoolsPrank.java new file mode 100644 index 0000000..ccc3039 --- /dev/null +++ b/src/main/java/com/alttd/playerutils/util/AprilFoolsPrank.java @@ -0,0 +1,84 @@ +package com.alttd.playerutils.util; + +import com.alttd.playerutils.PlayerUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneId; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Encapsulates the April Fools' prank logic. + * - schedule(): registers the timed prank that only runs on April 1st and only during overworld night. + * - playExplosionAround(Player): immediately plays the explosion sound around the target for testing (no date/time checks), + * but still requires the target to be in the overworld named "world" to match intended environment. + */ +public class AprilFoolsPrank { + + private final PlayerUtils plugin; + + public AprilFoolsPrank(PlayerUtils plugin) { + this.plugin = plugin; + } + + /** + * Register the timed prank task. Safe to call on the main thread during plugin enable. + */ + public void schedule() { + // April 1st prank: during overworld night, every 2 minutes pick one player and play an explosion somewhere in a 30-block radius + Bukkit.getScheduler().runTaskTimer(plugin, () -> { + LocalDate now = LocalDate.now(ZoneId.systemDefault()); + if (now.getMonth() != Month.APRIL || now.getDayOfMonth() != 1) { + return; // only active on April 1st + } + +// World world = Bukkit.getWorld("world"); + World world = Bukkit.getWorld("lobby"); + if (world == null) return; // overworld not present + + long time = world.getTime() % 24000L; + if (time < 13000L || time > 23000L) { + return; // only at night + } + + List players = world.getPlayers(); + if (players.isEmpty()) return; + + Player target = players.get(ThreadLocalRandom.current().nextInt(players.size())); + playOnce(world, target); + }, 20L, 20L * 60L * 2L); // start after 1s, repeat every 2 minutes + } + + /** + * Trigger the prank once around the given player for testing. Returns true if executed. + * This method ignores the date and time checks so it can be tested easily, but it still + * requires the player to be in the overworld named "world". + */ + public boolean playExplosionAround(Player target) { + if (target == null) return false; + World world = target.getWorld(); +// if (!"world".equalsIgnoreCase(world.getName())) { + if (!"lobby".equalsIgnoreCase(world.getName())) { + return false; // only intended for overworld + } + playOnce(world, target); + return true; + } + + private void playOnce(World world, Player target) { + Location base = target.getLocation(); + double radius = 30.0; + double r = ThreadLocalRandom.current().nextDouble(radius); + double theta = ThreadLocalRandom.current().nextDouble(Math.PI * 2); + double dx = r * Math.cos(theta); + double dz = r * Math.sin(theta); + Location soundLoc = new Location(world, base.getX() + dx, base.getY(), base.getZ() + dz); + world.playSound(soundLoc, Sound.ENTITY_GENERIC_EXPLODE, 0.8f, 1.0f); + } +}