mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 13:42:59 +00:00 
			
		
		
		
	Render enchanted upgrades with a glint (#1532)
This commit is contained in:
		| @@ -27,10 +27,9 @@ import java.util.List; | ||||
|  * This is typically used with a {@link BakedModel} subclass - see the loader-specific projects. | ||||
|  */ | ||||
| public class ModelTransformer { | ||||
|     @SuppressWarnings("MutablePublicArray") // It's not nice, but is efficient. | ||||
|     public static final int[] INVERSE_ORDER = new int[]{ 3, 2, 1, 0 }; | ||||
|     private static final int[] INVERSE_ORDER = new int[]{ 3, 2, 1, 0 }; | ||||
| 
 | ||||
|     public static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize(); | ||||
|     private static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize(); | ||||
|     private static final int POS_OFFSET = findOffset(DefaultVertexFormat.BLOCK, DefaultVertexFormat.ELEMENT_POSITION); | ||||
| 
 | ||||
|     protected final Matrix4f transformation; | ||||
| @@ -62,7 +61,7 @@ public class ModelTransformer { | ||||
|         for (var i = 0; i < 4; i++) { | ||||
|             var inStart = STRIDE * i; | ||||
|             // Reverse the order of the quads if we're inverting | ||||
|             var outStart = STRIDE * (invert ? INVERSE_ORDER[i] : i); | ||||
|             var outStart = getVertexOffset(i, invert); | ||||
|             System.arraycopy(inputData, inStart, outputData, outStart, STRIDE); | ||||
| 
 | ||||
|             // Apply the matrix to our position | ||||
| @@ -86,6 +85,10 @@ public class ModelTransformer { | ||||
|         return new BakedQuad(outputData, quad.getTintIndex(), direction, quad.getSprite(), quad.isShade()); | ||||
|     } | ||||
| 
 | ||||
|     public static int getVertexOffset(int vertex, boolean invert) { | ||||
|         return (invert ? ModelTransformer.INVERSE_ORDER[vertex] : vertex) * ModelTransformer.STRIDE; | ||||
|     } | ||||
| 
 | ||||
|     private record TransformedQuads(List<BakedQuad> original, List<BakedQuad> transformed) { | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -4,8 +4,13 @@ | ||||
| 
 | ||||
| package dan200.computercraft.client.platform; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import dan200.computercraft.shared.network.NetworkMessage; | ||||
| import dan200.computercraft.shared.network.server.ServerNetworkContext; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.resources.model.BakedModel; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| 
 | ||||
| public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper { | ||||
|     static ClientPlatformHelper get() { | ||||
| @@ -18,4 +23,16 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C | ||||
|      * @param message The message to send. | ||||
|      */ | ||||
|     void sendToServer(NetworkMessage<ServerNetworkContext> message); | ||||
| 
 | ||||
|     /** | ||||
|      * Render a {@link BakedModel}, using any loader-specific hooks. | ||||
|      * | ||||
|      * @param transform     The current matrix transformation to apply. | ||||
|      * @param buffers       The current pool of render buffers. | ||||
|      * @param model         The model to draw. | ||||
|      * @param lightmapCoord The current packed lightmap coordinate. | ||||
|      * @param overlayLight  The current overlay light. | ||||
|      * @param tints         Block colour tints to apply to the model. | ||||
|      */ | ||||
|     void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints); | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,102 @@ | ||||
| // SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers | ||||
| // | ||||
| // SPDX-License-Identifier: MPL-2.0 | ||||
| 
 | ||||
| package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import dan200.computercraft.client.model.turtle.ModelTransformer; | ||||
| import dan200.computercraft.client.platform.ClientPlatformHelper; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.renderer.block.model.BakedQuad; | ||||
| import net.minecraft.client.renderer.entity.ItemRenderer; | ||||
| import net.minecraft.client.resources.model.BakedModel; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import org.joml.Vector4f; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * Utilities for rendering {@link BakedModel}s and {@link BakedQuad}s. | ||||
|  */ | ||||
| public final class ModelRenderer { | ||||
|     private ModelRenderer() { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Render a list of {@linkplain BakedQuad quads} to a buffer. | ||||
|      * <p> | ||||
|      * This is not intended to be used directly, but instead by {@link ClientPlatformHelper#renderBakedModel(PoseStack, MultiBufferSource, BakedModel, int, int, int[])}. The | ||||
|      * implementation here is pretty similar to {@link ItemRenderer#renderQuadList(PoseStack, VertexConsumer, List, ItemStack, int, int)}, | ||||
|      * but supports inverted quads (i.e. those with a negative scale). | ||||
|      * | ||||
|      * @param transform     The current matrix transformation to apply. | ||||
|      * @param buffer        The buffer to draw to. | ||||
|      * @param quads         The quads to draw. | ||||
|      * @param lightmapCoord The current packed lightmap coordinate. | ||||
|      * @param overlayLight  The current overlay light. | ||||
|      * @param tints         Block colour tints to apply to the model. | ||||
|      */ | ||||
|     public static void renderQuads(PoseStack transform, VertexConsumer buffer, List<BakedQuad> quads, int lightmapCoord, int overlayLight, @Nullable int[] tints) { | ||||
|         var matrix = transform.last(); | ||||
|         var inverted = matrix.pose().determinant() < 0; | ||||
| 
 | ||||
|         for (var bakedquad : quads) { | ||||
|             var tint = -1; | ||||
|             if (tints != null && bakedquad.isTinted()) { | ||||
|                 var idx = bakedquad.getTintIndex(); | ||||
|                 if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()]; | ||||
|             } | ||||
| 
 | ||||
|             var r = (float) (tint >> 16 & 255) / 255.0F; | ||||
|             var g = (float) (tint >> 8 & 255) / 255.0F; | ||||
|             var b = (float) (tint & 255) / 255.0F; | ||||
|             putBulkQuad(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight, inverted); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} which | ||||
|      * will reverse vertex order when the matrix is inverted. | ||||
|      * | ||||
|      * @param buffer        The buffer to draw to. | ||||
|      * @param pose          The current matrix stack. | ||||
|      * @param quad          The quad to draw. | ||||
|      * @param red           The red tint of this quad. | ||||
|      * @param green         The  green tint of this quad. | ||||
|      * @param blue          The blue tint of this quad. | ||||
|      * @param lightmapCoord The lightmap coordinate | ||||
|      * @param overlayLight  The overlay light. | ||||
|      */ | ||||
|     private static void putBulkQuad(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight, boolean invert) { | ||||
|         var matrix = pose.pose(); | ||||
|         // It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in | ||||
|         // Direction.rotate (so not out of nowhere!), but is a little suspicious. | ||||
|         var dirNormal = quad.getDirection().getNormal(); | ||||
|         var vector = new Vector4f(); | ||||
| 
 | ||||
|         matrix.transform(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f, vector).normalize(); | ||||
|         float normalX = vector.x(), normalY = vector.y(), normalZ = vector.z(); | ||||
| 
 | ||||
|         var vertices = quad.getVertices(); | ||||
|         for (var vertex = 0; vertex < 4; vertex++) { | ||||
|             var i = ModelTransformer.getVertexOffset(vertex, invert); | ||||
| 
 | ||||
|             var x = Float.intBitsToFloat(vertices[i]); | ||||
|             var y = Float.intBitsToFloat(vertices[i + 1]); | ||||
|             var z = Float.intBitsToFloat(vertices[i + 2]); | ||||
| 
 | ||||
|             matrix.transform(x, y, z, 1, vector); | ||||
| 
 | ||||
|             var u = Float.intBitsToFloat(vertices[i + 4]); | ||||
|             var v = Float.intBitsToFloat(vertices[i + 5]); | ||||
|             buffer.vertex( | ||||
|                 vector.x(), vector.y(), vector.z(), | ||||
|                 red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord, | ||||
|                 normalX, normalY, normalZ | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -5,36 +5,28 @@ | ||||
| package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.PoseStack; | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import com.mojang.math.Axis; | ||||
| import com.mojang.math.Transformation; | ||||
| import dan200.computercraft.api.ComputerCraftAPI; | ||||
| import dan200.computercraft.api.turtle.TurtleSide; | ||||
| import dan200.computercraft.client.model.turtle.ModelTransformer; | ||||
| import dan200.computercraft.client.platform.ClientPlatformHelper; | ||||
| import dan200.computercraft.client.turtle.TurtleUpgradeModellers; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; | ||||
| import dan200.computercraft.shared.util.DirectionUtil; | ||||
| import dan200.computercraft.shared.util.Holiday; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.gui.Font; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.renderer.Sheets; | ||||
| import net.minecraft.client.renderer.block.model.BakedQuad; | ||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; | ||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; | ||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; | ||||
| import net.minecraft.client.resources.model.BakedModel; | ||||
| import net.minecraft.client.resources.model.ModelResourceLocation; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.util.RandomSource; | ||||
| import net.minecraft.world.phys.BlockHitResult; | ||||
| import net.minecraft.world.phys.HitResult; | ||||
| import org.joml.Vector4f; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> { | ||||
|     private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory"); | ||||
| @@ -42,8 +34,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc | ||||
|     private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour"); | ||||
|     private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay"); | ||||
| 
 | ||||
|     private final RandomSource random = RandomSource.create(0); | ||||
| 
 | ||||
|     private final BlockEntityRenderDispatcher renderer; | ||||
|     private final Font font; | ||||
| 
 | ||||
| @@ -109,23 +99,22 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc | ||||
|         var family = turtle.getFamily(); | ||||
|         var overlay = turtle.getOverlay(); | ||||
| 
 | ||||
|         var buffer = buffers.getBuffer(Sheets.translucentCullBlockSheet()); | ||||
|         renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour }); | ||||
|         renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour }); | ||||
| 
 | ||||
|         // Render the overlay | ||||
|         var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS); | ||||
|         if (overlayModel != null) { | ||||
|             renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null); | ||||
|             renderModel(transform, buffers, lightmapCoord, overlayLight, overlayModel, null); | ||||
|         } | ||||
| 
 | ||||
|         // Render the upgrades | ||||
|         renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks); | ||||
|         renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks); | ||||
|         renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks); | ||||
|         renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks); | ||||
| 
 | ||||
|         transform.popPose(); | ||||
|     } | ||||
| 
 | ||||
|     private void renderUpgrade(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) { | ||||
|     private void renderUpgrade(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) { | ||||
|         var upgrade = turtle.getUpgrade(side); | ||||
|         if (upgrade == null) return; | ||||
|         transform.pushPose(); | ||||
| @@ -136,16 +125,15 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc | ||||
|         transform.translate(0.0f, -0.5f, -0.5f); | ||||
| 
 | ||||
|         var model = TurtleUpgradeModellers.getModel(upgrade, turtle.getAccess(), side); | ||||
|         pushPoseFromTransformation(transform, model.getMatrix()); | ||||
|         renderModel(transform, renderer, lightmapCoord, overlayLight, model.getModel(), null); | ||||
|         transform.popPose(); | ||||
|         applyTransformation(transform, model.getMatrix()); | ||||
|         renderModel(transform, buffers, lightmapCoord, overlayLight, model.getModel(), null); | ||||
| 
 | ||||
|         transform.popPose(); | ||||
|     } | ||||
| 
 | ||||
|     private void renderModel(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, @Nullable int[] tints) { | ||||
|     private void renderModel(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, @Nullable int[] tints) { | ||||
|         var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getModelManager(); | ||||
|         renderModel(transform, renderer, lightmapCoord, overlayLight, ClientPlatformHelper.get().getModel(modelManager, modelLocation), tints); | ||||
|         renderModel(transform, buffers, lightmapCoord, overlayLight, ClientPlatformHelper.get().getModel(modelManager, modelLocation), tints); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -159,80 +147,11 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc | ||||
|      * @param tints         Tints for the quads, as an array of RGB values. | ||||
|      * @see net.minecraft.client.renderer.block.ModelBlockRenderer#renderModel | ||||
|      */ | ||||
|     private void renderModel(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, @Nullable int[] tints) { | ||||
|         for (var facing : DirectionUtil.FACINGS) { | ||||
|             random.setSeed(42); | ||||
|             renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, random), tints); | ||||
|         } | ||||
| 
 | ||||
|         random.setSeed(42); | ||||
|         renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, random), tints); | ||||
|     private void renderModel(PoseStack transform, MultiBufferSource renderer, int lightmapCoord, int overlayLight, BakedModel model, @Nullable int[] tints) { | ||||
|         ClientPlatformHelper.get().renderBakedModel(transform, renderer, model, lightmapCoord, overlayLight, tints); | ||||
|     } | ||||
| 
 | ||||
|     private static void renderQuads(PoseStack transform, VertexConsumer buffer, int lightmapCoord, int overlayLight, List<BakedQuad> quads, @Nullable int[] tints) { | ||||
|         var matrix = transform.last(); | ||||
|         var inverted = matrix.pose().determinant() < 0; | ||||
| 
 | ||||
|         for (var bakedquad : quads) { | ||||
|             var tint = -1; | ||||
|             if (tints != null && bakedquad.isTinted()) { | ||||
|                 var idx = bakedquad.getTintIndex(); | ||||
|                 if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()]; | ||||
|             } | ||||
| 
 | ||||
|             var r = (float) (tint >> 16 & 255) / 255.0F; | ||||
|             var g = (float) (tint >> 8 & 255) / 255.0F; | ||||
|             var b = (float) (tint & 255) / 255.0F; | ||||
|             if (inverted) { | ||||
|                 putBulkQuadInvert(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); | ||||
|             } else { | ||||
|                 buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} for | ||||
|      * when the matrix is inverted. | ||||
|      * | ||||
|      * @param buffer        The buffer to draw to. | ||||
|      * @param pose          The current matrix stack. | ||||
|      * @param quad          The quad to draw. | ||||
|      * @param red           The red tint of this quad. | ||||
|      * @param green         The  green tint of this quad. | ||||
|      * @param blue          The blue tint of this quad. | ||||
|      * @param lightmapCoord The lightmap coordinate | ||||
|      * @param overlayLight  The overlay light. | ||||
|      */ | ||||
|     private static void putBulkQuadInvert(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight) { | ||||
|         var matrix = pose.pose(); | ||||
|         // It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in | ||||
|         // Direction.rotate (so not out of nowhere!), but is a little suspicious. | ||||
|         var dirNormal = quad.getDirection().getNormal(); | ||||
|         var normal = matrix.transform(new Vector4f(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f)).normalize(); | ||||
| 
 | ||||
|         var vertices = quad.getVertices(); | ||||
|         for (var vertex : ModelTransformer.INVERSE_ORDER) { | ||||
|             var i = vertex * ModelTransformer.STRIDE; | ||||
| 
 | ||||
|             var x = Float.intBitsToFloat(vertices[i]); | ||||
|             var y = Float.intBitsToFloat(vertices[i + 1]); | ||||
|             var z = Float.intBitsToFloat(vertices[i + 2]); | ||||
|             var transformed = matrix.transform(new Vector4f(x, y, z, 1)); | ||||
| 
 | ||||
|             var u = Float.intBitsToFloat(vertices[i + 4]); | ||||
|             var v = Float.intBitsToFloat(vertices[i + 5]); | ||||
|             buffer.vertex( | ||||
|                 transformed.x(), transformed.y(), transformed.z(), | ||||
|                 red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord, | ||||
|                 normal.x(), normal.y(), normal.z() | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void pushPoseFromTransformation(PoseStack stack, Transformation transformation) { | ||||
|         stack.pushPose(); | ||||
| 
 | ||||
|     private static void applyTransformation(PoseStack stack, Transformation transformation) { | ||||
|         var trans = transformation.getTranslation(); | ||||
|         stack.translate(trans.x(), trans.y(), trans.z()); | ||||
| 
 | ||||
|   | ||||
| @@ -100,12 +100,12 @@ public class TurtleTool extends AbstractTurtleUpgrade { | ||||
|     public ItemStack getUpgradeItem(CompoundTag upgradeData) { | ||||
|         // Copy upgrade data back to the item. | ||||
|         var item = super.getUpgradeItem(upgradeData).copy(); | ||||
|         item.setTag(upgradeData.contains(TAG_ITEM_TAG, TAG_COMPOUND) ? upgradeData.getCompound(TAG_ITEM_TAG).copy() : null); | ||||
|         item.setTag(upgradeData.contains(TAG_ITEM_TAG, TAG_COMPOUND) ? upgradeData.getCompound(TAG_ITEM_TAG) : null); | ||||
|         return item; | ||||
|     } | ||||
| 
 | ||||
|     private ItemStack getToolStack(ITurtleAccess turtle, TurtleSide side) { | ||||
|         return getUpgradeItem(turtle.getUpgradeNBTData(side)); | ||||
|         return getUpgradeItem(turtle.getUpgradeNBTData(side)).copy(); | ||||
|     } | ||||
| 
 | ||||
|     private void setToolStack(ITurtleAccess turtle, TurtleSide side, ItemStack stack) { | ||||
|   | ||||
| @@ -0,0 +1,56 @@ | ||||
| // SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers | ||||
| // | ||||
| // SPDX-License-Identifier: MPL-2.0 | ||||
| 
 | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import java.util.AbstractList; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * A list which prepends a single value to another list. | ||||
|  * | ||||
|  * @param <T> The type of item in the list. | ||||
|  */ | ||||
| public final class ConsList<T> extends AbstractList<T> { | ||||
|     private final T head; | ||||
|     private final List<T> tail; | ||||
| 
 | ||||
|     public ConsList(T head, List<T> tail) { | ||||
|         this.head = head; | ||||
|         this.tail = tail; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public T get(int index) { | ||||
|         return index == 0 ? head : tail.get(index - 1); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int size() { | ||||
|         return 1 + tail.size(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Iterator<T> iterator() { | ||||
|         return new Iterator<>() { | ||||
|             private @Nullable Iterator<T> tailIterator; | ||||
| 
 | ||||
|             @Override | ||||
|             public boolean hasNext() { | ||||
|                 return tailIterator == null || tailIterator.hasNext(); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public T next() { | ||||
|                 if (tailIterator != null) return tailIterator.next(); | ||||
| 
 | ||||
|                 tailIterator = tail.iterator(); | ||||
|                 return head; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| // SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers | ||||
| // | ||||
| // SPDX-License-Identifier: MPL-2.0 | ||||
| 
 | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertArrayEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| 
 | ||||
| /** | ||||
|  * A couple of trivial tests for {@link ConsList}, mostly as a quick safety check. | ||||
|  */ | ||||
| public class ConsListTest { | ||||
|     @Test | ||||
|     public void testGet() { | ||||
|         var list = new ConsList<>(1, List.of(2, 3, 4)); | ||||
|         assertEquals(1, list.get(0)); | ||||
|         assertEquals(2, list.get(1)); | ||||
|         assertEquals(4, list.get(3)); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testSize() { | ||||
|         var list = new ConsList<>(1, List.of(2, 3, 4)); | ||||
|         assertEquals(4, list.size()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testIterator() { | ||||
|         var list = new ConsList<>(1, List.of(2, 3, 4)); | ||||
|         assertArrayEquals(new Integer[]{ 1, 2, 3, 4 }, list.toArray(Integer[]::new)); | ||||
|     } | ||||
| } | ||||
| @@ -623,6 +623,24 @@ class Turtle_Test { | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Turtle sucking from items | ||||
| 
 | ||||
|     /** | ||||
|      * Render turtles as an item. | ||||
|      */ | ||||
|     @ClientGameTest | ||||
|     fun Render_turtle_items(helper: GameTestHelper) = helper.sequence { | ||||
|         thenExecute { helper.positionAtArmorStand() } | ||||
|         thenScreenshot() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Render turtles as a block entity. | ||||
|      */ | ||||
|     @ClientGameTest | ||||
|     fun Render_turtle_blocks(helper: GameTestHelper) = helper.sequence { | ||||
|         thenExecute { helper.positionAtArmorStand() } | ||||
|         thenScreenshot() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| private val LuaTaskContext.turtle get() = getApi<TurtleAPI>() | ||||
|   | ||||
							
								
								
									
										140
									
								
								projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_blocks.snbt
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_blocks.snbt
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| { | ||||
|     DataVersion: 3337, | ||||
|     size: [5, 5, 5], | ||||
|     data: [ | ||||
|         {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 3], state: "computercraft:turtle_normal{facing:east,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}, On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, Slot: 0, id: "computercraft:turtle_normal"}}, | ||||
|         {pos: [1, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 3], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], Label: "Dinnerbone", On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}, Slot: 0, id: "computercraft:turtle_normal"}}, | ||||
|         {pos: [3, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 3], state: "computercraft:turtle_normal{facing:east,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], Label: "Dinnerbone", LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}, On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, Slot: 0, id: "computercraft:turtle_normal"}}, | ||||
|         {pos: [1, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 3], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}, Slot: 0, id: "computercraft:turtle_normal"}}, | ||||
|         {pos: [3, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 4], state: "minecraft:air"} | ||||
|     ], | ||||
|     entities: [ | ||||
|         {blockPos: [2, 1, 0], pos: [2.52408694606693d, 1.0d, 0.6487863808268131d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"turtle_test.render_turtle_blocks"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [124.52408694606693d, -58.0d, 50.64878638082681d], Pose: {}, Rotation: [0.14965993f, 4.066999f], ShowArms: 0b, Small: 0b, UUID: [I; 755114464, 1289439453, -2088751844, 985194758], id: "minecraft:armor_stand"}} | ||||
|     ], | ||||
|     palette: [ | ||||
|         "minecraft:polished_andesite", | ||||
|         "minecraft:air", | ||||
|         "computercraft:turtle_normal{facing:east,waterlogged:false}", | ||||
|         "computercraft:turtle_normal{facing:west,waterlogged:false}" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										142
									
								
								projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_items.snbt
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_items.snbt
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| { | ||||
|     DataVersion: 3337, | ||||
|     size: [5, 5, 5], | ||||
|     data: [ | ||||
|         {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, | ||||
|         {pos: [0, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 1, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 2, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 4], state: "minecraft:air"} | ||||
|     ], | ||||
|     entities: [ | ||||
|         {blockPos: [2, 1, 0], pos: [2.5d, 1.0d, 0.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"turtle_test.render_turtle_items"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [125.5d, -58.0d, 53.6501934495752d], Pose: {}, Rotation: [0.14965993f, 4.066999f], ShowArms: 0b, Small: 0b, UUID: [I; -1678989666, 1780632657, -1267321893, 665166246], id: "minecraft:armor_stand"}}, | ||||
|         {blockPos: [3, 1, 3], pos: [3.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334450, -268547745, -1360971514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}, | ||||
|         {blockPos: [3, 3, 3], pos: [3.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334422, -268542345, -1362491514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}, | ||||
|         {blockPos: [1, 1, 3], pos: [1.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 625334450, -268647745, -1360971514, -649724242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}, | ||||
|         {blockPos: [1, 3, 3], pos: [1.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 675334422, -268542245, -1362491514, -649755242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}} | ||||
|     ], | ||||
|     palette: [ | ||||
|         "minecraft:polished_andesite", | ||||
|         "minecraft:air" | ||||
|     ] | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates