diff --git a/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/PropertyMap.java b/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/PropertyMap.java index de065d18..548689ea 100644 --- a/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/PropertyMap.java +++ b/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/PropertyMap.java @@ -64,6 +64,18 @@ public boolean put(@NotNull PropertyType type, @NotNull T value) { return properties.computeIfAbsent(type, PropertyImpl::new).setValue(value); } + public boolean isEmpty() { + return properties.isEmpty(); + } + + public void remove(PropertyType type) { + properties.remove(type); + } + + public void clear() { + properties.clear(); + } + private static class PropertyImpl implements Property { private final PropertyType type; private T value; diff --git a/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/type/PropertyType.java b/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/type/PropertyType.java index 8cff8aea..062c25a9 100644 --- a/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/type/PropertyType.java +++ b/easyarmorstands-api/src/main/java/me/m56738/easyarmorstands/api/property/type/PropertyType.java @@ -30,6 +30,10 @@ default boolean canChange(@NotNull Player player) { } } + default boolean canCopy(@NotNull Player player) { + return true; + } + /** * Display name of this property. * diff --git a/easyarmorstands-display/src/main/java/me/m56738/easyarmorstands/display/menu/BlockDisplaySlot.java b/easyarmorstands-display/src/main/java/me/m56738/easyarmorstands/display/menu/BlockDisplaySlot.java index 01c7741a..1084700d 100644 --- a/easyarmorstands-display/src/main/java/me/m56738/easyarmorstands/display/menu/BlockDisplaySlot.java +++ b/easyarmorstands-display/src/main/java/me/m56738/easyarmorstands/display/menu/BlockDisplaySlot.java @@ -1,5 +1,6 @@ package me.m56738.easyarmorstands.display.menu; +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; import me.m56738.easyarmorstands.api.menu.MenuClick; import me.m56738.easyarmorstands.api.property.Property; import me.m56738.easyarmorstands.api.property.PropertyContainer; @@ -39,5 +40,9 @@ protected SimpleItemTemplate prepareTemplate(SimpleItemTemplate template) { @Override public void onClick(@NotNull MenuClick click) { + if (click.isShiftClick()) { + EasyArmorStandsPlugin.getInstance().getClipboard(click.player()) + .handlePropertyShiftClick(property, click); + } } } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/EasyArmorStandsPlugin.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/EasyArmorStandsPlugin.java index fa8b0d95..a1ffd805 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/EasyArmorStandsPlugin.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/EasyArmorStandsPlugin.java @@ -18,16 +18,22 @@ import me.m56738.easyarmorstands.capability.CapabilityLoader; import me.m56738.easyarmorstands.capability.handswap.SwapHandItemsCapability; import me.m56738.easyarmorstands.capability.tool.ToolCapability; +import me.m56738.easyarmorstands.clipboard.Clipboard; +import me.m56738.easyarmorstands.clipboard.ClipboardListener; +import me.m56738.easyarmorstands.clipboard.ClipboardManager; import me.m56738.easyarmorstands.color.ColorAxisChangeSlotType; import me.m56738.easyarmorstands.color.ColorAxisSlotType; import me.m56738.easyarmorstands.color.ColorIndicatorSlotType; import me.m56738.easyarmorstands.color.ColorPresetSlotType; import me.m56738.easyarmorstands.color.ColorSlot; +import me.m56738.easyarmorstands.command.ClipboardCommands; import me.m56738.easyarmorstands.command.GlobalCommands; import me.m56738.easyarmorstands.command.HistoryCommands; import me.m56738.easyarmorstands.command.SessionCommands; import me.m56738.easyarmorstands.command.annotation.PropertyPermission; import me.m56738.easyarmorstands.command.parser.NodeValueArgumentParser; +import me.m56738.easyarmorstands.command.processor.ClipboardInjector; +import me.m56738.easyarmorstands.command.processor.ClipboardProcessor; import me.m56738.easyarmorstands.command.processor.ElementInjector; import me.m56738.easyarmorstands.command.processor.ElementProcessor; import me.m56738.easyarmorstands.command.processor.ElementSelectionInjector; @@ -147,6 +153,7 @@ public class EasyArmorStandsPlugin extends JavaPlugin implements EasyArmorStands private MenuProviderImpl menuProvider; private SessionManagerImpl sessionManager; private HistoryManager historyManager; + private ClipboardManager clipboardManager; private UpdateManager updateManager; private BukkitAudiences adventure; private LegacyPaperCommandManager commandManager; @@ -209,11 +216,13 @@ public void onEnable() { sessionManager = new SessionManagerImpl(); historyManager = new HistoryManager(); + clipboardManager = new ClipboardManager(); SessionListener sessionListener = new SessionListener(this, sessionManager); getServer().getPluginManager().registerEvents(new MenuListener(), this); getServer().getPluginManager().registerEvents(sessionListener, this); getServer().getPluginManager().registerEvents(historyManager, this); + getServer().getPluginManager().registerEvents(new ClipboardListener(clipboardManager), this); getServer().getPluginManager().registerEvents(new EntityElementListener(), this); getServer().getScheduler().runTaskTimer(this, sessionManager::update, 0, 1); getServer().getScheduler().runTaskTimer(this, sessionListener::update, 0, 1); @@ -255,6 +264,7 @@ public void onEnable() { commandManager.parameterInjectorRegistry() .registerInjector(ValueNode.class, new ValueNodeInjector()) .registerInjector(SessionImpl.class, new SessionInjector()) + .registerInjector(Clipboard.class, new ClipboardInjector()) .registerInjector(Element.class, new ElementInjector()) .registerInjector(ElementSelection.class, new ElementSelectionInjector()); @@ -268,6 +278,7 @@ public void onEnable() { commandManager.registerCommandPreProcessor(new GroupProcessor()); commandManager.registerCommandPreProcessor(new ElementProcessor()); commandManager.registerCommandPreProcessor(new SessionProcessor()); + commandManager.registerCommandPreProcessor(new ClipboardProcessor()); commandManager.registerCommandPostProcessor(new CommandRequirementPostProcessor()); @@ -282,6 +293,7 @@ public void onEnable() { annotationParser.parse(new GlobalCommands(commandManager, sessionListener)); annotationParser.parse(new SessionCommands()); annotationParser.parse(new HistoryCommands()); + annotationParser.parse(new ClipboardCommands()); if (Bukkit.getPluginManager().isPluginEnabled("WorldGuard")) { getLogger().info("Enabling WorldGuard integration"); @@ -515,6 +527,10 @@ public History getHistory(Player player) { return historyManager.getHistory(player); } + public Clipboard getClipboard(Player player) { + return clipboardManager.getClipboard(player); + } + @Contract(pure = true) public T getCapability(Class type) { return loader.get(type); diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/Clipboard.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/Clipboard.java new file mode 100644 index 00000000..1f265e97 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/Clipboard.java @@ -0,0 +1,43 @@ +package me.m56738.easyarmorstands.clipboard; + +import me.m56738.easyarmorstands.api.menu.MenuClick; +import me.m56738.easyarmorstands.api.property.Property; +import me.m56738.easyarmorstands.api.property.PropertyMap; +import me.m56738.easyarmorstands.api.property.type.PropertyType; +import me.m56738.easyarmorstands.message.Message; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public class Clipboard { + private final Player player; + private final PropertyMap properties = new PropertyMap(); + + Clipboard(Player player) { + this.player = player; + } + + public PropertyMap getProperties() { + return properties; + } + + public void handlePropertyShiftClick(Property property, MenuClick click) { + PropertyType type = property.getType(); + if (type.canCopy(click.player())) { + properties.put(type, property.getValue()); + click.sendMessage(Message.success("easyarmorstands.success.property-copied", type.getName())); + } + } + + void removeDisallowed() { + List> types = new ArrayList<>(); + properties.forEach(property -> types.add(property.getType())); + + for (PropertyType type : types) { + if (!type.canCopy(player)) { + properties.remove(type); + } + } + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/ClipboardListener.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/ClipboardListener.java new file mode 100644 index 00000000..3521bae2 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/ClipboardListener.java @@ -0,0 +1,27 @@ +package me.m56738.easyarmorstands.clipboard; + +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerGameModeChangeEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class ClipboardListener implements Listener { + private final ClipboardManager clipboardManager; + + public ClipboardListener(ClipboardManager clipboardManager) { + this.clipboardManager = clipboardManager; + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + clipboardManager.remove(event.getPlayer()); + } + + @EventHandler + public void onGameModeChange(PlayerGameModeChangeEvent event) { + EasyArmorStandsPlugin plugin = EasyArmorStandsPlugin.getInstance(); + Bukkit.getScheduler().runTask(plugin, () -> clipboardManager.getClipboard(event.getPlayer()).removeDisallowed()); + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/ClipboardManager.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/ClipboardManager.java new file mode 100644 index 00000000..c4edb2e3 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/clipboard/ClipboardManager.java @@ -0,0 +1,18 @@ +package me.m56738.easyarmorstands.clipboard; + +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class ClipboardManager { + private final Map clipboards = new HashMap<>(); + + public Clipboard getClipboard(Player player) { + return clipboards.computeIfAbsent(player, Clipboard::new); + } + + public void remove(Player player) { + clipboards.remove(player); + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/ClipboardCommands.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/ClipboardCommands.java new file mode 100644 index 00000000..bf253bd5 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/ClipboardCommands.java @@ -0,0 +1,106 @@ +package me.m56738.easyarmorstands.command; + +import me.m56738.easyarmorstands.api.element.Element; +import me.m56738.easyarmorstands.api.property.Property; +import me.m56738.easyarmorstands.api.property.PropertyContainer; +import me.m56738.easyarmorstands.api.property.type.PropertyType; +import me.m56738.easyarmorstands.clipboard.Clipboard; +import me.m56738.easyarmorstands.command.requirement.RequireElement; +import me.m56738.easyarmorstands.command.requirement.RequireElementSelection; +import me.m56738.easyarmorstands.command.sender.EasPlayer; +import me.m56738.easyarmorstands.command.util.ElementSelection; +import me.m56738.easyarmorstands.message.Message; +import me.m56738.easyarmorstands.permission.Permissions; +import me.m56738.easyarmorstands.util.PropertyCopier; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.CommandDescription; +import org.incendo.cloud.annotations.Permission; + +@Command("eas") +public class ClipboardCommands { + @Command("clipboard") + @Permission(Permissions.CLIPBOARD) + @CommandDescription("easyarmorstands.command.description.clipboard") + public void clipboard(EasPlayer sender, Clipboard clipboard) { + if (clipboard.getProperties().isEmpty()) { + sender.sendMessage(Message.warning("easyarmorstands.warning.clipboard-empty")); + return; + } + sender.sendMessage(Message.title("easyarmorstands.title.clipboard")); + clipboard.getProperties().forEach(property -> sender.sendMessage(Component.text() + .content("* ") + .color(NamedTextColor.GRAY) + .append(describeProperty(property)))); + sender.sendMessage(Message.hint("easyarmorstands.hint.paste-clipboard", Message.command("/eas paste"))); + sender.sendMessage(Message.hint("easyarmorstands.hint.clear-clipboard", Message.command("/eas clipboard clear"))); + } + + private Component describeProperty(Property property) { + PropertyType type = property.getType(); + return Component.text() + .append(type.getName()) + .append(Component.text(": ")) + .append(type.getValueComponent(property.getValue())) + .build(); + } + + @Command("clipboard clear") + @Permission(Permissions.CLIPBOARD) + @CommandDescription("easyarmorstands.command.description.clipboard.clear") + public void clear(EasPlayer sender, Clipboard clipboard) { + if (clipboard.getProperties().isEmpty()) { + sender.sendMessage(Message.warning("easyarmorstands.warning.clipboard-empty")); + } else { + clipboard.getProperties().clear(); + sender.sendMessage(Message.success("easyarmorstands.success.clipboard-cleared")); + } + } + + @Command("copy") + @Permission(Permissions.CLIPBOARD) + @CommandDescription("easyarmorstands.command.description.copy") + @RequireElement + public void copy(EasPlayer sender, Clipboard clipboard, Element element) { + element.getProperties().forEach(property -> { + if (property.getType().canCopy(sender.player())) { + copyProperty(clipboard, property); + } + }); + sender.sendMessage(Message.success("easyarmorstands.success.clipboard-copied")); + sender.sendMessage(Message.hint("easyarmorstands.hint.show-clipboard", Message.command("/eas clipboard"))); + sender.sendMessage(Message.hint("easyarmorstands.hint.paste-clipboard", Message.command("/eas paste"))); + } + + private void copyProperty(Clipboard clipboard, Property property) { + clipboard.getProperties().put(property.getType(), property.getValue()); + } + + @Command("paste") + @Permission(Permissions.CLIPBOARD) + @CommandDescription("easyarmorstands.command.description.paste") + @RequireElementSelection + public void paste(EasPlayer sender, Clipboard clipboard, ElementSelection selection) { + if (clipboard.getProperties().isEmpty()) { + sender.sendMessage(Message.error("easyarmorstands.error.clipboard-empty")); + sender.sendMessage(Message.hint("easyarmorstands.hint.copy-property")); + sender.sendMessage(Message.hint("easyarmorstands.hint.copy-all-properties", Message.command("/eas copy"))); + return; + } + + PropertyCopier copier = new PropertyCopier(); + PropertyContainer properties = selection.properties(sender); + copier.copyProperties(properties, clipboard.getProperties()); + properties.commit(Message.component("easyarmorstands.history.clipboard-pasted")); + + if (copier.getSuccessCount() > 0) { + sender.sendMessage(Message.success("easyarmorstands.success.clipboard-pasted")); + if (copier.getFailureCount() > 0) { + sender.sendMessage(Message.warning("easyarmorstands.warning.clipboard-partial")); + } + } else if (copier.getFailureCount() > 0) { + sender.sendMessage(Message.error("easyarmorstands.error.clipboard-failed")); + } + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/processor/ClipboardInjector.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/processor/ClipboardInjector.java new file mode 100644 index 00000000..8a74aa40 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/processor/ClipboardInjector.java @@ -0,0 +1,18 @@ +package me.m56738.easyarmorstands.command.processor; + +import me.m56738.easyarmorstands.clipboard.Clipboard; +import me.m56738.easyarmorstands.command.sender.EasCommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.injection.ParameterInjector; +import org.incendo.cloud.util.annotation.AnnotationAccessor; + +import static me.m56738.easyarmorstands.command.processor.ClipboardProcessor.clipboardKey; + +public class ClipboardInjector implements ParameterInjector { + @Override + public @Nullable Clipboard create(@NonNull CommandContext context, @NonNull AnnotationAccessor annotationAccessor) { + return context.getOrDefault(clipboardKey(), null); + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/processor/ClipboardProcessor.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/processor/ClipboardProcessor.java new file mode 100644 index 00000000..b60dd47b --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/processor/ClipboardProcessor.java @@ -0,0 +1,30 @@ +package me.m56738.easyarmorstands.command.processor; + +import me.m56738.easyarmorstands.clipboard.Clipboard; +import me.m56738.easyarmorstands.command.sender.EasCommandSender; +import me.m56738.easyarmorstands.command.sender.EasPlayer; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.execution.preprocessor.CommandPreprocessingContext; +import org.incendo.cloud.execution.preprocessor.CommandPreprocessor; +import org.incendo.cloud.key.CloudKey; + +import static org.incendo.cloud.key.CloudKey.cloudKey; + +public class ClipboardProcessor implements CommandPreprocessor { + private static final CloudKey KEY = cloudKey("clipboard", Clipboard.class); + + public static CloudKey clipboardKey() { + return KEY; + } + + @Override + public void accept(@NonNull CommandPreprocessingContext context) { + CommandContext commandContext = context.commandContext(); + EasCommandSender sender = commandContext.sender(); + if (sender instanceof EasPlayer) { + Clipboard clipboard = ((EasPlayer) sender).clipboard(); + commandContext.set(KEY, clipboard); + } + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/sender/EasPlayer.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/sender/EasPlayer.java index 1595c583..22e40b02 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/sender/EasPlayer.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/command/sender/EasPlayer.java @@ -11,6 +11,7 @@ import me.m56738.easyarmorstands.api.event.player.PlayerEditPropertyEvent; import me.m56738.easyarmorstands.api.property.Property; import me.m56738.easyarmorstands.api.property.PropertyContainer; +import me.m56738.easyarmorstands.clipboard.Clipboard; import me.m56738.easyarmorstands.context.ChangeContext; import me.m56738.easyarmorstands.history.ChangeTracker; import me.m56738.easyarmorstands.history.History; @@ -27,11 +28,13 @@ public class EasPlayer extends EasCommandSender implements ChangeContext { private final @NotNull Player player; private final @NotNull History history; + private final @NotNull Clipboard clipboard; public EasPlayer(@NotNull Player player, @NotNull Audience audience) { super(player, audience); this.player = player; this.history = EasyArmorStandsPlugin.getInstance().getHistory(player); + this.clipboard = EasyArmorStandsPlugin.getInstance().getClipboard(player); } public EasPlayer(@NotNull Player player) { @@ -48,6 +51,10 @@ public EasPlayer(@NotNull Player player) { return history; } + public @NotNull Clipboard clipboard() { + return clipboard; + } + @Override public @NotNull ChangeTracker tracker() { return history.getTracker(); diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartResetAction.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartResetAction.java deleted file mode 100644 index 1cc233aa..00000000 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartResetAction.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.m56738.easyarmorstands.menu.slot; - -import me.m56738.easyarmorstands.api.ArmorStandPart; -import me.m56738.easyarmorstands.api.menu.MenuClick; -import me.m56738.easyarmorstands.api.property.Property; -import me.m56738.easyarmorstands.api.property.PropertyContainer; -import me.m56738.easyarmorstands.api.property.type.ArmorStandPropertyTypes; -import org.bukkit.util.EulerAngle; - -import java.util.function.Consumer; - -public class ArmorStandPartResetAction implements Consumer { - private final Property property; - private final PropertyContainer container; - private final T value; - - public ArmorStandPartResetAction(Property property, PropertyContainer container, T value) { - this.property = property; - this.container = container; - this.value = value; - } - - public static ArmorStandPartResetAction pose(ArmorStandPart part, PropertyContainer container) { - Property property = container.get(ArmorStandPropertyTypes.POSE.get(part)); - return new ArmorStandPartResetAction<>(property, container, EulerAngle.ZERO); - } - - @Override - public void accept(MenuClick click) { - property.setValue(value); - container.commit(); - } -} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlot.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlot.java new file mode 100644 index 00000000..3938c6e6 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlot.java @@ -0,0 +1,56 @@ +package me.m56738.easyarmorstands.menu.slot; + +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; +import me.m56738.easyarmorstands.api.editor.Session; +import me.m56738.easyarmorstands.api.menu.MenuClick; +import me.m56738.easyarmorstands.api.menu.MenuSlot; +import me.m56738.easyarmorstands.api.property.Property; +import me.m56738.easyarmorstands.api.property.PropertyContainer; +import me.m56738.easyarmorstands.api.property.type.PropertyType; +import me.m56738.easyarmorstands.clipboard.Clipboard; +import me.m56738.easyarmorstands.editor.node.NodeFactory; +import me.m56738.easyarmorstands.item.SimpleItemTemplate; +import me.m56738.easyarmorstands.message.Message; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.EulerAngle; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public class ArmorStandPartSlot implements MenuSlot { + private final Session session; + private final NodeFactory nodeFactory; + private final PropertyContainer container; + private final Property property; + private final SimpleItemTemplate itemTemplate; + private final TagResolver resolver; + + public ArmorStandPartSlot(Session session, NodeFactory nodeFactory, PropertyContainer container, Property property, SimpleItemTemplate itemTemplate, TagResolver resolver) { + this.session = session; + this.nodeFactory = nodeFactory; + this.container = container; + this.property = property; + this.itemTemplate = itemTemplate; + this.resolver = resolver; + } + + @Override + public ItemStack getItem(Locale locale) { + return itemTemplate.render(locale, resolver); + } + + @Override + public void onClick(@NotNull MenuClick click) { + if (click.isShiftClick()) { + EasyArmorStandsPlugin.getInstance().getClipboard(click.player()) + .handlePropertyShiftClick(property, click); + } else if (click.isLeftClick()) { + session.pushNode(nodeFactory.createNode()); + click.close(); + } else if (click.isRightClick()) { + property.setValue(EulerAngle.ZERO); + container.commit(); + } + } +} diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlotFactory.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlotFactory.java index ecfca295..2c5e4054 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlotFactory.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ArmorStandPartSlotFactory.java @@ -5,6 +5,8 @@ import me.m56738.easyarmorstands.api.menu.MenuSlot; import me.m56738.easyarmorstands.api.menu.MenuSlotContext; import me.m56738.easyarmorstands.api.menu.MenuSlotFactory; +import me.m56738.easyarmorstands.api.property.PropertyContainer; +import me.m56738.easyarmorstands.api.property.type.ArmorStandPropertyTypes; import me.m56738.easyarmorstands.editor.armorstand.node.ArmorStandRootNode; import me.m56738.easyarmorstands.item.SimpleItemTemplate; import org.jetbrains.annotations.NotNull; @@ -29,9 +31,12 @@ public ArmorStandPartSlotFactory(ArmorStandPart part, SimpleItemTemplate itemTem if (root == null) { return null; } - return new NodeSlot(session, + PropertyContainer properties = context.properties(root.getElement()); + return new ArmorStandPartSlot( + session, root.getPartButton(part), - ArmorStandPartResetAction.pose(part, context.properties(root.getElement())), + properties, + properties.get(ArmorStandPropertyTypes.POSE.get(part)), itemTemplate, context.resolver()); } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/EntityCopySlotFactory.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/EntityCopySlotFactory.java index b88464b8..4d36c29b 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/EntityCopySlotFactory.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/EntityCopySlotFactory.java @@ -33,7 +33,7 @@ public EntityCopySlotFactory(SimpleItemTemplate buttonTemplate, SimpleItemTempla Element element = context.element(); if (element instanceof EntityElement) { Entity entity = ((EntityElement) element).getEntity(); - if (context.permissions().test(Permissions.COPY)) { + if (context.permissions().test(Permissions.COPY_ENTITY)) { ItemStack item = spawnEggCapability.createSpawnEgg(entity); if (item != null) { return new EntityCopySlot( diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ItemPropertySlot.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ItemPropertySlot.java index 61ab8cc0..cd414587 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ItemPropertySlot.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/menu/slot/ItemPropertySlot.java @@ -1,5 +1,6 @@ package me.m56738.easyarmorstands.menu.slot; +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; import me.m56738.easyarmorstands.api.menu.MenuClick; import me.m56738.easyarmorstands.api.menu.MenuSlot; import me.m56738.easyarmorstands.api.property.Property; @@ -35,6 +36,12 @@ public ItemStack getItem(Locale locale) { @Override public void onClick(@NotNull MenuClick click) { + if (click.isShiftClick()) { + EasyArmorStandsPlugin.getInstance().getClipboard(click.player()) + .handlePropertyShiftClick(property, click); + return; + } + if (!property.canChange(click.player())) { return; } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/permission/Permissions.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/permission/Permissions.java index 1022679c..c2dcbc7d 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/permission/Permissions.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/permission/Permissions.java @@ -15,6 +15,9 @@ public class Permissions { @Description("Allow aligning entities to the block grid") public static final String ALIGN = "easyarmorstands.align"; + @Description("Allow copying properties") + public static final String CLIPBOARD = "easyarmorstands.clipboard"; + @Description("Allow cloning entities") public static final String CLONE = "easyarmorstands.clone"; @@ -25,7 +28,7 @@ public class Permissions { public static final String CONVERT = "easyarmorstands.convert"; @Description("Allow copying entities as items") - public static final String COPY = "easyarmorstands.copy"; + public static final String COPY_ENTITY = "easyarmorstands.copy.entity"; @Description("Allow viewing troubleshooting information") public static final String DEBUG = "easyarmorstands.debug"; diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ComponentButton.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ComponentButton.java index 723e0268..0f7a4f82 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ComponentButton.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ComponentButton.java @@ -1,5 +1,6 @@ package me.m56738.easyarmorstands.property.button; +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; import me.m56738.easyarmorstands.api.menu.MenuClick; import me.m56738.easyarmorstands.api.property.Property; import me.m56738.easyarmorstands.api.property.PropertyContainer; @@ -18,7 +19,10 @@ public ComponentButton(Property property, PropertyContainer container @Override public void onClick(@NotNull MenuClick click) { - if (click.isLeftClick()) { + if (click.isShiftClick()) { + EasyArmorStandsPlugin.getInstance().getClipboard(click.player()) + .handlePropertyShiftClick(property, click); + } else if (click.isLeftClick()) { click.close(); SessionCommands.showText(click, property.getType().getName(), property.getValue(), command); } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/OptionalComponentButton.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/OptionalComponentButton.java index 978c9da0..b69b4042 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/OptionalComponentButton.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/OptionalComponentButton.java @@ -1,5 +1,6 @@ package me.m56738.easyarmorstands.property.button; +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; import me.m56738.easyarmorstands.api.menu.MenuClick; import me.m56738.easyarmorstands.api.property.Property; import me.m56738.easyarmorstands.api.property.PropertyContainer; @@ -20,7 +21,10 @@ public OptionalComponentButton(Property> property, PropertyC @Override public void onClick(@NotNull MenuClick click) { - if (click.isLeftClick()) { + if (click.isShiftClick()) { + EasyArmorStandsPlugin.getInstance().getClipboard(click.player()) + .handlePropertyShiftClick(property, click); + } else if (click.isLeftClick()) { click.close(); SessionCommands.showText(click, property.getType().getName(), property.getValue().orElse(null), command); } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ToggleButton.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ToggleButton.java index f883b23d..bb4c8fea 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ToggleButton.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/button/ToggleButton.java @@ -1,5 +1,6 @@ package me.m56738.easyarmorstands.property.button; +import me.m56738.easyarmorstands.EasyArmorStandsPlugin; import me.m56738.easyarmorstands.api.menu.MenuClick; import me.m56738.easyarmorstands.api.property.Property; import me.m56738.easyarmorstands.api.property.PropertyContainer; @@ -23,7 +24,11 @@ protected boolean setValue(T value) { @Override public void onClick(@NotNull MenuClick click) { boolean changed; - if (click.isLeftClick()) { + if (click.isShiftClick()) { + EasyArmorStandsPlugin.getInstance().getClipboard(click.player()) + .handlePropertyShiftClick(property, click); + return; + } else if (click.isLeftClick()) { changed = setValue(getNextValue()); } else if (click.isRightClick()) { changed = setValue(getPreviousValue()); diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/ItemPropertyType.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/ItemPropertyType.java index f1bfa990..e4f409d7 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/ItemPropertyType.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/ItemPropertyType.java @@ -8,6 +8,8 @@ import me.m56738.easyarmorstands.menu.slot.ItemPropertySlot; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,4 +33,9 @@ public ItemPropertyType(@NotNull Key key) { public @Nullable MenuSlot createSlot(@NotNull Property property, @NotNull PropertyContainer container) { return new ItemPropertySlot(property, container); } + + @Override + public boolean canCopy(@NotNull Player player) { + return player.getGameMode() == GameMode.CREATIVE; + } } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/LocationPropertyType.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/LocationPropertyType.java index 6d393a0f..fbda1d35 100644 --- a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/LocationPropertyType.java +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/property/type/LocationPropertyType.java @@ -4,6 +4,7 @@ import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import org.bukkit.Location; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; public class LocationPropertyType extends ConfigurablePropertyType { @@ -20,4 +21,9 @@ public LocationPropertyType(@NotNull Key key) { public @NotNull Location cloneValue(@NotNull Location value) { return value.clone(); } + + @Override + public boolean canCopy(@NotNull Player player) { + return false; + } } diff --git a/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/util/PropertyCopier.java b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/util/PropertyCopier.java new file mode 100644 index 00000000..4ae901f5 --- /dev/null +++ b/easyarmorstands-plugin/src/main/java/me/m56738/easyarmorstands/util/PropertyCopier.java @@ -0,0 +1,35 @@ +package me.m56738.easyarmorstands.util; + +import me.m56738.easyarmorstands.api.property.Property; +import me.m56738.easyarmorstands.api.property.PropertyContainer; + +public class PropertyCopier { + private int successCount; + private int failureCount; + + public int getSuccessCount() { + return successCount; + } + + public int getFailureCount() { + return failureCount; + } + + public void copyProperties(PropertyContainer destination, PropertyContainer source) { + source.forEach(p -> { + if (copyProperty(destination, p)) { + successCount++; + } else { + failureCount++; + } + }); + } + + private boolean copyProperty(PropertyContainer destination, Property source) { + Property property = destination.getOrNull(source.getType()); + if (property != null) { + return property.setValue(source.getValue()); + } + return false; + } +} diff --git a/easyarmorstands-plugin/src/main/resources/me/m56738/easyarmorstands/messages.properties b/easyarmorstands-plugin/src/main/resources/me/m56738/easyarmorstands/messages.properties index e61d885c..3c44ce0a 100644 --- a/easyarmorstands-plugin/src/main/resources/me/m56738/easyarmorstands/messages.properties +++ b/easyarmorstands-plugin/src/main/resources/me/m56738/easyarmorstands/messages.properties @@ -41,8 +41,11 @@ easyarmorstands.command.description.brightness.here=Apply the light level at you easyarmorstands.command.description.brightness.reset=Remove the custom light level from the selected display easyarmorstands.command.description.brightness.sky=Set the sky light level of the selected display easyarmorstands.command.description.cantick=Toggle whether the selected armor stand should be ticked +easyarmorstands.command.description.clipboard.clear=Clear your clipboard +easyarmorstands.command.description.clipboard=Show your clipboard easyarmorstands.command.description.clone=Spawn a copy of the selected entity easyarmorstands.command.description.convert=Convert the selected armor stand to an item display +easyarmorstands.command.description.copy=Copy all properties of the selected entity easyarmorstands.command.description.debug=Displays debug information easyarmorstands.command.description.destroy=Destroy the selected entity easyarmorstands.command.description.give=Gives you the editor tool @@ -58,10 +61,11 @@ easyarmorstands.command.description.name.visible=Change the custom name visibili easyarmorstands.command.description.name=Show the custom name of the selected entity easyarmorstands.command.description.open.entity=Open the menu of an entity easyarmorstands.command.description.open=Open the menu +easyarmorstands.command.description.paste=Apply your clipboard to the selected entity easyarmorstands.command.description.pitch=Set the pitch of the selected entity easyarmorstands.command.description.position=Teleport the selected entity easyarmorstands.command.description.redo=Redo a change -easyarmorstands.command.description.reload=Reloads the configuration +easyarmorstands.command.description.reload=Reload the configuration easyarmorstands.command.description.reset=Reset the value of the selected tool easyarmorstands.command.description.scale=Rescale the selected entity easyarmorstands.command.description.set=Set the value of the selected tool @@ -93,6 +97,8 @@ easyarmorstands.error.cannot-edit=Unable to edit this entity. easyarmorstands.error.cannot-move=Unable to move this entity. easyarmorstands.error.cannot-redo=Unable to redo change: {0}. easyarmorstands.error.cannot-undo=Unable to undo change: {0}. +easyarmorstands.error.clipboard-empty=Your clipboard is empty. +easyarmorstands.error.clipboard-failed=Your clipboard is not compatible with this entity. easyarmorstands.error.convert-unsupported=Only armor stands can be converted. easyarmorstands.error.destroy-unsupported=This entity cannot be destroyed. easyarmorstands.error.entity-not-found=Entity not found. @@ -122,14 +128,20 @@ easyarmorstands.error.text-unsupported=This entity doesn''t support custom text. easyarmorstands.error.unsupported-entity=This entity cannot be edited. easyarmorstands.error.worldguard.deny-create=Can''t spawn this entity here. easyarmorstands.error.worldguard.deny-destroy=Can''t destroy that entity here. +easyarmorstands.hint.clear-clipboard=Use {0} to clear your clipboard. +easyarmorstands.hint.copy-all-properties=Use {0} to copy all properties of an entity. +easyarmorstands.hint.copy-property=Shift click a property in the menu to copy it. easyarmorstands.hint.deselect-entity=Drop to stop editing. easyarmorstands.hint.give-tool=Use {0} to obtain the tool. easyarmorstands.hint.not-set=Not set. +easyarmorstands.hint.paste-clipboard=Use {0} to apply your clipboard to an entity. easyarmorstands.hint.select-entity=Right click an entity to select it. +easyarmorstands.hint.show-clipboard=Use {0} to view your clipboard. easyarmorstands.hint.show-help=Use {0} to browse available commands. easyarmorstands.hint.spawn-entity=Left click to spawn an entity. easyarmorstands.history.changed-color=Change color to {0} easyarmorstands.history.changed-property=Change {0} from {1} to {2} +easyarmorstands.history.clipboard-pasted=Apply clipboard easyarmorstands.history.clone-element=Clone entity easyarmorstands.history.clone-group=Clone group easyarmorstands.history.converted-armor-stand=Convert armor stand @@ -293,11 +305,15 @@ easyarmorstands.success.changed-text-line-width=Changed the text line width to { easyarmorstands.success.changed-text=Changed the text to: {0} easyarmorstands.success.changed-value=Set {0} to {1}. easyarmorstands.success.changed-yaw=Changed yaw to {0}. +easyarmorstands.success.clipboard-cleared=Cleared your clipboard. +easyarmorstands.success.clipboard-copied=Copied all properties of the selected entity. +easyarmorstands.success.clipboard-pasted=Applied your clipboard to the selected entity. easyarmorstands.success.entity-cloned.multiple={0} entities cloned. easyarmorstands.success.entity-cloned=Entity cloned. easyarmorstands.success.entity-destroyed.multiple={0} entities destroyed. easyarmorstands.success.entity-destroyed=Entity destroyed. easyarmorstands.success.moved=Moved to {0}. +easyarmorstands.success.property-copied=Copied {0} to your clipboard. easyarmorstands.success.redone-change=Redone change: {0}. easyarmorstands.success.reloaded-config=Reloaded the configuration. easyarmorstands.success.removed-box=Removed the bounding box. @@ -306,8 +322,11 @@ easyarmorstands.success.reset-value=Reset the selected property. easyarmorstands.success.snap-changed.angle=Set angle snapping step size to {0}° easyarmorstands.success.snap-changed.position=Set position snapping step size to {0} easyarmorstands.success.undone-change=Undone change: {0}. +easyarmorstands.title.clipboard=Clipboard easyarmorstands.title.history=History easyarmorstands.tool-name=EasyArmorStands easyarmorstands.update.available=EasyArmorStands v{0} is available easyarmorstands.update.click-to-visit=Click to visit EasyArmorStands on SpigotMC +easyarmorstands.warning.clipboard-empty=Your clipboard is empty. +easyarmorstands.warning.clipboard-partial=Some properties in your clipboard are not compatible with this entity. easyarmorstands.warning.history-empty=Your history is empty. diff --git a/easyarmorstands-plugin/src/main/resources/plugin.yml b/easyarmorstands-plugin/src/main/resources/plugin.yml index 14646079..f8260f19 100644 --- a/easyarmorstands-plugin/src/main/resources/plugin.yml +++ b/easyarmorstands-plugin/src/main/resources/plugin.yml @@ -15,6 +15,7 @@ permissions: easyarmorstands.survival: description: "Allow editing armor stands and their basic properties" children: + - easyarmorstands.clipboard - easyarmorstands.edit.armorstand - easyarmorstands.give - easyarmorstands.property.armorstand.arms @@ -40,7 +41,7 @@ permissions: - easyarmorstands.clone - easyarmorstands.color - easyarmorstands.convert - - easyarmorstands.copy + - easyarmorstands.copy.entity - easyarmorstands.destroy.armorstand - easyarmorstands.destroy.blockdisplay - easyarmorstands.destroy.interaction diff --git a/gradle.properties b/gradle.properties index 1b9c664f..375733fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ -version=2.2.2-SNAPSHOT -minecraftVersion=1.8-1.21 +version=2.3.0-SNAPSHOT +minecraftVersion=1.8-1.21.1