From 0a2e5865bf0c6e7e399a3f478751d44470cf6b32 Mon Sep 17 00:00:00 2001 From: stranzjakob Date: Wed, 13 May 2026 22:23:58 +0000 Subject: [PATCH] Show villager workstation feature --- .../com/alttd/playerutils/PlayerUtils.java | 11 ++- .../com/alttd/playerutils/config/Config.java | 83 ++++++++++++++++++ .../alttd/playerutils/config/Messages.java | 28 +++++++ .../VillagerWorkstationEvent.java | 84 +++++++++++++++++++ src/main/resources/plugin.yml | 4 + 5 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/alttd/playerutils/event_listeners/VillagerWorkstationEvent.java diff --git a/src/main/java/com/alttd/playerutils/PlayerUtils.java b/src/main/java/com/alttd/playerutils/PlayerUtils.java index f75ef7d..f5f0e7e 100644 --- a/src/main/java/com/alttd/playerutils/PlayerUtils.java +++ b/src/main/java/com/alttd/playerutils/PlayerUtils.java @@ -2,17 +2,20 @@ package com.alttd.playerutils; import com.alttd.playerutils.commands.PlayerUtilsCommand; import com.alttd.playerutils.commands.playerutils_subcommands.GhastSpeed; +import com.alttd.playerutils.commands.playerutils_subcommands.RandomPlotPort; import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock; import com.alttd.playerutils.config.Config; import com.alttd.playerutils.config.KeyStorage; import com.alttd.playerutils.config.Messages; import com.alttd.playerutils.event_listeners.*; +import lombok.extern.slf4j.Slf4j; import org.bukkit.Bukkit; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import java.util.concurrent.TimeUnit; +@Slf4j public final class PlayerUtils extends JavaPlugin { private PlayerUtilsCommand playerUtilsCommand; @@ -20,8 +23,8 @@ public final class PlayerUtils extends JavaPlugin { @Override public void onEnable() { registerCommands(); - registerEvents(); reloadConfigs(); + registerEvents(); registerSchedulers(); } @@ -32,6 +35,11 @@ public final class PlayerUtils extends JavaPlugin { private void registerCommands() { playerUtilsCommand = new PlayerUtilsCommand(this); + if (getServer().getPluginManager().isPluginEnabled("PlotSquared")) { + playerUtilsCommand.addSubCommand(new RandomPlotPort(this)); + } else { + log.warn("PlotSquared not found — rpp subcommand will not be registered."); + } } private void registerEvents() { @@ -44,6 +52,7 @@ public final class PlayerUtils extends JavaPlugin { pluginManager.registerEvents(new PlayerJoin(this), this); pluginManager.registerEvents(new BookWriteEvent(), this); pluginManager.registerEvents(new BookByteLimitListener(), this); + pluginManager.registerEvents(new VillagerWorkstationEvent(this), this); RotateBlockEvent rotateBlockEvent = new RotateBlockEvent(); pluginManager.registerEvents(rotateBlockEvent, this); diff --git a/src/main/java/com/alttd/playerutils/config/Config.java b/src/main/java/com/alttd/playerutils/config/Config.java index 05db98f..523e251 100644 --- a/src/main/java/com/alttd/playerutils/config/Config.java +++ b/src/main/java/com/alttd/playerutils/config/Config.java @@ -5,6 +5,7 @@ import org.bukkit.configuration.ConfigurationSection; import java.io.File; import java.util.HashMap; +import java.util.List; import java.util.Set; @Slf4j public class Config extends AbstractConfig{ @@ -92,4 +93,86 @@ import java.util.Set; WAYPOINT_TRANSMIT_RANGE = config.getDouble(prefix, "waypoint_transmit_range", WAYPOINT_TRANSMIT_RANGE); } } + + public static class VILLAGER_WORKSTATION { + private static final String prefix = "villager-workstation."; + + public static int RANGE = 8; + public static int CHECK_INTERVAL_TICKS = 5; + public static int PARTICLE_RING_COUNT = 8; + + @SuppressWarnings("unused") + private static void load() { + RANGE = config.getInt(prefix, "range", RANGE); + CHECK_INTERVAL_TICKS = config.getInt(prefix, "check-interval-ticks", CHECK_INTERVAL_TICKS); + PARTICLE_RING_COUNT = config.getInt(prefix, "particle-ring-count", PARTICLE_RING_COUNT); + } + } + + public static class RANDOM_PLOT { + private static final String prefix = "random-plot."; + + // Command + public static String PORT_COMMAND = "rpp"; + public static List ALLOWED_WORLDS = List.of("plotworld"); + + // Permissions + public static boolean USE_LUCKPERMS = true; + public static String LUCKPERMS_GROUP = "default"; + public static boolean USE_VAULT = false; + public static String VAULT_GROUP = "member"; + public static String FALLBACK_PERM = "randomplot.use"; + + // Countdown + public static int COUNTDOWN_SECONDS = 3; + public static HashMap COUNTDOWN_TITLES = new HashMap<>(); + public static String COUNTDOWN_SUBTITLE = "Preparing teleportation"; + + // Messages + public static String MSG_TELEPORT_START = "Starting teleport countdown..."; + public static String MSG_TELEPORT_SUCCESS = "You have arrived at a random plot!"; + public static String MSG_TELEPORT_CANCELLED = "Teleport cancelled! You moved."; + public static String MSG_NO_PLOTS_FOUND = "No plots found in this world."; + + @SuppressWarnings("unused") + private static void load() { + PORT_COMMAND = config.getString(prefix, "port-command", PORT_COMMAND); + ALLOWED_WORLDS = config.getStringList(prefix, "allowed-worlds", ALLOWED_WORLDS); + + USE_LUCKPERMS = config.getBoolean(prefix + "permissions.", "use-luckperms", USE_LUCKPERMS); + LUCKPERMS_GROUP = config.getString(prefix + "permissions.", "luckperms-group", LUCKPERMS_GROUP); + USE_VAULT = config.getBoolean(prefix + "permissions.", "use-vault", USE_VAULT); + VAULT_GROUP = config.getString(prefix + "permissions.", "vault-group", VAULT_GROUP); + FALLBACK_PERM = config.getString(prefix + "permissions.", "fallback-perm", FALLBACK_PERM); + + COUNTDOWN_SECONDS = config.getInt(prefix + "countdown.", "seconds", COUNTDOWN_SECONDS); + COUNTDOWN_SUBTITLE = config.getString(prefix + "countdown.", "subtitle", COUNTDOWN_SUBTITLE); + + // Countdown titles — integer keys map to display strings + COUNTDOWN_TITLES.clear(); + ConfigurationSection titlesSection = + config.getConfigurationSection("random-plot.countdown.titles"); + if (titlesSection != null) { + for (String key : titlesSection.getKeys(false)) { + try { + COUNTDOWN_TITLES.put(Integer.parseInt(key), + titlesSection.getString(key, key)); + } catch (NumberFormatException ignored) {} + } + } else { + // Write defaults on first run + config.yaml.addDefault("random-plot.countdown.titles.3", "<#08FBFF>3"); + config.yaml.addDefault("random-plot.countdown.titles.2", "<#08FBFF>2"); + config.yaml.addDefault("random-plot.countdown.titles.1", "<#08FBFF>1"); + COUNTDOWN_TITLES.put(3, "<#08FBFF>3"); + COUNTDOWN_TITLES.put(2, "<#08FBFF>2"); + COUNTDOWN_TITLES.put(1, "<#08FBFF>1"); + } + + MSG_TELEPORT_START = config.getString(prefix + "messages.", "teleport-start", MSG_TELEPORT_START); + MSG_TELEPORT_SUCCESS = config.getString(prefix + "messages.", "teleport-success", MSG_TELEPORT_SUCCESS); + MSG_TELEPORT_CANCELLED = config.getString(prefix + "messages.", "teleport-cancelled", MSG_TELEPORT_CANCELLED); + MSG_NO_PLOTS_FOUND = config.getString(prefix + "messages.", "no-plots-found", MSG_NO_PLOTS_FOUND); + } + } } diff --git a/src/main/java/com/alttd/playerutils/config/Messages.java b/src/main/java/com/alttd/playerutils/config/Messages.java index c4ae211..7367970 100644 --- a/src/main/java/com/alttd/playerutils/config/Messages.java +++ b/src/main/java/com/alttd/playerutils/config/Messages.java @@ -35,6 +35,7 @@ public class Messages extends AbstractConfig { public static String KEY = "Receive a key that you are owed: /pu key"; public static String GHAST_SPEED = "Set the speed of a ghast: /pu ghastspeed "; public static String RECOUNT_ARMOR_STANDS = "Recount armor stands in current chunk: /pu recount"; + public static String RPP = "Teleport to a random plot: /pu rpp"; @SuppressWarnings("unused") private static void load() { @@ -47,6 +48,7 @@ public class Messages extends AbstractConfig { ROTATE_BLOCK = config.getString(prefix, "rotate-block", ROTATE_BLOCK); GHAST_SPEED = config.getString(prefix, "ghast-speed", GHAST_SPEED); RECOUNT_ARMOR_STANDS = config.getString(prefix, "recount-armor-stands", RECOUNT_ARMOR_STANDS); + RPP = config.getString(prefix, "rpp", RPP); } } @@ -185,6 +187,19 @@ public class Messages extends AbstractConfig { } + public static class RANDOM_PLOT_PORT { + private static final String prefix = "pu-command.rpp."; + + public static String NOT_ALLOWED_WORLD = "You must be in an allowed world to use this command."; + public static String PLOTSQUARED_UNAVAILABLE = "PlotSquared is not available on this server."; + + @SuppressWarnings("unused") + private static void load() { + NOT_ALLOWED_WORLD = config.getString(prefix, "not-allowed-world", NOT_ALLOWED_WORLD); + PLOTSQUARED_UNAVAILABLE = config.getString(prefix, "plotsquared-unavailable", PLOTSQUARED_UNAVAILABLE); + } + } + public static class RECOUNT_ARMOR_STANDS { private static final String prefix = "recount-armor-stands."; @@ -197,4 +212,17 @@ public class Messages extends AbstractConfig { SUCCESS = config.getString(prefix, "success", SUCCESS); } } + + public static class VILLAGER_WORKSTATION { + private static final String prefix = "villager-workstation."; + + public static String WORKSTATION = "Villager (): "; + public static String NO_WORKSTATION = "Villager (): No workstation"; + + @SuppressWarnings("unused") + private static void load() { + WORKSTATION = config.getString(prefix, "workstation", WORKSTATION); + NO_WORKSTATION = config.getString(prefix, "no-workstation", NO_WORKSTATION); + } + } } diff --git a/src/main/java/com/alttd/playerutils/event_listeners/VillagerWorkstationEvent.java b/src/main/java/com/alttd/playerutils/event_listeners/VillagerWorkstationEvent.java new file mode 100644 index 0000000..42d571d --- /dev/null +++ b/src/main/java/com/alttd/playerutils/event_listeners/VillagerWorkstationEvent.java @@ -0,0 +1,84 @@ +package com.alttd.playerutils.event_listeners; + +import com.alttd.playerutils.config.Config; +import com.alttd.playerutils.config.Messages; +import lombok.extern.slf4j.Slf4j; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Villager; +import org.bukkit.entity.memory.MemoryKey; +import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; + +@Slf4j +public class VillagerWorkstationEvent implements Listener { + + public VillagerWorkstationEvent(JavaPlugin plugin) { + Bukkit.getScheduler().runTaskTimer(plugin, this::checkPlayersLookingAtVillagers, + 0L, Config.VILLAGER_WORKSTATION.CHECK_INTERVAL_TICKS); + } + + private void checkPlayersLookingAtVillagers() { + for (Player player : Bukkit.getOnlinePlayers()) { + if (!player.getInventory().getItemInMainHand().getType().equals(Material.AIR)) { + continue; + } + Entity target = player.getTargetEntity(Config.VILLAGER_WORKSTATION.RANGE); + if (!(target instanceof Villager villager)) { + continue; + } + showWorkstation(player, villager); + } + } + + private void showWorkstation(Player player, Villager villager) { + String professionName = formatName(villager.getProfession().name()); + Location jobSite = villager.getMemory(MemoryKey.JOB_SITE); + + if (jobSite == null) { + player.sendActionBar(MiniMessage.miniMessage().deserialize( + Messages.VILLAGER_WORKSTATION.NO_WORKSTATION + .replace("", professionName))); + return; + } + + // Read the actual block type at the linked location so it reflects reality + String blockName = formatName(jobSite.getBlock().getType().name()); + player.sendActionBar(MiniMessage.miniMessage().deserialize( + Messages.VILLAGER_WORKSTATION.WORKSTATION + .replace("", professionName) + .replace("", blockName))); + + spawnWorkstationParticles(player, jobSite.getBlock().getLocation()); + } + + private void spawnWorkstationParticles(Player player, Location blockLoc) { + World world = blockLoc.getWorld(); + if (world == null) { + return; + } + + double cx = blockLoc.getX() + 0.5; + double cy = blockLoc.getY() + 1.1; + double cz = blockLoc.getZ() + 0.5; + int count = Config.VILLAGER_WORKSTATION.PARTICLE_RING_COUNT; + + for (int i = 0; i < count; i++) { + double angle = (2 * Math.PI * i) / count; + double x = cx + 0.5 * Math.cos(angle); + double z = cz + 0.5 * Math.sin(angle); + player.spawnParticle(Particle.VILLAGER_HAPPY, x, cy, z, 1, 0, 0, 0, 0); + } + } + + private static String formatName(String enumName) { + String lower = enumName.replace("_", " ").toLowerCase(); + return Character.toUpperCase(lower.charAt(0)) + lower.substring(1); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a91e0dd..12740ae 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,6 +2,10 @@ name: PlayerUtils version: '${version}' main: com.alttd.playerutils.PlayerUtils api-version: '1.20' +softdepend: + - PlotSquared + - LuckPerms + - Vault commands: playerutils: description: Base command for player utils