From 63b2b39fcf6da094eab8a1ca456f017347504fb4 Mon Sep 17 00:00:00 2001 From: Luncaaa Date: Sun, 11 Aug 2024 20:04:37 +0200 Subject: [PATCH] [1.5.3] - Add ATTACHED billboard --- .../api/util/ComponentSerializer.java | 1 + .../advanceddisplays/AdvancedDisplays.java | 2 +- .../subcommands/CreateSubCommand.java | 44 ++++++++--- .../data/AttachedDisplay.java | 62 +++++++++++++++ .../events/PlayerEventsListener.java | 19 ++++- .../inventory/inventories/EditorGUI.java | 2 +- .../managers/DisplaysManager.java | 79 ++++++++++++++++++- 7 files changed, 191 insertions(+), 18 deletions(-) create mode 100644 plugin/src/main/java/me/lucaaa/advanceddisplays/data/AttachedDisplay.java diff --git a/api/src/main/java/me/lucaaa/advanceddisplays/api/util/ComponentSerializer.java b/api/src/main/java/me/lucaaa/advanceddisplays/api/util/ComponentSerializer.java index 367064b..8c42b27 100644 --- a/api/src/main/java/me/lucaaa/advanceddisplays/api/util/ComponentSerializer.java +++ b/api/src/main/java/me/lucaaa/advanceddisplays/api/util/ComponentSerializer.java @@ -22,6 +22,7 @@ public interface ComponentSerializer { * @return The component. */ static Component deserialize(String text) { + text = text.replace("\\n", "\n"); // From legacy and minimessage format to a component Component legacy = LegacyComponentSerializer.legacyAmpersand().deserialize(text); // From component to Minimessage String. Replacing the "\" with nothing makes the minimessage formats work. diff --git a/plugin/src/main/java/me/lucaaa/advanceddisplays/AdvancedDisplays.java b/plugin/src/main/java/me/lucaaa/advanceddisplays/AdvancedDisplays.java index 0514943..bd932e7 100644 --- a/plugin/src/main/java/me/lucaaa/advanceddisplays/AdvancedDisplays.java +++ b/plugin/src/main/java/me/lucaaa/advanceddisplays/AdvancedDisplays.java @@ -58,7 +58,7 @@ public void reloadConfigs() { // Managers HashMap savedApiDisplays = new HashMap<>(); // If the plugin is reloaded, this will save the click actions for API displays. - if (displaysManager != null) displaysManager.removeAllEntities(); // If the plugin has been reloaded, remove the displays to prevent duplicate displays. + if (displaysManager != null) displaysManager.removeAll(); // If the plugin has been reloaded, remove the displays to prevent duplicate displays. if (interactionsManager != null) savedApiDisplays = interactionsManager.getApiDisplays(); if (packetsManager != null) packetsManager.removeAll(); // If the plugin has been reloaded, remove and add all players again. if (inventoryManager != null) inventoryManager.clearAll(); // If the plugin has been reloaded, clear the map. diff --git a/plugin/src/main/java/me/lucaaa/advanceddisplays/commands/subcommands/CreateSubCommand.java b/plugin/src/main/java/me/lucaaa/advanceddisplays/commands/subcommands/CreateSubCommand.java index 3dbe09f..6463d88 100644 --- a/plugin/src/main/java/me/lucaaa/advanceddisplays/commands/subcommands/CreateSubCommand.java +++ b/plugin/src/main/java/me/lucaaa/advanceddisplays/commands/subcommands/CreateSubCommand.java @@ -2,7 +2,7 @@ import me.lucaaa.advanceddisplays.AdvancedDisplays; import me.lucaaa.advanceddisplays.api.util.ComponentSerializer; -import me.lucaaa.advanceddisplays.displays.ADBaseDisplay; +import me.lucaaa.advanceddisplays.data.AttachedDisplay; import me.lucaaa.advanceddisplays.api.displays.enums.DisplayType; import org.bukkit.Material; import org.bukkit.command.CommandSender; @@ -37,7 +37,9 @@ public CreateSubCommand(AdvancedDisplays plugin) { @Override public List getTabCompletions(CommandSender sender, String[] args) { if (args.length == 2) { - return Arrays.stream(DisplayType.values()).map(Enum::name).toList(); + List completions = new ArrayList<>(Arrays.stream(DisplayType.values()).map(Enum::name).toList()); + completions.add("ATTACHED"); + return completions; } else if (args.length == 4) { if (args[1].equalsIgnoreCase("ITEM")) { @@ -45,6 +47,9 @@ public List getTabCompletions(CommandSender sender, String[] args) { } else if (args[1].equalsIgnoreCase("BLOCK")) { return this.blocksList; + + } else if (args[1].equalsIgnoreCase("ATTACHED")) { + return Arrays.stream(AttachedDisplay.Side.values()).map(Enum::name).toList(); } } @@ -54,6 +59,28 @@ public List getTabCompletions(CommandSender sender, String[] args) { @Override public void run(CommandSender sender, String[] args) { Player player = (Player) sender; + + if (plugin.getDisplaysManager().existsDisplay(args[2])) { + sender.sendMessage(plugin.getMessagesManager().getColoredMessage("&cA display with the name &b" + args[2] + " &calready exists!", true)); + return; + } + + if (args[1].equalsIgnoreCase("ATTACHED")) { + AttachedDisplay.Side side; + try { + side = AttachedDisplay.Side.valueOf(args[3].toUpperCase()); + } catch (IllegalArgumentException e) { + sender.sendMessage(plugin.getMessagesManager().getColoredMessage("&cThe side &b" + args[3] + " &cis not a valid side.", true)); + return; + } + + String value = String.join(" ", Arrays.copyOfRange(args, 4, args.length)); + plugin.getDisplaysManager().addAttachingPlayer(player, new AttachedDisplay(args[2], side, ComponentSerializer.deserialize(value), true)); + sender.sendMessage(plugin.getMessagesManager().getColoredMessage("&6Right-click the block where you want your display to be attached or run &e/ad finish &6to cancel the action.", true)); + + return; + } + DisplayType type; try { type = DisplayType.valueOf(args[1].toUpperCase()); @@ -79,17 +106,12 @@ public void run(CommandSender sender, String[] args) { } } - ADBaseDisplay newDisplay = null; switch (type) { - case TEXT -> newDisplay = plugin.getDisplaysManager().createTextDisplay(player.getEyeLocation(), args[2], ComponentSerializer.deserialize(value.replace("\\n", "\n")), true); - case ITEM -> newDisplay = plugin.getDisplaysManager().createItemDisplay(player.getEyeLocation(), args[2], Material.getMaterial(value), true); - case BLOCK -> newDisplay = plugin.getDisplaysManager().createBlockDisplay(player.getEyeLocation(), args[2], Objects.requireNonNull(Material.getMaterial(value)).createBlockData(), true); + case TEXT -> plugin.getDisplaysManager().createTextDisplay(player.getEyeLocation(), args[2], ComponentSerializer.deserialize(value), true); + case ITEM -> plugin.getDisplaysManager().createItemDisplay(player.getEyeLocation(), args[2], Material.getMaterial(value), true); + case BLOCK -> plugin.getDisplaysManager().createBlockDisplay(player.getEyeLocation(), args[2], Objects.requireNonNull(Material.getMaterial(value)).createBlockData(), true); } - if (newDisplay == null) { - sender.sendMessage(plugin.getMessagesManager().getColoredMessage("&cA display with the name &b" + args[2] + " &calready exists!", true)); - } else { - sender.sendMessage(plugin.getMessagesManager().getColoredMessage("&aThe display &e" + args[2] + " &ahas been successfully created.", true)); - } + sender.sendMessage(plugin.getMessagesManager().getColoredMessage("&aThe display &e" + args[2] + " &ahas been successfully created.", true)); } } diff --git a/plugin/src/main/java/me/lucaaa/advanceddisplays/data/AttachedDisplay.java b/plugin/src/main/java/me/lucaaa/advanceddisplays/data/AttachedDisplay.java new file mode 100644 index 0000000..7cfada1 --- /dev/null +++ b/plugin/src/main/java/me/lucaaa/advanceddisplays/data/AttachedDisplay.java @@ -0,0 +1,62 @@ +package me.lucaaa.advanceddisplays.data; + +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.block.BlockFace; + +public record AttachedDisplay(String name, Side side, Component content, boolean saveToConfig) { + public enum Side { + LEFT, + RIGHT, + CENTER + } + + public static float getYaw(BlockFace clickedFace) { + return switch (clickedFace) { + case NORTH -> -180.0f; + // South is covered by "default" branch + case EAST -> -90.0f; + case WEST -> 90.0f; + default -> 0.0f; + }; + } + + public static float getPos(BlockFace clickedFace, Side side) { + if (clickedFace == BlockFace.NORTH || clickedFace == BlockFace.EAST) { + return switch (side) { + case LEFT -> 1.0f; + case CENTER -> 0.5f; + case RIGHT -> 0.0f; + }; + + } else { + return switch (side) { + case LEFT -> 0.0f; + case CENTER -> 0.5f; + case RIGHT -> 1.0f; + }; + } + } + + public static Location addSides(BlockFace face, Location clickedBlockLocation, double pos, boolean add) { + Location location = clickedBlockLocation.clone(); + + // If the clicked face is UP or DOWN, don't add 1 (so it's at the base of the clicked block.) + double add1 = 0.0; + if (add) { + add1 = switch (face) { + case NORTH, WEST -> 1.0; + case SOUTH, EAST -> -1.0; + default -> 0.0; + }; + } + + switch (face) { + case NORTH -> location.add(pos, 0.0, add1 -0.001); + case SOUTH -> location.add(pos, 0.0, add1 + 1.001); + case EAST -> location.add(add1 + 1.001, 0.0, pos); + case WEST -> location.add(add1 -0.001, 0.0, pos); + } + return location; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/me/lucaaa/advanceddisplays/events/PlayerEventsListener.java b/plugin/src/main/java/me/lucaaa/advanceddisplays/events/PlayerEventsListener.java index ddd1001..23547eb 100644 --- a/plugin/src/main/java/me/lucaaa/advanceddisplays/events/PlayerEventsListener.java +++ b/plugin/src/main/java/me/lucaaa/advanceddisplays/events/PlayerEventsListener.java @@ -2,7 +2,10 @@ import me.lucaaa.advanceddisplays.AdvancedDisplays; import me.lucaaa.advanceddisplays.api.ADAPIImplementation; +import me.lucaaa.advanceddisplays.data.AttachedDisplay; +import me.lucaaa.advanceddisplays.displays.ADTextDisplay; import me.lucaaa.advanceddisplays.managers.ConfigManager; +import me.lucaaa.advanceddisplays.managers.DisplaysManager; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; @@ -64,6 +67,7 @@ public void onPlayerLeave(PlayerQuitEvent event) { plugin.getPacketsManager().remove(event.getPlayer()); if (plugin.getInventoryManager().isPlayerNotEditing(event.getPlayer())) return; plugin.getInventoryManager().getEditingPlayer(event.getPlayer()).finishEditing(); + plugin.getDisplaysManager().removeAttachingDisplay(event.getPlayer()); } @EventHandler @@ -80,11 +84,24 @@ public void run() { @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { - if (plugin.getInventoryManager().isPlayerNotEditing(event.getPlayer()) || event.getHand() == EquipmentSlot.OFF_HAND) return; + if (event.getHand() == EquipmentSlot.OFF_HAND) return; Player player = event.getPlayer(); Action action = event.getAction(); + DisplaysManager manager = plugin.getDisplaysManager(); + if (manager.isPlayerAttaching(player) && action == Action.RIGHT_CLICK_BLOCK) { + AttachedDisplay display = manager.getAttachingDisplay(player); + ADTextDisplay newDisplay = manager.createAttachedDisplay(event, display); + if (newDisplay == null) { + player.sendMessage(plugin.getMessagesManager().getColoredMessage("&cA display with the name &b" + display.name() + " &calready exists!", true)); + } else { + player.sendMessage(plugin.getMessagesManager().getColoredMessage("&aThe display &e" + display.name() + " &ahas been successfully created.", true)); + } + } + + if (plugin.getInventoryManager().isPlayerNotEditing(player)) return; + // Because the event is fired twice, the current time is stored in a map along with the player that interacted with the display. // When the event is called again, the current time and the one stored in the map are compared. If less than or 20ms have passed, ignore this event. if (action == Action.RIGHT_CLICK_AIR) { diff --git a/plugin/src/main/java/me/lucaaa/advanceddisplays/inventory/inventories/EditorGUI.java b/plugin/src/main/java/me/lucaaa/advanceddisplays/inventory/inventories/EditorGUI.java index 57014fc..a02d36a 100644 --- a/plugin/src/main/java/me/lucaaa/advanceddisplays/inventory/inventories/EditorGUI.java +++ b/plugin/src/main/java/me/lucaaa/advanceddisplays/inventory/inventories/EditorGUI.java @@ -412,7 +412,7 @@ public void handleChatEdit(Player player, String input) { } String identifier = input.substring(0, firstSpace); - String joined = input.substring(firstSpace + 1).replace("\\n", "\n"); + String joined = input.substring(firstSpace + 1); if (((TextDisplay) display).addText(identifier, ComponentSerializer.deserialize(joined))) { player.sendMessage(plugin.getMessagesManager().getColoredMessage("&aThe animation &e" + identifier + " &a has been created and added after the last animation.", true)); } else { diff --git a/plugin/src/main/java/me/lucaaa/advanceddisplays/managers/DisplaysManager.java b/plugin/src/main/java/me/lucaaa/advanceddisplays/managers/DisplaysManager.java index 2823334..e834145 100644 --- a/plugin/src/main/java/me/lucaaa/advanceddisplays/managers/DisplaysManager.java +++ b/plugin/src/main/java/me/lucaaa/advanceddisplays/managers/DisplaysManager.java @@ -3,6 +3,7 @@ import com.google.common.io.Files; import me.lucaaa.advanceddisplays.AdvancedDisplays; import me.lucaaa.advanceddisplays.actions.actionTypes.ActionType; +import me.lucaaa.advanceddisplays.data.AttachedDisplay; import me.lucaaa.advanceddisplays.displays.*; import me.lucaaa.advanceddisplays.api.displays.enums.DisplayType; import me.lucaaa.advanceddisplays.data.ConfigAxisAngle4f; @@ -12,10 +13,12 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.*; +import org.bukkit.event.player.PlayerInteractEvent; import java.io.File; import java.util.*; @@ -25,8 +28,9 @@ public class DisplaysManager { private final AdvancedDisplays plugin; private final PacketInterface packets; private final String configsFolder; - private final HashMap displays = new HashMap<>(); + private final Map displays = new HashMap<>(); private final boolean isApi; + private final Map attachDisplays = new HashMap<>(); @SuppressWarnings("ResultOfMethodCallIgnored") public DisplaysManager(AdvancedDisplays plugin, String configsFolder, boolean createFolders, boolean isApi) { @@ -114,6 +118,51 @@ private ConfigManager createConfigManager(String name, DisplayType type, Locatio return displayConfigManager; } + public ADTextDisplay createAttachedDisplay(PlayerInteractEvent event, AttachedDisplay display) { + if (event.getClickedBlock() == null) return null; + + Player player = event.getPlayer(); + BlockFace clickedFace = event.getBlockFace(); + + float yaw; + if (clickedFace == BlockFace.UP || clickedFace == BlockFace.DOWN) { + // Get the yaw depending on where the player is facing. + yaw = AttachedDisplay.getYaw(player.getFacing().getOppositeFace()); + } else { + // Get the yaw depending on the clicked face. + yaw = AttachedDisplay.getYaw(clickedFace); + } + + float pitch = 0.0f; + if (clickedFace == BlockFace.UP) { + pitch = -90.0f; + } else if (clickedFace == BlockFace.DOWN) { + pitch = 90.0f; + } + + Location location; + if (clickedFace == BlockFace.UP || clickedFace == BlockFace.DOWN) { + double addY = (clickedFace == BlockFace.UP) ? 1.001 : -0.001; + boolean add = clickedFace == BlockFace.UP; + Location loc = event.getClickedBlock().getLocation().clone().add(0.0, addY, 0.0); + float pos = AttachedDisplay.getPos(player.getFacing().getOppositeFace(), display.side()); + location = AttachedDisplay.addSides(player.getFacing(), loc, pos, add); + } else { + float pos = AttachedDisplay.getPos(clickedFace, display.side()); + location = AttachedDisplay.addSides(clickedFace, event.getClickedBlock().getLocation(), pos, false); + } + + ADTextDisplay newDisplay = createTextDisplay(location, display.name(), display.content(), display.saveToConfig()); + + if (newDisplay != null) { + newDisplay.setBillboard(Display.Billboard.FIXED); + newDisplay.setSeeThrough(false); + newDisplay.setRotation(yaw, pitch); + } + + return newDisplay; + } + public ADTextDisplay createTextDisplay(Location location, String name, Component value, boolean saveToConfig) { if (this.displays.containsKey(name)) { return null; @@ -209,13 +258,15 @@ public boolean removeDisplay(String name) { return true; } - public void removeAllEntities() { + public void removeAll() { for (ADBaseDisplay display : this.displays.values()) { display.remove(); if (display instanceof ADTextDisplay) { ((ADTextDisplay) display).stopRunnable(); } } + + attachDisplays.clear(); } public ADBaseDisplay getDisplayFromMap(String name) { @@ -254,14 +305,34 @@ public void loadDisplay(ConfigManager configManager) { } } - this.displays.put(configManager.getFile().getName().replace(".yml", ""), newDisplay); + displays.put(configManager.getFile().getName().replace(".yml", ""), newDisplay); plugin.getInteractionsManager().addInteraction(newDisplay.getInteractionId(), newDisplay); for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { ((DisplayMethods) newDisplay).sendMetadataPackets(onlinePlayer); } } + public void addAttachingPlayer(Player player, AttachedDisplay display) { + attachDisplays.put(player, display); + } + + public boolean isPlayerAttaching(Player player) { + return attachDisplays.containsKey(player); + } + + public AttachedDisplay getAttachingDisplay(Player player) { + return attachDisplays.remove(player); + } + + public void removeAttachingDisplay(Player player) { + attachDisplays.remove(player); + } + public Map getDisplays() { return this.displays; } -} + + public boolean existsDisplay(String name) { + return displays.containsKey(name); + } +} \ No newline at end of file