From 9dd5cde7456ff18d74431e23c3769f1f3c4b95c9 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Sat, 25 Mar 2023 21:11:07 +0100 Subject: [PATCH] Keeping notes - Added the Clipboard --- src/generated/resources/.cache/cache | 8 +- .../resources/assets/create/lang/en_ud.json | 1 + .../resources/assets/create/lang/en_us.json | 2 + .../assets/create/models/item/clipboard.json | 26 + .../create/models/item/clipboard_0.json | 6 + .../create/models/item/clipboard_1.json | 6 + .../create/models/item/clipboard_2.json | 6 + .../java/com/simibubi/create/AllItems.java | 7 +- .../clipboard/ClipboardEditPacket.java | 44 + .../curiosities/clipboard/ClipboardEntry.java | 52 ++ .../curiosities/clipboard/ClipboardItem.java | 62 ++ .../clipboard/ClipboardOverrides.java | 57 ++ .../clipboard/ClipboardScreen.java | 775 ++++++++++++++++++ .../clipboard/ClipboardScreenHelper.java | 11 + .../create/foundation/gui/AllGuiTextures.java | 2 + .../create/foundation/gui/AllIcons.java | 2 + .../foundation/networking/AllPackets.java | 2 + .../assets/create/lang/default/interface.json | 2 + .../assets/create/textures/gui/clipboard.pdn | Bin 0 -> 24482 bytes .../assets/create/textures/gui/clipboard.png | Bin 0 -> 7684 bytes .../assets/create/textures/gui/icons.png | Bin 3637 -> 3676 bytes .../assets/create/textures/item/clipboard.png | Bin 0 -> 290 bytes .../textures/item/clipboard_and_quill.png | Bin 0 -> 332 bytes .../create/textures/item/empty_clipboard.png | Bin 0 -> 281 bytes 24 files changed, 1068 insertions(+), 3 deletions(-) create mode 100644 src/generated/resources/assets/create/models/item/clipboard.json create mode 100644 src/generated/resources/assets/create/models/item/clipboard_0.json create mode 100644 src/generated/resources/assets/create/models/item/clipboard_1.json create mode 100644 src/generated/resources/assets/create/models/item/clipboard_2.json create mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java create mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java create mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java create mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java create mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java create mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java create mode 100644 src/main/resources/assets/create/textures/gui/clipboard.pdn create mode 100644 src/main/resources/assets/create/textures/gui/clipboard.png create mode 100644 src/main/resources/assets/create/textures/item/clipboard.png create mode 100644 src/main/resources/assets/create/textures/item/clipboard_and_quill.png create mode 100644 src/main/resources/assets/create/textures/item/empty_clipboard.png diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 3250272bd..840b6279b 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -566,8 +566,8 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo 5616dda664dd106d576848124fc0fc1de18d0fd3 assets/create/blockstates/yellow_valve_handle.json 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json -f1bedeb51c35e70a2247178634e61ea637a6622e assets/create/lang/en_ud.json -59fd557ef593efa3c7b783195ec5dc789eae1834 assets/create/lang/en_us.json +0f4e5a2fc58580df5b156fdac438ddeb6b57e386 assets/create/lang/en_ud.json +85d790bedbdc65bb6e6377edcc63e7b00455e879 assets/create/lang/en_us.json 487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json 3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json @@ -1663,6 +1663,10 @@ f7aca6aff65e1de269a99cf2a280d9841b7a0076 assets/create/models/item/brass_sheet.j 87637b39c3a5a386457d52b37eb65f1c4bcabaf0 assets/create/models/item/chocolate_glazed_berries.json fe67c3f380d17735a9436a4579a8be1a02b8e4a0 assets/create/models/item/chute.json 6680a68526576ded5dac2aa3bc9fb9de3e744146 assets/create/models/item/cinder_flour.json +0f79260d962011817a41af8c46996f426669b089 assets/create/models/item/clipboard.json +1bd8ad56fe261f3ff2f7bb2ac12f691a76ae5c94 assets/create/models/item/clipboard_0.json +7a4d58451e051796e21138faf3428f3d3c14ef85 assets/create/models/item/clipboard_1.json +376e88de90f33fd32f3f2ffe0062ae5b3bdc1370 assets/create/models/item/clipboard_2.json c1da21be9f1af4f7a2ef4ec9cd92195d65ada316 assets/create/models/item/clockwork_bearing.json 0a2a0f0aafeab0088172f77afd40c1fa2cc1f2b8 assets/create/models/item/clutch.json dcb09deae110077bcddf090996b51cc66e9a7de3 assets/create/models/item/cogwheel.json diff --git a/src/generated/resources/assets/create/lang/en_ud.json b/src/generated/resources/assets/create/lang/en_ud.json index ebd111718..f04109d00 100644 --- a/src/generated/resources/assets/create/lang/en_ud.json +++ b/src/generated/resources/assets/create/lang/en_ud.json @@ -595,6 +595,7 @@ "item.create.chocolate_glazed_berries": "s\u01DD\u0131\u0279\u0279\u01DD\u15FA p\u01DDz\u0250\u05DF\u2141 \u01DD\u0287\u0250\u05DFo\u0254o\u0265\u0186", "item.create.chromatic_compound": "punod\u026Fo\u0186 \u0254\u0131\u0287\u0250\u026Fo\u0279\u0265\u0186", "item.create.cinder_flour": "\u0279no\u05DF\u2132 \u0279\u01DDpu\u0131\u0186", + "item.create.clipboard": "p\u0279\u0250oqd\u0131\u05DF\u0186", "item.create.copper_backtank": "\u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186", "item.create.copper_backtank_placeable": "\u01DD\u05DFq\u0250\u01DD\u0254\u0250\u05DF\u0500 \u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186", "item.create.copper_diving_boots": "s\u0287oo\u15FA bu\u0131\u028C\u0131\u15E1 \u0279\u01DDddo\u0186", diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index af41fc01d..05c6ce5ab 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -602,6 +602,7 @@ "item.create.chocolate_glazed_berries": "Chocolate Glazed Berries", "item.create.chromatic_compound": "Chromatic Compound", "item.create.cinder_flour": "Cinder Flour", + "item.create.clipboard": "Clipboard", "item.create.copper_backtank": "Copper Backtank", "item.create.copper_backtank_placeable": "Copper Backtank Placeable", "item.create.copper_diving_boots": "Copper Diving Boots", @@ -1130,6 +1131,7 @@ "create.gui.sequenced_gearshift.speed.forward_fast": "Double speed, Forwards", "create.gui.sequenced_gearshift.speed.back": "Input speed, Reversed", "create.gui.sequenced_gearshift.speed.back_fast": "Double speed, Reversed", + "create.gui.clipboard.erase_checked": "Erase checked items", "create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s", "create.schematicAndQuill.firstPos": "First position set.", diff --git a/src/generated/resources/assets/create/models/item/clipboard.json b/src/generated/resources/assets/create/models/item/clipboard.json new file mode 100644 index 000000000..07fd62820 --- /dev/null +++ b/src/generated/resources/assets/create/models/item/clipboard.json @@ -0,0 +1,26 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "create:item/clipboard" + }, + "overrides": [ + { + "predicate": { + "create:clipboard_type": 0.0 + }, + "model": "create:item/clipboard_0" + }, + { + "predicate": { + "create:clipboard_type": 1.0 + }, + "model": "create:item/clipboard_1" + }, + { + "predicate": { + "create:clipboard_type": 2.0 + }, + "model": "create:item/clipboard_2" + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/assets/create/models/item/clipboard_0.json b/src/generated/resources/assets/create/models/item/clipboard_0.json new file mode 100644 index 000000000..46f7226fa --- /dev/null +++ b/src/generated/resources/assets/create/models/item/clipboard_0.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "create:item/empty_clipboard" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/create/models/item/clipboard_1.json b/src/generated/resources/assets/create/models/item/clipboard_1.json new file mode 100644 index 000000000..ee59b74f7 --- /dev/null +++ b/src/generated/resources/assets/create/models/item/clipboard_1.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "create:item/clipboard" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/create/models/item/clipboard_2.json b/src/generated/resources/assets/create/models/item/clipboard_2.json new file mode 100644 index 000000000..a51972bbc --- /dev/null +++ b/src/generated/resources/assets/create/models/item/clipboard_2.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "create:item/clipboard_and_quill" + } +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index 3b92b6273..66717e1d0 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -39,6 +39,8 @@ import com.simibubi.create.content.curiosities.armor.BacktankItem; import com.simibubi.create.content.curiosities.armor.BacktankItem.BacktankBlockItem; import com.simibubi.create.content.curiosities.armor.DivingBootsItem; import com.simibubi.create.content.curiosities.armor.DivingHelmetItem; +import com.simibubi.create.content.curiosities.clipboard.ClipboardItem; +import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides; import com.simibubi.create.content.curiosities.symmetry.SymmetryWandItem; import com.simibubi.create.content.curiosities.tools.BlueprintItem; import com.simibubi.create.content.curiosities.tools.ExtendoGripItem; @@ -72,7 +74,10 @@ public class AllItems { REGISTRATE.creativeModeTab(() -> AllCreativeModeTabs.BASE_CREATIVE_TAB); } - // Materials + public static final ItemEntry CLIPBOARD = REGISTRATE.item("clipboard", ClipboardItem::new) + .onRegister(ClipboardItem::registerModelOverrides) + .model((c, p) -> ClipboardOverrides.addOverrideModels(c, p)) + .register(); public static final ItemEntry WHEAT_FLOUR = taggedIngredient("wheat_flour", forgeItemTag("flour/wheat"), forgeItemTag("flour")), diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java new file mode 100644 index 000000000..713201792 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEditPacket.java @@ -0,0 +1,44 @@ +package com.simibubi.create.content.curiosities.clipboard; + +import com.simibubi.create.AllItems; +import com.simibubi.create.foundation.networking.SimplePacketBase; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.network.NetworkEvent.Context; + +public class ClipboardEditPacket extends SimplePacketBase { + + private int hotbarSlot; + private CompoundTag data; + + public ClipboardEditPacket(int hotbarSlot, CompoundTag data) { + this.hotbarSlot = hotbarSlot; + this.data = data; + } + + public ClipboardEditPacket(FriendlyByteBuf buffer) { + hotbarSlot = buffer.readVarInt(); + data = buffer.readNbt(); + } + + @Override + public void write(FriendlyByteBuf buffer) { + buffer.writeVarInt(hotbarSlot); + buffer.writeNbt(data); + } + + @Override + public boolean handle(Context context) { + ServerPlayer sender = context.getSender(); + ItemStack itemStack = sender.getInventory() + .getItem(hotbarSlot); + if (!AllItems.CLIPBOARD.isIn(itemStack)) + return true; + itemStack.setTag(data.isEmpty() ? null : data); + return true; + } + +} diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java new file mode 100644 index 000000000..570546412 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java @@ -0,0 +1,52 @@ +package com.simibubi.create.content.curiosities.clipboard; + +import java.util.ArrayList; +import java.util.List; + +import com.simibubi.create.foundation.utility.NBTHelper; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.ItemStack; + +public class ClipboardEntry { + + boolean checked; + MutableComponent text; + + public ClipboardEntry(boolean checked, MutableComponent text) { + this.checked = checked; + this.text = text; + } + + public static List> readAll(ItemStack clipboardItem) { + CompoundTag tag = clipboardItem.getTag(); + if (tag == null) + return new ArrayList<>(); + return NBTHelper.readCompoundList(tag.getList("Pages", Tag.TAG_COMPOUND), pageTag -> NBTHelper + .readCompoundList(pageTag.getList("Entries", Tag.TAG_COMPOUND), ClipboardEntry::readNBT)); + } + + public static void saveAll(List> entries, ItemStack clipboardItem) { + CompoundTag tag = clipboardItem.getOrCreateTag(); + tag.put("Pages", NBTHelper.writeCompoundList(entries, list -> { + CompoundTag pageTag = new CompoundTag(); + pageTag.put("Entries", NBTHelper.writeCompoundList(list, ClipboardEntry::writeNBT)); + return pageTag; + })); + } + + public CompoundTag writeNBT() { + CompoundTag nbt = new CompoundTag(); + nbt.putBoolean("Checked", checked); + nbt.putString("Text", Component.Serializer.toJson(text)); + return nbt; + } + + public static ClipboardEntry readNBT(CompoundTag tag) { + return new ClipboardEntry(tag.getBoolean("Checked"), Component.Serializer.fromJson(tag.getString("Text"))); + } + +} diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java new file mode 100644 index 000000000..478fa1f4e --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardItem.java @@ -0,0 +1,62 @@ +package com.simibubi.create.content.curiosities.clipboard; + +import javax.annotation.Nonnull; + +import com.simibubi.create.foundation.gui.ScreenOpener; + +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.DistExecutor; + +public class ClipboardItem extends Item { + + public ClipboardItem(Properties pProperties) { + super(pProperties); + } + + @Nonnull + @Override + public InteractionResult useOn(UseOnContext context) { + if (context.getPlayer() == null) + return InteractionResult.PASS; + return use(context.getLevel(), context.getPlayer(), context.getHand()).getResult(); + } + + @Override + public InteractionResultHolder use(Level world, Player player, InteractionHand hand) { + ItemStack heldItem = player.getItemInHand(hand); + if (hand == InteractionHand.OFF_HAND) + return InteractionResultHolder.pass(heldItem); + + player.getCooldowns() + .addCooldown(heldItem.getItem(), 10); + if (world.isClientSide) + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> openScreen(player, heldItem)); + CompoundTag tag = heldItem.getOrCreateTag(); + tag.putInt("Type", ClipboardOverrides.ClipboardType.EDITING.ordinal()); + heldItem.setTag(tag); + + return InteractionResultHolder.success(heldItem); + } + + @OnlyIn(Dist.CLIENT) + private void openScreen(Player player, ItemStack stack) { + if (Minecraft.getInstance().player == player) + ScreenOpener.open(new ClipboardScreen(player.getInventory().selected, stack)); + } + + public void registerModelOverrides() { + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClipboardOverrides.registerModelOverridesClient(this)); + } + +} diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java new file mode 100644 index 000000000..2845b9d9f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardOverrides.java @@ -0,0 +1,57 @@ +package com.simibubi.create.content.curiosities.clipboard; + +import com.simibubi.create.Create; +import com.tterrag.registrate.providers.DataGenContext; +import com.tterrag.registrate.providers.RegistrateItemModelProvider; + +import net.minecraft.client.renderer.item.ItemProperties; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.generators.ItemModelBuilder; +import net.minecraftforge.client.model.generators.ModelFile.UncheckedModelFile; + +public class ClipboardOverrides { + + public enum ClipboardType { + EMPTY("empty_clipboard"), WRITTEN("clipboard"), EDITING("clipboard_and_quill"); + + public String file; + public static ResourceLocation ID = Create.asResource("clipboard_type"); + + private ClipboardType(String file) { + this.file = file; + } + } + + public static void switchTo(ClipboardType type, ItemStack clipboardItem) { + CompoundTag tag = clipboardItem.getOrCreateTag(); + tag.putInt("Type", type.ordinal()); + } + + @OnlyIn(Dist.CLIENT) + public static void registerModelOverridesClient(ClipboardItem item) { + ItemProperties.register(item, ClipboardType.ID, (pStack, pLevel, pEntity, pSeed) -> { + CompoundTag tag = pStack.getTag(); + return tag == null ? 0 : tag.getInt("Type"); + }); + } + + public static ItemModelBuilder addOverrideModels(DataGenContext c, + RegistrateItemModelProvider p) { + ItemModelBuilder builder = p.generated(() -> c.get()); + for (int i = 0; i < ClipboardType.values().length; i++) { + builder.override() + .predicate(ClipboardType.ID, i) + .model(p.getBuilder(c.getName() + "_" + i) + .parent(new UncheckedModelFile("item/generated")) + .texture("layer0", Create.asResource("item/" + ClipboardType.values()[i].file))) + .end(); + } + return builder; + } + +} diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java new file mode 100644 index 000000000..f9cf68581 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java @@ -0,0 +1,775 @@ +package com.simibubi.create.content.curiosities.clipboard; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.apache.commons.lang3.mutable.MutableInt; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides.ClipboardType; +import com.simibubi.create.foundation.gui.AbstractSimiScreen; +import com.simibubi.create.foundation.gui.AllGuiTextures; +import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.gui.widget.IconButton; +import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.utility.Components; +import com.simibubi.create.foundation.utility.Lang; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.SharedConstants; +import net.minecraft.Util; +import net.minecraft.client.StringSplitter; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.font.TextFieldHelper; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.PageButton; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.Mth; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +public class ClipboardScreen extends AbstractSimiScreen { + + private ItemStack item; + + List> pages; + List currentEntries; + int editingIndex; + int frameTick; + PageButton forward; + PageButton backward; + int currentPage; + long lastClickTime; + int lastIndex = -1; + + int hoveredEntry; + boolean hoveredCheck; + + DisplayCache displayCache = DisplayCache.EMPTY; + TextFieldHelper editContext; + + IconButton closeBtn; + IconButton clearBtn; + + private int targetSlot; + + public ClipboardScreen(int targetSlot, ItemStack item) { + this.targetSlot = targetSlot; + this.item = item; + pages = ClipboardEntry.readAll(item); + if (pages.isEmpty()) + pages.add(new ArrayList<>()); + currentPage = item.getTag() == null ? 0 + : item.getTag() + .getInt("PreviouslyOpenedPage"); + currentPage = Mth.clamp(currentPage, 0, pages.size() - 1); + currentEntries = pages.get(currentPage); + boolean startEmpty = currentEntries.isEmpty(); + if (startEmpty) + currentEntries.add(new ClipboardEntry(false, Components.empty())); + editingIndex = 0; + editContext = new TextFieldHelper(this::getCurrentEntryText, this::setCurrentEntryText, this::getClipboard, + this::setClipboard, this::validateTextForEntry); + editingIndex = startEmpty ? 0 : -1; + } + + @Override + protected void init() { + setWindowSize(256, 256); + super.init(); + minecraft.keyboardHandler.setSendRepeatsToGui(true); + clearDisplayCache(); + + int x = guiLeft; + int y = guiTop - 8; + + clearWidgets(); + clearBtn = new IconButton(x + 234, y + 153, AllIcons.I_CLEAR_CHECKED).withCallback(() -> { + editingIndex = -1; + currentEntries.removeIf(ce -> ce.checked); + if (currentEntries.isEmpty()) + currentEntries.add(new ClipboardEntry(false, Components.empty())); + }); + clearBtn.setToolTip(Lang.translateDirect("gui.clipboard.erase_checked")); + closeBtn = new IconButton(x + 234, y + 175, AllIcons.I_PRIORITY_VERY_LOW) + .withCallback(() -> minecraft.setScreen(null)); + closeBtn.setToolTip(Lang.translateDirect("station.close")); + addRenderableWidget(closeBtn); + addRenderableWidget(clearBtn); + + forward = new PageButton(x + 176, y + 229, true, $ -> changePage(true), true); + backward = new PageButton(x + 53, y + 229, false, $ -> changePage(false), true); + addRenderableWidget(forward); + addRenderableWidget(backward); + + forward.visible = currentPage < 50; + backward.visible = currentPage > 0; + } + + private int getNumPages() { + return pages.size(); + } + + public void tick() { + super.tick(); + frameTick++; + + int mx = (int) (this.minecraft.mouseHandler.xpos() * (double) this.minecraft.getWindow() + .getGuiScaledWidth() / (double) this.minecraft.getWindow() + .getScreenWidth()); + int my = (int) (this.minecraft.mouseHandler.ypos() * (double) this.minecraft.getWindow() + .getGuiScaledHeight() / (double) this.minecraft.getWindow() + .getScreenHeight()); + + mx -= guiLeft + 35; + my -= guiTop + 41; + + hoveredCheck = false; + hoveredEntry = -1; + + if (mx > 0 && mx < 183 && my > 0 && my < 190) { + hoveredCheck = mx < 20; + int totalHeight = 0; + for (int i = 0; i < currentEntries.size(); i++) { + ClipboardEntry clipboardEntry = currentEntries.get(i); + String text = clipboardEntry.text.getString(); + totalHeight += Math.max(12, font.split(Components.literal(text), 150) + .size() * 9 + 3); + + if (totalHeight > my) { + hoveredEntry = i; + return; + } + } + hoveredEntry = currentEntries.size(); + } + } + + private String getCurrentEntryText() { + return currentEntries.get(editingIndex).text.getString(); + } + + private void setCurrentEntryText(String text) { + currentEntries.get(editingIndex).text = Components.literal(text); + } + + private void setClipboard(String p_98148_) { + if (minecraft != null) + TextFieldHelper.setClipboardContents(minecraft, p_98148_); + } + + private String getClipboard() { + return minecraft != null ? TextFieldHelper.getClipboardContents(minecraft) : ""; + } + + private boolean validateTextForEntry(String newText) { + int totalHeight = 0; + for (int i = 0; i < currentEntries.size(); i++) { + ClipboardEntry clipboardEntry = currentEntries.get(i); + String text = i == editingIndex ? newText : clipboardEntry.text.getString(); + totalHeight += Math.max(12, font.split(Components.literal(text), 150) + .size() * 9 + 3); + } + return totalHeight < 185; + } + + private int yOffsetOfEditingEntry() { + int totalHeight = 0; + for (int i = 0; i < currentEntries.size(); i++) { + if (i == editingIndex) + break; + ClipboardEntry clipboardEntry = currentEntries.get(i); + totalHeight += Math.max(12, font.split(clipboardEntry.text, 150) + .size() * 9 + 3); + } + return totalHeight; + } + + private void changePage(boolean next) { + int previously = currentPage; + currentPage = Mth.clamp(currentPage + (next ? 1 : -1), 0, 50); + if (currentPage == previously) + return; + editingIndex = -1; + if (pages.size() <= currentPage) + pages.add(new ArrayList<>()); + currentEntries = pages.get(currentPage); + if (currentEntries.isEmpty()) { + currentEntries.add(new ClipboardEntry(false, Components.empty())); + editingIndex = 0; + editContext.setCursorToEnd(); + clearDisplayCacheAfterChange(); + } + + forward.visible = currentPage < 50; + backward.visible = currentPage > 0; + + if (next) + return; + if (pages.get(currentPage + 1) + .stream() + .allMatch(ce -> ce.text.getString() + .isBlank())) + pages.remove(currentPage + 1); + } + + @Override + protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) { + int x = guiLeft; + int y = guiTop - 8; + + AllGuiTextures.CLIPBOARD.render(ms, x, y); + font.draw(ms, new TranslatableComponent("book.pageIndicator", currentPage + 1, getNumPages()), x + 150, y + 9, + 0x43ffffff); + + for (int i = 0; i < currentEntries.size(); i++) { + ClipboardEntry clipboardEntry = currentEntries.get(i); + boolean checked = clipboardEntry.checked; + + font.draw(ms, "\u25A1", x + 45, y + 51, checked ? 0x668D7F6B : 0xff8D7F6B); + if (checked) + font.draw(ms, "\u2714", x + 45, y + 50, 0x31B25D); + + List split = font.split(clipboardEntry.text, 150); + if (split.isEmpty()) { + y += 12; + continue; + } + + for (FormattedCharSequence sequence : split) { + if (i != editingIndex) + font.draw(ms, sequence, x + 58, y + 50, checked ? 0x31B25D : 0x311A00); + y += 9; + } + y += 3; + } + + if (editingIndex == -1) + return; + + boolean checked = currentEntries.get(editingIndex).checked; + + setFocused(null); + DisplayCache cache = getDisplayCache(); + + for (LineInfo line : cache.lines) + font.draw(ms, line.asComponent, line.x, line.y, checked ? 0x31B25D : 0x311A00); + + renderHighlight(cache.selection); + renderCursor(ms, cache.cursor, cache.cursorAtEnd); + } + + @Override + public void removed() { + minecraft.keyboardHandler.setSendRepeatsToGui(false); + pages.forEach(list -> list.removeIf(ce -> ce.text.getString() + .isBlank())); + pages.removeIf(List::isEmpty); + + ClipboardEntry.saveAll(pages, item); + ClipboardOverrides.switchTo(ClipboardType.WRITTEN, item); + + for (int i = 0; i < pages.size(); i++) + if (pages.get(i) == currentEntries) + item.getTag() + .putInt("PreviouslyOpenedPage", i); + + if (pages.isEmpty()) + item.setTag(new CompoundTag()); + AllPackets.getChannel() + .sendToServer(new ClipboardEditPacket(targetSlot, item.getOrCreateTag())); + super.removed(); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + @Override + public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) { + if (pKeyCode == 266) { + backward.onPress(); + return true; + } + if (pKeyCode == 267) { + forward.onPress(); + return true; + } + if (editingIndex != -1 && pKeyCode != 256) { + keyPressedWhileEditing(pKeyCode, pScanCode, pModifiers); + clearDisplayCache(); + return true; + } + if (super.keyPressed(pKeyCode, pScanCode, pModifiers)) + return true; + return true; + } + + @Override + public boolean charTyped(char pCodePoint, int pModifiers) { + if (super.charTyped(pCodePoint, pModifiers)) + return true; + if (!SharedConstants.isAllowedChatCharacter(pCodePoint)) + return false; + if (editingIndex == -1) + return false; + editContext.insertText(Character.toString(pCodePoint)); + clearDisplayCache(); + return true; + } + + private boolean keyPressedWhileEditing(int pKeyCode, int pScanCode, int pModifiers) { + if (Screen.isSelectAll(pKeyCode)) { + editContext.selectAll(); + return true; + } else if (Screen.isCopy(pKeyCode)) { + editContext.copy(); + return true; + } else if (Screen.isPaste(pKeyCode)) { + editContext.paste(); + return true; + } else if (Screen.isCut(pKeyCode)) { + editContext.cut(); + return true; + } else { + switch (pKeyCode) { + case 257: + case 335: + if (hasShiftDown()) { + editContext.insertText("\n"); + return true; + } else if (!hasControlDown()) { + if (currentEntries.size() <= editingIndex + 1 + || !currentEntries.get(editingIndex + 1).text.getString() + .isEmpty()) + currentEntries.add(editingIndex + 1, new ClipboardEntry(false, Components.empty())); + editingIndex += 1; + editContext.setCursorToEnd(); + if (validateTextForEntry(" ")) + return true; + currentEntries.remove(editingIndex); + editingIndex -= 1; + editContext.setCursorToEnd(); + return true; + } + editingIndex = -1; + return true; + case 259: + if (currentEntries.get(editingIndex).text.getString() + .isEmpty() && currentEntries.size() > 1) { + currentEntries.remove(editingIndex); + editingIndex -= 1; + editContext.setCursorToEnd(); + return true; + } else if (hasControlDown()) { + int prevPos = editContext.getCursorPos(); + editContext.moveByWords(-1); + if (prevPos != editContext.getCursorPos()) + editContext.removeCharsFromCursor(prevPos - editContext.getCursorPos()); + return true; + } + editContext.removeCharsFromCursor(-1); + return true; + case 261: + if (hasControlDown()) { + int prevPos = editContext.getCursorPos(); + editContext.moveByWords(1); + if (prevPos != editContext.getCursorPos()) + editContext.removeCharsFromCursor(prevPos - editContext.getCursorPos()); + return true; + } + editContext.removeCharsFromCursor(1); + return true; + case 262: + if (hasControlDown()) { + editContext.moveByWords(1, Screen.hasShiftDown()); + return true; + } + editContext.moveByChars(1, Screen.hasShiftDown()); + return true; + case 263: + if (hasControlDown()) { + editContext.moveByWords(-1, Screen.hasShiftDown()); + return true; + } + editContext.moveByChars(-1, Screen.hasShiftDown()); + return true; + case 264: + keyDown(); + return true; + case 265: + keyUp(); + return true; + case 268: + keyHome(); + return true; + case 269: + keyEnd(); + return true; + default: + return false; + } + } + } + + private void keyUp() { + changeLine(-1); + } + + private void keyDown() { + changeLine(1); + } + + private void changeLine(int pYChange) { + int i = editContext.getCursorPos(); + int j = getDisplayCache().changeLine(i, pYChange); + editContext.setCursorPos(j, Screen.hasShiftDown()); + } + + private void keyHome() { + int i = editContext.getCursorPos(); + int j = getDisplayCache().findLineStart(i); + editContext.setCursorPos(j, Screen.hasShiftDown()); + } + + private void keyEnd() { + DisplayCache cache = getDisplayCache(); + int i = editContext.getCursorPos(); + int j = cache.findLineEnd(i); + editContext.setCursorPos(j, Screen.hasShiftDown()); + } + + private void renderCursor(PoseStack pPoseStack, Pos2i pCursorPos, boolean pIsEndOfText) { + if (frameTick / 6 % 2 != 0) + return; + pCursorPos = convertLocalToScreen(pCursorPos); + if (!pIsEndOfText) { + GuiComponent.fill(pPoseStack, pCursorPos.x, pCursorPos.y - 1, pCursorPos.x + 1, pCursorPos.y + 9, + -16777216); + } else { + font.draw(pPoseStack, "_", (float) pCursorPos.x, (float) pCursorPos.y, 0); + } + } + + private void renderHighlight(Rect2i[] pSelected) { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder bufferbuilder = tesselator.getBuilder(); + RenderSystem.setShader(GameRenderer::getPositionShader); + RenderSystem.setShaderColor(0.0F, 0.0F, 255.0F, 255.0F); + RenderSystem.disableTexture(); + RenderSystem.enableColorLogicOp(); + RenderSystem.logicOp(GlStateManager.LogicOp.OR_REVERSE); + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION); + + for (Rect2i rect2i : pSelected) { + int i = rect2i.getX(); + int j = rect2i.getY(); + int k = i + rect2i.getWidth(); + int l = j + rect2i.getHeight(); + bufferbuilder.vertex((double) i, (double) l, 0.0D) + .endVertex(); + bufferbuilder.vertex((double) k, (double) l, 0.0D) + .endVertex(); + bufferbuilder.vertex((double) k, (double) j, 0.0D) + .endVertex(); + bufferbuilder.vertex((double) i, (double) j, 0.0D) + .endVertex(); + } + + tesselator.end(); + RenderSystem.disableColorLogicOp(); + RenderSystem.enableTexture(); + } + + private Pos2i convertScreenToLocal(Pos2i pScreenPos) { + return new Pos2i(pScreenPos.x - (width - 192) / 2 - 36 + 10, pScreenPos.y - 32 - 24 - yOffsetOfEditingEntry()); + } + + private Pos2i convertLocalToScreen(Pos2i pLocalScreenPos) { + return new Pos2i(pLocalScreenPos.x + (width - 192) / 2 + 36 - 10, + pLocalScreenPos.y + 32 + 24 + yOffsetOfEditingEntry()); + } + + public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) { + if (super.mouseClicked(pMouseX, pMouseY, pButton)) + return true; + if (pButton != 0) + return true; + + if (hoveredEntry != -1) { + if (hoveredCheck) { + editingIndex = -1; + if (hoveredEntry < currentEntries.size()) + currentEntries.get(hoveredEntry).checked ^= true; + return true; + } + + if (hoveredEntry != editingIndex) { + editingIndex = hoveredEntry; + if (hoveredEntry >= currentEntries.size()) { + currentEntries.add(new ClipboardEntry(false, Components.empty())); + if (!validateTextForEntry(" ")) { + currentEntries.remove(hoveredEntry); + editingIndex = -1; + return true; + } + } + clearDisplayCacheAfterChange(); + } + } + + if (editingIndex == -1) + return false; + + long i = Util.getMillis(); + DisplayCache cache = getDisplayCache(); + int j = cache.getIndexAtPosition(font, convertScreenToLocal(new Pos2i((int) pMouseX, (int) pMouseY))); + if (j >= 0) { + if (j == lastIndex && i - lastClickTime < 250L) { + if (!editContext.isSelecting()) { + selectWord(j); + } else { + editContext.selectAll(); + } + } else { + editContext.setCursorPos(j, Screen.hasShiftDown()); + } + + clearDisplayCache(); + } + + lastIndex = j; + lastClickTime = i; + return true; + } + + private void selectWord(int pIndex) { + String s = getCurrentEntryText(); + editContext.setSelectionRange(StringSplitter.getWordPosition(s, -1, pIndex, false), + StringSplitter.getWordPosition(s, 1, pIndex, false)); + } + + public boolean mouseDragged(double pMouseX, double pMouseY, int pButton, double pDragX, double pDragY) { + if (super.mouseDragged(pMouseX, pMouseY, pButton, pDragX, pDragY)) + return true; + if (pButton != 0) + return true; + if (editingIndex == -1) + return false; + + DisplayCache cache = getDisplayCache(); + int i = cache.getIndexAtPosition(font, convertScreenToLocal(new Pos2i((int) pMouseX, (int) pMouseY))); + editContext.setCursorPos(i, true); + clearDisplayCache(); + return true; + } + + private DisplayCache getDisplayCache() { + if (displayCache == null) + displayCache = rebuildDisplayCache(); + return displayCache; + } + + private void clearDisplayCache() { + displayCache = null; + } + + private void clearDisplayCacheAfterChange() { + editContext.setCursorToEnd(); + clearDisplayCache(); + } + + private DisplayCache rebuildDisplayCache() { + String s = getCurrentEntryText(); + if (s.isEmpty()) + return DisplayCache.EMPTY; + + int i = editContext.getCursorPos(); + int j = editContext.getSelectionPos(); + IntList intlist = new IntArrayList(); + List list = Lists.newArrayList(); + MutableInt mutableint = new MutableInt(); + MutableBoolean mutableboolean = new MutableBoolean(); + StringSplitter stringsplitter = font.getSplitter(); + stringsplitter.splitLines(s, 150, Style.EMPTY, true, (p_98132_, p_98133_, p_98134_) -> { + int k3 = mutableint.getAndIncrement(); + String s2 = s.substring(p_98133_, p_98134_); + mutableboolean.setValue(s2.endsWith("\n")); + String s3 = StringUtils.stripEnd(s2, " \n"); + int l3 = k3 * 9; + Pos2i pos1 = convertLocalToScreen(new Pos2i(0, l3)); + intlist.add(p_98133_); + list.add(new LineInfo(p_98132_, s3, pos1.x, pos1.y)); + }); + + int[] aint = intlist.toIntArray(); + boolean flag = i == s.length(); + Pos2i pos; + if (flag && mutableboolean.isTrue()) { + pos = new Pos2i(0, list.size() * 9); + } else { + int k = findLineFromPos(aint, i); + int l = font.width(s.substring(aint[k], i)); + pos = new Pos2i(l, k * 9); + } + + List list1 = Lists.newArrayList(); + if (i != j) { + int l2 = Math.min(i, j); + int i1 = Math.max(i, j); + int j1 = findLineFromPos(aint, l2); + int k1 = findLineFromPos(aint, i1); + if (j1 == k1) { + int l1 = j1 * 9; + int i2 = aint[j1]; + list1.add(createPartialLineSelection(s, stringsplitter, l2, i1, l1, i2)); + } else { + int i3 = j1 + 1 > aint.length ? s.length() : aint[j1 + 1]; + list1.add(createPartialLineSelection(s, stringsplitter, l2, i3, j1 * 9, aint[j1])); + + for (int j3 = j1 + 1; j3 < k1; ++j3) { + int j2 = j3 * 9; + String s1 = s.substring(aint[j3], aint[j3 + 1]); + int k2 = (int) stringsplitter.stringWidth(s1); + list1.add(createSelection(new Pos2i(0, j2), new Pos2i(k2, j2 + 9))); + } + + list1.add(createPartialLineSelection(s, stringsplitter, aint[k1], i1, k1 * 9, aint[k1])); + } + } + + return new DisplayCache(s, pos, flag, aint, list.toArray(new LineInfo[0]), list1.toArray(new Rect2i[0])); + } + + static int findLineFromPos(int[] pLineStarts, int pFind) { + int i = Arrays.binarySearch(pLineStarts, pFind); + return i < 0 ? -(i + 2) : i; + } + + private Rect2i createPartialLineSelection(String pInput, StringSplitter pSplitter, int p_98122_, int p_98123_, + int p_98124_, int p_98125_) { + String s = pInput.substring(p_98125_, p_98122_); + String s1 = pInput.substring(p_98125_, p_98123_); + Pos2i firstPos = new Pos2i((int) pSplitter.stringWidth(s), p_98124_); + Pos2i secondPos = new Pos2i((int) pSplitter.stringWidth(s1), p_98124_ + 9); + return createSelection(firstPos, secondPos); + } + + private Rect2i createSelection(Pos2i pCorner1, Pos2i pCorner2) { + Pos2i firstPos = convertLocalToScreen(pCorner1); + Pos2i secondPos = convertLocalToScreen(pCorner2); + int i = Math.min(firstPos.x, secondPos.x); + int j = Math.max(firstPos.x, secondPos.x); + int k = Math.min(firstPos.y, secondPos.y); + int l = Math.max(firstPos.y, secondPos.y); + return new Rect2i(i, k, j - i, l - k); + } + + @OnlyIn(Dist.CLIENT) + static class DisplayCache { + static final DisplayCache EMPTY = new DisplayCache("", new Pos2i(0, 0), true, new int[] { 0 }, + new LineInfo[] { new LineInfo(Style.EMPTY, "", 0, 0) }, new Rect2i[0]); + private final String fullText; + final Pos2i cursor; + final boolean cursorAtEnd; + private final int[] lineStarts; + final LineInfo[] lines; + final Rect2i[] selection; + + public DisplayCache(String pFullText, Pos2i pCursor, boolean pCursorAtEnd, int[] pLineStarts, LineInfo[] pLines, + Rect2i[] pSelection) { + fullText = pFullText; + cursor = pCursor; + cursorAtEnd = pCursorAtEnd; + lineStarts = pLineStarts; + lines = pLines; + selection = pSelection; + } + + public int getIndexAtPosition(Font pFont, Pos2i pCursorPosition) { + int i = pCursorPosition.y / 9; + if (i < 0) + return 0; + if (i >= lines.length) + return fullText.length(); + LineInfo line = lines[i]; + return lineStarts[i] + pFont.getSplitter() + .plainIndexAtWidth(line.contents, pCursorPosition.x, line.style); + } + + public int changeLine(int pXChange, int pYChange) { + int i = findLineFromPos(lineStarts, pXChange); + int j = i + pYChange; + int k; + if (0 <= j && j < lineStarts.length) { + int l = pXChange - lineStarts[i]; + int i1 = lines[j].contents.length(); + k = lineStarts[j] + Math.min(l, i1); + } else { + k = pXChange; + } + + return k; + } + + public int findLineStart(int pLine) { + int i = findLineFromPos(lineStarts, pLine); + return lineStarts[i]; + } + + public int findLineEnd(int pLine) { + int i = findLineFromPos(lineStarts, pLine); + return lineStarts[i] + lines[i].contents.length(); + } + } + + @OnlyIn(Dist.CLIENT) + static class LineInfo { + final Style style; + final String contents; + final Component asComponent; + final int x; + final int y; + + public LineInfo(Style pStyle, String pContents, int pX, int pY) { + style = pStyle; + contents = pContents; + x = pX; + y = pY; + asComponent = (new TextComponent(pContents)).setStyle(pStyle); + } + } + + @OnlyIn(Dist.CLIENT) + static class Pos2i { + public final int x; + public final int y; + + Pos2i(int pX, int pY) { + x = pX; + y = pY; + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java new file mode 100644 index 000000000..0e53a7075 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java @@ -0,0 +1,11 @@ +package com.simibubi.create.content.curiosities.clipboard; + +import net.minecraft.SharedConstants; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.BookEditScreen; + +public class ClipboardScreenHelper { + + + +} diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java index bcb832da5..52797d1da 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java @@ -76,6 +76,8 @@ public enum AllGuiTextures implements ScreenElement { LINKED_CONTROLLER("curiosities_2", 179, 109), BLUEPRINT("curiosities_2", 0, 109, 179, 109), + + CLIPBOARD("clipboard", 0, 0, 256, 256), PROJECTOR("projector", 235, 185), PROJECTOR_FILTER_STRENGTH("projector", 0, 14, 162, 22), diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java index b0bb6a60e..d52de789a 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllIcons.java @@ -127,6 +127,8 @@ public class AllIcons implements ScreenElement { I_PATTERN_CHANCE_75 = next(), I_FOLLOW_DIAGONAL = next(), I_FOLLOW_MATERIAL = next(), + + I_CLEAR_CHECKED = next(), I_SCHEMATIC = newRow(), I_SEQ_REPEAT = next(), diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java index 4ba7f21f4..6220cffa9 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java +++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java @@ -37,6 +37,7 @@ import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Config import com.simibubi.create.content.contraptions.relays.gauge.GaugeObservedPacket; import com.simibubi.create.content.curiosities.armor.NetheriteDivingHandler; import com.simibubi.create.content.curiosities.bell.SoulPulseEffectPacket; +import com.simibubi.create.content.curiosities.clipboard.ClipboardEditPacket; import com.simibubi.create.content.curiosities.symmetry.ConfigureSymmetryWandPacket; import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket; import com.simibubi.create.content.curiosities.toolbox.ToolboxDisposeAllPacket; @@ -157,6 +158,7 @@ public enum AllPackets { REQUEST_FLOOR_LIST(ElevatorFloorListPacket.RequestFloorList.class, ElevatorFloorListPacket.RequestFloorList::new, PLAY_TO_SERVER), ELEVATOR_SET_FLOOR(ElevatorTargetFloorPacket.class, ElevatorTargetFloorPacket::new, PLAY_TO_SERVER), + CLIPBOARD_EDIT(ClipboardEditPacket.class, ClipboardEditPacket::new, PLAY_TO_SERVER), // Server to Client SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT), diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index dd6433bd8..e3fba6afb 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -272,6 +272,8 @@ "create.gui.sequenced_gearshift.speed.back": "Input speed, Reversed", "create.gui.sequenced_gearshift.speed.back_fast": "Double speed, Reversed", + "create.gui.clipboard.erase_checked": "Erase checked items", + "create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s", "create.schematicAndQuill.firstPos": "First position set.", "create.schematicAndQuill.secondPos": "Second position set.", diff --git a/src/main/resources/assets/create/textures/gui/clipboard.pdn b/src/main/resources/assets/create/textures/gui/clipboard.pdn new file mode 100644 index 0000000000000000000000000000000000000000..a44cea80b775d3d56c35f787570aaff7901f2b9c GIT binary patch literal 24482 zcmeFZd6*l;)i*lBV2{lW0qiJgU8@yz}t9Xc8%(m40%Jo<-Xs&&vXBJujc9Q z>aMC&Rj1DRol~dEHd98)k}I2TNC%Qk!pj9a_(&ih?z~hilV92q4o0|e{>PWNw;aso zIxm%6+L7}Xg8^qGA9kQGkyNttQrWfQYjLSebgf)2l3qIJ27e)!PbKEukPjCUzK(Q~ z>%27LAWRlss8e$(fPO}+JxtpD*~KK(Oy93 ziXhHPh`0z)@f1!vjWn%xXy`;*uS&?wY8PQqi{m;~!lG8GRTS+EXjLM=f)t}N4jG`- z9yOgPnbc01QSC%^Br2=MrL}1qbsK37VbN&NGpVv@oMEj>oi?axIt5gWh)5v18dTO9 zU{q?0ns#a_N{q@lwKOGGQB>06VALKB-DuxsacCqCC!=vV7>5$IOORATMN*EOl63GG zDG~uvoHnRXeOj!gX%#CbV$w2*@jP${F*05Pq=2$!^J**=N*08MvMiys2%SYtu8846 zB2*MgO^Pt@uuBOH3PU+MMM@82WCmh-79=rU)sd3bR5g$BHOZ zB=BI#qR5#wuv!B7nMVoMAoQ-%1JN@Y7gc6HAERJmc@-e6cH`-U7FW}l({027g4>UL?SmU z8ui7pV1?spVF|>TG@#fZK=kAxL^p)V8f8)4#@K|?d{mQZyoV4ku!`v9I78-SnHH7- zhUy?5;i{N|3>8Z!`w~KR%&1RA)OI7k(U(A!$u@Y>XtRj&m53&Km!h-;L;dkPOR*3d zBkts54PFOBok#6uH<}U!+p9i$NapeHbW&zEibgTgENVZPuQQi>X z+zo~D1yov-9sB=({}l%SNtxsfVi^EhKOOe?5g(~s1Md_R@CS++H3B#y<*?cqttab;p;L1m1@@_tW2$m_6lK$0|9s6@q} zD3&Y6n1>Ig2&bYDO?r~Wkjs*grOUFiN>mI~I2U0evRKmMajO`M3Q1o!BM*T@(Wz1= zxQxnS!_v4(XVe1LnHS^XKqjT-91Mn;NrT<1Q(KkVqSBjka8!v6160-#1uEPi4(ezz zRpy06l5~WlGLfH3S_2#vU~Jy9lqrxD$E7F}je%gA3k^p83`7&}-OdAr0GihH|WHbl^!aSN7 zn;#j90s;e!&+tLi7Ue17xWE_Gl?5`HM(hhp3KE|&Q4aWvYE40}tN27NUqD=rTD>@i z39-Dyl@+D&q$D3y2%On?BvMR86%{lyVpTFzR0yIzHEE5w2;L_TYo)<}QfgLHo`OUl z31E4F*yAV`l?9Whe5S zk~Yw?T-FecE3+cb>m`F6X69*WNh$CeZ5mmb1!*U#a!cJ=cg}0XJfajK<`^|0#?(=P zC4rZ9&TQ7f&^g>|Vyw$RaXv{o=aUnD zbvWhC#H|^d1PPi)N0B3ZCNrBGD(TX3=BzECtdt5RgUu}o z1yuz$83|iBizO%|*@}#IfP%tj6oa%;f*BQV!pPZ-9Eb#XmCdfeLv}hK16CcEr*n}s zVdVrkMF?HGv^{2wL@Jn+R}{sHvdGSB*(e84BXWb7EqW==O%? zS7DZ$6GmmxK?B+G3Sj7QC1auKllu4BZDJJzO1r`I5 zRI(+P0E_1wQb{?&Gx`!w7lpiDMNrC_TlC2?Ua!~k6`B9Y`h zPKk?Q%_N7LGzGgi?#dRDGCBYX4mQAWDz~C&3!7b-GiJt|K~Wq;BS63f!@3}EVI4A# z7U?y9x!)|&*tMC6r04-Ejh^(fNm?b=l97nSf)h$BAu8)}jZ7g7C>3m|L^@d)9@g?e zAEOj;yGkkWCmG5L6bzG1X|)z$P7xs@VjxqTyPyjB0G-v7L7R)`)h0qJ2f>WPu1?z- zT_{JS+=N2S+1y0IVx&U}i^k3(9YQ15Q86;jIf0iZiU#BYinYERAniIcXJvI}!USls zfXxFB=eKeoJjgU)A&4vgE0+h-YW88)W zTi&%ep}#?4T-Q+LTIBVo#j1r_~M@ zZ?SVq6(`D=fKEub9N97;;$%oln}96k2i{6hYULaTlifrFb3&^@8!l_)pyC$_*f4H% z5`c6+WcfNYv5QaPtk%Bpu8aN#mTI>6yeCCNarV&`Y4Ek90x>k zDIOqL1L5c5?iiy=Xha~NGjTzYi1WnUbe51(NdhN5Nzw%j2O!`&$_iPN!W*akLxpNNvwDC zIu#XW1sZ~I0vuFy9-yHtG0Gc?Q7lLj0agWwyfNl5mx-7YkP%%uVk0cP))@n0;8&&n zZd#?#g>WWANP)~2!iotENN~asV^#A4A%Y~No5l(ddW{-^HNpi64;M)38-(MYqQI{8 z1C=9a*T`&mM(nY3MLxpi{Ged6Cfv9UxRZoi6EaxiajUvQd6B-jWyQRdpu!>$Vf^NB zE-iH^tp#90vxRiU>5Gtc4fR{C~j)aU3vS!M_@uZ2QBLq$q zNChZskkWIMuFOe{QDh0(kOHuJkvNT%BA2w%d|t(+9X`q(L*5=CCJRM9S+?L&w?6H3 z^J*`H(8P-X0@%ZO&aW-=IbldECB?voH^hYjnY3FEShTaC*DZMq@=I?@Q^0tR_QBH7Sl^C3xEGlmZM`OaxHL zwR$f|0X``stO+||uSmUUajfDvnPmp}1oN}pixfmL++C!QA`lLbc z1o9#k(P1j1O{t_q7FB?Cky6eiHOVzJ2l6bbG#K@`gO7>4ye5#6vbvm3Y6|lVS`-9W zXGO|kevQJe_2`q944^%fSjmUjELq}I6U!BdLJQ~{s3A_##>Dv_}ooIvHzIB?`65O$7;atfy6q*+>&536%>18EPW ztbU#h`2yNCXH&lGC~WaMAl-WBS2!MBXk~UW1g_om<^|h z9G6DQ8wfHLNmOkF@&aq8K~x)3o75HL#`slXDx^%&PI1^5)0S{SI#uMOfa8fck&?L5 zK?d1KF>B>%#QO?cO8J8lU^ELsnhcWBZPLd-_?lPCxmdKDF*cgRUS0>c90>Sgf3`AfW_WsjtA(9zmg!6LC0cB~1(p)Ffl7fWWPh5+LJG$O>_PR+1;Q23pJLy3 zq69`2X<%&9KrC$IwL(g&r=%u6=VMJ}m5;RwX(>gOj8f7<2BnnH7WW4n_N+$ib^&36 zCzTG1n^c9}PA8vErFh!uCh{PV@>TR@Ste?C16wMcpesa%Fh$A?OIW1hq%S6LS|t^P zA7rqU*IR`;9lun4)h)KTG!3_;5Ut{B1|Dz7tBrchi4m?B!!tAi@9L&*5t>aw3R z%M2E6B&jLTMUZ6qGU@aRq}E7D4RT2)S|)I}l$3&UoFya)xg{9%3-ski8yPlXcY$!w zuga20Bdnx|V38kbL#t*a^+dKDpd$uCC6l^%jn)AuMv>BM@rcP%WUQ3W76&R5@;@vZ z4}lCLTeb)!w1VeSjF3d0SCm0MX;>H*&|XT0u|+l|Bn=$H5pk{CY+xB-C}stDnUqVG z%yz<&BbjuL^yUgEfQ#G`GnX)zgsPBDSY(k-1l*2f)J>U!A}OcKP|;*Kz=Dv&L;FEi z?z16tsI74Eu%J{C7HKVSE9n`9JQ*Vr$-K)?OWBA(o|5r)roq1oACCxaoPo~SbsVFS zVvH0p$VHH_PP!<{`6D7JayB^NaFZDZ!Jiq&^+u3QxGD?GTvH} z8|@KBA4(EVmM@w7bT0T$OmhlDaRnE%2l1rE>(cuvHsUl8bOK3$g$|eqH-nsbohcE^ z@#Y+>aw29@`Ly1Eq8X(e;zKz?%cOxk=i;Mm5LcjROU@l25Olkx$WaR`P~0BbISyo4 zkJTs;vRaPO$3jwdEa#_aeawiXn3dFKF;_^!2M82+(noR(i7~vY7`6!sgHX${GCYuu zszKhPAsY54|@xDjm}n8lVvp5e6_A8B;kU5I8hM+OldgMt=O7Dr?1pvEB; z0;k=DIwjSZ?R1pUEApT$7V0fFkpelc;ao|{MRZ((rSsXa(5@_$d6EVc$AARFT)$Jw z6*Oj=0?rhN*ozy2`jR>F1eq!?X|AN)q@n1h9c4L4mI9>LhA@g|Q!F73D2vftjx+(0l1Nv0Mb_>I zPN7O;rsB9i88#RQm(7~PjUh6JYn_~4%G*`3fRd?X$Q(v%1uRNxJkpRXK!Y@l2Si~vi>YK`XELHO zBSDVD1=)xaPpMH%P^5_lwSL@W;`Mw`>PU-`5w%*3DX{`GP^?DH8`aSgAJZYH)r<_J z8|1>Eh&GUHUTpw`$xuum^tyNo-fqBu$_=JgW^R zuyT&q&`~i?$XPpH&YEqmG$jWp+99LMF*B=UlSs8ePLG$Qc+~9G$i#M#k{aop0AXa# zpiBcd=kX^CqDVZTvmrk$nT+zR-y$K5Y(|zT@g7bc57~t(i#o0k#d1Jq^%JB4E4g_G zOJhVyt+gnT(~Ha{@_*zaH(;`2*5mmeM$~SqZhvNs|VTNlO~ZGH!J{3LMJ`(DEe_RjOH|Gs$T{&OpnvtbtFMfR;5X z<#CY8vnUqCNXlU@RgjAaw+$1Dpo?jd4MAp66-FV8n6!|!NI{7;Csja)Pz;yHk>MphYPXEyJWxj-Rp z7lLR3*{q1v9ZM#QAk69nhMXG|apV9Q(e&u$daOwmKxj2#Sib;)Z+8j>ml3KmM0OC@4PM$RC^8lpf zDUmgesRKM(ha=5tEN^vkp4J92tRe3K4el|4G*`$`dWQ6J6i%>0A7E3+1*U>%?d!`J z6NFcnM7EB>^dYPy4f0kxfE+-zJ7x2mi}`#dl*scUV`MnD;{U|FWI&W+f~oHeOTVL6Ind<>^odyB{&mnF?)Do4;cQc})j zK*1{z`Z%>1c+s}8D-+Tq)i&{D%!h|Dmmx(7IV!`;-KsQ#zZ8cX?RKRbSxihXB#emL zG;$y{Y)}=J`|S*`bC&W3+!&4qv>BF?sVW}c8Y?lXFcAeNrH!+@7zTY;^HymB2X4yk zCQ&S)NGA-0Fqcde3qUFjqYaZ{Sm4W7tQx{<&dZa?A4utQfX59sB!Rpt1kxqUEw!jY z!I;p|6isR=J`y)6i?NtSl}GUyHHt;*fC9l788oUwC<3MzN~0kipwJG?f0nZYMyLUt zG3tuvfQI+U5>{0Z5AztIH-_fN%@| zp7XQTWH2eVQIT>aft+6zotN_|WPT%L6a<7?XnI7K?3LK~ntdBGvFqQ#vy?**l_KdV(S5mL^Df;OGh zYRyJ%d1O}82p?P}S27z9t2MOGZ^&vAB3p`1vk3e%K|gTeDmK9;B;~S(^W{C#U|Or> zQ|LrOIWG(V5ksgQWsmaBX^K$1LH zpjg^!K=Em1$jy>Md(5n{igmKG$-?qROc5{nVlrnwWk5yb|6C*|-gZ6Uez#&hv6|~0_(hG&itzsOg z25E_!bR61DNEq$T91&s^3f#u~#7QBpm!cSAjLpRIS*pxbDn!F}hzNBmo}}G2b1ZAr zvjoc!ep+c0VziykYS<8O=3KbhW-F`3ET*SPOWYJx1>8=9fg_V#!iqqLaT?z?z;UQ3V+i;xKT78@@%$Zb4E#(fQ zH5x%stee#!*a+oFsV8C6iv1Q1NeFZdspe%;8mAOo2J=UamZVfI;LYxmoU@putivSL zhSexkuC^mPWoI(RK*oiOg@zP?*K8;1aZ_#1jbD`A}HL zC$KE(lAFT@V2z>pI3po-9P(jv7|yuz936|MC|$^{5r#=SC|g`wb44vn@K(Pvf?T4M zmZ~VdA*W0qjN+IALRc3g~&)7)7GpsgYc4;C(1T`{{jo5^8 z0d8RQac97!m*TVo_gBb<0@G-}mLo8MEuB;$1IU_$1PajV3pfhbu6fRfQ}pd(0@O=u}v z6tF}sXn(6Lm18(&!*FS(vE^BmSEzs*195k@fR}7`NHMGO% zOlG_hm(=8mq!^jE=vRcINx8`-m&GKR43+fYD!W;3kvUx%LKp!`XH1neI&6`G-9pFF z>cZhBl(wuj&s!=5J4c4l?uQWrYPr6#_+`<6qX~n_fh8Nkmu2g*Wo~0DdLne0s>03>f&{7Wr#Pr1S}?S#T7hD zTEa?37bF!r6k@ahoggo+AXAd6AO)4#l?jPLV7Ca0aV*N)I9o1cQOMnPTqBISO%~kg zlB)F5tco;;1y&9PJCpEG2$SUGD?DAXCbK&i}UFgk<@xDtlrObBCr6@efrKwE5> zK`q8`IUOjHCTYYKb%t~bfllYwWvwAY0>@KXofbv=Y?R1tL&nMEbd(Dr6J`rZHM}`! z!ICALlnap}Q%p^~U@Su%waLQDODM$ll znV(0|`XV}MWK2|i3WdiOtoQ}!jD|p~;6?F#Q4y$UC6Z{R;xi^Q{tN*^F|?X;Nt9Z# zw1PW~5m~{772G0ICU4Ksk$gOtbqX>uB28$3%??~K5O(k`T#aizB?<1b3c_Y>TAPx{ zSfRBLRxykKge+(WRxPlQCaOqjDwj6gsslxDpo z97(IJ9&xmU8F*EWBW!dkW9KubEN-RjHj`c{)7kYl426(!je^tI1r>!ynbxI%p=dUu zEgh?ultf)P=JR1Z76^+18aAJE>Kf;Tkh5g5%jj%Oi!iRU^U@A%&JEZ<4+x^OCqEoK zoYT~d{+gN~`Zb=7-x;T%o5AdjB#{%JHwN?9Qr^7x>W+UNJN&m(hgWxyg?PS@4R$7j zg?!cp<^DtfAKXqwObRj?EZU7PoE!g<2^(@#U6s2dueIwApddRi#Ev?Gqd=u#0nV4W|g zgXnnmr5z^;bYALLtJ1zCTks`NV4boT45YLSzz>+&JdF6-;O+D?{;d^Jqm?t!C+uIX zh?R)=$yN~U9~wc&wts8{9pXF*!p+D3NwDqT2#Om7&qkjVKOu-CLMI90|0IaU@gu>M z44q^&amY0ONO|ZT`<@8{&7~Pit6z=thGX%`;~F@~2rp zG=B2_?*vW);RUG1Oaxesia%U4FKj#;96rhFi%??b#pw300+%$>4ep;TFdHRiwxip> z3Ur|KvXQOI#pw)*&B4A3NdihVsU`I9=h20w&Ie{^iO?Z)ud-An0h?4_Bz}qyf zF_8ZiUmBA0Z}xn84w=(Ha9YmqO$t%wsR+aWZORS7L@HY*;wgWu!PLg}PX;yQaOS1x zeOiN4|23~0B6Dien+X2Tu0w0Xil7mn5y=r=e~inf3dulgD(&@0^5vN)Sz$;8g3S#p zb?Sc?uc5j5lnZ}4`6os9&d(h$;@A&m*yroZ70H?|C`DuCF~@2 zb$2(-Xpn0Bnj12HQV}k1yfuXVq(=Ox5GUvRibhUD@EdbJ{YrE@?O!wYL-WnB`E%5K zqYEgzskxyTC#eb=Zw(FkpQv8l$mu}0lT@!ki62KK{^y9AKmG~HX$>L&KU54<@}y!+ zYe?$EfvQWqM} z|0b?Yb3-y3+;46OLW8A0m?QLPZf?x)$-KsN{(Ii(|5*&V2hR4pW}p)2ciQ2H?|SKH z@BV!0tgBz3h3~X#?GY6?N_$_+H<&<-^bqME{F>$NGnEZa267>M#B4a*s4p=O-4Q z`&DYu*b09CyjksqOzptX8=Bt!cR$|TTR3*;_gzb09PO+gT)Fne!NUW~kJKdZY#!}9 z@XeoE;Uj(DKU=-wo)OsyJWl~XxxGg6L5e{60VL>O(yvJ)(Hm(C|p1ciW+zXJ6#lb-{*BBJJ>RMt6MQ8Cm<%1<$T{^14Lt z6{R!2eRFkb|A8U$hU$sbKT6||R}1&3dOv-!Z>ezQP|rU)tDh`f*00DM_`3QAK6Mb@ zQQaWwsI)IT=OgZ&(Y2pY3lBg0{^W)|``4boVrTzdnFHX&=&#-`e%ZZn|8I9zG1b+n z34HX8(#4Y_U%flBBlj^WSoZESq3gdHS@*_Whaa5Uy?lOre(8e0sUYUR^ zpXDCA_RIsDk1DR5+O>9I*FB0qZ7S;TJm=uNrxzqH(O))u!JiLIJaGEq9QLJW(X!4_ z--31L9=!i>*Upu%o;i>h`R%*0-i4REUGFzunJ zJG+38j-C9~gcAsh-RJJo?t@%z{+m zny>f;tNJX1D<*ocKkFU;Ez1YCesDaV?&=I|mamv39frFYJAL$**U% zy$x6GX})LMvhP-P-jF}?LJaB-R~PQu@nZCPU$uR~nQzV6zV*7Z50($q6))VdZhCr0 z+lzbQHy0c&f7|+EjM}Mq?6B+pOLniHxoz(P!-C}fBfosYSQ@RC&Ug_&eq<_t@z2&P zmc(BEtcQlZi>enva7XEqNB38cT-XG`s;Tztwq8HVAE<8W*#f~mx5Cr5!Pmw7)S~a3 z7oRfLcc`leK1zJL#ofEwGTD7#^56^bSi9|lV_g@we72>LJUb3!^-t?dem2&91hyR2 z&+CssC2R#p`{vYJcmLwqJ%*kYQ=6)1p9&)o%wl0|54;=Ra>n7p3%@U4^?SIdYi>(D zS8sb`ch^BUcR9Yr?rwRmSU1(%@AbYt)>Uu8>F;Y*DQrDhoqNm7`htt$OJa92S8I)^ zw!ry=u&au#8Cf>c{wq!H#y?HK3)OEf2oF+WORR zUw$fe&uhbdYezBE+MidAbPJ*4U=`f)hmnCC>_Tlkf>O`I$4|g-Vav43pM%3VY=zAi z9_<~6kKXldtvUglo_ZdhC`~PcO>fkfPt-4fEmx^(L-#aC>z~55)9$_-zCU5ASIu)@ zp29wwdbfJ7?^ZZ_CF0bh&G7Yl>-@`agbNbzFR<hi*H_exSFV?x@!Zsjr_&md}wQR<(}&CQMh}e>GD_Mef4KQ zgw`uB(|h1u@Yec8zu9+NkeV8-ceT7W`+;XBdp9?K`wCok44&~&t7aM8(>FHJ^~&*6 z>ZyrZZ{cr_o4)-BK89xI{;k8^k$V5em2lqZ=%*vGwrC)un*a1KK4q`onm+x(Bs@@B^mo~yX27WvE;=u6lYuyj->pFb>b8FW90j4Ci zR@W6LYc?G`6)`OT@%IDY?ihtyFH#K_j7RsD|+li`u5N7Iar_C6I zT)q9hCGa7*8dZe;G6p3 z2OmvT?|m#=PbIIM=&yC%``9ONf8$_AQG_ow@qQg<@8jW`0lZR-{1bsI1+#J z&A);x{szD5T>l928iz+49o-Lax%RSyuyYw) zJ^>$qr;BGyn(E#6F0FN;LZ{aya8~}8a8@04^I+8lPd~o9>u}e%x5BZ8=1n2lK04NQ z0`>InkKU`6r9cJON+wKe87# zp_-<8*Vbw9eb|bIJ=kE(;5NAN;*O?AewgYZIRC?fzK)r_@4zE-I>y>|x7Aimwa)*6 z8C)IvR(u~+ADif{b#*LBPj{+|0Nb8;`g-um?jd*rzz3-+zRCfARw@vkbHQEZn@6LfAol|Sw`XPLF zZ(rwG`+f~u?;P*G5N@b8ZMt(c{4RqqZg051pxXnr^*_S4y-&XkPph}D{r;-)Dp%Le08f=4Lwio911&sRq9^_x2< zy5AUWN5Fqxt*vMASX)N{w%6JQJDV20RYll~pk6(RdZf6rV+T}RS4T42bg=qQoqFd4 z-2Xx|68&G?HUzy>e|^0?cQ0)D#cxL8?-rFtdJ&7B`lhNFSqhuxyaltptta66>od>K zdkmUZA;`M-7F48)=J7kEJJKn|8?l8OA^7v}Ak+T9^`pAjB@e)s-<_yxW8OFXx2*Ix z?SW0_?T6QQ!uHXwT`d=^mt+pZCr!t?E-JQ?y71dVP_|ES*6+_7+|tHw>WR4e;=6@xN>~pHSay85%}F`*IU)CPwnn|{p&B_hQXnq4|aZxgp`J>mt1z! zR9E)vJqLQ0Jpfyey#FN>4-Z^?>;U}y-NYr()P2-?zUx6`Fu$n{?7DFARo+GJhsNE? zZyx%luyJ(dhg|B(V|OijGji;$OLs;0e6)FLY}b?Yn;+LFE7vYscc&!r&ZZ5nLicUi zyW`ypzxqw@+JSxZo$pleS9Uoz*56zP4cATAZ?7)x|7xaa!K9;ei}B7?{o4%>tyT6; z1UqP(l?CUBpyh6I@W>sd|M>l?p^H@ud+FM9PY#9qu21Sb zSFCyCFJFx8>%;o`M&~bhv(du$<6jTG_Cd;h_Y=lHA4qssU-9bHBQ?e4m$Zyt|H*Zt zg%|hE8-7Xt*p01gFS(x|?%DnFVeF>Xy<4W;k$Ct1_ts+9vBzbz)+qn{#L&u~{r3-D zo#=jH{^nOtEZKJE=-|4ZcYU&EwkOzb{Jo{~FP_vR zdyl#3?GC;!`K*u05-%>A8nt%e>4PXz7KbG5=hf zqIL9~pRTd`@0q_TvvGL(!sa=D>0Z3%xi|M;zB%Aue|mFs^A&e>oPBFY^R4T}XSmMl z(9FHN zD;9MRf7dtk=90M`XU{nGlw1CfAM70YgJ<3*pL>N8`{1!JEOfj{1T{U;S-hd{b)nU5{P-2Y6%O?OUhIZs-^sxU4>S z!`u(vzO%Oc$ieD2-+jt-FRH$(EY&A0ZI`{F=-hDsksHpu3%))%@XJg37C!Dc)c5s) z`uA(BznL1Zz;(ZT<>~W2tiIQ>9xgQZ-etQFc6BLte)-4)D|(-p?7t~xyeV;Qp>{lB zTXsry|Nhknn}1m?jeYZxc9i*9$MtoE}H$()Jw|t z8Nq`9P&o6eZc>JnO z^E12txbXb*C8Em=yMCbe>sk!o_I@@lu8m9^_^_h4z4YWO-5JNwPjHRrCH_s#sK+U6w-@7p1Gca^OhreA&i z`trD<%h>m!3DdXf5_`_3kO<7aaTJ#Q0VFf32NZ|Lkh#aqXwGI*tu68DY=x z^Vx$>_eY=UU%heY;BPjyY6gt2+`f0vufK7!dg84WE_lW6-pQs9tN+pSoqOwRyFR{4 zeCUr)EfZ~q_kDTWfz`L%25)R@g2T6NV@~9LHFW>c{^2K2o%|3!)VlcVbvXw*9@Wz7HIH+*hlP)(p1Y1CPAk z6C4W`r||J#HM=eSnA=GTdy1I_x6sj=zns!>1cOv^~KfIgn7x-hFP1(H%;^{I?z|R zhZx?m`-#g&=GQaOblsgj7q&e2e%JP$LvsA859f@fW*ynyyV3vFx=w!b+h*Mt>Zzfn z)q@Y+w&T@X_C3%(hFRg0bDBoJ#-8l#;Mes(KlwDQ)W15`brn=wdzQ^C@Q37AuKFB) ze82kYGvVtTwBB4d_4iFa+MIom%{BQPJttb4&RlX zfV-<-jKkdnbB5H**Nk>?UoN|C0vj9{fOk!#`qw{w+QV@9hOT|m30NMiYIjWRCccF` zB`+13Ez`Gr-M*(pO4ej-=jtc>V6a!{dF+`IlSAmbPxs zy`mZ!9P9gV^{fiwS89H}XJmfci(5ob_sd`V`;jZ&x_)fvkFZrz^TTDGT<33IK4SWy z^FVd-1hm#BXQ#_U=Z!S|^`%!YuRnPxHVNWv3k;6@1jUU}V@72V z<9nd!mg<@laPWr3Uu-+NOtR$gFAm)@b>1&0*R?)z{F9Z?1^W)w29)C`UVa6M`@E;G z9^Em~f8YLJkHF6d2j;il_(vogeTSBQ-t_Lz<_(=*-+5wasAWdEY5Qs}b><=64IMA{ zx7JoGVb{h*iG~pV$A zTRs0MHrV~?=(1zWMi(upfBxR02;y4zY$!ed-pySbYlp{QazIR&I4{Wck*fOqyeOJTYE~Vh__5Q)?`(uj>bI8qO+jc?stv&paW!Vc)z#xjN z%-($jKDeiSw(ySSS9{@WXHUA|awn7xH9gmQO52qpb@<_J=8NjORYUhoJzih2WRkh6 zJ~jB*zV_KK4%}qR@@Lm)I5td1XqzaFhVIo9=?_xcJqk;moXcNcxwhV$bm z9<3id(6#!pC)N&b=Q`hfO)#UjY|mxqAA0G=`ag!@j$vLk*lYW1)%i3WSva!0`To)syg-CbXK zaeazk@L6@iu8LJR(o@gWP4nv?!_|Y&a+j!gO`CVjGz>R>H1BDoM-Scp@fWFm&DTQD z$W^L~VcS1os;cjYtG}J}*Q>W5GWB2cr}JdnS3ER)5H4u{N;TELw`*VXh4swr_wYmg z$NJ0clAfd8$y)Eu+?E#4s%4ATU-tn2kNc_$Cks!E@!O^*YnyI(G}MbYbkoSZ<3od2 zjcr>xZ_-~|dc%5dt$gx=)4%OqupHhuIK^ylJ7(e+M_m_WYNikRhMHw#)8WCJ;XCYm zy}AC{eU-Uacg?T=w#U=*sJFScq#9dSvAwr^ep|=kTF)=h%yoR-+y6>4{QE?I&tlik ziQzs+d;1jP0Q1aV6yhl&XmYw(bmH+5l9=;0Z zW(zmX8N<5jU7M=9wioyNq}L=)Jc+tL_k*slxZCT?3+Jo`D|Cq z6E*9WTVWQBrb7l>>tCOvti?7p6Ni>SaVTAHIfys*AU-SuBG`n=V3ew8ej(y0>+#{N4Vm_rn9n`rjBmRG3xU z&~j+3>-l$j?(F$sbz%#B{G8hIl`yy9IbUscs5gHb-+bPPZ1(P5XU<-8+1Q%F&-!kd zb>c|u+AEM~+&ZF}JW9a#{cw7ADsYN^2jeGVkm>V5x`|XL}%a%V_ueFcB z%?JAKsE#y^)=gKz?W6wwo7)cc&iG=qv*S<8=fLf7#SmPv2VHkn559T(2icX+ z!>c1V`D!cYKXX_U|6&B&gS{J1$&PP&ps%y~`GH%iyEdFw-+64=_@V_PpChxm`gC~6bPas+ zi%nw#i;nf?_V<5stn0Jt(bPv9!l%H4RoHzUbVh%~vDiqBiFF--*Nt_5)4V$suUiLu z9);bo*7p$d-Jot`>vJc5;n@f?mqXolez5-S4L5%X-ya|ChuYzmFQ=+GG(Q`U-uLT6 z?X!IC2jPyRrb@@r#pmX3`@DSSD_{5hZ1TwVt^?Kjcvo(yej}8j(jz-{(Z6HEExpxB z>( z`&xKlqI!7Z-r=rdz5A;A@?B>J9_}}n+wOhnyWY)x^Cmz3zp6FYsHU!Mj|ZcsY9*ff zMqc%(NR`s6vE`MG$9fP^!_n3UD$!D^7?mSbF~qRqqlcj)w2Gp(64X+#Dvv56AOrxPMP>yK7GYwV8oE>&htdaqx?*?$!;F18~~%Jt5NC0pk)SATE0BOW(Q7%5^1`ldHd zHEj~hY7}Rs$NyFh5nAx-R#d(w9>DsC%uw?hAFNNs>?hsOc_eF7)P`y2(bE>USFIZ$ zS{Wv|Zv&cuaXRSp(S3u@K{aKky{=x!r(1_BpINZbnOSKJ_G~kcYHi8FP?QZ5n z$rX$<{5q9qVk~wvH%o1Qv)BaRr9)a6G#!`f*_%1WoUlQ%er~^fJ%kJh|4?$d8@%ma zRq>8=V4+$Tl9L{o!cHizcBAOQd!KivWui%aK z^ZZ&ZHC#!^{|Qn#mDo11Xo%E8uqC?Bp?+o3<*y<9aweq|mpj|r6t&j3Ivg+KO}!Eo z^^12mp>bHlMC}?!!vZaOI-q{czAlzEN@S_15tmY*CAuA#f^>IigtL1kR%;0paPG1mhGl?hlu#VBqW!}(FLfs$zv#?c(k&2fL!=aLVKK9 zmT2W)By|D)tN&X`t@=-#Dm1*lb1<)viB3lA6DOZbI>+AY&_Ccy)m%orf^N#_ z7w8C6^a~*;%yca1dCv&$R!z$9zJ|x$Tki8MWEz_H);8ESQ?~Us=;bFD6 zoVgo5jv}YR8WYzWI~Egbm6!7XB`~qZF#`t09!CC^MfGpt5md;{Xy5u3L+}q2e_TY_ zMbi3m1#hAKw?@isokp(8KLmejp}6-L5`~*q`RGHe#FH+fif_p+VUXG){4+iBRUGYO znoYSaZj(Pa6$&-EpdbS{BJ8o}o;TL}8|Fc*KCMF?z@9)OF`afDJo*jH7wIc`M3t^ zZ?4D_6nsd7uMWuY>lnYAp5z?DRdh#>9Yz{f%7#;tb$|he}xcg~@fC|yr zV*K4JapwEzOA6#e+8*WGh8gGUEk6wP<;LU>Vr`3Pw>S zuI1EA35zC+5)`)>RTRK$PRGMOQWDrp<)(6+b1Sj3>CdBJx3-7bAdsRs z;PxU{xWub}LP*MQyScYvU=!9XA9-t*`i6KU6#KBgms_QH$}0EGCHx;~)u2*)n>2}+{v&usa8=_W_O$rfs!HdWo%@D-WcMK#{%(;NG#JN zPZ$GtO=*^_=K4GqlQuvukasA43N#HHz;ik5jS=DoAA78V4&UZnlX_m zw!!YMWC1V&a1GSJy|Frl@(?e@{s1`|FZek~nN6)w5KSc-%pon{SB*##KAv)2+{|If zmE?(8p4v57umc!Dc1Way7>sQKMI#CRRhP~ZZAI!9F z=ia&5VqQo(WY&+U3+H@*W31k$Dd>b*kE4d*|jea4;m-vv=lknWiV)9TruLTZG)|fr%3`S2D3PNWFIhZkp^b+rQBqG&mKMcjh-}Y82WuM%WWNRks@FfwS;f1wxa>XTLi$2TC zKI>a~tHa!^f5AQ)br5x&XC|xGKVyzDpdWMf-;VS&ylBWonW`p#xkbT-cw1O zFy+RmaW}m2nbVlTn$Lg$ve}H{r)H>iy_EC0#ge05=jXnI=JS2Yf=R%)=Ww1m5giFl zWZX`oZjtTSFtLCsVoF>CX|s{`FOhsPTL-A#N?um?q|bY}eOzIe`tY&$)L4IWXFfTI zI)?4Rn{iqA?nq8;dTf?;UR^Dh+`D@&mU%RI-u%sl=ttxkO}=`2@S`n~2k(i!VdG|} zs0On&u0dl&*M;ae@s{?{gN&}75Mo+_y%%}UtbNBe2eFx>&T`np^C2i_R@W#b- zu~ATTV>;V;HwE5IFIl*|l4O0%-7xb+v&^Ize-37R5#5(Z)+cD>F`FM(JfuMPD!q(j z={+y%tq4P-AD)%wKx$2!aLT8E?n(~!$YXmySN2#pq0!k1GoIab%NKlWXek!>=m&GJ zCSIvSWA`X{C(`X@2d8H5nz3WUw4c$)5qn+VjYG0S@->l3M;UN*W>iAETs}kQMADlLDXX%g5hY z$|=rjTYPxB-^%ajOpI&i%$z+s>?#xdN!>U59Pb{Q@Y$?p)y3t7*YFMMx}u_hr%oA; z#40(T7>#dqpcEI@Z~@$ESn1@)YTTuElodCj0N2YAoItuy9nc(9?I;_YbPMi;2W|g> z!&+t{>X~Pb&xq7Iv}6AsyVNfbLiaSZaclhH zl1a*8MYh^Kf&tUX$XN1{d|R!#zwB#!i%Y+VJ74U+#NJ*uEzPiVr=(?cqQ*%X`6f=- z=`-}y%Ky)P7H>FO)5oH(z+&@%nBe;Zgrv#lF@xS7y$RkBC?dpF3tuu?G8De9vLe%M z7kG}C2WS0jA$T8!8v5eNi`&i)I2|=VR73D{Fz#=Xkn?XQsod`d**um(ER&f2$msMX hcpPh1-I}KQ?<=VP$Gyk@XJ3aN^-dm@djD-Q{ukbM=<5Ig literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/gui/clipboard.png b/src/main/resources/assets/create/textures/gui/clipboard.png new file mode 100644 index 0000000000000000000000000000000000000000..53e099083d9831198dd4b52f81949ea944a2a28c GIT binary patch literal 7684 zcmdsc`9D;D^#8qgW|$eo*mq+I*_BYpwS;6DOHsB7Sz2r@l_dodl_PvjDAFtQzanI|V^V-gNzRpc1*;(@7#qj_D@Eox+ zcLV^CT?GLwid~#7a&`W%b-~fn6e#{K`Ip^5`sf-0oQ3uf+x)FXTg8YPAte)?95S-P)DgSC+Q%2iBLz$5C_RnN9j-p zsemJbXKX}GlmW;69D&w?VNO#1R)W431aC9mfFnZQ=DerOc3nJ1^s^KQv=+K}Oy<&Y z;-zDh7)E{EM-$VLnFFvh#XT(s`uuKK6U z`OdG^nfEWA+}W77*THPA(B?-Qj_GqU=ewBm-5ZO8thFiD+VtkiAJ*FV2EBJy7V|HtOYJ=XtxjeG9I=;RBx1r(7=F;HC;=mT;-`46_H>*nv-#I1W@%JgZOC*Fpd$@zt^eI(%09U&hk- z#xlFh{O0QPcu&(CKsi=N-?YKVYi`1a;o^sND%q*?pm>$P|Y2bh? zV5|ffD*}huv>PyDlOkZa8!(Uu3={wZIp83hWPzhvkfjD-sSa4MNewVp1+3J8BO0KM z)_*(Kz~(XsyBNIaXy>%^|Lp@1zfU9VVd6rpPFw(hT}}VJ;OF21UjPsnI%00(9N{_l zgv&?r*OeQ`DuY~4NwnP{p7Q=veo-iq<1w&1Wsp=kkkR8-9hxl1{o)nrm+FpyGk%xOD!mvT33a`y6)$Qeqe zko-;uwnbdo_oL#@$)~QU7A(A-D|8c#+UK7lKwfHs{c)~7Q!c0{N@SjHfwLOeDk3c& z`7S`0&R6>VBNhF7Htr5_h<+i9tBOMsYLGy5?*T7A+ZoH_9@JUo0kH>mh_`oonQg>G-1 zY`TWkd2|Fq9A4Gb)bHR+uKakxlO44@`CC=9dOSKSGP3eqf7zukpQOjyzkPH$KU{W8 zv36(6xbo7!o?NBhy*11;6Dg|GX|*$dx31^B-rUgrgj0UV$+3};BOU&mS5!A>sOrh~ zNX2Qlg|#_}%HWZ@(?>Pm=EMlHC2KHgoNr}g7R_V^iiFlzSJ%VCE44b!fr*9s$$@{x zHS+^!H{aJwVbqnv$_Tx zoJVcf6Oy6nDP#R4R!*M@E;*}a3`3G2ub81H+rQrSI@`nX7&mQf4C;R#dAwhn*#_)! zm319W=_}2P^*;#?KpdRGpVG$tmgOE4zqjJ=5Vyu^JqMTPnt7Ga_Fg`fp)M+!&#FJ9 z-*-{7)168Sjw%YKxQmO67djWJ2gp+8&(@S!H_=hcsuRlO!I}Q930^p z)@H8fF5m@W4oPrTe5dM0>^n7{D(0P%=SY`h+xsl@J5XU0qwwb`GEx!84f#rt!O%Cc zdW&wkeee7`g1IV(!j9%ZhxSnJTg;pxSA7KoxLQki({0JVXs(JrqqSG1rHgLCA1}QP z8~n|}L@ixLxg=<&=z>D&flB)Nt}norLmq`;pBJJweuNJ#)(8mrg#Q*ES4t5i9~LRc zaWe@djk$N1p?l%1-+kyl0bM{Y9CUK3mmyhR0p%#=N9FPbDVx-WuCP~*X5OjBjHa5k z1$AfYeR+i2#y(HibnRLkGJjK@T;M?MlW~aP_?ukZG0nWSw*06V#kot`P!vlP+P81t z4|Jh?A2p;x7yy4l&!FvywRz*w+!%AJ|0=i3Ci9%jt)*0m>oYKip1EWU1t__``RNM2 zrG2-LnzuI&e|SWO2o=T*2(!)CrtyP0WAoVygq88PX=5BtRV zL8N21iJ@qv9rtLjwpp}{)gIci9KvZPbO1|kOF9qRR>wCJNHPVzYZ>h-wBVYh zY=qWvxFQcg)Ay7fe9%rbj0?k4x?r99VW{jD$6w`iBG4iTc_&h;_z1M;N(GQ9Cdn&c zn*%j3n?4pPNK)bHveOczPvP-TVnIV&jz*)U2YmChSv$(6@__Z>_WGMSC*rz8p zxh|a%qWU+hp%lmkXLel^!_bPf9vJHB8Q0ICCHE>NIy}Quo{$y60fAlU%-gh|EY`Kx zHq-|CxPD6ZWla7MZsuzoFvo3Q3g{vkZ=oWVH=??_hx$=NOCD+kdOQs;3UXswcYB*+ zGNa*&CQ76S+0O!9B#p|Jf&4-q0u9i7o4zo16C{Ayx!e>ael&=0YKqR??hN*CfP8$_ z8e03_d0_;NL`nHFqMy?6eY>Ma&q+O{e?VZUkNVW?I&UIc2w`{d1)D73ff%?G3h{tR zV0pigG11o*c&4g+4$?YDD(Hx+Yif^))Hr9XpMkEN7SH4f~_T+|ZH7UqmZ4FUA&fcj!-_n-p{p92Sk9yJCHVowhyyR2G#m`kw*&SX zP|Bi`#)D;vgLWj0zJZDPWs^WAUz<#&KrNAS3(CClhiLsJ(JF>OVQz7C=D|0SB=G(N zUTsJx#vFO+0t>jcNt%XVuc}0I~G%A=O{i$p>roQ&>(;>s>PDE+lsiqQSKfl2*Wb;Glm_mgVQQ#i?)PG-p3e|lkP_ybtwMa8d4cP#zY&DiM zWL=}H=}p>8ozq51_Ej9^cKk#=E31TZ$k$Jrr&t^;!^e;wddeI95$!x!jX^_uy+lXf zfgjw3`9{{_ga_D>Pv+2zRQgS<76&O{_^Kx{3+P9e2sne^xC27xs5)CfzLGP*9q=T| zv^f`{DFdJ_H>IkA&qIHV6Z(ntv_XY2=*~m1bY^Bc<~8DrDo~e6A2|;>PNLD%4#ed{ zFeVAaTInF&OW+<%XbJ7rM*PzVn>iJg)%9 zPG_n%^n3z6Ch!z^Q?60@Wiuj7hmU$bwKqiH6L{I-ft-cWndhmAz@sO+pfJ%AJkfAU z8|ef7;shT^B-x9C-pCrL2J6OkorA`eNpE`m@8qIOKm`}R91{;_93z1MbNKx`rXp?` zw*o~3&7%GPy?f_sh$G&Xt-1HVENFaoR@U>dk8|jIXodI>1C33*e-2|C1xEX2?V*dj z@lCW@wezSlp>qCCI~1=#1dcCP zpHrrPkMV@%7(zt<>wzBF^KoTiD5f^QcXnh#Ev~bP_~7h3e<}ef3zKh^5v6GWY$y6V z=+2d?Lu?V<`1D7Isn@44Md~ny_#T7*g42u51_n54pYY3rA~_<|uw(y^&Z07hm@DuT z;|9rPL1(^Pw~FCM7>dSHm}nzSI19LR-TxNyJ<^k?Liv*;LIPJ$5U8qu%cn5$7vf!G zJ{xKBWeYTy8O&S(A4j@`*zu*#7ffNW?D2Nsyu0+d`CITv9D{WmkjnsuF$wi}uCM&0BSBF~`>*tLhf5*VwfX977=6&U(0&e~ZL zT$*UY7b{AOK@v*=wzov^>8Ismx*#&KT80*=7NX6SJ|gQqFJR7U#FT)AaB4~;#JY*8 z6I?-m!^Q22x}5n5>WPj|@?t3xcT)?ON{ELL0HWeb-|gx6xJWxTUd6A4{3b z+LW_*_j+SOoK}^o)wZ`EcJzzAk%!KllXrDscQ=3{ln`jlBx5cw-9ORg7I5P5?~YxZ zMkeyax;d13xw_!$Yzg;`uhH>xlt-X?v6SH2YzY=dA&4Re$=xkULvj%Xo*@eI8ETI$ z9x}mN=^_J5Aahue$TTuG7*Zy7vC$xBvyvPXWo%#r%|0QQ#ns9@Hj8Q`P}08NfcCDT zT{Mtf5!1i}aSU^1SXye0F ze8ao<6{s5X<)*LP{Oq5|ya?}MeQd0~h_ymrdVigjDTv&?B!0_R9fB1Of)@zw&^e z*dX9D4lgsFhHveITH#s%ZTrjvVk@qE?8QT@SEUp|7Vof>+oWn8F#?i(BxG9`3tygW z#$63wdIvVA1%rPvhVAcuc&2ZF6~MSnz7drEOpyNnXWb6uV)#YHb8?0Zx(&Wj1zCL!azXsd`%xokIh!{ZdBeJ>f? zS+=Ms^Z`v|qQJ+-7Vxi^NQj!2-D~)(qCP~@`~4hBB}syI;%*a@4OfGQTA;_`a6i<(rj96DPjNC2Q+?8mUc5wI zzxrl=;$~;Tppf5u&?z|exE_ z@!&+H)s^hN9pne6ppM5Fv%#bSQHvho6Qc)!Y0rT>M%1G_kU;#xGFST!!nz-iUY z2Y5Bp^+*S zltY(=-`*@mG?x^Kdbrz~MJnKXC&^r%p*E{3Vx}?iVexoj#43voF zQ3pO1!vM$+ZjE(sM|A@;O)oI{bz0AOc^vFr(bM&s zuq^;J1N@dIqo@A1V5#iI>EOAccjOho6wwW_wy_fC^8pr|;tsdU0!#^XRTfp0=LQ7! zFYx<--EEU~H2pDwBM>7dXsBCKFs0{jyPT?+7IDjZfF)*=*BD%a}QK>(YMwEfJxc~dr8Mws>p?o~u02vPEBU#TSNiorS zRZqatP5;-wtczrVI$Et7Qt`<_w>nT+67sc-z@q>+`|9}eYV^HbYR?`4VuDAnHl_i4 ziFx2KZmjEsXDvGeKn#+%TisBUa!nA0x08Ujdf(zyQjD+`2N)O|3v?XS!MeKVUowHm z>~lj4d-eWO<&w~-$FXhhV*H?Gj^sYQBhRCvej9PmiJ2P>~x&*7( zgM50Zs^BQcRUmU^Z`Bc%KX#olmorIVluJ(aDbY}gkoM16&eQz&MNyA)LNF{LTzXbi znKZ|of8r}8wui)>FU^ae3AGRW~T=qs}vtR%faLPV~D9P;ctv ze3I%VU&pfNSj)BNqG2QM;PWd=mgEf^*v{%Fq2NKNs0~w9ryPV4cY&WPti$M{i!xM( z2z}*8>-lt6DP-Z4Bh(|T)e7a{?*4g=b?tYcN?<#(qYlc}6NoN}+M+g*f>cFzCZOi1 zSLcWtu?HBdbfLB-uofZ5Am}MF>aUq1zh8_YG0W>CO);P6bDqZ~_*)^Tfqo3Op!bZ|w+{l`SK}w1zTHwc75Jm{ZIUJm z`-=P@E=e#+BP52uBx|G5D{oW_oj=FfIwVJ_6P(>?6sZ6eZX@l{aVrVCfi|HwW9{lJ zboSbANr>1#Me+n?ox-JqIquv2`f&{ij22-w*8h1|v#raV?|!sOIqHt5^DSkr^>UiTP(! zbCo%jqrKqd}~_0{KE<`%-+ zo>zl9)Vfw&jy@f5r4zt=-Y;9iX8W|8cXhH9Xom-Y?`oRjM<&-i( z;3u8N90ykqc2}Y=R9it&z_cEnW4avMwLf$B1jQ&9tH-r&e7)K|PuFMWS(`p(3}0vfXTFOyZHLM}0f z>MGAsQy%Y&f6H|0uUwcQY%pWf&@{WxT?;P+>Cy7!@u(9q7|oOxEF;Dm)aPEpgf*$Y zWxaL``t#IaCYuYjxJ1^(CKUcLrWl!VlBg=}* z?W)QdR0plqQ(9WcL`M76_bok!4C=QfbJ0FYhHFRST9o|!1=S}Hjh6(r=~OBZi#rgm z!H9dxnt##z`5wmA=;@Ni=uq_qUf}z7?AQKx!FBz%vqQ`tH%1P<$1QknsBI)-`P)1H zSM6~iQ$IFiedS42M~iK0*2C?|{Rdw=7g)V{(B8B03d|=^ZDEmg?6HPe4*G-8k(TZa zfwug|dJ{bS9|%_RG#Lw|R$|;#-=IlGdpgpFyRJ=>!I`Luj!oWlAJS1g__mI>-d;1p zJKy=yfMM)D@Eju8{Oq|_!3@i4zkq)Il|80EShg5?3{A@Z-CS;<3c_8xY;s@WF;_(I e^4j1I?{K1vrRkusD*LYyaOAL^d9f)u{(k@=i6c+| literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/gui/icons.png b/src/main/resources/assets/create/textures/gui/icons.png index ef0384d3908717974d0955da5365c64e1167bce7..6a7a7d5cc20a47d004c3536e6773062ad84a29d5 100644 GIT binary patch delta 3494 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 EU)`FhU;qFB delta 3435 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 bhe`zuypNeOOpkwneuch{v37-~eb~PM!Y7!~ diff --git a/src/main/resources/assets/create/textures/item/clipboard.png b/src/main/resources/assets/create/textures/item/clipboard.png new file mode 100644 index 0000000000000000000000000000000000000000..00ff940e124306946e0ba97d5043b85d717b4592 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|&0G|-oLVx~XOU}f^#Lgu3&6_u0xpL*)n%vIL&NpW#FRZoDlHvp^yq3JT z6-e=v1o;L3M*s$)`{&L9#W@Q+B8wRq_zr_GC|3VSrwkA;`%C%?D{HC-~D4S%;Vs1 TpQPjuw3@-w)z4*}Q$iB}wK`;Q literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/item/clipboard_and_quill.png b/src/main/resources/assets/create/textures/item/clipboard_and_quill.png new file mode 100644 index 0000000000000000000000000000000000000000..7740e0dabc5e8d2178be4759781905a5cc50716d GIT binary patch literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}B0G|-o|Ns9#c<^BD+O>uL{K1x-iHV7wN$Q(7Z@zNnN_~C(xiz_+otk=mb1VkvY3H^?=T269?xHq z0u*fYba4#P2#!7LEp$YIgC)Rjk=uoq-|=M%v(9eV&=r1{NS%G}T0G|-oLVx~XOU}f^#Lgu3&6_u0xpL*)n%vIL&NpW#Ye{heRh87PIs~M+ zOM?7@|HA=;mp|htpfG2FM`SSr1K(i~W;~w1A_XXz<>}%Wq7fW>Hj(!pPo79=+8H~@C