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 1/3] 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 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 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 Date: Sun, 26 Mar 2023 18:01:52 +0200 Subject: [PATCH 2/3] No anvil, no problem - Clipboards can now be used to manually write to Display Boards and Nixie Tubes - Clipboards can now be used as Material Checklists in the Schematicannon - Added Clipboard recipe and clearing recipe --- src/generated/resources/.cache/cache | 6 +- .../resources/assets/create/lang/en_us.json | 2 +- .../crafting/appliances/clipboard.json | 34 ++++++ .../crafting/appliances/clipboard_clear.json | 34 ++++++ .../crafting/appliances/clipboard.json | 22 ++++ .../crafting/appliances/clipboard_clear.json | 11 ++ .../curiosities/clipboard/ClipboardEntry.java | 31 ++++- .../clipboard/ClipboardScreen.java | 39 ++++-- .../clipboard/ClipboardScreenHelper.java | 11 -- .../block/redstone/NixieTubeBlock.java | 23 +++- .../management/display/FlapDisplayBlock.java | 24 +++- .../content/schematics/MaterialChecklist.java | 111 ++++++++++++++++-- .../block/SchematicannonBlockEntity.java | 5 +- .../block/SchematicannonInventory.java | 3 +- .../data/recipe/StandardRecipeGen.java | 12 ++ .../assets/create/lang/default/interface.json | 2 +- 16 files changed, 320 insertions(+), 50 deletions(-) create mode 100644 src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard.json create mode 100644 src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard_clear.json create mode 100644 src/generated/resources/data/create/recipes/crafting/appliances/clipboard.json create mode 100644 src/generated/resources/data/create/recipes/crafting/appliances/clipboard_clear.json delete mode 100644 src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 840b6279b..a1146bf1b 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -567,7 +567,7 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json 0f4e5a2fc58580df5b156fdac438ddeb6b57e386 assets/create/lang/en_ud.json -85d790bedbdc65bb6e6377edcc63e7b00455e879 assets/create/lang/en_us.json +2502e2934b3e39763f5619e1522b0b6f98bc26d7 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 @@ -2316,6 +2316,8 @@ ba80332510acab3f60f30d8b802ee2d450fd51b9 data/create/advancements/recipes/create 1dea56b4759da676f0edf0878ec834a4129d110b data/create/advancements/recipes/create.base/copper_ladder_from_plates_copper_stonecutting.json 3397bed32684183c2896348e3010f47137e3216d data/create/advancements/recipes/create.base/copycat_panel_from_zinc_ingot_stonecutting.json 3c22b58635d4b1c1e4c4033e43b7b4a74b1af186 data/create/advancements/recipes/create.base/copycat_step_from_zinc_ingot_stonecutting.json +3194b8da04cfb26f070b0a14f210f0117f252993 data/create/advancements/recipes/create.base/crafting/appliances/clipboard.json +a1746099602e91fd23fba112016d41c71f9de62e data/create/advancements/recipes/create.base/crafting/appliances/clipboard_clear.json 376bda381f3dedb52b03eb1504b103d8ddd1b672 data/create/advancements/recipes/create.base/crafting/appliances/copper_backtank.json 9833d16405f8c51646590e98588fb410f96d9523 data/create/advancements/recipes/create.base/crafting/appliances/copper_diving_boots.json 680c982dd1d3c45bed4d390fc0da5042750c157a data/create/advancements/recipes/create.base/crafting/appliances/copper_diving_helmet.json @@ -4004,6 +4006,8 @@ bea832822e0e5f0048eb94649641ea541e11f943 data/create/recipes/copper_shingles_fro 10fdc13f5b2b745e13e6e4e949a07ceaf4544a26 data/create/recipes/copper_tiles_from_plates_copper_stonecutting.json 89aed29928cdfa7cdde43d4a51fc4387497ddde0 data/create/recipes/copycat_panel_from_zinc_ingot_stonecutting.json c06a4519139a33250c01297b532ba6ac7d76284c data/create/recipes/copycat_step_from_zinc_ingot_stonecutting.json +005f8ad32598ea98314031e66b06e95e1f8ddd13 data/create/recipes/crafting/appliances/clipboard.json +db648fd89bc2030f3e1e9baa0d2b5b69238dec4c data/create/recipes/crafting/appliances/clipboard_clear.json eb18d5972484418fa5a768633e68688ad20d2bd7 data/create/recipes/crafting/appliances/copper_backtank.json 5771562086710eb5a3a05d464989d2f23d6c5e86 data/create/recipes/crafting/appliances/copper_diving_boots.json ec38ddb44e4bf8eaaba6f9d27e8469234fc98528 data/create/recipes/crafting/appliances/copper_diving_helmet.json diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index 05c6ce5ab..c2f53b0e1 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -1212,7 +1212,7 @@ "create.gui.schematicannon.option.skipMissing": "Skip missing Blocks", "create.gui.schematicannon.option.skipBlockEntities": "Protect Block Entities", "create.gui.schematicannon.slot.gunpowder": "Add gunpowder to fuel the cannon", - "create.gui.schematicannon.slot.listPrinter": "Place books here to print a Checklist for your Schematic", + "create.gui.schematicannon.slot.listPrinter": "Place a Clipboard or Book here to print a Checklist for your Schematic", "create.gui.schematicannon.slot.schematic": "Add your Schematic here. Make sure it is deployed at a specific location.", "create.gui.schematicannon.option.skipMissing.description": "If the cannon cannot find a required Block for placement, it will continue at the next Location.", "create.gui.schematicannon.option.skipBlockEntities.description": "The cannon will avoid replacing data holding blocks such as Chests.", diff --git a/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard.json b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard.json new file mode 100644 index 000000000..98fd32361 --- /dev/null +++ b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "rewards": { + "recipes": [ + "create:crafting/appliances/clipboard" + ] + }, + "criteria": { + "has_item": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "items": [ + "create:andesite_alloy" + ] + } + ] + } + }, + "has_the_recipe": { + "trigger": "minecraft:recipe_unlocked", + "conditions": { + "recipe": "create:crafting/appliances/clipboard" + } + } + }, + "requirements": [ + [ + "has_item", + "has_the_recipe" + ] + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard_clear.json b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard_clear.json new file mode 100644 index 000000000..39d0e338d --- /dev/null +++ b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/clipboard_clear.json @@ -0,0 +1,34 @@ +{ + "parent": "minecraft:recipes/root", + "rewards": { + "recipes": [ + "create:crafting/appliances/clipboard_clear" + ] + }, + "criteria": { + "has_item": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "items": [ + "create:clipboard" + ] + } + ] + } + }, + "has_the_recipe": { + "trigger": "minecraft:recipe_unlocked", + "conditions": { + "recipe": "create:crafting/appliances/clipboard_clear" + } + } + }, + "requirements": [ + [ + "has_item", + "has_the_recipe" + ] + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/crafting/appliances/clipboard.json b/src/generated/resources/data/create/recipes/crafting/appliances/clipboard.json new file mode 100644 index 000000000..c32ad1552 --- /dev/null +++ b/src/generated/resources/data/create/recipes/crafting/appliances/clipboard.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "A", + "P", + "G" + ], + "key": { + "G": { + "tag": "minecraft:planks" + }, + "P": { + "item": "minecraft:paper" + }, + "A": { + "item": "create:andesite_alloy" + } + }, + "result": { + "item": "create:clipboard" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/crafting/appliances/clipboard_clear.json b/src/generated/resources/data/create/recipes/crafting/appliances/clipboard_clear.json new file mode 100644 index 000000000..de19409bd --- /dev/null +++ b/src/generated/resources/data/create/recipes/crafting/appliances/clipboard_clear.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "create:clipboard" + } + ], + "result": { + "item": "create:clipboard" + } +} \ No newline at end of file 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 index 570546412..dcdbb066e 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardEntry.java @@ -13,12 +13,19 @@ import net.minecraft.world.item.ItemStack; public class ClipboardEntry { - boolean checked; - MutableComponent text; + public boolean checked; + public MutableComponent text; + public ItemStack icon; public ClipboardEntry(boolean checked, MutableComponent text) { this.checked = checked; this.text = text; + this.icon = ItemStack.EMPTY; + } + + public ClipboardEntry displayItem(ItemStack icon) { + this.icon = icon; + return this; } public static List> readAll(ItemStack clipboardItem) { @@ -29,6 +36,17 @@ public class ClipboardEntry { .readCompoundList(pageTag.getList("Entries", Tag.TAG_COMPOUND), ClipboardEntry::readNBT)); } + public static List getLastViewedEntries(ItemStack heldItem) { + List> pages = ClipboardEntry.readAll(heldItem); + if (pages.isEmpty()) + return new ArrayList<>(); + int page = heldItem.getTag() == null ? 0 + : Math.min(heldItem.getTag() + .getInt("PreviouslyOpenedPage"), pages.size() - 1); + List entries = pages.get(page); + return entries; + } + public static void saveAll(List> entries, ItemStack clipboardItem) { CompoundTag tag = clipboardItem.getOrCreateTag(); tag.put("Pages", NBTHelper.writeCompoundList(entries, list -> { @@ -42,11 +60,18 @@ public class ClipboardEntry { CompoundTag nbt = new CompoundTag(); nbt.putBoolean("Checked", checked); nbt.putString("Text", Component.Serializer.toJson(text)); + if (icon.isEmpty()) + return nbt; + nbt.put("Icon", icon.serializeNBT()); return nbt; } public static ClipboardEntry readNBT(CompoundTag tag) { - return new ClipboardEntry(tag.getBoolean("Checked"), Component.Serializer.fromJson(tag.getString("Text"))); + ClipboardEntry clipboardEntry = + new ClipboardEntry(tag.getBoolean("Checked"), Component.Serializer.fromJson(tag.getString("Text"))); + if (tag.contains("Icon")) + clipboardEntry.displayItem(ItemStack.of(tag.getCompound("Icon"))); + return clipboardEntry; } } 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 index f9cf68581..80dd935d7 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java +++ b/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreen.java @@ -64,6 +64,7 @@ public class ClipboardScreen extends AbstractSimiScreen { int hoveredEntry; boolean hoveredCheck; + boolean readonly; DisplayCache displayCache = DisplayCache.EMPTY; TextFieldHelper editContext; @@ -91,6 +92,10 @@ public class ClipboardScreen extends AbstractSimiScreen { editContext = new TextFieldHelper(this::getCurrentEntryText, this::setCurrentEntryText, this::getClipboard, this::setClipboard, this::validateTextForEntry); editingIndex = startEmpty ? 0 : -1; + readonly = item.getTag() != null && item.getTag() + .getBoolean("Readonly"); + if (readonly) + editingIndex = -1; } @Override @@ -122,7 +127,7 @@ public class ClipboardScreen extends AbstractSimiScreen { addRenderableWidget(forward); addRenderableWidget(backward); - forward.visible = currentPage < 50; + forward.visible = currentPage < 50 && (!readonly || currentPage + 1 < pages.size()); backward.visible = currentPage > 0; } @@ -153,8 +158,9 @@ public class ClipboardScreen extends AbstractSimiScreen { 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); + totalHeight += + Math.max(12, font.split(Components.literal(text), clipboardEntry.icon.isEmpty() ? 150 : 130) + .size() * 9 + 3); if (totalHeight > my) { hoveredEntry = i; @@ -211,17 +217,24 @@ public class ClipboardScreen extends AbstractSimiScreen { if (currentPage == previously) return; editingIndex = -1; - if (pages.size() <= currentPage) + if (pages.size() <= currentPage) { + if (readonly) { + currentPage = previously; + return; + } pages.add(new ArrayList<>()); + } currentEntries = pages.get(currentPage); if (currentEntries.isEmpty()) { currentEntries.add(new ClipboardEntry(false, Components.empty())); - editingIndex = 0; - editContext.setCursorToEnd(); - clearDisplayCacheAfterChange(); + if (!readonly) { + editingIndex = 0; + editContext.setCursorToEnd(); + clearDisplayCacheAfterChange(); + } } - forward.visible = currentPage < 50; + forward.visible = currentPage < 50 && (!readonly || currentPage + 1 < pages.size()); backward.visible = currentPage > 0; if (next) @@ -245,20 +258,24 @@ public class ClipboardScreen extends AbstractSimiScreen { for (int i = 0; i < currentEntries.size(); i++) { ClipboardEntry clipboardEntry = currentEntries.get(i); boolean checked = clipboardEntry.checked; + int iconOffset = clipboardEntry.icon.isEmpty() ? 0 : 16; 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); + List split = font.split(clipboardEntry.text, 150 - iconOffset); if (split.isEmpty()) { y += 12; continue; } + if (!clipboardEntry.icon.isEmpty()) + itemRenderer.renderGuiItem(clipboardEntry.icon, x + 54, y + 50); + for (FormattedCharSequence sequence : split) { if (i != editingIndex) - font.draw(ms, sequence, x + 58, y + 50, checked ? 0x31B25D : 0x311A00); + font.draw(ms, sequence, x + 58 + iconOffset, y + 50, checked ? 0x31B25D : 0x311A00); y += 9; } y += 3; @@ -525,7 +542,7 @@ public class ClipboardScreen extends AbstractSimiScreen { return true; } - if (hoveredEntry != editingIndex) { + if (hoveredEntry != editingIndex && !readonly) { editingIndex = hoveredEntry; if (hoveredEntry >= currentEntries.size()) { currentEntries.add(new ClipboardEntry(false, Components.empty())); 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 deleted file mode 100644 index 0e53a7075..000000000 --- a/src/main/java/com/simibubi/create/content/curiosities/clipboard/ClipboardScreenHelper.java +++ /dev/null @@ -1,11 +0,0 @@ -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/content/logistics/block/redstone/NixieTubeBlock.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java index 5cb745bd1..de3810d1c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java @@ -2,13 +2,16 @@ package com.simibubi.create.content.logistics.block.redstone; import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED; +import java.util.List; import java.util.Random; import java.util.function.BiConsumer; import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; import com.simibubi.create.AllShapes; import com.simibubi.create.content.contraptions.wrench.IWrenchable; +import com.simibubi.create.content.curiosities.clipboard.ClipboardEntry; import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType; @@ -19,6 +22,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -76,20 +80,31 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock return InteractionResult.SUCCESS; } - boolean display = heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName(); + boolean display = + heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName() || AllItems.CLIPBOARD.isIn(heldItem); DyeColor dye = DyeColor.getColor(heldItem); if (!display && dye == null) return InteractionResult.PASS; - if (world.isClientSide) - return InteractionResult.SUCCESS; CompoundTag tag = heldItem.getTagElement("display"); String tagElement = tag != null && tag.contains("Name", Tag.TAG_STRING) ? tag.getString("Name") : null; + if (AllItems.CLIPBOARD.isIn(heldItem)) { + List entries = ClipboardEntry.getLastViewedEntries(heldItem); + for (int i = 0; i < entries.size();) { + tagElement = Component.Serializer.toJson(entries.get(i).text); + break; + } + } + + if (world.isClientSide) + return InteractionResult.SUCCESS; + + String tagUsed = tagElement; walkNixies(world, pos, (currentPos, rowPosition) -> { if (display) - withBlockEntityDo(world, currentPos, be -> be.displayCustomText(tagElement, rowPosition)); + withBlockEntityDo(world, currentPos, be -> be.displayCustomText(tagUsed, rowPosition)); if (dye != null) world.setBlockAndUpdate(currentPos, withColor(state, dye)); }); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java index aa3cbefb8..d64ada626 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java @@ -8,12 +8,15 @@ import java.util.function.Predicate; import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; import com.simibubi.create.AllShapes; import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock; import com.simibubi.create.content.contraptions.base.KineticBlockEntity; import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel; import com.simibubi.create.content.contraptions.wrench.IWrenchable; +import com.simibubi.create.content.curiosities.clipboard.ClipboardEntry; import com.simibubi.create.foundation.block.IBE; +import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.placement.IPlacementHelper; import com.simibubi.create.foundation.utility.placement.PlacementHelpers; @@ -27,6 +30,7 @@ import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.AxisDirection; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; @@ -158,7 +162,8 @@ public class FlapDisplayBlock extends HorizontalKineticBlock return InteractionResult.SUCCESS; } - boolean display = heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName(); + boolean display = + heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName() || AllItems.CLIPBOARD.isIn(heldItem); DyeColor dye = DyeColor.getColor(heldItem); if (!display && dye == null) @@ -171,8 +176,20 @@ public class FlapDisplayBlock extends HorizontalKineticBlock CompoundTag tag = heldItem.getTagElement("display"); String tagElement = tag != null && tag.contains("Name", Tag.TAG_STRING) ? tag.getString("Name") : null; - if (display) + if (display) { + if (AllItems.CLIPBOARD.isIn(heldItem)) { + List entries = ClipboardEntry.getLastViewedEntries(heldItem); + int line = lineIndex; + for (int i = 0; i < entries.size(); i++) { + for (String string : entries.get(i).text.getString() + .split("\n")) + flapBE.applyTextManually(line++, Component.Serializer.toJson(Components.literal(string))); + } + return InteractionResult.SUCCESS; + } + flapBE.applyTextManually(lineIndex, tagElement); + } if (dye != null) { world.playSound(null, pos, SoundEvents.DYE_USE, SoundSource.BLOCKS, 1.0F, 1.0F); flapBE.setColour(lineIndex, dye); @@ -315,7 +332,8 @@ public class FlapDisplayBlock extends HorizontalKineticBlock BlockPos relative = pPos.relative(d); BlockState adjacent = pLevel.getBlockState(relative); if (canConnect(pState, adjacent)) - KineticBlockEntity.switchToBlockState(pLevel, relative, updateColumn(pLevel, relative, adjacent, false)); + KineticBlockEntity.switchToBlockState(pLevel, relative, + updateColumn(pLevel, relative, adjacent, false)); } } diff --git a/src/main/java/com/simibubi/create/content/schematics/MaterialChecklist.java b/src/main/java/com/simibubi/create/content/schematics/MaterialChecklist.java index d79dc1586..0b76235a6 100644 --- a/src/main/java/com/simibubi/create/content/schematics/MaterialChecklist.java +++ b/src/main/java/com/simibubi/create/content/schematics/MaterialChecklist.java @@ -6,6 +6,10 @@ import java.util.List; import java.util.Locale; import com.google.common.collect.Sets; +import com.simibubi.create.AllItems; +import com.simibubi.create.content.curiosities.clipboard.ClipboardEntry; +import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides; +import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides.ClipboardType; import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType; import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Lang; @@ -27,6 +31,7 @@ import net.minecraft.world.item.Items; public class MaterialChecklist { public static final int MAX_ENTRIES_PER_PAGE = 5; + public static final int MAX_ENTRIES_PER_CLIPBOARD_PAGE = 7; public Object2IntMap gathered = new Object2IntArrayMap<>(); public Object2IntMap required = new Object2IntArrayMap<>(); @@ -70,7 +75,7 @@ public class MaterialChecklist { gathered.put(item, stack.getCount()); } - public ItemStack createItem() { + public ItemStack createWrittenBook() { ItemStack book = new ItemStack(Items.WRITTEN_BOOK); CompoundTag tag = book.getOrCreateTag(); @@ -88,9 +93,11 @@ public class MaterialChecklist { List keys = new ArrayList<>(Sets.union(required.keySet(), damageRequired.keySet())); Collections.sort(keys, (item1, item2) -> { Locale locale = Locale.ENGLISH; - String name1 = item1.getDescription().getString() + String name1 = item1.getDescription() + .getString() .toLowerCase(locale); - String name2 = item2.getDescription().getString() + String name2 = item2.getDescription() + .getString() .toLowerCase(locale); return name1.compareTo(name2); }); @@ -109,30 +116,33 @@ public class MaterialChecklist { if (itemsWritten == MAX_ENTRIES_PER_PAGE) { itemsWritten = 0; - textComponent.append(Components.literal("\n >>>").withStyle(ChatFormatting.BLUE)); + textComponent.append(Components.literal("\n >>>") + .withStyle(ChatFormatting.BLUE)); pages.add(StringTag.valueOf(Component.Serializer.toJson(textComponent))); textComponent = Components.empty(); } itemsWritten++; - textComponent.append(entry(new ItemStack(item), amount, true)); + textComponent.append(entry(new ItemStack(item), amount, true, true)); } for (Item item : completed) { if (itemsWritten == MAX_ENTRIES_PER_PAGE) { itemsWritten = 0; - textComponent.append(Components.literal("\n >>>").withStyle(ChatFormatting.DARK_GREEN)); + textComponent.append(Components.literal("\n >>>") + .withStyle(ChatFormatting.DARK_GREEN)); pages.add(StringTag.valueOf(Component.Serializer.toJson(textComponent))); textComponent = Components.empty(); } itemsWritten++; - textComponent.append(entry(new ItemStack(item), getRequiredAmount(item), false)); + textComponent.append(entry(new ItemStack(item), getRequiredAmount(item), false, true)); } pages.add(StringTag.valueOf(Component.Serializer.toJson(textComponent))); tag.put("pages", pages); + tag.putBoolean("readonly", true); tag.putString("author", "Schematicannon"); tag.putString("title", ChatFormatting.BLUE + "Material Checklist"); textComponent = Lang.translateDirect("materialChecklist") @@ -145,6 +155,80 @@ public class MaterialChecklist { return book; } + public ItemStack createWrittenClipboard() { + ItemStack clipboard = AllItems.CLIPBOARD.asStack(); + CompoundTag tag = clipboard.getOrCreateTag(); + int itemsWritten = 0; + + List> pages = new ArrayList<>(); + List currentPage = new ArrayList<>(); + + if (blocksNotLoaded) { + currentPage.add(new ClipboardEntry(false, Lang.translateDirect("materialChecklist.blocksNotLoaded") + .withStyle(ChatFormatting.RED))); + } + + List keys = new ArrayList<>(Sets.union(required.keySet(), damageRequired.keySet())); + Collections.sort(keys, (item1, item2) -> { + Locale locale = Locale.ENGLISH; + String name1 = item1.getDescription() + .getString() + .toLowerCase(locale); + String name2 = item2.getDescription() + .getString() + .toLowerCase(locale); + return name1.compareTo(name2); + }); + + List completed = new ArrayList<>(); + for (Item item : keys) { + int amount = getRequiredAmount(item); + if (gathered.containsKey(item)) + amount -= gathered.getInt(item); + + if (amount <= 0) { + completed.add(item); + continue; + } + + if (itemsWritten == MAX_ENTRIES_PER_CLIPBOARD_PAGE) { + itemsWritten = 0; + currentPage.add(new ClipboardEntry(false, Components.literal(">>>") + .withStyle(ChatFormatting.DARK_GRAY))); + pages.add(currentPage); + currentPage = new ArrayList<>(); + } + + itemsWritten++; + currentPage.add(new ClipboardEntry(false, entry(new ItemStack(item), amount, true, false)) + .displayItem(new ItemStack(item))); + } + + for (Item item : completed) { + if (itemsWritten == MAX_ENTRIES_PER_PAGE) { + itemsWritten = 0; + currentPage.add(new ClipboardEntry(true, Components.literal(">>>") + .withStyle(ChatFormatting.DARK_GREEN))); + pages.add(currentPage); + currentPage = new ArrayList<>(); + } + + itemsWritten++; + currentPage.add(new ClipboardEntry(true, entry(new ItemStack(item), getRequiredAmount(item), false, false)) + .displayItem(new ItemStack(item))); + } + + pages.add(currentPage); + ClipboardEntry.saveAll(pages, clipboard); + ClipboardOverrides.switchTo(ClipboardType.WRITTEN, clipboard); + clipboard.getOrCreateTagElement("display") + .putString("Name", Component.Serializer.toJson(Lang.translateDirect("materialChecklist") + .setStyle(Style.EMPTY.withItalic(Boolean.FALSE)))); + tag.putBoolean("Readonly", true); + clipboard.setTag(tag); + return clipboard; + } + public int getRequiredAmount(Item item) { int amount = required.getOrDefault(item, 0); if (damageRequired.containsKey(item)) @@ -152,7 +236,7 @@ public class MaterialChecklist { return amount; } - private Component entry(ItemStack item, int amount, boolean unfinished) { + private MutableComponent entry(ItemStack item, int amount, boolean unfinished, boolean forBook) { int stacks = amount / 64; int remainder = amount % 64; MutableComponent tc = Components.empty(); @@ -160,11 +244,14 @@ public class MaterialChecklist { .setStyle(Style.EMPTY .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ITEM, new HoverEvent.ItemStackInfo(item))))); - if (!unfinished) + if (!unfinished && forBook) tc.append(" \u2714"); - tc.withStyle(unfinished ? ChatFormatting.BLUE : ChatFormatting.DARK_GREEN); - return tc.append(Components.literal("\n" + " x" + amount).withStyle(ChatFormatting.BLACK)) - .append(Components.literal(" | " + stacks + "\u25A4 +" + remainder + "\n").withStyle(ChatFormatting.GRAY)); + if (!unfinished || forBook) + tc.withStyle(unfinished ? ChatFormatting.BLUE : ChatFormatting.DARK_GREEN); + return tc.append(Components.literal("\n" + " x" + amount) + .withStyle(ChatFormatting.BLACK)) + .append(Components.literal(" | " + stacks + "\u25A4 +" + remainder + (forBook ? "\n" : "")) + .withStyle(ChatFormatting.GRAY)); } } diff --git a/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonBlockEntity.java b/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonBlockEntity.java index d4cb56776..33e94bf4f 100644 --- a/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonBlockEntity.java @@ -700,8 +700,9 @@ public class SchematicannonBlockEntity extends SmartBlockEntity implements MenuP updateChecklist(); dontUpdateChecklist = true; - inventory.extractItem(BookInput, 1, false); - ItemStack stack = checklist.createItem(); + ItemStack extractItem = inventory.extractItem(BookInput, 1, false); + ItemStack stack = AllItems.CLIPBOARD.isIn(extractItem) ? checklist.createWrittenClipboard() + : checklist.createWrittenBook(); stack.setCount(inventory.getStackInSlot(BookOutput) .getCount() + 1); inventory.setStackInSlot(BookOutput, stack); diff --git a/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonInventory.java b/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonInventory.java index febefdb34..8db0581cf 100644 --- a/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonInventory.java +++ b/src/main/java/com/simibubi/create/content/schematics/block/SchematicannonInventory.java @@ -28,7 +28,8 @@ public class SchematicannonInventory extends ItemStackHandler { case 1: // Blueprint output return false; case 2: // Book input - return stack.sameItem(new ItemStack(Items.BOOK)) || stack.sameItem(new ItemStack(Items.WRITTEN_BOOK)); + return AllItems.CLIPBOARD.isIn(stack) || stack.sameItem(new ItemStack(Items.BOOK)) + || stack.sameItem(new ItemStack(Items.WRITTEN_BOOK)); case 3: // Material List output return false; case 4: // Gunpowder diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java index 00d226019..6f056de67 100644 --- a/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java @@ -1011,6 +1011,18 @@ public class StandardRecipeGen extends CreateRecipeProvider { .viaShapeless(b -> b.requires(I.wheatFlour()) .requires(Items.WATER_BUCKET)), + CLIPBOARD = create(AllItems.CLIPBOARD).unlockedBy(I::andesite) + .viaShaped(b -> b.define('G', I.planks()) + .define('P', Items.PAPER) + .define('A', I.andesite()) + .pattern("A") + .pattern("P") + .pattern("G")), + + CLIPBOARD_CLEAR = create(AllItems.CLIPBOARD).withSuffix("_clear") + .unlockedBy(AllItems.CLIPBOARD::get) + .viaShapeless(b -> b.requires(AllItems.CLIPBOARD.get())), + DIVING_HELMET = create(AllItems.COPPER_DIVING_HELMET).unlockedBy(I::copper) .viaShaped(b -> b.define('G', Tags.Items.GLASS) .define('P', I.copper()) diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index e3fba6afb..472453f5f 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -357,7 +357,7 @@ "create.gui.schematicannon.option.skipBlockEntities": "Protect Block Entities", "create.gui.schematicannon.slot.gunpowder": "Add gunpowder to fuel the cannon", - "create.gui.schematicannon.slot.listPrinter": "Place books here to print a Checklist for your Schematic", + "create.gui.schematicannon.slot.listPrinter": "Place a Clipboard or Book here to print a Checklist for your Schematic", "create.gui.schematicannon.slot.schematic": "Add your Schematic here. Make sure it is deployed at a specific location.", "create.gui.schematicannon.option.skipMissing.description": "If the cannon cannot find a required Block for placement, it will continue at the next Location.", From fc91b63d127c5bea6d37cf40b541683679ab4a72 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Mon, 27 Mar 2023 14:29:08 +0200 Subject: [PATCH 3/3] Riveting Development - Valve handles no longer create stress config entries for each dyed variant - Place near initial angle mode on bearings now has a smaller interval considered 'near' - Rollers are now a little easier to place and expand - Players can now take items from saws via right-click - Item Drains now accept dropped items as input - Train track placement overlay now explicitly mentions the ctrl key --- src/generated/resources/.cache/cache | 5 +- .../resources/assets/create/lang/en_us.json | 3 +- .../data/create/advancements/long_bend.json | 30 ------ .../data/create/advancements/long_train.json | 2 +- .../java/com/simibubi/create/AllBlocks.java | 4 +- .../com/simibubi/create/CreateClient.java | 2 + .../components/actors/RollerBlock.java | 100 ++++++++++++++++++ .../components/actors/RollerBlockItem.java | 29 +++++ .../crank/HandCrankBlockEntity.java | 3 +- .../contraptions/components/saw/SawBlock.java | 48 +++++++-- .../bearing/MechanicalBearingBlockEntity.java | 2 +- .../fluids/actors/ItemDrainBlock.java | 37 ++++++- .../trains/track/TrackPlacement.java | 66 ++++++++++-- .../advancement/AllAdvancements.java | 8 +- .../foundation/data/BuilderTransformers.java | 1 - .../create/foundation/item/KineticStats.java | 5 + .../assets/create/lang/default/interface.json | 1 + 17 files changed, 279 insertions(+), 67 deletions(-) delete mode 100644 src/generated/resources/data/create/advancements/long_bend.json create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlockItem.java diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index a1146bf1b..9a78f0224 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -567,7 +567,7 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json 0f4e5a2fc58580df5b156fdac438ddeb6b57e386 assets/create/lang/en_ud.json -2502e2934b3e39763f5619e1522b0b6f98bc26d7 assets/create/lang/en_us.json +aa858245829503947285d0a41b41ec7119de4219 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 @@ -2263,8 +2263,7 @@ eff2f22dfdf7ed94f868bfdf960fd6a6c8f6a567 data/create/advancements/hose_pulley.js c119d8187e57286836579cf33d4bcdced2406932 data/create/advancements/hose_pulley_lava.json ce5a2e1e2ac7049f03c6c8f15130112500bbae2f data/create/advancements/lava_wheel_00000.json 63d86087604035e4e70c203d9b73a7d567a173c1 data/create/advancements/linked_controller.json -f81d3e42370e8429bcd165d745dfc19fdcf6dedd data/create/advancements/long_bend.json -22d19a986e27169ad170792ef5dcbcdf2ea4d214 data/create/advancements/long_train.json +5678c0e3fae7a7cd45a5c5b60f651350d314b8a8 data/create/advancements/long_train.json 648e40cace7bd3d2cb1375ae89fd2ee0e0ad561e data/create/advancements/long_travel.json 258a8c893b8a0265fb1c80f6fa528e80157884e8 data/create/advancements/mechanical_arm.json 418feaeabccc9bfa6ed89fa36de9d6991ce2c7c2 data/create/advancements/mechanical_crafter.json diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index c2f53b0e1..bf7b0fb6c 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -852,8 +852,6 @@ "advancement.create.train_portal.desc": "Ride a Train through a Nether portal", "advancement.create.track_crafting_factory": "Track Factory", "advancement.create.track_crafting_factory.desc": "Produce more than 1000 Train Tracks with the same Mechanical Press", - "advancement.create.long_bend": "The Longest Bend", - "advancement.create.long_bend.desc": "Create a curved track section that spans more than 30 blocks in length", "advancement.create.long_train": "Ambitious Endeavours", "advancement.create.long_train.desc": "Create a Train with at least 6 carriages", "advancement.create.long_travel": "Field Trip", @@ -1576,6 +1574,7 @@ "create.track.turn_start": "Cannot start connection from a Turn", "create.track.not_enough_tracks": "Not holding enough tracks", "create.track.not_enough_pavement": "Not holding enough pavement blocks", + "create.track.hold_for_smooth_curve": "Hold %1$s for maximized turn", "create.portal_track.failed": "Cannot place portal track:", "create.portal_track.missing": "Target portal not generated yet", diff --git a/src/generated/resources/data/create/advancements/long_bend.json b/src/generated/resources/data/create/advancements/long_bend.json deleted file mode 100644 index 5b6f5f513..000000000 --- a/src/generated/resources/data/create/advancements/long_bend.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "parent": "create:track_crafting_factory", - "display": { - "icon": { - "item": "create:track" - }, - "title": { - "translate": "advancement.create.long_bend" - }, - "description": { - "color": "#DBA213", - "translate": "advancement.create.long_bend.desc" - }, - "frame": "goal", - "show_toast": true, - "announce_to_chat": true, - "hidden": false - }, - "criteria": { - "0": { - "trigger": "create:long_bend_builtin", - "conditions": {} - } - }, - "requirements": [ - [ - "0" - ] - ] -} \ No newline at end of file diff --git a/src/generated/resources/data/create/advancements/long_train.json b/src/generated/resources/data/create/advancements/long_train.json index e72513d0a..83ff7a661 100644 --- a/src/generated/resources/data/create/advancements/long_train.json +++ b/src/generated/resources/data/create/advancements/long_train.json @@ -1,5 +1,5 @@ { - "parent": "create:long_bend", + "parent": "create:track_crafting_factory", "display": { "icon": { "item": "minecraft:minecart" diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index f3b0d4064..84fdcd984 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -27,6 +27,7 @@ import com.simibubi.create.content.contraptions.components.actors.PloughMovement import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceBlock; import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceMovement; import com.simibubi.create.content.contraptions.components.actors.RollerBlock; +import com.simibubi.create.content.contraptions.components.actors.RollerBlockItem; import com.simibubi.create.content.contraptions.components.actors.RollerMovementBehaviour; import com.simibubi.create.content.contraptions.components.actors.SawMovementBehaviour; import com.simibubi.create.content.contraptions.components.actors.SeatBlock; @@ -904,6 +905,7 @@ public class AllBlocks { REGISTRATE.block("copper_valve_handle", ValveHandleBlock::copper) .transform(pickaxeOnly()) .transform(BuilderTransformers.valveHandle(null)) + .transform(BlockStressDefaults.setCapacity(8.0)) .register(); public static final DyedBlockList DYED_VALVE_HANDLES = new DyedBlockList<>(colour -> { @@ -1375,7 +1377,7 @@ public class AllBlocks { .onRegister(movementBehaviour(new RollerMovementBehaviour())) .blockstate(BlockStateGen.horizontalBlockProvider(true)) .addLayer(() -> RenderType::cutoutMipped) - .item() + .item(RollerBlockItem::new) .transform(customItemModel()) .register(); diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index 83bfe4fbf..54ea1a696 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -15,6 +15,7 @@ import com.simibubi.create.content.curiosities.weapons.PotatoCannonRenderHandler import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler; import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler; import com.simibubi.create.content.logistics.trains.GlobalRailwayManager; +import com.simibubi.create.content.logistics.trains.track.TrackPlacement; import com.simibubi.create.content.schematics.ClientSchematicLoader; import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler; import com.simibubi.create.content.schematics.client.SchematicHandler; @@ -104,6 +105,7 @@ public class CreateClient { OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Linked Controller", LinkedControllerClientHandler.OVERLAY); OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Schematics", SCHEMATIC_HANDLER.getOverlayRenderer()); OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Toolboxes", ToolboxHandlerClient.OVERLAY); + OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Track Placement", TrackPlacement.OVERLAY); } public static void invalidateRenderers() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlock.java index 748e4efd8..6f0ecfc6a 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlock.java @@ -1,24 +1,43 @@ package com.simibubi.create.content.contraptions.components.actors; +import java.util.function.Predicate; + import com.simibubi.create.AllBlockEntityTypes; +import com.simibubi.create.AllBlocks; import com.simibubi.create.AllShapes; +import com.simibubi.create.content.curiosities.tools.ExtendoGripItem; import com.simibubi.create.foundation.block.IBE; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.utility.placement.IPlacementHelper; +import com.simibubi.create.foundation.utility.placement.PlacementHelpers; +import com.simibubi.create.foundation.utility.placement.PlacementOffset; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.common.ForgeMod; public class RollerBlock extends AttachedActorBlock implements IBE { public static DamageSource damageSourceRoller = new DamageSource("create.mechanical_roller"); + private static final int placementHelperId = PlacementHelpers.register(new PlacementHelper()); + public RollerBlock(Properties p_i48377_1_) { super(p_i48377_1_); } @@ -50,4 +69,85 @@ public class RollerBlock extends AttachedActorBlock implements IBE getItemPredicate() { + return AllBlocks.MECHANICAL_ROLLER::isIn; + } + + @Override + public Predicate getStatePredicate() { + return AllBlocks.MECHANICAL_ROLLER::has; + } + + public int attachedSteps(Level world, BlockPos pos, Direction direction) { + BlockPos checkPos = pos.relative(direction); + BlockState state = world.getBlockState(checkPos); + int count = 0; + while (getStatePredicate().test(state)) { + count++; + checkPos = checkPos.relative(direction); + state = world.getBlockState(checkPos); + } + return count; + } + + @Override + public PlacementOffset getOffset(Player player, Level world, BlockState state, BlockPos pos, + BlockHitResult ray) { + + Direction dir = null; + Direction facing = state.getValue(FACING); + + for (Direction nearest : Direction.orderedByNearest(player)) { + if (nearest.getAxis() != facing.getClockWise() + .getAxis()) + continue; + dir = nearest; + break; + } + + int range = AllConfigs.server().curiosities.placementAssistRange.get(); + if (player != null) { + AttributeInstance reach = player.getAttribute(ForgeMod.REACH_DISTANCE.get()); + if (reach != null && reach.hasModifier(ExtendoGripItem.singleRangeAttributeModifier)) + range += 4; + } + + int row = attachedSteps(world, pos, dir); + if (row >= range) + return PlacementOffset.fail(); + + BlockPos newPos = pos.relative(dir, row + 1); + BlockState newState = world.getBlockState(newPos); + + if (!state.canSurvive(world, newPos)) + return PlacementOffset.fail(); + + if (newState.getMaterial() + .isReplaceable()) + return PlacementOffset.success(newPos, bState -> bState.setValue(FACING, facing)); + return PlacementOffset.fail(); + } + + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlockItem.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlockItem.java new file mode 100644 index 000000000..36d101a3d --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/RollerBlockItem.java @@ -0,0 +1,29 @@ +package com.simibubi.create.content.contraptions.components.actors; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +public class RollerBlockItem extends BlockItem { + + public RollerBlockItem(Block pBlock, Properties pProperties) { + super(pBlock, pProperties); + } + + @Override + public InteractionResult place(BlockPlaceContext ctx) { + BlockPos clickedPos = ctx.getClickedPos(); + Level level = ctx.getLevel(); + BlockState blockStateBelow = level.getBlockState(clickedPos.below()); + if (!Block.isFaceFull(blockStateBelow.getCollisionShape(level, clickedPos.below()), Direction.UP)) + return super.place(ctx); + Direction clickedFace = ctx.getClickedFace(); + return super.place(BlockPlaceContext.at(ctx, clickedPos.relative(Direction.UP), clickedFace)); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankBlockEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankBlockEntity.java index b1ce4981d..5d75891f0 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankBlockEntity.java @@ -76,7 +76,8 @@ public class HandCrankBlockEntity extends GeneratingKineticBlockEntity { @Override protected Block getStressConfigKey() { - return AllBlocks.HAND_CRANK.get(); + return AllBlocks.HAND_CRANK.has(getBlockState()) ? AllBlocks.HAND_CRANK.get() + : AllBlocks.COPPER_VALVE_HANDLE.get(); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawBlock.java index a1ac7f78b..ef7cddab1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawBlock.java @@ -12,9 +12,13 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; @@ -24,6 +28,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; @@ -40,7 +45,8 @@ public class SawBlock extends DirectionalAxisKineticBlock implements IBE { + for (int i = 0; i < be.inventory.getSlots(); i++) { + ItemStack heldItemStack = be.inventory.getStackInSlot(i); + if (!worldIn.isClientSide && !heldItemStack.isEmpty()) + player.getInventory() + .placeItemBackInInventory(heldItemStack); + } + be.inventory.clear(); + be.notifyUpdate(); + return InteractionResult.SUCCESS; + }); + } + @Override public void entityInside(BlockState state, Level worldIn, BlockPos pos, Entity entityIn) { if (entityIn instanceof ItemEntity) return; - if (!new AABB(pos).deflate(.1f).intersects(entityIn.getBoundingBox())) + if (!new AABB(pos).deflate(.1f) + .intersects(entityIn.getBoundingBox())) return; withBlockEntityDo(worldIn, pos, be -> { if (be.getSpeed() == 0) @@ -85,30 +114,33 @@ public class SawBlock extends DirectionalAxisKineticBlock implements IBE getBlockEntityClass() { return SawBlockEntity.class; } - + @Override public BlockEntityType getBlockEntityType() { return AllBlockEntityTypes.SAW.get(); } - + @Override public boolean isPathfindable(BlockState state, BlockGetter reader, BlockPos pos, PathComputationType type) { return false; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingBlockEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingBlockEntity.java index 7b50883ac..8a92027e3 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingBlockEntity.java @@ -242,7 +242,7 @@ public class MechanicalBearingBlockEntity extends GeneratingKineticBlockEntity } public boolean isNearInitialAngle() { - return Math.abs(angle) < 45 || Math.abs(angle) > 7 * 45; + return Math.abs(angle) < 22.5 || Math.abs(angle) > 360 - 22.5; } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/ItemDrainBlock.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/ItemDrainBlock.java index 11b5a90a8..5688b4f90 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/ItemDrainBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/ItemDrainBlock.java @@ -6,7 +6,9 @@ import com.simibubi.create.content.contraptions.processing.EmptyingByBasin; import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.foundation.advancement.AdvancementBehaviour; import com.simibubi.create.foundation.block.IBE; +import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour; import com.simibubi.create.foundation.blockEntity.ComparatorUtil; +import com.simibubi.create.foundation.blockEntity.behaviour.belt.DirectBeltInputBehaviour; import com.simibubi.create.foundation.fluid.FluidHelper; import net.minecraft.core.BlockPos; @@ -14,7 +16,9 @@ import net.minecraft.core.Direction; import net.minecraft.world.Containers; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; @@ -25,6 +29,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; @@ -56,7 +61,8 @@ public class ItemDrainBlock extends Block implements IWrenchable, IBE getBlockEntityClass() { return ItemDrainBlockEntity.class; } - + @Override public void setPlacedBy(Level pLevel, BlockPos pPos, BlockState pState, LivingEntity pPlacer, ItemStack pStack) { super.setPlacedBy(pLevel, pPos, pState, pPlacer, pStack); AdvancementBehaviour.setPlacedBy(pLevel, pPos, pPlacer); } - + @Override public BlockEntityType getBlockEntityType() { return AllBlockEntityTypes.ITEM_DRAIN.get(); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java index 74be032b8..8e852252f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java @@ -6,17 +6,19 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import com.jozufozu.flywheel.util.Color; +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.CreateClient; import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer; import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.ITrackBlock; -import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.block.ProperWaterloggedBlock; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.utility.AngleHelper; +import com.simibubi.create.foundation.utility.Color; +import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Lang; @@ -27,6 +29,7 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -35,6 +38,7 @@ import net.minecraft.core.Direction.AxisDirection; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Inventory; @@ -42,6 +46,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; @@ -53,6 +58,9 @@ import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.gui.ForgeIngameGui; +import net.minecraftforge.client.gui.IIngameOverlay; +import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import net.minecraftforge.items.ItemHandlerHelper; public class TrackPlacement { @@ -98,6 +106,8 @@ public class TrackPlacement { static int hoveringAngle; static ItemStack lastItem; + static int extraTipWarmup; + public static PlacementInfo tryConnect(Level level, Player player, BlockPos pos2, BlockState state2, ItemStack stack, boolean girder, boolean maximiseTurn) { Vec3 lookVec = player.getLookAngle(); @@ -283,10 +293,10 @@ public class TrackPlacement { if (skipCurve && !Mth.equal(ascend, 0)) { int hDistance = info.end1Extent; if (axis1.y == 0 || !Mth.equal(absAscend + 1, dist / axis1.length())) { - + if (axis1.y != 0 && axis1.y == -axis2.y) return info.withMessage("ascending_s_curve"); - + info.end1Extent = 0; double minHDistance = Math.max(absAscend < 4 ? absAscend * 4 : absAscend * 3, 6) / axis1.length(); if (hDistance < minHDistance) @@ -447,10 +457,6 @@ public class TrackPlacement { BlockItem paveItem = (BlockItem) offhandItem.getItem(); paveTracks(level, info, paveItem, false); } - - if (info.curve != null && info.curve.getLength() > 29) - AllAdvancements.LONG_BEND.awardTo(player); - return placeTracks(level, info, state1, state2, targetPos1, targetPos2, false); } @@ -576,6 +582,8 @@ public class TrackPlacement { LocalPlayer player = Minecraft.getInstance().player; ItemStack stack = player.getMainHandItem(); HitResult hitResult = Minecraft.getInstance().hitResult; + int restoreWarmup = extraTipWarmup; + extraTipWarmup = 0; if (hitResult == null) return; @@ -609,8 +617,14 @@ public class TrackPlacement { if (!(hitState.getBlock() instanceof TrackBlock)) return; + extraTipWarmup = restoreWarmup; boolean maxTurns = Minecraft.getInstance().options.keySprint.isDown(); PlacementInfo info = tryConnect(level, player, pos, hitState, stack, false, maxTurns); + if (extraTipWarmup < 20) + extraTipWarmup++; + if (!info.valid || !hoveringMaxed && (info.end1Extent == 0 || info.end2Extent == 0)) + extraTipWarmup = 0; + if (!player.isCreative() && (info.valid || !info.hasRequiredTracks || !info.hasRequiredPavement)) BlueprintOverlayRenderer.displayTrackRequirements(info, player.getOffhandItem()); @@ -625,7 +639,7 @@ public class TrackPlacement { if (bhr.getDirection() == Direction.UP) { Vec3 lookVec = player.getLookAngle(); int lookAngle = (int) (22.5 + AngleHelper.deg(Mth.atan2(lookVec.z, lookVec.x)) % 360) / 8; - + if (!pos.equals(hintPos) || lookAngle != hintAngle) { hints = Couple.create(ArrayList::new); hintAngle = lookAngle; @@ -761,4 +775,38 @@ public class TrackPlacement { .colored(color); } + @OnlyIn(Dist.CLIENT) + public static final IIngameOverlay OVERLAY = TrackPlacement::renderOverlay; + + @OnlyIn(Dist.CLIENT) + public static void renderOverlay(ForgeIngameGui gui, PoseStack poseStack, float partialTicks, int width, + int height) { + Minecraft mc = Minecraft.getInstance(); + if (mc.options.hideGui || mc.gameMode.getPlayerMode() == GameType.SPECTATOR) + return; + if (hoveringPos == null) + return; + if (cached == null || cached.curve == null || !cached.valid) + return; + if (extraTipWarmup < 4) + return; + + if (ObfuscationReflectionHelper.getPrivateValue(Gui.class, gui, + "toolHighlightTimer") instanceof Integer toolHighlightTimer && toolHighlightTimer > 0) + return; + + boolean active = mc.options.keySprint.isDown(); + MutableComponent text = Lang.translateDirect("track.hold_for_smooth_curve", Components.keybind("key.sprint") + .withStyle(active ? ChatFormatting.WHITE : ChatFormatting.GRAY)); + + Window window = mc.getWindow(); + int x = (window.getGuiScaledWidth() - gui.getFont() + .width(text)) / 2; + int y = window.getGuiScaledHeight() - 61; + Color color = new Color(0x4ADB4A).setAlpha(Mth.clamp((extraTipWarmup - 4) / 3f, 0.1f, 1)); + gui.getFont() + .draw(poseStack, text, x, y, color.getRGB()); + + } + } diff --git a/src/main/java/com/simibubi/create/foundation/advancement/AllAdvancements.java b/src/main/java/com/simibubi/create/foundation/advancement/AllAdvancements.java index 2ed37ea6e..d667b0f42 100644 --- a/src/main/java/com/simibubi/create/foundation/advancement/AllAdvancements.java +++ b/src/main/java/com/simibubi/create/foundation/advancement/AllAdvancements.java @@ -565,16 +565,10 @@ public class AllAdvancements implements DataProvider { .after(STURDY_SHEET) .special(EXPERT)), - LONG_BEND = create("long_bend", b -> b.icon(AllBlocks.TRACK) - .title("The Longest Bend") - .description("Create a curved track section that spans more than 30 blocks in length") - .after(TRACK_CRAFTING) - .special(EXPERT)), - LONG_TRAIN = create("long_train", b -> b.icon(Items.MINECART) .title("Ambitious Endeavours") .description("Create a Train with at least 6 carriages") - .after(LONG_BEND) + .after(TRACK_CRAFTING) .special(EXPERT)), LONG_TRAVEL = create("long_travel", b -> b.icon(AllBlocks.SEATS.get(DyeColor.GREEN)) diff --git a/src/main/java/com/simibubi/create/foundation/data/BuilderTransformers.java b/src/main/java/com/simibubi/create/foundation/data/BuilderTransformers.java index a8e289bc3..57023b69e 100644 --- a/src/main/java/com/simibubi/create/foundation/data/BuilderTransformers.java +++ b/src/main/java/com/simibubi/create/foundation/data/BuilderTransformers.java @@ -250,7 +250,6 @@ public class BuilderTransformers { .texture("3", p.modLoc("block/valve_handle/valve_handle_" + variant))); }) .tag(AllBlockTags.BRITTLE.tag, AllBlockTags.VALVE_HANDLES.tag) - .transform(BlockStressDefaults.setCapacity(8.0)) .transform(BlockStressDefaults.setGeneratorSpeed(ValveHandleBlock::getSpeedRange)) .onRegister(ItemUseOverrides::addBlock) .item() diff --git a/src/main/java/com/simibubi/create/foundation/item/KineticStats.java b/src/main/java/com/simibubi/create/foundation/item/KineticStats.java index a0a3bd2b2..347e49c58 100644 --- a/src/main/java/com/simibubi/create/foundation/item/KineticStats.java +++ b/src/main/java/com/simibubi/create/foundation/item/KineticStats.java @@ -8,8 +8,10 @@ import java.util.List; import org.jetbrains.annotations.Nullable; +import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.IRotate.StressImpact; +import com.simibubi.create.content.contraptions.components.crank.ValveHandleBlock; import com.simibubi.create.content.contraptions.components.steam.SteamEngineBlock; import com.simibubi.create.content.contraptions.goggles.GogglesItem; import com.simibubi.create.foundation.block.BlockStressValues; @@ -71,6 +73,9 @@ public class KineticStats implements TooltipModifier { showStressImpact = true; } + if (block instanceof ValveHandleBlock) + block = AllBlocks.COPPER_VALVE_HANDLE.get(); + boolean hasStressImpact = StressImpact.isEnabled() && showStressImpact && BlockStressValues.getImpact(block) > 0; boolean hasStressCapacity = StressImpact.isEnabled() && BlockStressValues.hasCapacity(block); diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index 472453f5f..606c01225 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -734,6 +734,7 @@ "create.track.turn_start": "Cannot start connection from a Turn", "create.track.not_enough_tracks": "Not holding enough tracks", "create.track.not_enough_pavement": "Not holding enough pavement blocks", + "create.track.hold_for_smooth_curve": "Hold %1$s for maximized turn", "create.portal_track.failed": "Cannot place portal track:", "create.portal_track.missing": "Target portal not generated yet",