diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java index f99bc32ac..f31ff8ab1 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java @@ -26,9 +26,11 @@ public class TurtleScreen extends AbstractComputerScreen { private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png"); private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png"); - private static final int TEX_WIDTH = 254; + private static final int TEX_WIDTH = 278; private static final int TEX_HEIGHT = 217; + private static final int FULL_TEX_SIZE = 512; + public TurtleScreen(TurtleMenu container, Inventory player, Component title) { super(container, player, title, BORDER); @@ -45,16 +47,16 @@ public class TurtleScreen extends AbstractComputerScreen { protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) { var advanced = family == ComputerFamily.ADVANCED; RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL); - blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT); + blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE); + // Render selected slot var slot = getMenu().getSelectedSlot(); if (slot >= 0) { - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); var slotX = slot % 4; var slotY = slot / 4; blit(transform, - leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, - 0, 217, 24, 24 + leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0, + 0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE ); } diff --git a/projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java b/projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java new file mode 100644 index 000000000..61d4dd6cf --- /dev/null +++ b/projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.data.client; + +import dan200.computercraft.data.DataProviders; +import dan200.computercraft.shared.turtle.inventory.UpgradeSlot; +import net.minecraft.client.renderer.texture.atlas.SpriteSources; +import net.minecraft.client.renderer.texture.atlas.sources.SingleFile; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; + +import java.util.List; +import java.util.Optional; + +/** + * A version of {@link DataProviders} which relies on client-side classes. + *

+ * This is called from {@link DataProviders#add(DataProviders.GeneratorSink)}. + */ +public final class ClientDataProviders { + private ClientDataProviders() { + } + + public static void add(DataProviders.GeneratorSink generator) { + generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> { + out.accept(new ResourceLocation("blocks"), List.of( + new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()), + new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()) + )); + }); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java index a4abd7c8f..c5f8aa661 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java +++ b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java @@ -41,6 +41,15 @@ public final class DataProviders { generator.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels); generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades)); + + // Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider + // and invoke that. + try { + Class.forName("dan200.computercraft.data.client.ClientDataProviders") + .getMethod("add", GeneratorSink.class).invoke(null, generator); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } public interface GeneratorSink { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java index 77116234b..61d2e67d8 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java @@ -4,6 +4,7 @@ package dan200.computercraft.shared.turtle.inventory; +import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; @@ -29,12 +30,13 @@ public final class TurtleMenu extends AbstractComputerMenu { public static final int PLAYER_START_Y = 134; public static final int TURTLE_START_X = SIDEBAR_WIDTH + 175; public static final int PLAYER_START_X = SIDEBAR_WIDTH + BORDER; + public static final int UPGRADE_START_X = SIDEBAR_WIDTH + 254; private final ContainerData data; private TurtleMenu( int id, Predicate canUse, ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData menuData, - Inventory playerInventory, Container inventory, ContainerData data + Inventory playerInventory, Container inventory, Container turtleUpgrades, ContainerData data ) { super(ModRegistry.Menus.TURTLE.get(), id, canUse, family, computer, menuData); this.data = data; @@ -58,19 +60,24 @@ public final class TurtleMenu extends AbstractComputerMenu { for (var x = 0; x < 9; x++) { addSlot(new Slot(playerInventory, x, PLAYER_START_X + x * 18, PLAYER_START_Y + 3 * 18 + 5)); } + + // Turtle upgrades + addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.LEFT, 0, UPGRADE_START_X, PLAYER_START_Y + 1)); + addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.RIGHT, 1, UPGRADE_START_X, PLAYER_START_Y + 1 + 18)); } public static TurtleMenu ofBrain(int id, Inventory player, TurtleBrain turtle) { return new TurtleMenu( // Laziness in turtle.getOwner() is important here! id, p -> turtle.getOwner().stillValid(p), turtle.getFamily(), turtle.getOwner().createServerComputer(), null, - player, turtle.getInventory(), (SingleContainerData) turtle::getSelectedSlot + player, turtle.getInventory(), new UpgradeContainer(turtle), (SingleContainerData) turtle::getSelectedSlot ); } public static TurtleMenu ofMenuData(int id, Inventory player, ComputerContainerData data) { return new TurtleMenu( - id, x -> true, data.family(), null, data, player, new SimpleContainer(TurtleBlockEntity.INVENTORY_SIZE), new SimpleContainerData(1) + id, x -> true, data.family(), null, data, + player, new SimpleContainer(TurtleBlockEntity.INVENTORY_SIZE), new SimpleContainer(2), new SimpleContainerData(1) ); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java new file mode 100644 index 000000000..22d918626 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.turtle.inventory; + +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.impl.TurtleUpgrades; +import net.minecraft.core.NonNullList; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +import java.util.Arrays; +import java.util.List; + +/** + * A fake {@link Container} which exposes the {@linkplain ITurtleAccess#getUpgrade(TurtleSide) upgrades} a turtle has. + * + * @see TurtleMenu + * @see UpgradeSlot + */ +class UpgradeContainer implements Container { + private static final int SIZE = 2; + + private final ITurtleAccess turtle; + + private final List lastUpgrade = Arrays.asList(null, null); + private final NonNullList lastStack = NonNullList.withSize(2, ItemStack.EMPTY); + + UpgradeContainer(ITurtleAccess turtle) { + this.turtle = turtle; + } + + private TurtleSide getSide(int slot) { + return switch (slot) { + case 0 -> TurtleSide.LEFT; + case 1 -> TurtleSide.RIGHT; + default -> throw new IllegalArgumentException("Invalid slot " + slot); + }; + } + + @Override + public ItemStack getItem(int slot) { + var upgrade = turtle.getUpgrade(getSide(slot)); + + // We don't want to return getCraftingItem directly here, as consumers may mutate the stack (they shouldn't!, + // but if they do it's a pain to track down). To avoid recreating the stack each tick, we maintain a simple + // cache. + if (upgrade == lastUpgrade.get(slot)) return lastStack.get(slot); + + var stack = upgrade == null ? ItemStack.EMPTY : upgrade.getCraftingItem().copy(); + lastUpgrade.set(slot, upgrade); + lastStack.set(slot, stack); + return stack; + } + + @Override + public void setItem(int slot, ItemStack itemStack) { + turtle.setUpgrade(getSide(slot), TurtleUpgrades.instance().get(itemStack)); + } + + @Override + public int getContainerSize() { + return SIZE; + } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Override + public boolean isEmpty() { + for (var i = 0; i < SIZE; i++) { + if (!getItem(i).isEmpty()) return false; + } + return true; + } + + @Override + public ItemStack removeItem(int slot, int count) { + return count <= 0 ? ItemStack.EMPTY : removeItemNoUpdate(slot); + } + + @Override + public ItemStack removeItemNoUpdate(int slot) { + var current = getItem(slot); + setItem(slot, ItemStack.EMPTY); + return current; + } + + @Override + public void setChanged() { + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void clearContent() { + for (var i = 0; i < SIZE; i++) setItem(i, ItemStack.EMPTY); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java new file mode 100644 index 000000000..7947785e1 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.turtle.inventory; + +import com.mojang.datafixers.util.Pair; +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.impl.TurtleUpgrades; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.Nullable; + +/** + * A slot in the turtle UI which holds the turtle's current upgrade. + * + * @see TurtleMenu + */ +public class UpgradeSlot extends Slot { + public static final ResourceLocation LEFT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_left"); + public static final ResourceLocation RIGHT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_right"); + + private final TurtleSide side; + + public UpgradeSlot(Container container, TurtleSide side, int slot, int xPos, int yPos) { + super(container, slot, xPos, yPos); + this.side = side; + } + + @Override + public boolean mayPlace(ItemStack stack) { + return TurtleUpgrades.instance().get(stack) != null; + } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Nullable + @Override + public Pair getNoItemIcon() { + return Pair.of(InventoryMenu.BLOCK_ATLAS, side == TurtleSide.LEFT ? LEFT_UPGRADE : RIGHT_UPGRADE); + } +} diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_advanced.png b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_advanced.png index 74a149a47..8f7d66500 100644 Binary files a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_advanced.png and b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_advanced.png differ diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_normal.png b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_normal.png index 83b6a882e..f676c56b2 100644 Binary files a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_normal.png and b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_normal.png differ diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png new file mode 100644 index 000000000..5c76e88be Binary files /dev/null and b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png differ diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png.license b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png.license new file mode 100644 index 000000000..05aed57f6 --- /dev/null +++ b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers + +SPDX-License-Identifier: MPL-2.0 diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png new file mode 100644 index 000000000..ab877ab72 Binary files /dev/null and b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png differ diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license new file mode 100644 index 000000000..05aed57f6 --- /dev/null +++ b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers + +SPDX-License-Identifier: MPL-2.0 diff --git a/projects/fabric/build.gradle.kts b/projects/fabric/build.gradle.kts index f2aef9cb8..13b8e7065 100644 --- a/projects/fabric/build.gradle.kts +++ b/projects/fabric/build.gradle.kts @@ -119,7 +119,7 @@ loom { register("data") { configName = "Datagen" - server() + client() runDir("run/dataGen") property("cct.pretty-json") diff --git a/projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json b/projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json new file mode 100644 index 000000000..d9d236296 --- /dev/null +++ b/projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json @@ -0,0 +1,6 @@ +{ + "sources": [ + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"}, + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"} + ] +} diff --git a/projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json b/projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json new file mode 100644 index 000000000..d9d236296 --- /dev/null +++ b/projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json @@ -0,0 +1,6 @@ +{ + "sources": [ + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"}, + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"} + ] +}