diff --git a/src/main/java/com/alttd/playerutils/PlayerUtils.java b/src/main/java/com/alttd/playerutils/PlayerUtils.java index 2a502e5..c90cb63 100644 --- a/src/main/java/com/alttd/playerutils/PlayerUtils.java +++ b/src/main/java/com/alttd/playerutils/PlayerUtils.java @@ -1,8 +1,10 @@ package com.alttd.playerutils; import com.alttd.playerutils.commands.PlayerUtilsCommand; +import com.alttd.playerutils.commands.playerutils_subcommands.RotateBlock; import com.alttd.playerutils.config.Config; import com.alttd.playerutils.config.Messages; +import com.alttd.playerutils.event_listeners.RotateBlockEvent; import com.alttd.playerutils.event_listeners.XpBottleEvent; import com.alttd.playerutils.util.Logger; import org.bukkit.plugin.java.JavaPlugin; @@ -10,6 +12,7 @@ import org.bukkit.plugin.java.JavaPlugin; public final class PlayerUtils extends JavaPlugin { private Logger logger; + private PlayerUtilsCommand playerUtilsCommand; @Override public void onEnable() { @@ -23,11 +26,15 @@ public final class PlayerUtils extends JavaPlugin { } private void registerCommands() { - new PlayerUtilsCommand(this, logger); + playerUtilsCommand = new PlayerUtilsCommand(this, logger); } private void registerEvents() { getServer().getPluginManager().registerEvents(new XpBottleEvent(this, logger), this); + + RotateBlockEvent rotateBlockEvent = new RotateBlockEvent(); + getServer().getPluginManager().registerEvents(rotateBlockEvent, this); + playerUtilsCommand.addSubCommand(new RotateBlock(rotateBlockEvent)); } public void reloadConfigs() { diff --git a/src/main/java/com/alttd/playerutils/commands/PlayerUtilsCommand.java b/src/main/java/com/alttd/playerutils/commands/PlayerUtilsCommand.java index 01ddaa2..0d1484c 100644 --- a/src/main/java/com/alttd/playerutils/commands/PlayerUtilsCommand.java +++ b/src/main/java/com/alttd/playerutils/commands/PlayerUtilsCommand.java @@ -1,11 +1,9 @@ package com.alttd.playerutils.commands; import com.alttd.playerutils.PlayerUtils; -import com.alttd.playerutils.commands.playerutils_subcommands.Glow; -import com.alttd.playerutils.commands.playerutils_subcommands.Reload; -import com.alttd.playerutils.commands.playerutils_subcommands.XPCalc; -import com.alttd.playerutils.commands.playerutils_subcommands.XPCheque; +import com.alttd.playerutils.commands.playerutils_subcommands.*; import com.alttd.playerutils.config.Messages; +import com.alttd.playerutils.event_listeners.RotateBlockEvent; import com.alttd.playerutils.util.Logger; import org.bukkit.command.*; import org.jetbrains.annotations.NotNull; @@ -30,11 +28,11 @@ public class PlayerUtilsCommand implements CommandExecutor, TabExecutor { command.setTabCompleter(this); command.setAliases(List.of("pu")); - subCommands = Arrays.asList( + subCommands = new ArrayList<>(List.of( new Glow(logger), new XPCheque(playerUtils), new XPCalc(), - new Reload(playerUtils) + new Reload(playerUtils)) ); } @@ -95,4 +93,11 @@ public class PlayerUtilsCommand implements CommandExecutor, TabExecutor { .findFirst() .orElse(null); } + + public void addSubCommand(SubCommand subCommand) { + if (subCommands.stream().anyMatch(entry -> entry.getName().equalsIgnoreCase(subCommand.getName()))) { + return; + } + subCommands.add(subCommand); + } } \ No newline at end of file diff --git a/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RotateBlock.java b/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RotateBlock.java new file mode 100644 index 0000000..a93da1b --- /dev/null +++ b/src/main/java/com/alttd/playerutils/commands/playerutils_subcommands/RotateBlock.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.event_listeners.RotateBlockEvent; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +public class RotateBlock extends SubCommand { + + private final RotateBlockEvent rotateBlockEvent; + + public RotateBlock(RotateBlockEvent rotateBlockEvent) { + this.rotateBlockEvent = rotateBlockEvent; + } + + @Override + public boolean onCommand(CommandSender commandSender, String[] args) { + if (!(commandSender instanceof Player player)) { + commandSender.sendMiniMessage(Messages.GENERIC.PLAYER_ONLY, null); + return true; + } + + boolean enabled = rotateBlockEvent.toggleRotate(player.getUniqueId()); + if (enabled) + commandSender.sendMiniMessage(Messages.ROTATE_BLOCK.ENABLED, null); + else + commandSender.sendMiniMessage(Messages.ROTATE_BLOCK.DISABLED, null); + return true; + } + + @Override + public String getName() { + return "rotateblock"; + } + + @Override + public List getTabComplete(CommandSender commandSender, String[] args) { + return List.of(); + } + + @Override + public String getHelpMessage() { + return Messages.HELP.ROTATE_BLOCK; + } +} diff --git a/src/main/java/com/alttd/playerutils/config/Messages.java b/src/main/java/com/alttd/playerutils/config/Messages.java index 7a3927b..3431312 100644 --- a/src/main/java/com/alttd/playerutils/config/Messages.java +++ b/src/main/java/com/alttd/playerutils/config/Messages.java @@ -33,6 +33,7 @@ public class Messages extends AbstractConfig { public static String XP_CHEQUE = "Create an xp cheque: /pu xpcheque "; public static String XP_CALC = "Calculate the amount of xp between levels: /pu xpcalc "; public static String RELOAD = "Reload the configs for PlayerUtils: /pu reload"; + public static String ROTATE_BLOCK = "Enable rotating blocks with a blaze rod: /pu rotateblock"; @SuppressWarnings("unused") private static void load() { @@ -42,6 +43,7 @@ public class Messages extends AbstractConfig { XP_CHEQUE = config.getString(prefix, "xp-cheque", XP_CHEQUE); XP_CALC = config.getString(prefix, "xp-calc", XP_CALC); RELOAD = config.getString(prefix, "reload", RELOAD); + ROTATE_BLOCK = config.getString(prefix, "rotate-block", ROTATE_BLOCK); } } @@ -115,4 +117,17 @@ public class Messages extends AbstractConfig { RELOADED = config.getString(prefix, "reloaded", RELOADED); } } + + public static class ROTATE_BLOCK { + private static final String prefix = "pu-command.rotate-block."; + + public static String ENABLED = "You enabled rotating blocks by left clicking them with a wooden hoe"; + public static String DISABLED = "You disabled rotating blocks"; + + @SuppressWarnings("unused") + private static void load() { + ENABLED = config.getString(prefix, "enabled", ENABLED); + DISABLED = config.getString(prefix, "disabled", DISABLED); + } + } } diff --git a/src/main/java/com/alttd/playerutils/event_listeners/RotateBlockEvent.java b/src/main/java/com/alttd/playerutils/event_listeners/RotateBlockEvent.java new file mode 100644 index 0000000..d2e7c39 --- /dev/null +++ b/src/main/java/com/alttd/playerutils/event_listeners/RotateBlockEvent.java @@ -0,0 +1,250 @@ +package com.alttd.playerutils.event_listeners; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Tag; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Bisected; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.type.Fence; +import org.bukkit.block.data.type.Stairs; +import org.bukkit.block.data.type.Wall; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.stream.Collectors; + +public class RotateBlockEvent implements Listener { + + private final HashSet rotateEnabled = new HashSet<>(); + private static final List VALID_FOUR_STATES = List.of(BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST); + + + public synchronized boolean toggleRotate(UUID uuid) { + if (rotateEnabled.contains(uuid)) { + rotateEnabled.remove(uuid); + return false; + } else { + rotateEnabled.add(uuid); + return true; + } + } + + private synchronized boolean hasRotateEnabled(UUID uuid) { + return rotateEnabled.contains(uuid); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onRightClick(PlayerInteractEvent event) { + Player player = event.getPlayer(); + if (!hasRotateEnabled(player.getUniqueId())) + return; + + ItemStack item = event.getItem(); + if (item == null || !item.getType().equals(Material.WOODEN_HOE)) + return; + + Block block = event.getClickedBlock(); + if (block == null) + return; + + Material type = block.getType(); + if (Tag.STAIRS.isTagged(type)) { + event.setCancelled(true); + rotateStairs(block, player); + } else if (Tag.WALLS.isTagged(type)) { + event.setCancelled(true); + toggleWall(block, event.getBlockFace(), player, event.getAction().isLeftClick()); + } else if (Tag.FENCES.isTagged(type)) { + event.setCancelled(true); + toggleFence(block, event.getBlockFace(), player); + } else if (block.getBlockData() instanceof Directional directional) { + event.setCancelled(true); + rotateNormalBlock(block, directional, player); + } + } + + private void rotateNormalBlock(Block block, Directional directional, Player player) { + if (cannotBuild(block, player)) { + return; + } + + BlockFace facing = directional.getFacing(); + LinkedList collect = directional.getFaces().stream() + .sorted() + .collect(Collectors.toCollection(LinkedList::new)); + + int index = collect.indexOf(facing); + if (player.isSneaking()) { + index--; + if (index < 0) + index = collect.size() - 1; + } else { + index++; + if (index >= collect.size()) + index = 0; + } + + directional.setFacing(collect.get(index)); + block.setBlockData(directional); + } + + private void toggleFence(Block block, BlockFace blockFace, Player player) { + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Fence fence)) { + return; + } + + if (cannotBuild(block, player)) { + return; + } + + if (player.isSneaking()) { + fence.getFaces().forEach(face -> fence.setFace(face, false)); + block.setBlockData(fence); + return; + } + + if (!fence.getAllowedFaces().contains(blockFace)) { + return; + } + + Set faces = fence.getFaces(); + fence.setFace(blockFace, !faces.contains(blockFace)); + block.setBlockData(fence); + } + + private void toggleWall(Block block, BlockFace blockFace, Player player, boolean leftClick) { + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Wall wall)) { + return; + } + + if (cannotBuild(block, player)) { + return; + } + + if (player.isSneaking()) { + VALID_FOUR_STATES.forEach(face -> wall.setHeight(face, Wall.Height.NONE)); + wall.setUp(true); + block.setBlockData(wall); + return; + } + + if (!leftClick) { + if (wallHasNoFaces(wall)) + return; + wall.setUp(!wall.isUp()); + block.setBlockData(wall); + return; + } + + if (!VALID_FOUR_STATES.contains(blockFace)) + return; + + int ordinal = wall.getHeight(blockFace).ordinal(); + if (ordinal == Wall.Height.values().length - 1) + ordinal = 0; + else + ordinal++; + wall.setHeight(blockFace, Wall.Height.values()[ordinal]); + if (!wall.isUp() && wallHasNoFaces(wall)) { + wall.setUp(true); + } + block.setBlockData(wall); + } + + private boolean wallHasNoFaces(Wall wall) { + for (BlockFace validFourState : VALID_FOUR_STATES) { + if (wall.getHeight(validFourState).equals(Wall.Height.NONE)) + continue; + return false; + } + return true; + } + + private void rotateStairs(Block block, Player player) { + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Stairs stairs)) + return; + + if (cannotBuild(block, player)) { + return; + } + + Stairs nextRotation; + if (player.isSneaking()) + nextRotation = getPreviousRotation(stairs); + else + nextRotation = getNextRotation(stairs); + block.setBlockData(nextRotation); + } + + private Stairs getNextRotation(Stairs stairs) { + Stairs.Shape shape = stairs.getShape(); + if (shape.ordinal() != Stairs.Shape.values().length - 1) { + stairs.setShape(Stairs.Shape.values()[shape.ordinal() + 1]); + return stairs; + } + + Bisected.Half half = stairs.getHalf(); + if (half.equals(Bisected.Half.BOTTOM)) { + stairs.setShape(Stairs.Shape.values()[0]); + stairs.setHalf(Bisected.Half.TOP); + return stairs; + } + + BlockFace facing = stairs.getFacing(); + int index = VALID_FOUR_STATES.indexOf(facing); + if (index == 3) { + index = 0; + } else { + index += 1; + } + + stairs.setFacing(VALID_FOUR_STATES.get(index)); + stairs.setHalf(Bisected.Half.BOTTOM); + stairs.setShape(Stairs.Shape.values()[0]); + return stairs; + } + + private Stairs getPreviousRotation(Stairs stairs) { + Stairs.Shape shape = stairs.getShape(); + if (shape.ordinal() != 0) { + stairs.setShape(Stairs.Shape.values()[shape.ordinal() - 1]); + return stairs; + } + Bisected.Half half = stairs.getHalf(); + if (half.equals(Bisected.Half.TOP)) { + stairs.setShape(Stairs.Shape.values()[Stairs.Shape.values().length - 1]); + stairs.setHalf(Bisected.Half.BOTTOM); + return stairs; + } + BlockFace facing = stairs.getFacing(); + int index = VALID_FOUR_STATES.indexOf(facing); + if (index == 0) { + index = 3; + } else { + index -= 1; + } + stairs.setFacing(VALID_FOUR_STATES.get(index)); + stairs.setHalf(Bisected.Half.TOP); + stairs.setShape(Stairs.Shape.values()[Stairs.Shape.values().length - 1]); + return stairs; + } + + private boolean cannotBuild(Block block, Player player) { + BlockPlaceEvent placeEvent = new BlockPlaceEvent(block, block.getState(), block, new ItemStack(block.getType()), player, true, EquipmentSlot.HAND); + Bukkit.getPluginManager().callEvent(placeEvent); + return placeEvent.isCancelled(); + } +}