From fd731fb48015a46c2f60ab92244aa6ccd5b0ccd4 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Thu, 20 Nov 2025 05:55:18 +0100 Subject: [PATCH 01/23] Add initil quarry with working (seems like) mining, first pass on model --- dependencies.gradle | 4 +- .../utilitiesinexcess/ModBlocks.java | 2 + .../utilitiesinexcess/UtilitiesInExcess.java | 2 + .../common/blocks/BlockEnderQuarry.java | 335 ++++++ .../tileentities/TileEntityEnderQuarry.java | 254 +++++ .../config/blocks/BlockConfig.java | 3 + .../utilitiesinexcess/blocks/ender_quarry.png | Bin 0 -> 2028 bytes .../blockstates/ender_quarry.json | 8 + .../models/blocks/ender_quarry.json | 986 ++++++++++++++++++ .../textures/blocks/ender_quarry.png | Bin 0 -> 2028 bytes .../textures/blocks/models/ender_quarry.png | Bin 0 -> 2028 bytes 11 files changed, 1593 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java create mode 100644 src/main/resources/assets/utilitiesinexcess/blocks/ender_quarry.png create mode 100644 src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/ender_quarry.json create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/ender_quarry.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/models/ender_quarry.png diff --git a/dependencies.gradle b/dependencies.gradle index dadaf6e2..419fe657 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -34,7 +34,7 @@ * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph */ dependencies { - implementation("com.github.GTNewHorizons:GTNHLib:0.7.7:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.8.9:dev") // TODO: remove MUI1 dep when the implicit dependency // TODO: in IItemHandlerModifiable is removed api("com.github.GTNewHorizons:ModularUI:1.2.20:dev") @@ -46,4 +46,6 @@ dependencies { compileOnly('com.github.GTNewHorizons:Angelica:1.0.0-beta62:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.31-GTNH:dev" ) + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-beta71-pre:dev" ) + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.17:dev" ) } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java index ad5a3c95..381eddf2 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java @@ -1,5 +1,6 @@ package com.fouristhenumber.utilitiesinexcess; +import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderQuarry; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.item.Item; @@ -104,6 +105,7 @@ public enum ModBlocks { SPIKE_DIAMOND(BlockConfig.spikes.enableDiamondSpike, new BlockSpike(BlockSpike.SpikeType.DIAMOND, "diamondSpike"), BlockSpike.ItemSpike.class, "diamondSpike"), UNDERWORLD_PORTAL(BlockConfig.enableUnderWorldPortal && UnderWorldConfig.enableUnderWorld, new BlockPortalUnderWorld(), "underworld_portal"), END_OF_TIME_PORTAL(BlockConfig.enableEndOfTimePortal && EndOfTimeConfig.enableEndOfTime, new BlockPortalEndOfTime(), BlockPortalEndOfTime.ItemBlockPortalEndOfTime.class, "temporal_gate"), + ENDER_QUARRY(BlockConfig.enableEnderQuarry, new BlockEnderQuarry(), "ender_quarry"), ; // leave trailing semicolon // spotless:on diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java index c6c8987a..c1654af7 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java @@ -1,5 +1,6 @@ package com.fouristhenumber.utilitiesinexcess; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; import net.minecraft.util.WeightedRandomChestContent; import net.minecraftforge.common.ChestGenHooks; import net.minecraftforge.common.MinecraftForge; @@ -124,6 +125,7 @@ public void init(FMLInitializationEvent event) { GameRegistry.registerTileEntity(TileEntityTNTGenerator.class, "TileEntityTNTGeneratorUIE"); GameRegistry.registerTileEntity(TileEntityPinkGenerator.class, "TileEntityPinkGeneratorUIE"); GameRegistry.registerTileEntity(TileEntityNetherStarGenerator.class, "TileEntityNetherStarGeneratorUIE"); + GameRegistry.registerTileEntity(TileEntityEnderQuarry.class, "TileEntityEnderQuarryUIE"); lapisAetheriusRenderID = RenderingRegistry.getNextAvailableRenderId(); RenderingRegistry.registerBlockHandler(new LapisAetheriusRenderer()); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java new file mode 100644 index 00000000..6c5ad034 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java @@ -0,0 +1,335 @@ +package com.fouristhenumber.utilitiesinexcess.common.blocks; + +import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; + +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +public class BlockEnderQuarry extends BlockContainer { + + public BlockEnderQuarry() { + super(Material.iron); + setBlockName("ender_quarry"); + setBlockTextureName("utilitiesinexcess:ender_quarry"); + } + + @Override + public boolean isOpaqueCube() { + return false; + } + + @Override + public int getRenderType() { + return JSON_ISBRH_ID; + } + + @Override + public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase placer, ItemStack itemIn) { + super.onBlockPlacedBy(worldIn, x, y, z, placer, itemIn); + + int direction = (int) (((placer.rotationYaw + 45f) / 90f + 4f) % 4f); + + worldIn.setBlockMetadataWithNotify(x, y, z, direction, 2); + + TileEntity te = worldIn.getTileEntity(x, y, z); + if (te instanceof TileEntityEnderQuarry quarry) { + quarry.setFacing(getFacing(direction)); + quarry.resetState(); + } + } + + @Override + public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, float subY, float subZ) { + if (worldIn.isRemote) { + return true; + } + TileEntity te = worldIn.getTileEntity(x, y, z); + if (te instanceof TileEntityEnderQuarry quarry) { + if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.STOPPED) { + BlockPos inFront = offsetByForward(x, y, z, quarry.getFacing(), 1, 0); + BlockPos farFront = offsetByForward(inFront, quarry.getFacing(), 64, 0); + farFront = offsetByRight(farFront, quarry.getFacing(), 64, 0); + + for (int i = 1; i < 256; i++) { + worldIn.setBlock(inFront.x, i, inFront.z, Blocks.diamond_block); + worldIn.setBlock(farFront.x, i, farFront.z, Blocks.diamond_block); + } + + player.addChatComponentMessage(new ChatComponentText(String.format("Set up work area from %s to %s", inFront, farFront))); + + quarry.setWorkArea(new TileEntityEnderQuarry.Area2d(inFront.x, inFront.z, farFront.x, farFront.z)); + quarry.state = TileEntityEnderQuarry.QuarryWorkState.RUNNING; + } + player.addChatComponentMessage(new ChatComponentText(quarry.getState())); + } + return true; + } + + public static ForgeDirection getFacing(int meta) { + return switch (meta) { + case 0 -> ForgeDirection.SOUTH; + case 1 -> ForgeDirection.WEST; + case 2 -> ForgeDirection.NORTH; + case 3 -> ForgeDirection.EAST; + default -> ForgeDirection.UNKNOWN; + }; + } + + @Override + public TileEntity createNewTileEntity(World worldIn, int meta) { + return new TileEntityEnderQuarry(); + } + + + // TODO: REMOVE + public static ForgeDirection turnRight90(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.SOUTH; + case NORTH -> ForgeDirection.EAST; + case SOUTH -> ForgeDirection.WEST; + case WEST -> ForgeDirection.NORTH; + default -> dir; + }; + } + + public static ForgeDirection turnLeft90(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.NORTH; + case NORTH -> ForgeDirection.WEST; + case SOUTH -> ForgeDirection.EAST; + case WEST -> ForgeDirection.SOUTH; + default -> dir; + }; + } + + public static ForgeDirection turn180(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.WEST; + case NORTH -> ForgeDirection.SOUTH; + case SOUTH -> ForgeDirection.NORTH; + case WEST -> ForgeDirection.EAST; + default -> dir; + }; + } + + public static ForgeDirection turnRight270(ForgeDirection dir) { + return turnLeft90(dir); + } + + public static ForgeDirection turnLeft270(ForgeDirection dir) { + return turnRight90(dir); + } + + /** + * Offsets coordinates to the right based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the right + */ + public static BlockPos offsetByRight(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + ForgeDirection rightDir = turnRight90(facing); + return new BlockPos(pos.x + rightDir.offsetX * amount, pos.y + vertical, pos.z + rightDir.offsetZ * amount); + } + + /** + * Offsets coordinates to the right based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the right + */ + public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); + } + + /** + * Offsets coordinates to the right based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the right + */ + public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } + + /** + * Offsets coordinates to the left based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the left + */ + public static BlockPos offsetByLeft(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + ForgeDirection leftDir = turnLeft90(facing); + return new BlockPos(pos.x + leftDir.offsetX * amount, pos.y + vertical, pos.z + leftDir.offsetZ * amount); + } + + /** + * Offsets coordinates to the left based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the left + */ + public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); + } + + /** + * Offsets coordinates to the left based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the left + */ + public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } + + /** + * Offsets coordinates backwards based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset backwards + */ + public static BlockPos offsetByBack(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + ForgeDirection backDir = facing.getOpposite(); + return new BlockPos(pos.x + backDir.offsetX * amount, pos.y + vertical, pos.z + backDir.offsetZ * amount); + } + + /** + * Offsets coordinates backwards based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset backwards + */ + public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); + } + + /** + * Offsets coordinates backwards based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset backwards + */ + public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } + + /** + * Offsets coordinates forward based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset forward + */ + public static BlockPos offsetByForward(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + return new BlockPos(pos.x + facing.offsetX * amount, pos.y + vertical, pos.z + facing.offsetZ * amount); + } + + /** + * Offsets coordinates forward based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset forward + */ + public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByForward(x, y, z, facing, amount, vertical, false); + } + + /** + * Offsets coordinates forward based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset forward + */ + public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByForward(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java new file mode 100644 index 00000000..e41ba0d7 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -0,0 +1,254 @@ +package com.fouristhenumber.utilitiesinexcess.common.tileentities; + +import cpw.mods.fml.common.FMLLog; +import net.minecraft.block.material.Material; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; +import org.joml.Vector2i; + +public class TileEntityEnderQuarry extends TileEntity { + + public static int STEPS_PER_TICK = 40; + public ForgeDirection facing; + private Area2d workArea; + public QuarryWorkState state; + private int dx; + private int dy; + private int dz; + private int chunkX; + private int chunkZ; + private int brokenBlocksTotal; + + public ForgeDirection getFacing() { + return this.facing; + } + + public void setFacing(ForgeDirection facing) { + this.facing = facing; + } + + public Area2d getWorkArea() { + return this.workArea; + } + + public void resetState() { + brokenBlocksTotal = 0; + this.state = QuarryWorkState.STOPPED; + } + + public String getState() { + return switch (state) { + case RUNNING -> String.format("Quarry is currently mining at %d %d %d, has already mined %d", dx, dy, dz, brokenBlocksTotal); + case STOPPED -> "Quarry is stopped."; + case FINISHED -> String.format("Quarry has finished after mining %d blocks", brokenBlocksTotal); + }; + } + + public void setWorkArea(Area2d area) { + workArea = area; + dx = area.low.x; + dy = yCoord + 5; + dz = area.low.y; + chunkX = dx >> 4; + chunkZ = dz >> 4; + } + + @Override + public void updateEntity() { + if (worldObj.isRemote) return; + if (facing == ForgeDirection.UNKNOWN) return; + if (state != QuarryWorkState.RUNNING) return; + + int brokenBlocksTick = 0; + + while (brokenBlocksTick < STEPS_PER_TICK && stepPos()) { + if (!isInBounds() || this.chunkX > 1000) { + FMLLog.getLogger().error("Tried to quarry outside of work area at {} {} {}", dx, dy, dz); + state = QuarryWorkState.FINISHED; + return; + } + if (worldObj.isAirBlock(dx, dy, dz) || worldObj.getBlock(dx, dy, dz).getMaterial() == Material.air) { + continue; + } + worldObj.setBlock(dx, dy, dz, Blocks.air); + brokenBlocksTick++; + // breakBlock() + } + if (brokenBlocksTick < STEPS_PER_TICK) { + state = QuarryWorkState.FINISHED; + } + + this.brokenBlocksTotal += brokenBlocksTick; + } + + /** + * Are the current dx & dy & dz in bounds + */ + public boolean isInBounds() { + return dy > 1 && this.workArea.isInBounds(dx, dz); + } + + /** + * Are the provided dx & dz in bounds + */ + public boolean isInBounds(int dx, int dz) { + return this.workArea.isInBounds(dx, dz); + } + + /** + * Step the quarry working position by one block + * @return True if we can keep moving + */ + public boolean stepPos() { + dy--; + if (dy <= 1) { + // stack is done, move back up + dy = this.yCoord + 5; + + boolean resetX = false; + if (dx + 1 >> 4 == chunkX && dx + 1 <= workArea.high.x) { + dx++; + } else { + if (dz + 1 >> 4 == chunkZ && dz + 1 <= workArea.high.y) { + dz++; + resetX = true; + } else { + // next pos up z is a new chunk and maybe oob + if (dz + 1 <= workArea.high.y) { + // just the next chunk + dz++; + chunkZ++; + resetX = true; + } else { + // the next z slice + if (dx + 1 <= workArea.high.x) { + dx++; + chunkX++; + dz = workArea.low.y; + chunkZ = workArea.low.y >> 4; + } else { + // Finished with area + return false; + } + } + } + } + + if (resetX) { + // need to move x back left towards/to bounds + int toMove = dx + 1 > workArea.high.x ? workArea.chunkOffX.y : 15; + if (dx - toMove >= workArea.low.x) { + dx -= toMove; + } else { + dx = workArea.low.x; + } + } + } + return true; + } + + @Override + public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + facing = ForgeDirection.getOrientation(nbt.getInteger("facing")); + state = QuarryWorkState.values()[nbt.getInteger("state")]; + dx = nbt.getInteger("dy"); + dy = nbt.getInteger("dz"); + dz = nbt.getInteger("dx"); + chunkX = dx >> 4; + chunkZ = dz >> 4; + brokenBlocksTotal = nbt.getInteger("blocks"); + + workArea = Area2d.fromNBTTag(nbt); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + nbt.setInteger("facing", facing.ordinal()); + nbt.setInteger("state", state.ordinal()); + nbt.setInteger("dx", dx); + nbt.setInteger("dy", dy); + nbt.setInteger("dz", dz); + nbt.setInteger("blocks", brokenBlocksTotal); + workArea.writeNBTTag(nbt); + } + + public enum QuarryWorkState { + STOPPED, + FINISHED, + RUNNING; + } + + public static class Area2d { + // The corner that has the lower x&y + public final Vector2i low; + // The corner that has the higher x&y + public final Vector2i high; + // The width of the entire working area + public final int width; + // The height of the entire working area + public final int height; + /** + * The relative x offset to the chunk grid, + * with vec.x as the extra space to the left, + * and vec.y as the extra space to the right + * */ + public final Vector2i chunkOffX; + /** + * The relative x offset to the chunk grid, + * with vec.x as the extra space to the bottom, + * and vec.y as the extra space to the top + * */ + public final Vector2i chunkOffZ; + + public Area2d(Vector2i first, Vector2i second) { + int lowX = Math.min(first.x, second.x); + int lowZ = Math.min(first.y, second.y); + int highX = Math.max(first.x, second.x); + int highZ = Math.max(first.y, second.y); + this.low = new Vector2i(lowX, lowZ); + this.high = new Vector2i(highX, highZ); + this.width = highX - lowX; + this.height = highZ - lowZ; + this.chunkOffX = new Vector2i(15 - lowX % 16, highX % 16); + this.chunkOffZ = new Vector2i(15 - lowZ % 16, highZ % 16); + } + + public Area2d(int x1, int z1, int x2, int z2) { + this(new Vector2i(x1, z1), new Vector2i(x2, z2)); + } + + public boolean isInBounds(int x, int z) { + return x >= this.low.x && x <= this.high.x && z >= this.low.y && z <= this.high.y; + } + + public void writeNBTTag(NBTTagCompound nbt) { + NBTTagCompound tag = new NBTTagCompound(); + tag.setInteger("lowX", low.x); + tag.setInteger("lowZ", low.y); + tag.setInteger("highX", high.x); + tag.setInteger("highY", high.y); + + nbt.setTag("area", tag); + } + + public static Area2d fromNBTTag(NBTTagCompound nbt) { + NBTTagCompound tag = nbt.getCompoundTag("area"); + int lowX = tag.getInteger("lowX"); + int lowZ = tag.getInteger("lowZ"); + int highX = tag.getInteger("highX"); + int highZ = tag.getInteger("highY"); + return new Area2d(lowX, lowZ, highX, highZ); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Area2d other)) return false; + return low.equals(other.low) && high.equals(other.high); + } + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java index b1070d77..0980d670 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java @@ -82,6 +82,9 @@ public static void registerConfig() throws ConfigException { @Config.DefaultBoolean(true) public static boolean enableEndOfTimePortal; + @Config.DefaultBoolean(true) + public static boolean enableEnderQuarry; + @Config.Comment("Cursed Earth Configuration") public static final CursedEarth cursedEarth = new CursedEarth(); diff --git a/src/main/resources/assets/utilitiesinexcess/blocks/ender_quarry.png b/src/main/resources/assets/utilitiesinexcess/blocks/ender_quarry.png new file mode 100644 index 0000000000000000000000000000000000000000..4912f4e9eb3defe584e68c34e02228d183615fa1 GIT binary patch literal 2028 zcmV-P)978d1k~0bsLxI=@M-$GQNk3UdL&2^*)? z8^?9o05HAyp{jXLrye+fFb2RSqJkg*Y}-;zE~KTMVCLSe9zaE8{9F!zx=5{&^=uJ9 zs0C1_D`)@}nO@Y_xLPy;Kv6LuAAr`(z5CKT@V+{y1t8_b=uq{6I_6tIRz3@G^#g4H zO0)|U187))2Y|z4luZ96faO|%nfDCfnIhT+ z-s&B!yG+Ygs{r^+nc}K0L>OiF(N39KxUI@6Jr0k^<*^6=Z_EzmC4fZqH3CS@sBM=H zF^HjOW`lN_!Lmr~2h1K8z%+~NQ5Mh=00AO^BK4fkvyZF@0HzN`FNNpNoCyMOt-D7Z z2_QBN+WFfj)1O0xn1Nj&>c=ESuLCjwY@2Lor2z2tb|Un}F&uL}KmY@C2UM1da)JEq zaJF>-$X@j;)>Y`s`D*S2mFgl(KXj={ijD#K0J5S5gen#QeYr@qKw1K_$G)TMqZUC> z<;ia!M*(1vveTk|HAU4#Y8}pem;bUeD_>UNri%5kUoo=}IRWHlwJ-pFVU|+LYs~R( zkYal#(7hYC$<-~97C;x^KguoQp}k@2raR7A9&ivEkv_t;$QdQjPg20_6h0 zjm~8PI5yCy7AJb9!iBmrvf=@)1@Mj7Dp2IPA<%l}+gn*|3 zRAg+&XGywjsRckT02m5pHt#tb0P9W36XBtvcZY1t6d$eR_I;#1*Yj>rxTbyFXEa<3#OwqiCjhf-cf1MU1Lc3d=Mn&e zed=qHecuNlENfKcTKD0k1eJLq3Ml|+*wsPGyw0U3x3}hBQ(OazwE+7sFKiFbnB~K# zJ5oDRPF$%NV0ZA}$u4=Xr2x*$hMq543IMUde#?YsO1XdyfE}dt_1m&E`CK7@UlwNM zp9}zZ{bc}{{UU&62SHy4A*{J^-wsYT0PgZw4g#=iRHhz>0f1E^SeXF$2AOdQzin|0 zsK}w}Tqyu3qb|&jul^znz(}65EP(A`5kvrl68i@!C>H?ASYw+2nsD47EU#SnTclQ( zeeWzE0EFkzj`wkW4#Bz99E^xQu!%1ViMy3D027r#K!Aac-z_tXKE9-*n0o&yw z0QN^xv`GLIQsMGlU~xv4E@dr1188;%@&ahARya7w9zp-Z& zV!II9hVVNd0Eq1(6CmS6FNEgLoUQY1im5CB402K3!)&1y`&S$QJIQl)9tr|b5t)6p z8SFSI%q%tlLPtNNgJAm0P;=8^6J{AQEy1)JUdsc({X6gD_o#2(!3VN5xjD({ut>

-P)978d1k~0bsLxI=@M-$GQNk3UdL&2^*)? z8^?9o05HAyp{jXLrye+fFb2RSqJkg*Y}-;zE~KTMVCLSe9zaE8{9F!zx=5{&^=uJ9 zs0C1_D`)@}nO@Y_xLPy;Kv6LuAAr`(z5CKT@V+{y1t8_b=uq{6I_6tIRz3@G^#g4H zO0)|U187))2Y|z4luZ96faO|%nfDCfnIhT+ z-s&B!yG+Ygs{r^+nc}K0L>OiF(N39KxUI@6Jr0k^<*^6=Z_EzmC4fZqH3CS@sBM=H zF^HjOW`lN_!Lmr~2h1K8z%+~NQ5Mh=00AO^BK4fkvyZF@0HzN`FNNpNoCyMOt-D7Z z2_QBN+WFfj)1O0xn1Nj&>c=ESuLCjwY@2Lor2z2tb|Un}F&uL}KmY@C2UM1da)JEq zaJF>-$X@j;)>Y`s`D*S2mFgl(KXj={ijD#K0J5S5gen#QeYr@qKw1K_$G)TMqZUC> z<;ia!M*(1vveTk|HAU4#Y8}pem;bUeD_>UNri%5kUoo=}IRWHlwJ-pFVU|+LYs~R( zkYal#(7hYC$<-~97C;x^KguoQp}k@2raR7A9&ivEkv_t;$QdQjPg20_6h0 zjm~8PI5yCy7AJb9!iBmrvf=@)1@Mj7Dp2IPA<%l}+gn*|3 zRAg+&XGywjsRckT02m5pHt#tb0P9W36XBtvcZY1t6d$eR_I;#1*Yj>rxTbyFXEa<3#OwqiCjhf-cf1MU1Lc3d=Mn&e zed=qHecuNlENfKcTKD0k1eJLq3Ml|+*wsPGyw0U3x3}hBQ(OazwE+7sFKiFbnB~K# zJ5oDRPF$%NV0ZA}$u4=Xr2x*$hMq543IMUde#?YsO1XdyfE}dt_1m&E`CK7@UlwNM zp9}zZ{bc}{{UU&62SHy4A*{J^-wsYT0PgZw4g#=iRHhz>0f1E^SeXF$2AOdQzin|0 zsK}w}Tqyu3qb|&jul^znz(}65EP(A`5kvrl68i@!C>H?ASYw+2nsD47EU#SnTclQ( zeeWzE0EFkzj`wkW4#Bz99E^xQu!%1ViMy3D027r#K!Aac-z_tXKE9-*n0o&yw z0QN^xv`GLIQsMGlU~xv4E@dr1188;%@&ahARya7w9zp-Z& zV!II9hVVNd0Eq1(6CmS6FNEgLoUQY1im5CB402K3!)&1y`&S$QJIQl)9tr|b5t)6p z8SFSI%q%tlLPtNNgJAm0P;=8^6J{AQEy1)JUdsc({X6gD_o#2(!3VN5xjD({ut>

-P)978d1k~0bsLxI=@M-$GQNk3UdL&2^*)? z8^?9o05HAyp{jXLrye+fFb2RSqJkg*Y}-;zE~KTMVCLSe9zaE8{9F!zx=5{&^=uJ9 zs0C1_D`)@}nO@Y_xLPy;Kv6LuAAr`(z5CKT@V+{y1t8_b=uq{6I_6tIRz3@G^#g4H zO0)|U187))2Y|z4luZ96faO|%nfDCfnIhT+ z-s&B!yG+Ygs{r^+nc}K0L>OiF(N39KxUI@6Jr0k^<*^6=Z_EzmC4fZqH3CS@sBM=H zF^HjOW`lN_!Lmr~2h1K8z%+~NQ5Mh=00AO^BK4fkvyZF@0HzN`FNNpNoCyMOt-D7Z z2_QBN+WFfj)1O0xn1Nj&>c=ESuLCjwY@2Lor2z2tb|Un}F&uL}KmY@C2UM1da)JEq zaJF>-$X@j;)>Y`s`D*S2mFgl(KXj={ijD#K0J5S5gen#QeYr@qKw1K_$G)TMqZUC> z<;ia!M*(1vveTk|HAU4#Y8}pem;bUeD_>UNri%5kUoo=}IRWHlwJ-pFVU|+LYs~R( zkYal#(7hYC$<-~97C;x^KguoQp}k@2raR7A9&ivEkv_t;$QdQjPg20_6h0 zjm~8PI5yCy7AJb9!iBmrvf=@)1@Mj7Dp2IPA<%l}+gn*|3 zRAg+&XGywjsRckT02m5pHt#tb0P9W36XBtvcZY1t6d$eR_I;#1*Yj>rxTbyFXEa<3#OwqiCjhf-cf1MU1Lc3d=Mn&e zed=qHecuNlENfKcTKD0k1eJLq3Ml|+*wsPGyw0U3x3}hBQ(OazwE+7sFKiFbnB~K# zJ5oDRPF$%NV0ZA}$u4=Xr2x*$hMq543IMUde#?YsO1XdyfE}dt_1m&E`CK7@UlwNM zp9}zZ{bc}{{UU&62SHy4A*{J^-wsYT0PgZw4g#=iRHhz>0f1E^SeXF$2AOdQzin|0 zsK}w}Tqyu3qb|&jul^znz(}65EP(A`5kvrl68i@!C>H?ASYw+2nsD47EU#SnTclQ( zeeWzE0EFkzj`wkW4#Bz99E^xQu!%1ViMy3D027r#K!Aac-z_tXKE9-*n0o&yw z0QN^xv`GLIQsMGlU~xv4E@dr1188;%@&ahARya7w9zp-Z& zV!II9hVVNd0Eq1(6CmS6FNEgLoUQY1im5CB402K3!)&1y`&S$QJIQl)9tr|b5t)6p z8SFSI%q%tlLPtNNgJAm0P;=8^6J{AQEy1)JUdsc({X6gD_o#2(!3VN5xjD({ut>

Date: Fri, 21 Nov 2025 21:44:58 +0100 Subject: [PATCH 02/23] Build a semi-generic chunk loading class --- .gitignore | 1 + dependencies.gradle | 2 + .../utilitiesinexcess/UtilitiesInExcess.java | 7 + .../tileentities/TileEntityEnderQuarry.java | 30 ++- .../common/tileentities/utils/LoadableTE.java | 191 ++++++++++++++++++ .../utils/TEChunkLoadingCallback.java | 24 +++ 6 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java diff --git a/.gitignore b/.gitignore index 5e80e0ae..dc96fe2b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ addon.local.gradle.kts addon.late.local.gradle addon.late.local.gradle.kts layout.json +libs/ diff --git a/dependencies.gradle b/dependencies.gradle index 419fe657..e2e8c465 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -48,4 +48,6 @@ dependencies { runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.31-GTNH:dev" ) runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-beta71-pre:dev" ) runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.17:dev" ) + + runtimeOnlyNonPublishable(files("libs/ChickenChunks-1.3.4.27-dev.jar")) } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java index c1654af7..a9fd579c 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java @@ -1,8 +1,10 @@ package com.fouristhenumber.utilitiesinexcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; +import com.fouristhenumber.utilitiesinexcess.utils.TEChunkLoadingCallback; import net.minecraft.util.WeightedRandomChestContent; import net.minecraftforge.common.ChestGenHooks; +import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.MinecraftForge; import org.apache.logging.log4j.LogManager; @@ -68,6 +70,9 @@ public class UtilitiesInExcess { public static final String MODID = "utilitiesinexcess"; public static final Logger LOG = LogManager.getLogger(MODID); + @Mod.Instance(MODID) + public static UtilitiesInExcess uieInstance; + public static int lapisAetheriusRenderID; public static int blackoutCurtainsRenderID; public static int spikeRenderID; @@ -166,6 +171,8 @@ public void init(FMLInitializationEvent event) { if (Mods.CraftTweaker.isLoaded()) { MineTweakerAPI.registerClass(QEDCraftTweakerSupport.class); } + + ForgeChunkManager.setForcedChunkLoadingCallback(uieInstance, new TEChunkLoadingCallback()); } @Mod.EventHandler diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index e41ba0d7..85ec79a8 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -1,17 +1,20 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import cpw.mods.fml.common.FMLLog; import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; -public class TileEntityEnderQuarry extends TileEntity { +public class TileEntityEnderQuarry extends LoadableTE { public static int STEPS_PER_TICK = 40; - public ForgeDirection facing; + private @Nullable ForgeChunkManager.Ticket ticket; + private ForgeDirection facing; private Area2d workArea; public QuarryWorkState state; private int dx; @@ -21,6 +24,10 @@ public class TileEntityEnderQuarry extends TileEntity { private int chunkZ; private int brokenBlocksTotal; + public TileEntityEnderQuarry() { + super(); + } + public ForgeDirection getFacing() { return this.facing; } @@ -78,6 +85,7 @@ public void updateEntity() { } if (brokenBlocksTick < STEPS_PER_TICK) { state = QuarryWorkState.FINISHED; + unloadSelf(); } this.brokenBlocksTotal += brokenBlocksTick; @@ -119,16 +127,21 @@ public boolean stepPos() { if (dz + 1 <= workArea.high.y) { // just the next chunk dz++; + unloadChunkShifted(chunkX, chunkZ); chunkZ++; + loadChunkShifted(chunkX, chunkZ); resetX = true; } else { // the next z slice if (dx + 1 <= workArea.high.x) { dx++; + unloadChunkShifted(chunkX, chunkZ); chunkX++; dz = workArea.low.y; chunkZ = workArea.low.y >> 4; + loadChunkShifted(chunkX, chunkZ); } else { + unloadChunkShifted(chunkX, chunkZ); // Finished with area return false; } @@ -176,6 +189,17 @@ public void writeToNBT(NBTTagCompound nbt) { workArea.writeNBTTag(nbt); } + @Override + public void receiveTicketOnLoad(ForgeChunkManager.Ticket ticket) { + super.receiveTicketOnLoad(ticket); + loadChunk(chunkX, chunkZ); + } + + @Override + public boolean keepsItselfLoaded() { + return state != QuarryWorkState.FINISHED; + } + public enum QuarryWorkState { STOPPED, FINISHED, diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java new file mode 100644 index 00000000..cdb71c78 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java @@ -0,0 +1,191 @@ +package com.fouristhenumber.utilitiesinexcess.common.tileentities.utils; + +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraftforge.common.ForgeChunkManager; + +import javax.annotation.Nullable; + +/** + * Base class for TileEntities that need to keep chunks loaded. + * Handles chunk loading tickets and manages forced chunk loading through Forge's ChunkManager. + */ +public class LoadableTE extends TileEntity { + /** The chunk loading ticket used to force chunks to stay loaded */ + @Nullable + ForgeChunkManager.Ticket ticket = null; + private final int selfChunkX = xCoord >> 4; + private final int selfChunkZ = zCoord >> 4; + protected boolean selfIsLoaded = false; + + public LoadableTE() { + // TODO: Check why loads 0, 0 sometimes + if (keepsItselfLoaded()) { + loadSelf(); + } + } + + /** + * Forces a chunk to stay loaded. + * Skips loading if attempting to load own chunk and keepsItselfLoaded() returns true. + * + * @param chunkCoord The chunk coordinates to load + */ + public void loadChunk(ChunkCoordIntPair chunkCoord) { + if (chunkCoord.chunkXPos == selfChunkX && chunkCoord.chunkZPos == selfChunkZ && keepsItselfLoaded()) return; + if (requestTicket()) { + ForgeChunkManager.forceChunk(ticket, chunkCoord); + } else { + UtilitiesInExcess.LOG.error("Failed to get ticket to load chunk at {} {} for Mod {}", chunkCoord.chunkXPos, chunkCoord.chunkZPos, UtilitiesInExcess.MODID); + } + } + + /** + * Forces a chunk to stay loaded using chunk coordinates. + * + * @param x Chunk X coordinate + * @param z Chunk Z coordinate + */ + public void loadChunkShifted(int x, int z) { + loadChunk(new ChunkCoordIntPair(x, z)); + } + + /** + * Forces a chunk to stay loaded using block coordinates. + * + * @param x Block X coordinate + * @param z Block Z coordinate + */ + public void loadChunk(int x, int z) { + loadChunk(new ChunkCoordIntPair(x >> 4, z >> 4)); + } + + /** + * Stops forcing a chunk to stay loaded. + * Skips unloading if attempting to unload own chunk and keepsItselfLoaded() returns true. + * + * @param chunkCoord The chunk coordinates to unload + */ + void unloadChunk(ChunkCoordIntPair chunkCoord) { + if (chunkCoord.chunkXPos == selfChunkX && chunkCoord.chunkZPos == selfChunkZ && keepsItselfLoaded()) return; + if (requestTicket()) { + ForgeChunkManager.unforceChunk(ticket, chunkCoord); + } else { + UtilitiesInExcess.LOG.error("Failed to get ticket to unload? chunk at {} {} for Mod {}", chunkCoord.chunkXPos, chunkCoord.chunkZPos, UtilitiesInExcess.MODID); + } + } + + /** + * Stops forcing a chunk to stay loaded using chunk coordinates. + * + * @param x Chunk X coordinate + * @param z Chunk Z coordinate + */ + public void unloadChunkShifted(int x, int z) { + unloadChunk(new ChunkCoordIntPair(x, z)); + } + + /** + * Stops forcing a chunk to stay loaded using block coordinates. + * + * @param x Block X coordinate + * @param z Block Z coordinate + */ + public void unloadChunk(int x, int z) { + unloadChunk(new ChunkCoordIntPair(x >> 4, z >> 4)); + } + + /** + * Forces the chunk containing this TileEntity to stay loaded. + */ + public void loadSelf() { + if (!selfIsLoaded) { + loadChunk(selfChunkX, selfChunkZ); + selfIsLoaded = true; + } + } + + /** + * Stops forcing the chunk containing this TileEntity to stay loaded. + */ + public void unloadSelf() { + if (selfIsLoaded) { + unloadChunk(selfChunkX, selfChunkZ); + selfIsLoaded = false; + } + } + + /** + * Releases the chunk loading ticket and cleans up all forced chunks. + * Called when the TileEntity is invalidated or the chunk is unloaded. + */ + public void invalidateTicket() { + if (ticket != null) { + ForgeChunkManager.releaseTicket(ticket); + ticket = null; + } + } + + /** + * Requests a chunk loading ticket from Forge's ChunkManager if one doesn't exist. + * + * @return true if a ticket is available (either existing or newly acquired), false otherwise + */ + private boolean requestTicket() { + if (ticket != null) return true; + ticket = ForgeChunkManager.requestTicket(UtilitiesInExcess.uieInstance, worldObj, ForgeChunkManager.Type.NORMAL); + + if (ticket != null) { + // Store TileEntity coordinates in ticket data for reloading + NBTTagCompound tag = ticket.getModData(); + tag.setInteger("teX", xCoord); + tag.setInteger("teY", yCoord); + tag.setInteger("teZ", zCoord); + } + return ticket != null; + } + + /** + * Called when the world reloads to restore the chunk loading ticket. + * If keepsItselfLoaded() returns true, automatically reloads the chunk containing this TileEntity. + * + * @param ticket The restored chunk loading ticket + */ + public void receiveTicketOnLoad(ForgeChunkManager.Ticket ticket) { + this.ticket = ticket; + if (keepsItselfLoaded()) { + loadSelf(); + } + } + + /** + * Override this method to determine if this TileEntity should keep its own chunk loaded. + * + * @return true if this TileEntity should keep its own chunk loaded, false otherwise + */ + public boolean keepsItselfLoaded() { + return false; + } + + /** + * Called when the chunk containing this TileEntity is unloaded. + * Releases the chunk loading ticket to prevent memory leaks. + */ + @Override + public void onChunkUnload() { + super.onChunkUnload(); + invalidateTicket(); + } + + /** + * Called when this TileEntity is removed from the world. + * Releases the chunk loading ticket to prevent memory leaks. + */ + @Override + public void invalidate() { + super.invalidate(); + invalidateTicket(); + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java new file mode 100644 index 00000000..fcd45c48 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java @@ -0,0 +1,24 @@ +package com.fouristhenumber.utilitiesinexcess.utils; + +import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.common.ForgeChunkManager; + +import java.util.List; + +public class TEChunkLoadingCallback implements ForgeChunkManager.LoadingCallback { + @Override + public void ticketsLoaded(List tickets, World world) { + for (ForgeChunkManager.Ticket ticket : tickets) { + NBTTagCompound tag = ticket.getModData(); + int x = tag.getInteger("teX"); + int y = tag.getInteger("teY"); + int z = tag.getInteger("teZ"); + + if (world.getTileEntity(x, y, z) instanceof LoadableTE pump) { + pump.receiveTicketOnLoad(ticket); + } + } + } +} From b5f938ec3fa9a6b91e35a5a6469501299363261b Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Sun, 23 Nov 2025 00:12:00 +0100 Subject: [PATCH 03/23] Finish chunk-loading logic & restarting on reload & correctly get distance to lower x boundary --- dependencies.gradle | 3 +- .../tileentities/TileEntityEnderQuarry.java | 77 +++++++++---------- .../common/tileentities/utils/LoadableTE.java | 33 +++++--- .../utils/TEChunkLoadingCallback.java | 4 +- 4 files changed, 61 insertions(+), 56 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index e2e8c465..5d5b5729 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -49,5 +49,6 @@ dependencies { runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-beta71-pre:dev" ) runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.17:dev" ) - runtimeOnlyNonPublishable(files("libs/ChickenChunks-1.3.4.27-dev.jar")) + // For debugging chunkloading + runtimeOnlyNonPublishable('curse.maven:chicken-chunks-229316:2233250') } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 85ec79a8..1efb6ace 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -5,15 +5,12 @@ import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; -import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.util.ForgeDirection; -import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; public class TileEntityEnderQuarry extends LoadableTE { - public static int STEPS_PER_TICK = 40; - private @Nullable ForgeChunkManager.Ticket ticket; + public static int STEPS_PER_TICK = 400; private ForgeDirection facing; private Area2d workArea; public QuarryWorkState state; @@ -25,7 +22,15 @@ public class TileEntityEnderQuarry extends LoadableTE { private int brokenBlocksTotal; public TileEntityEnderQuarry() { - super(); + state = QuarryWorkState.STOPPED; + } + + @Override + public void validate() { + super.validate(); + if (state != QuarryWorkState.FINISHED && workArea != null) { + loadChunkShifted(chunkX, chunkZ); + } } public ForgeDirection getFacing() { @@ -36,9 +41,6 @@ public void setFacing(ForgeDirection facing) { this.facing = facing; } - public Area2d getWorkArea() { - return this.workArea; - } public void resetState() { brokenBlocksTotal = 0; @@ -53,6 +55,9 @@ public String getState() { }; } + public Area2d getWorkArea() { + return this.workArea; + } public void setWorkArea(Area2d area) { workArea = area; dx = area.low.x; @@ -71,9 +76,11 @@ public void updateEntity() { int brokenBlocksTick = 0; while (brokenBlocksTick < STEPS_PER_TICK && stepPos()) { - if (!isInBounds() || this.chunkX > 1000) { + // TODO: Remove after this has been tested by others + if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { FMLLog.getLogger().error("Tried to quarry outside of work area at {} {} {}", dx, dy, dz); state = QuarryWorkState.FINISHED; + worldObj.setBlock(dx, dy, dz, Blocks.gold_block); return; } if (worldObj.isAirBlock(dx, dy, dz) || worldObj.getBlock(dx, dy, dz).getMaterial() == Material.air) { @@ -150,8 +157,8 @@ public boolean stepPos() { } if (resetX) { - // need to move x back left towards/to bounds - int toMove = dx + 1 > workArea.high.x ? workArea.chunkOffX.y : 15; + // the distance we have to move left now, lower if we would cross the high x bound + int toMove = dx + 1 > workArea.high.x ? workArea.chunkOffX : 15; if (dx - toMove >= workArea.low.x) { dx -= toMove; } else { @@ -167,14 +174,15 @@ public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); facing = ForgeDirection.getOrientation(nbt.getInteger("facing")); state = QuarryWorkState.values()[nbt.getInteger("state")]; - dx = nbt.getInteger("dy"); - dy = nbt.getInteger("dz"); - dz = nbt.getInteger("dx"); - chunkX = dx >> 4; - chunkZ = dz >> 4; + if (state != QuarryWorkState.FINISHED) { + workArea = Area2d.fromNBTTag(nbt); + dx = nbt.getInteger("dx"); + dy = nbt.getInteger("dy"); + dz = nbt.getInteger("dz"); + chunkX = dx >> 4; + chunkZ = dz >> 4; + } brokenBlocksTotal = nbt.getInteger("blocks"); - - workArea = Area2d.fromNBTTag(nbt); } @Override @@ -182,17 +190,13 @@ public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); nbt.setInteger("facing", facing.ordinal()); nbt.setInteger("state", state.ordinal()); - nbt.setInteger("dx", dx); - nbt.setInteger("dy", dy); - nbt.setInteger("dz", dz); + if (state != QuarryWorkState.FINISHED && workArea != null) { + workArea.writeNBTTag(nbt); + nbt.setInteger("dx", dx); + nbt.setInteger("dy", dy); + nbt.setInteger("dz", dz); + } nbt.setInteger("blocks", brokenBlocksTotal); - workArea.writeNBTTag(nbt); - } - - @Override - public void receiveTicketOnLoad(ForgeChunkManager.Ticket ticket) { - super.receiveTicketOnLoad(ticket); - loadChunk(chunkX, chunkZ); } @Override @@ -215,18 +219,8 @@ public static class Area2d { public final int width; // The height of the entire working area public final int height; - /** - * The relative x offset to the chunk grid, - * with vec.x as the extra space to the left, - * and vec.y as the extra space to the right - * */ - public final Vector2i chunkOffX; - /** - * The relative x offset to the chunk grid, - * with vec.x as the extra space to the bottom, - * and vec.y as the extra space to the top - * */ - public final Vector2i chunkOffZ; + // The distance to the closest lower x chunk border from the high x bound + public final int chunkOffX; public Area2d(Vector2i first, Vector2i second) { int lowX = Math.min(first.x, second.x); @@ -237,8 +231,7 @@ public Area2d(Vector2i first, Vector2i second) { this.high = new Vector2i(highX, highZ); this.width = highX - lowX; this.height = highZ - lowZ; - this.chunkOffX = new Vector2i(15 - lowX % 16, highX % 16); - this.chunkOffZ = new Vector2i(15 - lowZ % 16, highZ % 16); + this.chunkOffX = highX - (highX & -16); } public Area2d(int x1, int z1, int x2, int z2) { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java index cdb71c78..4b8daeb4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java @@ -15,15 +15,26 @@ public class LoadableTE extends TileEntity { /** The chunk loading ticket used to force chunks to stay loaded */ @Nullable - ForgeChunkManager.Ticket ticket = null; - private final int selfChunkX = xCoord >> 4; - private final int selfChunkZ = zCoord >> 4; - protected boolean selfIsLoaded = false; + private ForgeChunkManager.Ticket ticket = null; + private int selfChunkX; + private int selfChunkZ; + protected boolean selfIsLoaded = true; - public LoadableTE() { - // TODO: Check why loads 0, 0 sometimes - if (keepsItselfLoaded()) { - loadSelf(); + /** + * Initializes this class after the TE is loaded. + * Makes sure the TE itself will stay loaded if requested. + * Overwrite and call super() to load your own chunks after readFromNBT() is called + */ + @Override + public void validate() { + super.validate(); + selfIsLoaded = false; + selfChunkX = xCoord >> 4; + selfChunkZ = zCoord >> 4; + if (worldObj != null && !worldObj.isRemote) { + if (keepsItselfLoaded()) { + loadSelf(); + } } } @@ -34,7 +45,7 @@ public LoadableTE() { * @param chunkCoord The chunk coordinates to load */ public void loadChunk(ChunkCoordIntPair chunkCoord) { - if (chunkCoord.chunkXPos == selfChunkX && chunkCoord.chunkZPos == selfChunkZ && keepsItselfLoaded()) return; + if (selfIsLoaded && chunkCoord.chunkXPos == selfChunkX && chunkCoord.chunkZPos == selfChunkZ && keepsItselfLoaded()) return; if (requestTicket()) { ForgeChunkManager.forceChunk(ticket, chunkCoord); } else { @@ -102,7 +113,7 @@ public void unloadChunk(int x, int z) { */ public void loadSelf() { if (!selfIsLoaded) { - loadChunk(selfChunkX, selfChunkZ); + loadChunkShifted(selfChunkX, selfChunkZ); selfIsLoaded = true; } } @@ -112,7 +123,7 @@ public void loadSelf() { */ public void unloadSelf() { if (selfIsLoaded) { - unloadChunk(selfChunkX, selfChunkZ); + unloadChunkShifted(selfChunkX, selfChunkZ); selfIsLoaded = false; } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java index fcd45c48..82adccf4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java @@ -16,8 +16,8 @@ public void ticketsLoaded(List tickets, World world) { int y = tag.getInteger("teY"); int z = tag.getInteger("teZ"); - if (world.getTileEntity(x, y, z) instanceof LoadableTE pump) { - pump.receiveTicketOnLoad(ticket); + if (world.getTileEntity(x, y, z) instanceof LoadableTE te) { + te.receiveTicketOnLoad(ticket); } } } From 8a06a13659cc085de16ef45f7eba03ad228a92c3 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Mon, 24 Nov 2025 01:24:30 +0100 Subject: [PATCH 04/23] Add dedicated eq config, Implement item & fluid storage logic, fix some bugs with chunkloading, more mining logic progress --- dependencies.gradle | 4 + .../utilitiesinexcess/ModBlocks.java | 5 +- .../utilitiesinexcess/UtilitiesInExcess.java | 4 +- .../common/blocks/BlockEnderQuarry.java | 38 +- .../tileentities/TileEntityEnderQuarry.java | 599 ++++++++++++++++-- .../common/tileentities/utils/LoadableTE.java | 48 +- .../config/blocks/BlockConfig.java | 4 +- .../config/blocks/EnderQuarryConfig.java | 26 + .../utils/TEChunkLoadingCallback.java | 6 +- 9 files changed, 631 insertions(+), 103 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java diff --git a/dependencies.gradle b/dependencies.gradle index 5d5b5729..0e6a6c80 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -51,4 +51,8 @@ dependencies { // For debugging chunkloading runtimeOnlyNonPublishable('curse.maven:chicken-chunks-229316:2233250') + + runtimeOnlyNonPublishable("com.github.GTNewHorizons:WAILAPlugins:0.7.2:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.9.15:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.5:dev") } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java index 381eddf2..6ea4e6f4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java @@ -1,6 +1,5 @@ package com.fouristhenumber.utilitiesinexcess; -import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderQuarry; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.item.Item; @@ -14,6 +13,7 @@ import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockCursedEarth; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockDrum; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderLotus; +import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderQuarry; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEtherealGlass; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockFloating; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockInverted; @@ -48,6 +48,7 @@ import com.fouristhenumber.utilitiesinexcess.config.blocks.BlockConfig; import com.fouristhenumber.utilitiesinexcess.config.blocks.CursedEarthConfig; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderLotusConfig; +import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.config.blocks.GeneratorConfig; import com.fouristhenumber.utilitiesinexcess.config.dimensions.EndOfTimeConfig; import com.fouristhenumber.utilitiesinexcess.config.dimensions.UnderWorldConfig; @@ -105,7 +106,7 @@ public enum ModBlocks { SPIKE_DIAMOND(BlockConfig.spikes.enableDiamondSpike, new BlockSpike(BlockSpike.SpikeType.DIAMOND, "diamondSpike"), BlockSpike.ItemSpike.class, "diamondSpike"), UNDERWORLD_PORTAL(BlockConfig.enableUnderWorldPortal && UnderWorldConfig.enableUnderWorld, new BlockPortalUnderWorld(), "underworld_portal"), END_OF_TIME_PORTAL(BlockConfig.enableEndOfTimePortal && EndOfTimeConfig.enableEndOfTime, new BlockPortalEndOfTime(), BlockPortalEndOfTime.ItemBlockPortalEndOfTime.class, "temporal_gate"), - ENDER_QUARRY(BlockConfig.enableEnderQuarry, new BlockEnderQuarry(), "ender_quarry"), + ENDER_QUARRY(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarry(), "ender_quarry"), ; // leave trailing semicolon // spotless:on diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java index a9fd579c..f3612ff8 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java @@ -1,7 +1,5 @@ package com.fouristhenumber.utilitiesinexcess; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; -import com.fouristhenumber.utilitiesinexcess.utils.TEChunkLoadingCallback; import net.minecraft.util.WeightedRandomChestContent; import net.minecraftforge.common.ChestGenHooks; import net.minecraftforge.common.ForgeChunkManager; @@ -17,6 +15,7 @@ import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityBlockUpdateDetector; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityConveyor; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityDrum; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityMarginallyMaximisedChest; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPortalUnderWorld; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPureLove; @@ -47,6 +46,7 @@ import com.fouristhenumber.utilitiesinexcess.utils.FMLEventHandler; import com.fouristhenumber.utilitiesinexcess.utils.ForgeEventHandler; import com.fouristhenumber.utilitiesinexcess.utils.PinkFuelHelper; +import com.fouristhenumber.utilitiesinexcess.utils.TEChunkLoadingCallback; import cpw.mods.fml.client.registry.RenderingRegistry; import cpw.mods.fml.common.FMLCommonHandler; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java index 6c5ad034..fd1f88ea 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java @@ -2,8 +2,7 @@ import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; -import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; +import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; import net.minecraft.entity.EntityLivingBase; @@ -15,6 +14,9 @@ import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; + public class BlockEnderQuarry extends BlockContainer { public BlockEnderQuarry() { @@ -43,29 +45,31 @@ public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderQuarry quarry) { - quarry.setFacing(getFacing(direction)); + quarry.facing = getFacing(direction); quarry.resetState(); } } @Override - public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, float subY, float subZ) { + public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, + float subY, float subZ) { if (worldIn.isRemote) { return true; } TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderQuarry quarry) { if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.STOPPED) { - BlockPos inFront = offsetByForward(x, y, z, quarry.getFacing(), 1, 0); - BlockPos farFront = offsetByForward(inFront, quarry.getFacing(), 64, 0); - farFront = offsetByRight(farFront, quarry.getFacing(), 64, 0); + BlockPos inFront = offsetByForward(x, y, z, quarry.facing, 1, 0); + BlockPos farFront = offsetByForward(inFront, quarry.facing, 64, 0); + farFront = offsetByRight(farFront, quarry.facing, 64, 0); for (int i = 1; i < 256; i++) { worldIn.setBlock(inFront.x, i, inFront.z, Blocks.diamond_block); worldIn.setBlock(farFront.x, i, farFront.z, Blocks.diamond_block); } - player.addChatComponentMessage(new ChatComponentText(String.format("Set up work area from %s to %s", inFront, farFront))); + player.addChatComponentMessage( + new ChatComponentText(String.format("Set up work area from %s to %s", inFront, farFront))); quarry.setWorkArea(new TileEntityEnderQuarry.Area2d(inFront.x, inFront.z, farFront.x, farFront.z)); quarry.state = TileEntityEnderQuarry.QuarryWorkState.RUNNING; @@ -75,6 +79,15 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer return true; } + @Override + public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neighbor) { + super.onNeighborBlockChange(worldIn, x, y, z, neighbor); + TileEntity te = worldIn.getTileEntity(x, y, z); + if (!worldIn.isRemote && te instanceof TileEntityEnderQuarry quarry) { + quarry.scanSidesForTEs(); + } + } + public static ForgeDirection getFacing(int meta) { return switch (meta) { case 0 -> ForgeDirection.SOUTH; @@ -90,7 +103,6 @@ public TileEntity createNewTileEntity(World worldIn, int meta) { return new TileEntityEnderQuarry(); } - // TODO: REMOVE public static ForgeDirection turnRight90(ForgeDirection dir) { return switch (dir) { @@ -171,7 +183,7 @@ public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, * @return New BlockPos offset to the right */ public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { + boolean relative) { BlockPos pos = offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); if (relative) { pos.x = x - pos.x; @@ -222,7 +234,7 @@ public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, * @return New BlockPos offset to the left */ public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { + boolean relative) { BlockPos pos = offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); if (relative) { pos.x = x - pos.x; @@ -273,7 +285,7 @@ public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, * @return New BlockPos offset backwards */ public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { + boolean relative) { BlockPos pos = offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); if (relative) { pos.x = x - pos.x; @@ -323,7 +335,7 @@ public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facin * @return New BlockPos offset forward */ public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { + boolean relative) { BlockPos pos = offsetByForward(new BlockPos(x, y, z), facing, amount, vertical); if (relative) { pos.x = x - pos.x; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 1efb6ace..e060d2ee 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -1,17 +1,55 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import cpw.mods.fml.common.FMLLog; +import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraftforge.common.ForgeChunkManager; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidBlock; +import net.minecraftforge.fluids.IFluidHandler; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; -public class TileEntityEnderQuarry extends LoadableTE { +import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; +import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; + +import cofh.api.energy.EnergyStorage; +import cofh.api.energy.IEnergyReceiver; +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; + +public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver, IFluidHandler { - public static int STEPS_PER_TICK = 400; - private ForgeDirection facing; + public static final int STEPS_PER_TICK = 400; + public static final int ITEM_BUFFER_CAPACITY = 256; + public static final Block REPLACE_BLOCK = Blocks.dirt; + private int storedItems; + public ForgeDirection facing; private Area2d workArea; public QuarryWorkState state; private int dx; @@ -20,44 +58,43 @@ public class TileEntityEnderQuarry extends LoadableTE { private int chunkX; private int chunkZ; private int brokenBlocksTotal; + private final HashMap sidedItemAcceptors = new HashMap<>(); + private final HashMap sidedFluidAcceptors = new HashMap<>(); + + protected final EnergyStorage energyStorage = new EnergyStorage(EnderQuarryConfig.enderQuarryEnergyStorage); + protected final List fluidStorage = Stream + .generate(() -> new FluidTank(EnderQuarryConfig.enderQuarryFluidTankStorage)) + .limit(EnderQuarryConfig.enderQuarryFluidTankAmount) + .collect(Collectors.toList()); + protected final Object2IntMap<@NotNull ItemStack> itemStorage = new Object2IntOpenCustomHashMap<>( + ITEM_BUFFER_CAPACITY, + ItemStackHashStrategy.instance); public TileEntityEnderQuarry() { - state = QuarryWorkState.STOPPED; - } - - @Override - public void validate() { - super.validate(); - if (state != QuarryWorkState.FINISHED && workArea != null) { - loadChunkShifted(chunkX, chunkZ); - } - } - - public ForgeDirection getFacing() { - return this.facing; - } - - public void setFacing(ForgeDirection facing) { - this.facing = facing; + resetState(); + storedItems = 0; } - public void resetState() { + state = QuarryWorkState.STOPPED; brokenBlocksTotal = 0; - this.state = QuarryWorkState.STOPPED; } public String getState() { return switch (state) { - case RUNNING -> String.format("Quarry is currently mining at %d %d %d, has already mined %d", dx, dy, dz, brokenBlocksTotal); + case RUNNING -> String + .format("Quarry is currently mining at %d %d %d, has already mined %d", dx, dy, dz, brokenBlocksTotal); + case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids"; + case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items"; case STOPPED -> "Quarry is stopped."; - case FINISHED -> String.format("Quarry has finished after mining %d blocks", brokenBlocksTotal); + case FINISHED -> String.format("Quarry has finished after mining %d blocks, still holding %d items", brokenBlocksTotal, storedItems); }; } public Area2d getWorkArea() { return this.workArea; } + public void setWorkArea(Area2d area) { workArea = area; dx = area.low.x; @@ -67,56 +104,19 @@ public void setWorkArea(Area2d area) { chunkZ = dz >> 4; } - @Override - public void updateEntity() { - if (worldObj.isRemote) return; - if (facing == ForgeDirection.UNKNOWN) return; - if (state != QuarryWorkState.RUNNING) return; - - int brokenBlocksTick = 0; - - while (brokenBlocksTick < STEPS_PER_TICK && stepPos()) { - // TODO: Remove after this has been tested by others - if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { - FMLLog.getLogger().error("Tried to quarry outside of work area at {} {} {}", dx, dy, dz); - state = QuarryWorkState.FINISHED; - worldObj.setBlock(dx, dy, dz, Blocks.gold_block); - return; - } - if (worldObj.isAirBlock(dx, dy, dz) || worldObj.getBlock(dx, dy, dz).getMaterial() == Material.air) { - continue; - } - worldObj.setBlock(dx, dy, dz, Blocks.air); - brokenBlocksTick++; - // breakBlock() - } - if (brokenBlocksTick < STEPS_PER_TICK) { - state = QuarryWorkState.FINISHED; - unloadSelf(); - } - - this.brokenBlocksTotal += brokenBlocksTick; - } - /** - * Are the current dx & dy & dz in bounds + * Are the current dx & dy & dz in work area bounds */ - public boolean isInBounds() { + private boolean isInBounds() { return dy > 1 && this.workArea.isInBounds(dx, dz); } - /** - * Are the provided dx & dz in bounds - */ - public boolean isInBounds(int dx, int dz) { - return this.workArea.isInBounds(dx, dz); - } - /** * Step the quarry working position by one block + * * @return True if we can keep moving */ - public boolean stepPos() { + private boolean stepPos() { dy--; if (dy <= 1) { // stack is done, move back up @@ -169,6 +169,336 @@ public boolean stepPos() { return true; } + /** + * Tries to store the resulting resources of the block at the current position, + * and store the items / fluid to the internal storage. + * + * @return True if we managed to harvest the current block, false if we couldn't / weren't allowed to touch/harvest + * it. + */ + private boolean tryHarvestCurrentBlock() { + if (worldObj.isAirBlock(dx, dy, dz) || worldObj.getBlock(dx, dy, dz) + .getMaterial() == Material.air) return false; + + Block block = worldObj.getBlock(dx, dy, dz); + return harvestAndStoreBlock(block); + } + + private boolean harvestAndStoreBlock(Block block) { + try { + // TODO: Use "shouldPumpFluids" or similar from upgrades + if (true) { + FluidStack fluid = null; + if (block == Blocks.water) { + fluid = new FluidStack(FluidRegistry.WATER, 1000); + } else if (block == Blocks.lava) { + fluid = new FluidStack(FluidRegistry.LAVA, 1000); + } else if (block instanceof IFluidBlock fluidBlock) { + fluid = fluidBlock.drain(worldObj, dx, dy, dz, false); + } + if (fluid != null) { + return tryStoreFluid(fluid); + } + } + + // TODO: Use fake player? Make sure to use fortune upgrade + // We can accept that the maximum stored amount is sometimes overrun by fortune + ArrayList drops = block.getDrops(worldObj, dx, dy, dz, worldObj.getBlockMetadata(dx, dy, dz), 0); + if (!drops.isEmpty()) { + return tryStoreItems(drops); + } + + // Block just has no drops + return true; + } catch (Exception ignored) { + UtilitiesInExcess.LOG.error("Failed while trying to harvest block {} at {} {} {}.", block.toString(), dx, dy, dz); + return false; + } + } + + /** + * Tries to store the provided fluid in the internal tanks + * + * @return If we could store all the provided fluid + */ + private boolean tryStoreFluid(FluidStack fluid) { + int toStore = fluid.amount; + for (FluidTank tank : fluidStorage) { + if (toStore > 0) { + toStore -= Math.min(tank.fill(fluid, false), toStore); + } + } + if (toStore == 0) { + toStore = fluid.amount; + for (FluidTank tank : fluidStorage) { + if (toStore > 0) { + toStore -= tank.fill(fluid, true); + } else { + break; + } + } + if (toStore == 0) { + return true; + } + } + state = QuarryWorkState.STOPPED_WAITING_FOR_FLUID_SPACE; + return false; + } + + /** + * Tries to store the provided list of items in the internal item storage + * + * @return If we could store all the provided items + */ + private boolean tryStoreItems(ArrayList items) { + int toStore = items.stream() + .mapToInt((item) -> item != null ? item.stackSize : 0) + .sum(); + if (storedItems + toStore <= ITEM_BUFFER_CAPACITY) { + for (ItemStack item : items) { + storeItemToStorage(item); + } + return true; + } + state = QuarryWorkState.STOPPED_WAITING_FOR_ITEM_SPACE; + return false; + } + + private void storeItemToStorage(ItemStack item) { + int stackSize = item.stackSize; + // Use an arbitrary size for all items of this type (type, meta, nbt), + // since we store the amount as the value + item.stackSize = 0; + storedItems += stackSize; + itemStorage.mergeInt(item, stackSize, Integer::sum); + } + + private boolean fluidStorageIsEmpty() { + return fluidStorage.stream() + .mapToInt(FluidTank::getFluidAmount) + .sum() == 0; + } + + /** + * Tries to eject internally stored items & fluids to adjacent containers + * + * @return False if the adjacent TEs changed without us noticing, and we need to rescan + */ + private boolean ejectStoredToAdjacent() { + // TODO: call markDirty() + if (storedItems > 0) { + for (IInventory inventory : sidedItemAcceptors.values()) { + if (inventory != null) { + for (int i = 0; i < inventory.getSizeInventory(); i++) { + ItemStack slotItem = inventory.getStackInSlot(i); + if (slotItem != null) { + // There is already an item in this slot, see if we can merge with it + int canBeAddedToStack = Math + .min(slotItem.getMaxStackSize(), inventory.getInventoryStackLimit()) + - slotItem.stackSize; + if (canBeAddedToStack > 0) { + ItemStack key = slotItem.copy(); + key.stackSize = 0; + + // Do we also have this item type in our storage? + int storedAmount = itemStorage.getOrDefault(key, -1); + if (storedAmount > 0) { + int toBeAdded = Math.min(storedAmount, canBeAddedToStack); + + ItemStack modifiedStack = key.copy(); + modifiedStack.stackSize = slotItem.stackSize + toBeAdded; + if (inventory.isItemValidForSlot(i, modifiedStack)) { + inventory.setInventorySlotContents(i, modifiedStack); + + itemStorage.put(key, storedAmount - toBeAdded); + storedItems -= toBeAdded; + } + } + } + } else { + // Empty slot, don't mind us + Object2IntMap.Entry entry = peekItem(); + if (entry != null) { + int canBeAddedToSlot = Math + .min(entry.getIntValue(), inventory.getInventoryStackLimit()); + if (canBeAddedToSlot > 0) { + ItemStack item = entry.getKey() + .copy(); + item.stackSize = canBeAddedToSlot; + if (inventory.isItemValidForSlot(i, item)) { + inventory.setInventorySlotContents(i, item); + + itemStorage.put(entry.getKey(), entry.getIntValue() - canBeAddedToSlot); + storedItems -= canBeAddedToSlot; + } + } + } else { + // Not sure why, but it seems our inventory is empty even though we thought it wasn't + // Lets re-count + storedItems = itemStorage.values() + .intStream() + .sum(); + } + } + } + + if (storedItems == 0) break; + } else { + return false; + } + } + } + + if (fluidStorageIsEmpty()) return true; + + for (Map.Entry entry : sidedFluidAcceptors.entrySet()) { + if (entry.getValue() != null) { + for (FluidTank tank : fluidStorage) { + if (tank.getFluidAmount() > 0) { + int toStore = entry.getValue() + .fill( + entry.getKey() + .getOpposite(), + tank.drain(tank.getCapacity(), false), + false); + if (toStore > 0) { + entry.getValue() + .fill( + entry.getKey() + .getOpposite(), + tank.drain(toStore, true), + true); + } + } + } + + if (fluidStorageIsEmpty()) break; + } else { + return false; + } + } + return true; + } + + /** + * Get the first item & int entry in the storage that we actually have something of + * + * @return An item if we have any, null otherwise + */ + public @Nullable Object2IntMap.Entry peekItem() { + for (Object2IntMap.Entry entry : itemStorage.object2IntEntrySet()) { + if (entry.getIntValue() > 0) { + return entry; + } + } + return null; + } + + public void scanSidesForTEs() { + sidedFluidAcceptors.clear(); + sidedItemAcceptors.clear(); + ArrayList loadedAdjacentChunks = new ArrayList<>(); + + // TODO: Load cross around if some side is in an adajcent chunk, only for the duration of this scan + for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { + int directionChunkX = (direction.offsetX + this.xCoord) >> 4; + int directionChunkZ = (direction.offsetZ + this.zCoord) >> 4; + if ((directionChunkX != this.selfChunkX || directionChunkZ != this.selfChunkZ) && !areWeLoadingThisChunk(directionChunkX, directionChunkZ)) { + loadChunkShifted(directionChunkX, directionChunkZ); + loadedAdjacentChunks.add(new ChunkCoordIntPair(directionChunkX, directionChunkZ)); + } + } + + // TODO: remove adjacent chunks that contain a valid side, from loadedAdjacentChunks + for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { + TileEntity te = this.worldObj.getTileEntity( + direction.offsetX + this.xCoord, + direction.offsetY + this.yCoord, + direction.offsetZ + this.zCoord); + + // TODO: Upgrades people + // if (te instanceof IQuarryUpgrade quarryUpgrade) { + // + // } + + if (te instanceof IFluidHandler fluidHandler) { + sidedFluidAcceptors.put(direction, fluidHandler); + + } + + if (direction == ForgeDirection.UP) { + if (te instanceof IInventory inventory) { + sidedItemAcceptors.put(direction, inventory); + } + } + } + + // TODO: Unload chunks in loadedAdjacentChunks + } + + // TileEntity & LoadableTE + @Override + public void validate() { + super.validate(); + if (state != QuarryWorkState.FINISHED) { + if (workArea != null) { + loadChunkShifted(chunkX, chunkZ); + } + scanSidesForTEs(); + } + } + + // TileEntity + @Override + public void updateEntity() { + if (worldObj.isRemote) return; + if (facing == ForgeDirection.UNKNOWN) return; + if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty()) return; + + int brokenBlocksTick = 0; + if (state == QuarryWorkState.STOPPED_WAITING_FOR_FLUID_SPACE + || state == QuarryWorkState.STOPPED_WAITING_FOR_ITEM_SPACE) { + if (tryHarvestCurrentBlock()) { + state = QuarryWorkState.RUNNING; + worldObj.setBlock(dx, dy, dz, Blocks.air); + brokenBlocksTick++; + } + } + + if (state == QuarryWorkState.RUNNING) { + while (brokenBlocksTick < STEPS_PER_TICK && stepPos()) { + // TODO: Remove after this has been tested by others + if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { + throw new RuntimeException( + String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); + } + + if (tryHarvestCurrentBlock()) { + worldObj.setBlock(dx, dy, dz, Blocks.air); + brokenBlocksTick++; + } else { + if (state != QuarryWorkState.RUNNING) + break; + // energy cost for moving but not breaking + } + } + if (brokenBlocksTick < STEPS_PER_TICK && state == QuarryWorkState.RUNNING) { + state = QuarryWorkState.FINISHED; + unloadSelf(); + } + if (brokenBlocksTick > 0) { + markDirty(); + } + + this.brokenBlocksTotal += brokenBlocksTick; + } + + if (worldObj.getTotalWorldTime() % 4 == 0) { + // Move internally stored stuff to adjacent blocks + if (!ejectStoredToAdjacent()) scanSidesForTEs(); + } + } + @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); @@ -183,6 +513,27 @@ public void readFromNBT(NBTTagCompound nbt) { chunkZ = dz >> 4; } brokenBlocksTotal = nbt.getInteger("blocks"); + energyStorage.readFromNBT(nbt); + + NBTTagList tanksNBT = nbt.getTagList("tanks", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < tanksNBT.tagCount(); i++) { + NBTTagCompound tag = tanksNBT.getCompoundTagAt(i); + int slot = tag.getByte("Slot") & 255; + if (slot < fluidStorage.size()) { + fluidStorage.get(slot) + .readFromNBT(tag); + } + } + + NBTTagList itemsNBT = nbt.getTagList("Items", Constants.NBT.TAG_COMPOUND); + storedItems = 0; + for (int i = 0; i < itemsNBT.tagCount(); i++) { + NBTTagCompound tag = itemsNBT.getCompoundTagAt(i); + ItemStack item = ItemStack.loadItemStackFromNBT(tag); + if (item != null) { + storeItemToStorage(item); + } + } } @Override @@ -197,20 +548,111 @@ public void writeToNBT(NBTTagCompound nbt) { nbt.setInteger("dz", dz); } nbt.setInteger("blocks", brokenBlocksTotal); + energyStorage.writeToNBT(nbt); + + NBTTagList tanksNBT = new NBTTagList(); + for (int i = 0; i < fluidStorage.size(); i++) { + NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("Slot", (byte) i); + fluidStorage.get(i) + .writeToNBT(tag); + tanksNBT.appendTag(tag); + } + nbt.setTag("tanks", tanksNBT); + + NBTTagList itemsNBT = new NBTTagList(); + for (Object2IntMap.Entry entry : itemStorage.object2IntEntrySet()) { + if (entry.getIntValue() > 0 && entry.getKey() != null) { + NBTTagCompound tag = new NBTTagCompound(); + ItemStack item = entry.getKey().copy(); + item.stackSize = entry.getIntValue(); + item.writeToNBT(tag); + itemsNBT.appendTag(tag); + } + } + nbt.setTag("Items", itemsNBT); } + // LoadableTE @Override public boolean keepsItselfLoaded() { return state != QuarryWorkState.FINISHED; } + // IEnergyReceiver + @Override + public int receiveEnergy(ForgeDirection forgeDirection, int i, boolean b) { + return energyStorage.receiveEnergy(i, b); + } + + @Override + public int getEnergyStored(ForgeDirection forgeDirection) { + return energyStorage.getEnergyStored(); + } + + @Override + public int getMaxEnergyStored(ForgeDirection forgeDirection) { + return energyStorage.getMaxEnergyStored(); + } + + @Override + public boolean canConnectEnergy(ForgeDirection forgeDirection) { + return true; + } + + // IFluidHandler + @Override + public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { + return 0; + } + + @Override + public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { + for (FluidTank tank : fluidStorage) { + if (resource != null && resource.isFluidEqual(tank.getFluid())) { + return tank.drain(resource.amount, doDrain); + } + } + return null; + } + + @Override + public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { + for (FluidTank tank : fluidStorage) { + if (tank.getFluidAmount() > 0) { + return tank.drain(maxDrain, doDrain); + } + } + return null; + } + + @Override + public boolean canFill(ForgeDirection from, Fluid fluid) { + return false; + } + + @Override + public boolean canDrain(ForgeDirection from, Fluid fluid) { + return true; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection from) { + return fluidStorage.stream() + .map(FluidTank::getInfo) + .toArray(FluidTankInfo[]::new); + } + public enum QuarryWorkState { STOPPED, + STOPPED_WAITING_FOR_FLUID_SPACE, + STOPPED_WAITING_FOR_ITEM_SPACE, FINISHED, - RUNNING; + RUNNING } public static class Area2d { + // The corner that has the lower x&y public final Vector2i low; // The corner that has the higher x&y @@ -254,8 +696,8 @@ public void writeNBTTag(NBTTagCompound nbt) { public static Area2d fromNBTTag(NBTTagCompound nbt) { NBTTagCompound tag = nbt.getCompoundTag("area"); - int lowX = tag.getInteger("lowX"); - int lowZ = tag.getInteger("lowZ"); + int lowX = tag.getInteger("lowX"); + int lowZ = tag.getInteger("lowZ"); int highX = tag.getInteger("highX"); int highZ = tag.getInteger("highY"); return new Area2d(lowX, lowZ, highX, highZ); @@ -268,4 +710,23 @@ public boolean equals(Object obj) { return low.equals(other.low) && high.equals(other.high); } } + + protected static class ItemStackHashStrategy implements Hash.Strategy { + + public static final ItemStackHashStrategy instance = new ItemStackHashStrategy(); + + @Override + public int hashCode(ItemStack itemStack) { + if (itemStack == null || itemStack.getItem() == null) return 0; + return Objects.hash(itemStack.getItem(), itemStack.getItemDamage(), itemStack.getTagCompound()); + } + + @Override + public boolean equals(ItemStack a, ItemStack b) { + if (a == null && b == null) return true; + if (a == null || b == null) return false; + return a.getItem() == b.getItem() && a.getItemDamage() == b.getItemDamage() + && Objects.equals(a.getTagCompound(), b.getTagCompound()); + } + } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java index 4b8daeb4..54f8d0f9 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java @@ -1,24 +1,26 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities.utils; -import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; +import javax.annotation.Nullable; + import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.ChunkCoordIntPair; import net.minecraftforge.common.ForgeChunkManager; -import javax.annotation.Nullable; +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; /** * Base class for TileEntities that need to keep chunks loaded. * Handles chunk loading tickets and manages forced chunk loading through Forge's ChunkManager. */ public class LoadableTE extends TileEntity { + /** The chunk loading ticket used to force chunks to stay loaded */ @Nullable private ForgeChunkManager.Ticket ticket = null; - private int selfChunkX; - private int selfChunkZ; - protected boolean selfIsLoaded = true; + protected int selfChunkX; + protected int selfChunkZ; + protected boolean selfIsLoaded = false; /** * Initializes this class after the TE is loaded. @@ -28,7 +30,6 @@ public class LoadableTE extends TileEntity { @Override public void validate() { super.validate(); - selfIsLoaded = false; selfChunkX = xCoord >> 4; selfChunkZ = zCoord >> 4; if (worldObj != null && !worldObj.isRemote) { @@ -45,11 +46,17 @@ public void validate() { * @param chunkCoord The chunk coordinates to load */ public void loadChunk(ChunkCoordIntPair chunkCoord) { - if (selfIsLoaded && chunkCoord.chunkXPos == selfChunkX && chunkCoord.chunkZPos == selfChunkZ && keepsItselfLoaded()) return; + if (selfIsLoaded && chunkCoord.chunkXPos == selfChunkX + && chunkCoord.chunkZPos == selfChunkZ + && keepsItselfLoaded()) return; if (requestTicket()) { ForgeChunkManager.forceChunk(ticket, chunkCoord); } else { - UtilitiesInExcess.LOG.error("Failed to get ticket to load chunk at {} {} for Mod {}", chunkCoord.chunkXPos, chunkCoord.chunkZPos, UtilitiesInExcess.MODID); + UtilitiesInExcess.LOG.error( + "Failed to get ticket to load chunk at {} {} for Mod {}", + chunkCoord.chunkXPos, + chunkCoord.chunkZPos, + UtilitiesInExcess.MODID); } } @@ -70,7 +77,7 @@ public void loadChunkShifted(int x, int z) { * @param z Block Z coordinate */ public void loadChunk(int x, int z) { - loadChunk(new ChunkCoordIntPair(x >> 4, z >> 4)); + loadChunk(new ChunkCoordIntPair(x >> 4, z >> 4)); } /** @@ -84,7 +91,11 @@ void unloadChunk(ChunkCoordIntPair chunkCoord) { if (requestTicket()) { ForgeChunkManager.unforceChunk(ticket, chunkCoord); } else { - UtilitiesInExcess.LOG.error("Failed to get ticket to unload? chunk at {} {} for Mod {}", chunkCoord.chunkXPos, chunkCoord.chunkZPos, UtilitiesInExcess.MODID); + UtilitiesInExcess.LOG.error( + "Failed to get ticket to unload? chunk at {} {} for Mod {}", + chunkCoord.chunkXPos, + chunkCoord.chunkZPos, + UtilitiesInExcess.MODID); } } @@ -123,8 +134,8 @@ public void loadSelf() { */ public void unloadSelf() { if (selfIsLoaded) { - unloadChunkShifted(selfChunkX, selfChunkZ); selfIsLoaded = false; + unloadChunkShifted(selfChunkX, selfChunkZ); } } @@ -146,7 +157,8 @@ public void invalidateTicket() { */ private boolean requestTicket() { if (ticket != null) return true; - ticket = ForgeChunkManager.requestTicket(UtilitiesInExcess.uieInstance, worldObj, ForgeChunkManager.Type.NORMAL); + ticket = ForgeChunkManager + .requestTicket(UtilitiesInExcess.uieInstance, worldObj, ForgeChunkManager.Type.NORMAL); if (ticket != null) { // Store TileEntity coordinates in ticket data for reloading @@ -180,6 +192,18 @@ public boolean keepsItselfLoaded() { return false; } + /** + * Check if this TE is already forcing a specific chunk to stay loaded. + * It is advised to load a chunk that is required for the TE to function properly, even if it is already laoded by + * another mod, because that mod may for some reason stop force-loading it. + * + * @return True if we are already loading this chunk, otherwise false. + */ + public boolean areWeLoadingThisChunk(int chunkX, int chunkY) { + ChunkCoordIntPair chunk = new ChunkCoordIntPair(chunkX, chunkY); + return this.ticket != null && this.ticket.getChunkList().contains(chunk); + } + /** * Called when the chunk containing this TileEntity is unloaded. * Releases the chunk loading ticket to prevent memory leaks. diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java index 0980d670..665358e4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/BlockConfig.java @@ -13,6 +13,7 @@ public static void registerConfig() throws ConfigException { ConfigurationManager.registerConfig(CursedEarthConfig.class); ConfigurationManager.registerConfig(EnderLotusConfig.class); ConfigurationManager.registerConfig(GeneratorConfig.class); + ConfigurationManager.registerConfig(EnderQuarryConfig.class); } @Config.DefaultBoolean(true) @@ -82,9 +83,6 @@ public static void registerConfig() throws ConfigException { @Config.DefaultBoolean(true) public static boolean enableEndOfTimePortal; - @Config.DefaultBoolean(true) - public static boolean enableEnderQuarry; - @Config.Comment("Cursed Earth Configuration") public static final CursedEarth cursedEarth = new CursedEarth(); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java new file mode 100644 index 00000000..f485a721 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java @@ -0,0 +1,26 @@ +package com.fouristhenumber.utilitiesinexcess.config.blocks; + +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; +import com.gtnewhorizon.gtnhlib.config.Config; + +@Config(modid = UtilitiesInExcess.MODID, category = "blocks.ender_quarry") +@Config.Comment("Ender Quarry Configuration") +@Config.LangKey("utilitiesinexcess.config.block.ender_quarry") +public class EnderQuarryConfig { + + @Config.DefaultBoolean(true) + public static boolean enableEnderQuarry; + + @Config.Comment("Energy (RF) capacity of the machine.") + @Config.DefaultInt(10_000_000) + public static int enderQuarryEnergyStorage; + + @Config.Comment("Amount of fluid tanks, with one for each fluid type.") + @Config.DefaultInt(2) + public static int enderQuarryFluidTankAmount; + + @Config.Comment("Amount of fluid (in mB) that can be stored per tank.") + @Config.DefaultInt(128_000) + @Config.RangeInt(min = 16_000, max = 1_024_000) + public static int enderQuarryFluidTankStorage; +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java index 82adccf4..0d99ce83 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/TEChunkLoadingCallback.java @@ -1,13 +1,15 @@ package com.fouristhenumber.utilitiesinexcess.utils; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; +import java.util.List; + import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; import net.minecraftforge.common.ForgeChunkManager; -import java.util.List; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; public class TEChunkLoadingCallback implements ForgeChunkManager.LoadingCallback { + @Override public void ticketsLoaded(List tickets, World world) { for (ForgeChunkManager.Ticket ticket : tickets) { From a02c5236b2532b4afd7f1d7f790276e5778b22f6 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Mon, 24 Nov 2025 22:01:49 +0100 Subject: [PATCH 05/23] Make mining logic accept more cases, add energy cost, fix weird stackoverflow from chunk loading --- .../common/blocks/BlockEnderQuarry.java | 9 +- .../tileentities/TileEntityEnderQuarry.java | 124 +++++++++++++----- .../common/tileentities/utils/LoadableTE.java | 6 +- .../config/blocks/EnderQuarryConfig.java | 5 + 4 files changed, 105 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java index fd1f88ea..5127bf12 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java @@ -7,7 +7,6 @@ import net.minecraft.block.material.Material; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; @@ -63,10 +62,10 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer BlockPos farFront = offsetByForward(inFront, quarry.facing, 64, 0); farFront = offsetByRight(farFront, quarry.facing, 64, 0); - for (int i = 1; i < 256; i++) { - worldIn.setBlock(inFront.x, i, inFront.z, Blocks.diamond_block); - worldIn.setBlock(farFront.x, i, farFront.z, Blocks.diamond_block); - } + // for (int i = 1; i < 256; i++) { + // worldIn.setBlock(inFront.x, i, inFront.z, Blocks.diamond_block); + // worldIn.setBlock(farFront.x, i, farFront.z, Blocks.diamond_block); + // } player.addChatComponentMessage( new ChatComponentText(String.format("Set up work area from %s to %s", inFront, farFront))); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index e060d2ee..e28fa602 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -8,8 +8,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; -import cpw.mods.fml.common.FMLLog; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; @@ -19,7 +17,6 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.ChunkCoordIntPair; -import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; @@ -34,6 +31,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; @@ -58,7 +56,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int chunkX; private int chunkZ; private int brokenBlocksTotal; - private final HashMap sidedItemAcceptors = new HashMap<>(); + private final HashMap sidedItemAcceptors = new HashMap<>(); private final HashMap sidedFluidAcceptors = new HashMap<>(); protected final EnergyStorage energyStorage = new EnergyStorage(EnderQuarryConfig.enderQuarryEnergyStorage); @@ -86,8 +84,13 @@ public String getState() { .format("Quarry is currently mining at %d %d %d, has already mined %d", dx, dy, dz, brokenBlocksTotal); case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids"; case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items"; + case STOPPED_WAITING_FOR_ENERGY -> "Quarry is missing energy"; + case THROTTLED_BY_ENERGY -> "Quarry is running, but not at full speed because of missing energy"; case STOPPED -> "Quarry is stopped."; - case FINISHED -> String.format("Quarry has finished after mining %d blocks, still holding %d items", brokenBlocksTotal, storedItems); + case FINISHED -> String.format( + "Quarry has finished after mining %d blocks%s", + brokenBlocksTotal, + String.format(storedItems > 0 ? ", still holding %d items" : "", storedItems)); }; } @@ -173,15 +176,34 @@ private boolean stepPos() { * Tries to store the resulting resources of the block at the current position, * and store the items / fluid to the internal storage. * - * @return True if we managed to harvest the current block, false if we couldn't / weren't allowed to touch/harvest - * it. + * @return A boolean tuple of: <Could we work & harvest the block, Did we skip this block> */ - private boolean tryHarvestCurrentBlock() { - if (worldObj.isAirBlock(dx, dy, dz) || worldObj.getBlock(dx, dy, dz) - .getMaterial() == Material.air) return false; - + private boolean[] tryHarvestCurrentBlock() { Block block = worldObj.getBlock(dx, dy, dz); - return harvestAndStoreBlock(block); + if (block.getMaterial() == Material.air || worldObj.isAirBlock(dx, dy, dz) + || block.getBlockHardness(worldObj, dx, dy, dz) < 0 + || block.getBlockHardness(worldObj, dx, dy, dz) > 100) { + return new boolean[] { tryConsumeEnergy(0.0f, false), true }; + } ; + + float hardness = block.getBlockHardness(worldObj, dx, dy, dz); + if (tryConsumeEnergy(hardness, true)) { + return new boolean[] { (harvestAndStoreBlock(block) && tryConsumeEnergy(hardness, false)), false }; + } + return new boolean[] { false, false }; + } + + private boolean tryConsumeEnergy(float hardness, boolean simulate) { + float costMultiplier = 1.0f; + int cost = (int) ((hardness == 0 ? 100 : EnderQuarryConfig.enderQuarryBaseRFCost) * costMultiplier); + if (energyStorage.extractEnergy(cost, true) >= cost) { + if (!simulate) { + energyStorage.extractEnergy(cost, false); + } + return true; + } + state = QuarryWorkState.STOPPED_WAITING_FOR_ENERGY; + return false; } private boolean harvestAndStoreBlock(Block block) { @@ -211,7 +233,8 @@ private boolean harvestAndStoreBlock(Block block) { // Block just has no drops return true; } catch (Exception ignored) { - UtilitiesInExcess.LOG.error("Failed while trying to harvest block {} at {} {} {}.", block.toString(), dx, dy, dz); + UtilitiesInExcess.LOG + .error("Failed while trying to harvest block {} at {} {} {}.", block.toString(), dx, dy, dz); return false; } } @@ -285,7 +308,6 @@ private boolean fluidStorageIsEmpty() { * @return False if the adjacent TEs changed without us noticing, and we need to rescan */ private boolean ejectStoredToAdjacent() { - // TODO: call markDirty() if (storedItems > 0) { for (IInventory inventory : sidedItemAcceptors.values()) { if (inventory != null) { @@ -399,22 +421,25 @@ public void scanSidesForTEs() { sidedItemAcceptors.clear(); ArrayList loadedAdjacentChunks = new ArrayList<>(); - // TODO: Load cross around if some side is in an adajcent chunk, only for the duration of this scan + // Make sure the directly adjacent blocks in different chunks are loaded whilst checking for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { int directionChunkX = (direction.offsetX + this.xCoord) >> 4; int directionChunkZ = (direction.offsetZ + this.zCoord) >> 4; - if ((directionChunkX != this.selfChunkX || directionChunkZ != this.selfChunkZ) && !areWeLoadingThisChunk(directionChunkX, directionChunkZ)) { + if ((directionChunkX != this.selfChunkX || directionChunkZ != this.selfChunkZ) + && !areWeLoadingThisChunk(directionChunkX, directionChunkZ)) { loadChunkShifted(directionChunkX, directionChunkZ); loadedAdjacentChunks.add(new ChunkCoordIntPair(directionChunkX, directionChunkZ)); } } - // TODO: remove adjacent chunks that contain a valid side, from loadedAdjacentChunks for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { TileEntity te = this.worldObj.getTileEntity( direction.offsetX + this.xCoord, direction.offsetY + this.yCoord, direction.offsetZ + this.zCoord); + ChunkCoordIntPair chunk = new ChunkCoordIntPair( + (direction.offsetX + this.xCoord) >> 4, + (direction.offsetZ + this.zCoord) >> 4); // TODO: Upgrades people // if (te instanceof IQuarryUpgrade quarryUpgrade) { @@ -423,7 +448,8 @@ public void scanSidesForTEs() { if (te instanceof IFluidHandler fluidHandler) { sidedFluidAcceptors.put(direction, fluidHandler); - + // If there is actually a TE present that we want, keep this chunk loaded (if it is a different one) + loadedAdjacentChunks.remove(chunk); } if (direction == ForgeDirection.UP) { @@ -433,7 +459,18 @@ public void scanSidesForTEs() { } } - // TODO: Unload chunks in loadedAdjacentChunks + // Unload all the temporarily loaded chunks + for (ChunkCoordIntPair loadedChunk : loadedAdjacentChunks) { + unloadChunk(loadedChunk); + } + } + + /** + * Remove / replace the current block. + * Does not check for anything, should be called after tryHarvestCurrentBlock() returns true. + */ + private void removeCurrentBlock() { + worldObj.setBlock(dx, dy, dz, Blocks.air); } // TileEntity & LoadableTE @@ -444,7 +481,13 @@ public void validate() { if (workArea != null) { loadChunkShifted(chunkX, chunkZ); } - scanSidesForTEs(); + /* + * preferably we would call scanSidesForTEs() here, however + * the contained getTileEntity() seems to always re-load the chunk at this stage + * which then re-validates back to this function, eventually leading to a stackoverflow + * Instead we add a null inventory, so that it gets rechecked later + **/ + sidedItemAcceptors.put(ForgeDirection.UP, null); } } @@ -456,12 +499,16 @@ public void updateEntity() { if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty()) return; int brokenBlocksTick = 0; - if (state == QuarryWorkState.STOPPED_WAITING_FOR_FLUID_SPACE - || state == QuarryWorkState.STOPPED_WAITING_FOR_ITEM_SPACE) { - if (tryHarvestCurrentBlock()) { + if (state != QuarryWorkState.RUNNING && state != QuarryWorkState.FINISHED && state != QuarryWorkState.STOPPED) { + boolean[] harvestResult = tryHarvestCurrentBlock(); + boolean wasAbleToHarvest = harvestResult[0]; + boolean blockWasSkipped = harvestResult[1]; + if (wasAbleToHarvest) { state = QuarryWorkState.RUNNING; - worldObj.setBlock(dx, dy, dz, Blocks.air); - brokenBlocksTick++; + if (!blockWasSkipped) { + removeCurrentBlock(); + brokenBlocksTick++; + } } } @@ -473,13 +520,17 @@ public void updateEntity() { String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); } - if (tryHarvestCurrentBlock()) { - worldObj.setBlock(dx, dy, dz, Blocks.air); - brokenBlocksTick++; + boolean[] harvestResult = tryHarvestCurrentBlock(); + boolean wasAbleToHarvest = harvestResult[0]; + boolean blockWasSkipped = harvestResult[1]; + if (wasAbleToHarvest) { + if (!blockWasSkipped) { + removeCurrentBlock(); + brokenBlocksTick++; + } } else { - if (state != QuarryWorkState.RUNNING) - break; - // energy cost for moving but not breaking + // Check if something has stopped us (out of space / energy) + if (state != QuarryWorkState.RUNNING) break; } } if (brokenBlocksTick < STEPS_PER_TICK && state == QuarryWorkState.RUNNING) { @@ -488,6 +539,12 @@ public void updateEntity() { } if (brokenBlocksTick > 0) { markDirty(); + if (state == QuarryWorkState.STOPPED_WAITING_FOR_ENERGY) { + // We were still able to mine some blocks this tick, so we don't consider this fully stopped + // If we fail to harvest again at the start of the next tick, it will be set to STOPPED_... either + // way + state = QuarryWorkState.THROTTLED_BY_ENERGY; + } } this.brokenBlocksTotal += brokenBlocksTick; @@ -564,7 +621,8 @@ public void writeToNBT(NBTTagCompound nbt) { for (Object2IntMap.Entry entry : itemStorage.object2IntEntrySet()) { if (entry.getIntValue() > 0 && entry.getKey() != null) { NBTTagCompound tag = new NBTTagCompound(); - ItemStack item = entry.getKey().copy(); + ItemStack item = entry.getKey() + .copy(); item.stackSize = entry.getIntValue(); item.writeToNBT(tag); itemsNBT.appendTag(tag); @@ -647,6 +705,8 @@ public enum QuarryWorkState { STOPPED, STOPPED_WAITING_FOR_FLUID_SPACE, STOPPED_WAITING_FOR_ITEM_SPACE, + STOPPED_WAITING_FOR_ENERGY, + THROTTLED_BY_ENERGY, FINISHED, RUNNING } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java index 54f8d0f9..75508ef1 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/LoadableTE.java @@ -86,7 +86,7 @@ public void loadChunk(int x, int z) { * * @param chunkCoord The chunk coordinates to unload */ - void unloadChunk(ChunkCoordIntPair chunkCoord) { + public void unloadChunk(ChunkCoordIntPair chunkCoord) { if (chunkCoord.chunkXPos == selfChunkX && chunkCoord.chunkZPos == selfChunkZ && keepsItselfLoaded()) return; if (requestTicket()) { ForgeChunkManager.unforceChunk(ticket, chunkCoord); @@ -201,7 +201,8 @@ public boolean keepsItselfLoaded() { */ public boolean areWeLoadingThisChunk(int chunkX, int chunkY) { ChunkCoordIntPair chunk = new ChunkCoordIntPair(chunkX, chunkY); - return this.ticket != null && this.ticket.getChunkList().contains(chunk); + return this.ticket != null && this.ticket.getChunkList() + .contains(chunk); } /** @@ -223,4 +224,5 @@ public void invalidate() { super.invalidate(); invalidateTicket(); } + } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java index f485a721..3d865068 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java @@ -23,4 +23,9 @@ public class EnderQuarryConfig { @Config.DefaultInt(128_000) @Config.RangeInt(min = 16_000, max = 1_024_000) public static int enderQuarryFluidTankStorage; + + @Config.Comment("Base factor of RF that is used per operation. Is influenced by upgrades & block hardness.") + @Config.DefaultInt(1_000) + @Config.RangeInt(min = 100, max = 1_024_000) + public static int enderQuarryBaseRFCost; } From 3ae85b25a07d37c26550f08c7efe964592801837 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Fri, 28 Nov 2025 01:04:24 +0100 Subject: [PATCH 06/23] Add first pass of ender marker, need to actually use in quarry now --- dependencies.gradle | 2 +- .../utilitiesinexcess/ModBlocks.java | 2 + .../utilitiesinexcess/UtilitiesInExcess.java | 2 + .../common/blocks/BlockEnderMarker.java | 112 ++++++++++ .../tileentities/TileEntityEnderMarker.java | 208 ++++++++++++++++++ .../common/tileentities/utils/IFacingTE.java | 21 ++ .../utils/ForgeEventHandler.java | 18 ++ .../utilitiesinexcess/utils/Tuple.java | 20 ++ .../utilitiesinexcess/blocks/ender_quarry.png | Bin 2028 -> 0 bytes .../blockstates/ender_marker.json | 5 + .../blockstates/ender_quarry.json | 5 +- .../models/blocks/ender_marker.json | 197 +++++++++++++++++ .../textures/blocks/ender_marker.png | Bin 0 -> 641 bytes 13 files changed, 587 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/IFacingTE.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/utils/Tuple.java delete mode 100644 src/main/resources/assets/utilitiesinexcess/blocks/ender_quarry.png create mode 100644 src/main/resources/assets/utilitiesinexcess/blockstates/ender_marker.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/ender_marker.json create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/ender_marker.png diff --git a/dependencies.gradle b/dependencies.gradle index 0e6a6c80..4d95a685 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -34,7 +34,7 @@ * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph */ dependencies { - implementation("com.github.GTNewHorizons:GTNHLib:0.8.9:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.8.15:dev") // TODO: remove MUI1 dep when the implicit dependency // TODO: in IItemHandlerModifiable is removed api("com.github.GTNewHorizons:ModularUI:1.2.20:dev") diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java index 6ea4e6f4..2223f1d8 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java @@ -13,6 +13,7 @@ import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockCursedEarth; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockDrum; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderLotus; +import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderMarker; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderQuarry; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEtherealGlass; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockFloating; @@ -107,6 +108,7 @@ public enum ModBlocks { UNDERWORLD_PORTAL(BlockConfig.enableUnderWorldPortal && UnderWorldConfig.enableUnderWorld, new BlockPortalUnderWorld(), "underworld_portal"), END_OF_TIME_PORTAL(BlockConfig.enableEndOfTimePortal && EndOfTimeConfig.enableEndOfTime, new BlockPortalEndOfTime(), BlockPortalEndOfTime.ItemBlockPortalEndOfTime.class, "temporal_gate"), ENDER_QUARRY(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarry(), "ender_quarry"), + ENDER_MARKER(EnderQuarryConfig.enableEnderQuarry, new BlockEnderMarker(), "ender_marker"), ; // leave trailing semicolon // spotless:on diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java index f3612ff8..465be6ef 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java @@ -15,6 +15,7 @@ import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityBlockUpdateDetector; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityConveyor; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityDrum; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityMarginallyMaximisedChest; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPortalUnderWorld; @@ -131,6 +132,7 @@ public void init(FMLInitializationEvent event) { GameRegistry.registerTileEntity(TileEntityPinkGenerator.class, "TileEntityPinkGeneratorUIE"); GameRegistry.registerTileEntity(TileEntityNetherStarGenerator.class, "TileEntityNetherStarGeneratorUIE"); GameRegistry.registerTileEntity(TileEntityEnderQuarry.class, "TileEntityEnderQuarryUIE"); + GameRegistry.registerTileEntity(TileEntityEnderMarker.class, "TileEntityEnderMarkerUIE"); lapisAetheriusRenderID = RenderingRegistry.getNextAvailableRenderId(); RenderingRegistry.registerBlockHandler(new LapisAetheriusRenderer()); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java new file mode 100644 index 00000000..4a2fec9a --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java @@ -0,0 +1,112 @@ +package com.fouristhenumber.utilitiesinexcess.common.blocks; + +import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; + +import java.util.List; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.Explosion; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; + +public class BlockEnderMarker extends BlockContainer { + + public static final ForgeDirection[] HORIZONTAL_DIRECTIONS = new ForgeDirection[] { ForgeDirection.NORTH, + ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST }; + + public BlockEnderMarker() { + super(Material.iron); + setBlockName("ender_marker"); + setBlockTextureName("utilitiesinexcess:ender_marker"); + setBlockBounds(7F / 16F, 0F / 16F, 7F / 16F, 9F / 16F, 13.5F / 16F, 9F / 16F); + setLightOpacity(0); + } + + @Override + public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase placer, ItemStack itemIn) { + if (worldIn.isRemote) return; + TileEntity te = worldIn.getTileEntity(x, y, z); + if (te instanceof TileEntityEnderMarker marker) { + marker.checkForAlignedMarkers(); + } + } + + @Override + public void addCollisionBoxesToList(World worldIn, int x, int y, int z, AxisAlignedBB mask, + List list, Entity collider) { + this.setBlockBounds(7F / 16F, 0F / 16F, 7F / 16F, 9F / 16F, 13.5F / 16F, 9F / 16F); + super.addCollisionBoxesToList(worldIn, x, y, z, mask, list, collider); + } + + @Override + public void onBlockDestroyedByPlayer(World worldIn, int x, int y, int z, int meta) { + if (worldIn.isRemote) return; + TileEntity te = worldIn.getTileEntity(x, y, z); + if (te instanceof TileEntityEnderMarker marker) { + marker.teardownConnections(); + } + } + + @Override + public void onBlockDestroyedByExplosion(World worldIn, int x, int y, int z, Explosion explosionIn) { + this.onBlockDestroyedByPlayer(worldIn, x, y, z, 0); + } + + @Override + public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neighbor) { + super.onNeighborBlockChange(worldIn, x, y, z, neighbor); + TileEntity te = worldIn.getTileEntity(x, y, z); + if (!worldIn.isRemote && te instanceof TileEntityEnderMarker marker) { + marker.checkForAlignedMarkers(); + } + } + + @Override + public void randomDisplayTick(World worldIn, int x, int y, int z, Random random) { + int meta = worldIn.getBlockMetadata(x, y, z); + for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { + if ((meta & (1 << (dir.ordinal() - 2))) != 0) { + for (int i = 0; i < 7; i++) { + worldIn.spawnParticle( + "reddust", + x + 0.5D + ((double) dir.offsetX / 4) + dir.offsetX * random.nextDouble(), + y + 0.7D, + z + 0.5D + ((double) dir.offsetZ / 4) + dir.offsetZ * random.nextDouble(), + 0.5D + ((double) dir.offsetX / 2) * i, + 0.0D, + 0.5D + ((double) dir.offsetZ / 2) * i); + } + } + } + } + + @Override + public TileEntity createNewTileEntity(World worldIn, int meta) { + return new TileEntityEnderMarker(); + } + + @Override + public boolean isOpaqueCube() { + return false; + } + + @Override + public boolean renderAsNormalBlock() { + return false; + } + + @Override + public int getRenderType() { + return JSON_ISBRH_ID; + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java new file mode 100644 index 00000000..61154c69 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -0,0 +1,208 @@ +package com.fouristhenumber.utilitiesinexcess.common.tileentities; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.Nullable; +import org.joml.Vector4i; + +import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.IFacingTE; +import com.fouristhenumber.utilitiesinexcess.utils.Tuple; +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; + +public class TileEntityEnderMarker extends TileEntity implements IFacingTE { + + public static final ForgeDirection[] HORIZONTAL_DIRECTIONS = new ForgeDirection[] { ForgeDirection.NORTH, + ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST }; + public static final ConcurrentHashMap> registeredMarkers = + new ConcurrentHashMap<>(); + + public final HashMap> alignedMarkers = new HashMap<>(); + private ForgeDirection facing = ForgeDirection.UNKNOWN; + private int activeDirections = 0; + + @Override + public void validate() { + super.validate(); + if (worldObj.isRemote) return; + // Always update the marker for the current position, for world load and stale cases + getRegistryForDimension().put(new BlockPos(xCoord, yCoord, zCoord), this); + } + + // Helper to get the marker register for the current dimension + private ConcurrentHashMap getRegistryForDimension() { + int dim = worldObj.provider.dimensionId; + return registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()); + } + + public static ForgeDirection turnRight90(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.SOUTH; + case NORTH -> ForgeDirection.EAST; + case SOUTH -> ForgeDirection.WEST; + case WEST -> ForgeDirection.NORTH; + default -> dir; + }; + } + + public static ForgeDirection turnLeft90(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.NORTH; + case NORTH -> ForgeDirection.WEST; + case SOUTH -> ForgeDirection.EAST; + case WEST -> ForgeDirection.SOUTH; + default -> dir; + }; + } + + public @Nullable Vector4i checkForBoundary(ForgeDirection starterFacing) { + Tuple secondCorner = alignedMarkers.getOrDefault(starterFacing, null); + if (secondCorner != null && secondCorner.getKey() != null) { + Tuple thirdCorner = Optional + .ofNullable(secondCorner.getKey().alignedMarkers.getOrDefault(turnRight90(starterFacing), null)) + .orElse(secondCorner.getKey().alignedMarkers.getOrDefault(turnLeft90(starterFacing), null)); + if (thirdCorner != null) { + return new Vector4i(this.xCoord, this.zCoord, thirdCorner.getValue().x, thirdCorner.getValue().z); + } + } + return null; + } + + public void checkForAlignedMarkers() { + ConcurrentHashMap dimRegistry = getRegistryForDimension(); + // ArrayList staleMarkers = new ArrayList<>(); +// for (Map.Entry> entry : alignedMarkers +// .entrySet()) { +// if (entry.getValue() +// .getKey() == null) { +// BlockPos alignedMarkerPos = entry.getValue() +// .getValue(); +// worldObj.getChunkProvider() +// .loadChunk(alignedMarkerPos.x >> 4, alignedMarkerPos.z >> 4); +// TileEntity te = worldObj.getTileEntity(alignedMarkerPos.x, alignedMarkerPos.y, alignedMarkerPos.z); +// if (te instanceof TileEntityEnderMarker marker) { +// setAlignedMarker(entry.getKey(), marker); +// marker.setAlignedMarker( +// entry.getKey() +// .getOpposite(), +// this); +// } else { +// removeAlignedMarker(entry.getKey()); +// } +// // staleMarkers.add(entry.getKey()); +// } +// } + // staleMarkers.forEach(alignedMarkers::remove); + //alignedMarkers.clear(); + for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { + if (!alignedMarkers.containsKey(dir)) { + for (Map.Entry entry : dimRegistry.entrySet()) { + if (entry.getValue() != null && entry.getValue() != this + && !entry.getValue().alignedMarkers.containsKey(dir.getOpposite()) + && entry.getKey().y == this.yCoord + && (entry.getKey().x == this.xCoord || entry.getKey().z == this.zCoord)) { + int dx = entry.getKey().x - this.xCoord; + int dz = entry.getKey().z - this.zCoord; + + // Don't want markers right next to us + if (Math.max(Math.abs(dx), Math.abs(dz)) < 2) continue; + + ForgeDirection markerDirection; + if (dx == 0) { + markerDirection = dz > 0 ? ForgeDirection.SOUTH : ForgeDirection.NORTH; + } else { + markerDirection = dx > 0 ? ForgeDirection.EAST : ForgeDirection.WEST; + } + if (markerDirection == dir) { + setAlignedMarker(dir, entry.getValue()); + entry.getValue() + .setAlignedMarker(dir.getOpposite(), this); + } + } + } + } + } + } + + public void setAlignedMarker(ForgeDirection dir, TileEntityEnderMarker marker) { + alignedMarkers.put(dir, new Tuple<>(marker, new BlockPos(marker.xCoord, marker.yCoord, marker.zCoord))); + int prevActiveDirs = this.activeDirections; + this.activeDirections |= 1 << (dir.ordinal() - 2); + if (prevActiveDirs != activeDirections) { + worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, this.activeDirections, 2); + } + } + + public void removeAlignedMarker(ForgeDirection dir) { + alignedMarkers.remove(dir); + int prevActiveDirs = this.activeDirections; + this.activeDirections &= ~(1 << (dir.ordinal() - 2)); + if (prevActiveDirs != activeDirections) { + worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, this.activeDirections, 2); + } + } + + public void teardownConnections() { + getRegistryForDimension().remove(new BlockPos(this.xCoord, this.yCoord, this.zCoord)); + for (Map.Entry> alignedMarker : alignedMarkers + .entrySet()) { + TileEntityEnderMarker markerTE = alignedMarker.getValue().getKey(); + if (markerTE != null) { + markerTE.removeAlignedMarker(alignedMarker.getKey().getOpposite()); + markerTE.checkForAlignedMarkers(); + } else { + BlockPos alignedMarkerPos = alignedMarker.getValue().getValue(); + TileEntity te = worldObj.getTileEntity(alignedMarkerPos.x, alignedMarkerPos.y, alignedMarkerPos.z); + if (te instanceof TileEntityEnderMarker marker) { + marker.removeAlignedMarker(alignedMarker.getKey().getOpposite()); + marker.checkForAlignedMarkers(); + } + } + } + alignedMarkers.clear(); + } + + @Override + public void invalidate() { + super.invalidate(); + if (!worldObj.isRemote) { + teardownConnections(); + } + } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + if (!worldObj.isRemote) { + teardownConnections(); + } + } + + @Override + public ForgeDirection getFacing() { + return this.facing; + } + + @Override + public void setFacing(ForgeDirection newFacing) { + this.facing = newFacing; + } + + @Override + public void readFromNBT(NBTTagCompound compound) { + super.readFromNBT(compound); + readFacingFromNBT(compound); + } + + @Override + public void writeToNBT(NBTTagCompound compound) { + super.writeToNBT(compound); + writeFacingToNBT(compound); + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/IFacingTE.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/IFacingTE.java new file mode 100644 index 00000000..e36c3289 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/utils/IFacingTE.java @@ -0,0 +1,21 @@ +package com.fouristhenumber.utilitiesinexcess.common.tileentities.utils; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +public interface IFacingTE { + + ForgeDirection getFacing(); + + void setFacing(ForgeDirection newFacing); + + default void writeFacingToNBT(NBTTagCompound nbt) { + if (getFacing() != null) { + nbt.setInteger("facing", getFacing().ordinal()); + } + } + + default void readFacingFromNBT(NBTTagCompound nbt) { + setFacing(ForgeDirection.getOrientation(nbt.getInteger("facing"))); + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java index c1d13dd3..3b585c70 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java @@ -6,8 +6,11 @@ import static com.fouristhenumber.utilitiesinexcess.common.items.ItemInvertedIngot.INVERTED_INGOT; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; @@ -38,6 +41,7 @@ import com.gtnewhorizon.gtnhlib.client.event.LivingEquipmentChangeEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.event.world.WorldEvent; public class ForgeEventHandler { @@ -150,4 +154,18 @@ public void onBlockBroken(BlockEvent.HarvestDropsEvent event) { event.drops.clear(); } } + + @SubscribeEvent + public void onWorldUnload(WorldEvent.Unload event) { + if (event.world.isRemote) return; + + // Clear the entire dimension registry + ConcurrentHashMap dimRegistry = + TileEntityEnderMarker.registeredMarkers.get(event.world.provider.dimensionId); + + if (dimRegistry != null) { + dimRegistry.clear(); + TileEntityEnderMarker.registeredMarkers.remove(event.world.provider.dimensionId); + } + } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/Tuple.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/Tuple.java new file mode 100644 index 00000000..c3fa65b9 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/Tuple.java @@ -0,0 +1,20 @@ +package com.fouristhenumber.utilitiesinexcess.utils; + +public class Tuple { + + public final X first; + public final Y second; + + public Tuple(X first, Y second) { + this.first = first; + this.second = second; + } + + public X getKey() { + return this.first; + } + + public Y getValue() { + return this.second; + } +} diff --git a/src/main/resources/assets/utilitiesinexcess/blocks/ender_quarry.png b/src/main/resources/assets/utilitiesinexcess/blocks/ender_quarry.png deleted file mode 100644 index 4912f4e9eb3defe584e68c34e02228d183615fa1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2028 zcmV-P)978d1k~0bsLxI=@M-$GQNk3UdL&2^*)? z8^?9o05HAyp{jXLrye+fFb2RSqJkg*Y}-;zE~KTMVCLSe9zaE8{9F!zx=5{&^=uJ9 zs0C1_D`)@}nO@Y_xLPy;Kv6LuAAr`(z5CKT@V+{y1t8_b=uq{6I_6tIRz3@G^#g4H zO0)|U187))2Y|z4luZ96faO|%nfDCfnIhT+ z-s&B!yG+Ygs{r^+nc}K0L>OiF(N39KxUI@6Jr0k^<*^6=Z_EzmC4fZqH3CS@sBM=H zF^HjOW`lN_!Lmr~2h1K8z%+~NQ5Mh=00AO^BK4fkvyZF@0HzN`FNNpNoCyMOt-D7Z z2_QBN+WFfj)1O0xn1Nj&>c=ESuLCjwY@2Lor2z2tb|Un}F&uL}KmY@C2UM1da)JEq zaJF>-$X@j;)>Y`s`D*S2mFgl(KXj={ijD#K0J5S5gen#QeYr@qKw1K_$G)TMqZUC> z<;ia!M*(1vveTk|HAU4#Y8}pem;bUeD_>UNri%5kUoo=}IRWHlwJ-pFVU|+LYs~R( zkYal#(7hYC$<-~97C;x^KguoQp}k@2raR7A9&ivEkv_t;$QdQjPg20_6h0 zjm~8PI5yCy7AJb9!iBmrvf=@)1@Mj7Dp2IPA<%l}+gn*|3 zRAg+&XGywjsRckT02m5pHt#tb0P9W36XBtvcZY1t6d$eR_I;#1*Yj>rxTbyFXEa<3#OwqiCjhf-cf1MU1Lc3d=Mn&e zed=qHecuNlENfKcTKD0k1eJLq3Ml|+*wsPGyw0U3x3}hBQ(OazwE+7sFKiFbnB~K# zJ5oDRPF$%NV0ZA}$u4=Xr2x*$hMq543IMUde#?YsO1XdyfE}dt_1m&E`CK7@UlwNM zp9}zZ{bc}{{UU&62SHy4A*{J^-wsYT0PgZw4g#=iRHhz>0f1E^SeXF$2AOdQzin|0 zsK}w}Tqyu3qb|&jul^znz(}65EP(A`5kvrl68i@!C>H?ASYw+2nsD47EU#SnTclQ( zeeWzE0EFkzj`wkW4#Bz99E^xQu!%1ViMy3D027r#K!Aac-z_tXKE9-*n0o&yw z0QN^xv`GLIQsMGlU~xv4E@dr1188;%@&ahARya7w9zp-Z& zV!II9hVVNd0Eq1(6CmS6FNEgLoUQY1im5CB402K3!)&1y`&S$QJIQl)9tr|b5t)6p z8SFSI%q%tlLPtNNgJAm0P;=8^6J{AQEy1)JUdsc({X6gD_o#2(!3VN5xjD({ut>

g8nzNT31~(CTrC*xTT-d@lQ2WBiU+%`S3_K ziGXispGD&!40){l; Date: Sat, 29 Nov 2025 04:46:44 +0100 Subject: [PATCH 07/23] Move TileEntities into their own enum, Fix rejoining world for ender markers, implement usage of ender markers for quarry --- .../utilitiesinexcess/CommonProxy.java | 1 + .../utilitiesinexcess/ModTileEntities.java | 98 +++++++ .../utilitiesinexcess/UtilitiesInExcess.java | 67 +---- .../common/blocks/BlockEnderMarker.java | 6 +- .../common/blocks/BlockEnderQuarry.java | 261 +----------------- .../tileentities/TileEntityEnderMarker.java | 138 +++++---- .../tileentities/TileEntityEnderQuarry.java | 53 +++- .../utils/DirectionUtil.java | 253 +++++++++++++++++ .../utils/ForgeEventHandler.java | 10 +- 9 files changed, 502 insertions(+), 385 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/utils/DirectionUtil.java diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java index 12dbfe22..2d8fcedf 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java @@ -34,6 +34,7 @@ public void preInit(FMLPreInitializationEvent event) { public void init(FMLInitializationEvent event) { soundVolumeChecks = new SoundVolumeChecks(); + ModTileEntities.init(); } public void postInit(FMLPostInitializationEvent event) {} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java new file mode 100644 index 00000000..26e7b2ed --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java @@ -0,0 +1,98 @@ +package com.fouristhenumber.utilitiesinexcess; + +import net.minecraft.tileentity.TileEntity; + +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityBlockUpdateDetector; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityConveyor; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityDrum; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityMarginallyMaximisedChest; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPortalUnderWorld; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPureLove; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityRadicallyReducedChest; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityRainMuffler; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityRedstoneClock; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntitySignificantlyShrunkChest; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntitySoundMuffler; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityTrashCanEnergy; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityTrashCanFluid; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityTrashCanItem; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityEnderGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityFoodGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityFurnaceGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityHighTemperatureFurnaceGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityLavaGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityLowTemperatureFurnaceGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityNetherStarGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityPinkGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityPotionGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityRedstoneGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntitySolarGenerator; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityTNTGenerator; + +import cpw.mods.fml.common.registry.GameRegistry; + +public enum ModTileEntities { + // spotless:off + + // make sure to leave a trailing comma + REDSTONE_CLOCK(TileEntityRedstoneClock .class , "RedstoneClock"), + TRASH_CAN_ITEM(TileEntityTrashCanItem.class , "TrashCanItem"), + TRASH_CAN_FLUID(TileEntityTrashCanFluid.class , "TrashCanFluid"), + TRASH_CAN_ENERGY(TileEntityTrashCanEnergy.class , "TrashCanEnergyUIE"), + DRUM(TileEntityDrum.class , "Drum"), + PURE_LOVE(TileEntityPureLove.class , "PureLove"), + CHEST_MAX(TileEntityMarginallyMaximisedChest.class , "MarginallyMaximisedChest"), + CHEST_SHRUNK(TileEntitySignificantlyShrunkChest.class , "SignificantlyShrunkChest"), + CHEST_SMALL(TileEntityRadicallyReducedChest.class , "RadicallyReducedChest"), + MUFFLER_SOUND(TileEntitySoundMuffler.class , "SoundMufflerUIE"), + MUFFLER_RAIN(TileEntityRainMuffler.class , "RainMufflerUIE"), + BLOCK_UPDATE_DETECTOR(TileEntityBlockUpdateDetector.class , "BlockUpdateDetector"), + CONVEYOR(TileEntityConveyor.class , "Conveyor"), + PORTAL_UNDERWORLD(TileEntityPortalUnderWorld.class , "PortalUnderWorld"), + GENERATOR_LOW_TEMP_FURNACE(TileEntityLowTemperatureFurnaceGenerator.class , "LowTemperatureFurnaceGeneratorUIE"), + GENERATOR_FURNACE(TileEntityFurnaceGenerator.class , "FurnaceGeneratorUIE"), + GENERATOR_HIGH_TEMP_FURNACE(TileEntityHighTemperatureFurnaceGenerator.class, "HighTemperatureFurnaceGeneratorUIE"), + GENERATOR_LAVA(TileEntityLavaGenerator.class , "LavaGeneratorUIE"), + GENERATOR_ENDER(TileEntityEnderGenerator.class , "EnderGeneratorUIE"), + GENERATOR_REDSTONE(TileEntityRedstoneGenerator.class , "RedstoneGeneratorUIE"), + GENERATOR_FOOD(TileEntityFoodGenerator.class , "FoodGeneratorUIE"), + GENERATOR_POTION(TileEntityPotionGenerator.class , "PotionGeneratorUIE"), + GENERATOR_SOLAR(TileEntitySolarGenerator.class , "SolarGeneratorUIE"), + GENERATOR_TNT(TileEntityTNTGenerator.class , "TNTGeneratorUIE"), + GENERATOR_PINK(TileEntityPinkGenerator.class , "PinkGeneratorUIE"), + GENERATOR_NETHER_STAR(TileEntityNetherStarGenerator.class , "NetherStarGeneratorUIE"), + ENDER_QUARRY(TileEntityEnderQuarry.class , "EnderQuarryUIE"), + ENDER_MARKER(TileEntityEnderMarker.class , "EnderMarkerUIE"), + ; + // spotless:on + + public static final ModTileEntities[] VALUES = values(); + + public static void init() { + for (ModTileEntities te : VALUES) { + if (te.isEnabled()) { + GameRegistry.registerTileEntity(te.clazz, te.name); + } + } + } + + private final boolean isEnabled; + private final Class clazz; + private final String name; + + ModTileEntities(Class clazz, String name) { + this(true, clazz, name); + } + + ModTileEntities(Boolean enabled, Class clazz, String name) { + this.isEnabled = enabled; + this.clazz = clazz; + this.name = "TileEntity" + name; + } + + public boolean isEnabled() { + return isEnabled; + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java index 465be6ef..11d6b47f 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java @@ -1,5 +1,6 @@ package com.fouristhenumber.utilitiesinexcess; +import net.minecraft.client.Minecraft; import net.minecraft.util.WeightedRandomChestContent; import net.minecraftforge.common.ChestGenHooks; import net.minecraftforge.common.ForgeChunkManager; @@ -12,35 +13,7 @@ import com.fouristhenumber.utilitiesinexcess.common.renderers.BlackoutCurtainsRenderer; import com.fouristhenumber.utilitiesinexcess.common.renderers.LapisAetheriusRenderer; import com.fouristhenumber.utilitiesinexcess.common.renderers.SpikeRenderer; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityBlockUpdateDetector; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityConveyor; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityDrum; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityMarginallyMaximisedChest; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPortalUnderWorld; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPureLove; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityRadicallyReducedChest; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityRainMuffler; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityRedstoneClock; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntitySignificantlyShrunkChest; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntitySoundMuffler; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntitySpike; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityTrashCanEnergy; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityTrashCanFluid; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityTrashCanItem; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityEnderGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityFoodGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityFurnaceGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityHighTemperatureFurnaceGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityLavaGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityLowTemperatureFurnaceGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityNetherStarGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityPinkGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityPotionGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityRedstoneGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntitySolarGenerator; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.generators.TileEntityTNTGenerator; import com.fouristhenumber.utilitiesinexcess.common.worldgen.WorldGenEnderLotus; import com.fouristhenumber.utilitiesinexcess.compat.Mods; import com.fouristhenumber.utilitiesinexcess.compat.crafttweaker.QEDCraftTweakerSupport; @@ -83,6 +56,10 @@ public class UtilitiesInExcess { serverSide = "com.fouristhenumber.utilitiesinexcess.CommonProxy") public static CommonProxy proxy; + public static void chat(String text) { + Minecraft.getMinecraft().thePlayer.sendChatMessage(text); + } + @Mod.EventHandler public void preInit(FMLPreInitializationEvent event) { GameRegistry.registerTileEntity(TileEntitySpike.class, "utilitiesinexcess:TileEntitySpike"); @@ -100,40 +77,6 @@ public void init(FMLInitializationEvent event) { .bus() .register(new FMLEventHandler()); - GameRegistry.registerTileEntity(TileEntityRedstoneClock.class, "TileEntityRedstoneClock"); - GameRegistry.registerTileEntity(TileEntityTrashCanItem.class, "TileEntityTrashCanItem"); - GameRegistry.registerTileEntity(TileEntityTrashCanFluid.class, "TileEntityTrashCanFluid"); - GameRegistry.registerTileEntity(TileEntityTrashCanEnergy.class, "TileEntityTrashCanEnergyUIE"); - GameRegistry.registerTileEntity(TileEntityDrum.class, "TileEntityDrum"); - GameRegistry.registerTileEntity(TileEntityPureLove.class, "TileEntityPureLove"); - GameRegistry.registerTileEntity(TileEntityMarginallyMaximisedChest.class, "TileEntityMarginallyMaximisedChest"); - GameRegistry.registerTileEntity(TileEntitySignificantlyShrunkChest.class, "TileEntitySignificantlyShrunkChest"); - GameRegistry.registerTileEntity(TileEntityRadicallyReducedChest.class, "TileEntityRadicallyReducedChest"); - GameRegistry.registerTileEntity(TileEntitySoundMuffler.class, "TileEntitySoundMufflerUIE"); - GameRegistry.registerTileEntity(TileEntityRainMuffler.class, "TileEntityRainMufflerUIE"); - GameRegistry.registerTileEntity(TileEntityBlockUpdateDetector.class, "TileEntityBlockUpdateDetector"); - GameRegistry.registerTileEntity(TileEntityConveyor.class, "TileEntityConveyor"); - GameRegistry.registerTileEntity(TileEntityPortalUnderWorld.class, "TileEntityPortalUnderWorld"); - - GameRegistry.registerTileEntity( - TileEntityLowTemperatureFurnaceGenerator.class, - "TileEntityLowTemperatureFurnaceGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityFurnaceGenerator.class, "TileEntityFurnaceGeneratorUIE"); - GameRegistry.registerTileEntity( - TileEntityHighTemperatureFurnaceGenerator.class, - "TileEntityHighTemperatureFurnaceGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityLavaGenerator.class, "TileEntityLavaGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityEnderGenerator.class, "TileEntityEnderGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityRedstoneGenerator.class, "TileEntityRedstoneGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityFoodGenerator.class, "TileEntityFoodGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityPotionGenerator.class, "TileEntityPotionGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntitySolarGenerator.class, "TileEntitySolarGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityTNTGenerator.class, "TileEntityTNTGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityPinkGenerator.class, "TileEntityPinkGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityNetherStarGenerator.class, "TileEntityNetherStarGeneratorUIE"); - GameRegistry.registerTileEntity(TileEntityEnderQuarry.class, "TileEntityEnderQuarryUIE"); - GameRegistry.registerTileEntity(TileEntityEnderMarker.class, "TileEntityEnderMarkerUIE"); - lapisAetheriusRenderID = RenderingRegistry.getNextAvailableRenderId(); RenderingRegistry.registerBlockHandler(new LapisAetheriusRenderer()); blackoutCurtainsRenderID = RenderingRegistry.getNextAvailableRenderId(); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java index 4a2fec9a..7a2ca845 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java @@ -18,12 +18,10 @@ import net.minecraftforge.common.util.ForgeDirection; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; +import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; public class BlockEnderMarker extends BlockContainer { - public static final ForgeDirection[] HORIZONTAL_DIRECTIONS = new ForgeDirection[] { ForgeDirection.NORTH, - ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST }; - public BlockEnderMarker() { super(Material.iron); setBlockName("ender_marker"); @@ -74,7 +72,7 @@ public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neig @Override public void randomDisplayTick(World worldIn, int x, int y, int z, Random random) { int meta = worldIn.getBlockMetadata(x, y, z); - for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { + for (ForgeDirection dir : DirectionUtil.HORIZONTAL_DIRECTIONS) { if ((meta & (1 << (dir.ordinal() - 2))) != 0) { for (int i = 0; i < 7; i++) { worldIn.spawnParticle( diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java index 5127bf12..4aab93ea 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java @@ -14,7 +14,6 @@ import net.minecraftforge.common.util.ForgeDirection; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; -import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; public class BlockEnderQuarry extends BlockContainer { @@ -58,20 +57,10 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderQuarry quarry) { if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.STOPPED) { - BlockPos inFront = offsetByForward(x, y, z, quarry.facing, 1, 0); - BlockPos farFront = offsetByForward(inFront, quarry.facing, 64, 0); - farFront = offsetByRight(farFront, quarry.facing, 64, 0); - - // for (int i = 1; i < 256; i++) { - // worldIn.setBlock(inFront.x, i, inFront.z, Blocks.diamond_block); - // worldIn.setBlock(farFront.x, i, farFront.z, Blocks.diamond_block); - // } - - player.addChatComponentMessage( - new ChatComponentText(String.format("Set up work area from %s to %s", inFront, farFront))); - - quarry.setWorkArea(new TileEntityEnderQuarry.Area2d(inFront.x, inFront.z, farFront.x, farFront.z)); - quarry.state = TileEntityEnderQuarry.QuarryWorkState.RUNNING; + quarry.scanForWorkAreaFromMarkers(player); + if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.RUNNING) { + return true; + } } player.addChatComponentMessage(new ChatComponentText(quarry.getState())); } @@ -101,246 +90,4 @@ public static ForgeDirection getFacing(int meta) { public TileEntity createNewTileEntity(World worldIn, int meta) { return new TileEntityEnderQuarry(); } - - // TODO: REMOVE - public static ForgeDirection turnRight90(ForgeDirection dir) { - return switch (dir) { - case EAST -> ForgeDirection.SOUTH; - case NORTH -> ForgeDirection.EAST; - case SOUTH -> ForgeDirection.WEST; - case WEST -> ForgeDirection.NORTH; - default -> dir; - }; - } - - public static ForgeDirection turnLeft90(ForgeDirection dir) { - return switch (dir) { - case EAST -> ForgeDirection.NORTH; - case NORTH -> ForgeDirection.WEST; - case SOUTH -> ForgeDirection.EAST; - case WEST -> ForgeDirection.SOUTH; - default -> dir; - }; - } - - public static ForgeDirection turn180(ForgeDirection dir) { - return switch (dir) { - case EAST -> ForgeDirection.WEST; - case NORTH -> ForgeDirection.SOUTH; - case SOUTH -> ForgeDirection.NORTH; - case WEST -> ForgeDirection.EAST; - default -> dir; - }; - } - - public static ForgeDirection turnRight270(ForgeDirection dir) { - return turnLeft90(dir); - } - - public static ForgeDirection turnLeft270(ForgeDirection dir) { - return turnRight90(dir); - } - - /** - * Offsets coordinates to the right based on the facing direction - * - * @param pos Current position - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset to the right - */ - public static BlockPos offsetByRight(BlockPos pos, ForgeDirection facing, int amount, int vertical) { - ForgeDirection rightDir = turnRight90(facing); - return new BlockPos(pos.x + rightDir.offsetX * amount, pos.y + vertical, pos.z + rightDir.offsetZ * amount); - } - - /** - * Offsets coordinates to the right based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset to the right - */ - public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { - return offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); - } - - /** - * Offsets coordinates to the right based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset to the right - */ - public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { - BlockPos pos = offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); - if (relative) { - pos.x = x - pos.x; - pos.y = y - pos.y; - pos.z = z - pos.z; - } - return pos; - } - - /** - * Offsets coordinates to the left based on the facing direction - * - * @param pos Current position - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset to the left - */ - public static BlockPos offsetByLeft(BlockPos pos, ForgeDirection facing, int amount, int vertical) { - ForgeDirection leftDir = turnLeft90(facing); - return new BlockPos(pos.x + leftDir.offsetX * amount, pos.y + vertical, pos.z + leftDir.offsetZ * amount); - } - - /** - * Offsets coordinates to the left based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset to the left - */ - public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { - return offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); - } - - /** - * Offsets coordinates to the left based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset to the left - */ - public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { - BlockPos pos = offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); - if (relative) { - pos.x = x - pos.x; - pos.y = y - pos.y; - pos.z = z - pos.z; - } - return pos; - } - - /** - * Offsets coordinates backwards based on the facing direction - * - * @param pos Current position - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset backwards - */ - public static BlockPos offsetByBack(BlockPos pos, ForgeDirection facing, int amount, int vertical) { - ForgeDirection backDir = facing.getOpposite(); - return new BlockPos(pos.x + backDir.offsetX * amount, pos.y + vertical, pos.z + backDir.offsetZ * amount); - } - - /** - * Offsets coordinates backwards based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset backwards - */ - public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { - return offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); - } - - /** - * Offsets coordinates backwards based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset backwards - */ - public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { - BlockPos pos = offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); - if (relative) { - pos.x = x - pos.x; - pos.y = y - pos.y; - pos.z = z - pos.z; - } - return pos; - } - - /** - * Offsets coordinates forward based on the facing direction - * - * @param pos Current position - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset forward - */ - public static BlockPos offsetByForward(BlockPos pos, ForgeDirection facing, int amount, int vertical) { - return new BlockPos(pos.x + facing.offsetX * amount, pos.y + vertical, pos.z + facing.offsetZ * amount); - } - - /** - * Offsets coordinates forward based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset forward - */ - public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { - return offsetByForward(x, y, z, facing, amount, vertical, false); - } - - /** - * Offsets coordinates forward based on the facing direction - * - * @param x Current X coordinate - * @param y Current Y coordinate - * @param z Current Z coordinate - * @param facing The direction being faced - * @param amount Number of blocks to offset - * @param vertical Number of blocks to offset vertically (positive = up, negative = down) - * @return New BlockPos offset forward - */ - public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical, - boolean relative) { - BlockPos pos = offsetByForward(new BlockPos(x, y, z), facing, amount, vertical); - if (relative) { - pos.x = x - pos.x; - pos.y = y - pos.y; - pos.z = z - pos.z; - } - return pos; - } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 61154c69..25e7d5da 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -1,6 +1,8 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -9,10 +11,12 @@ import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector4i; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.IFacingTE; +import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; import com.fouristhenumber.utilitiesinexcess.utils.Tuple; import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; @@ -20,8 +24,7 @@ public class TileEntityEnderMarker extends TileEntity implements IFacingTE { public static final ForgeDirection[] HORIZONTAL_DIRECTIONS = new ForgeDirection[] { ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST }; - public static final ConcurrentHashMap> registeredMarkers = - new ConcurrentHashMap<>(); + public static final ConcurrentHashMap> registeredMarkers = new ConcurrentHashMap<>(); public final HashMap> alignedMarkers = new HashMap<>(); private ForgeDirection facing = ForgeDirection.UNKNOWN; @@ -41,32 +44,14 @@ private ConcurrentHashMap getRegistryForDimensi return registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()); } - public static ForgeDirection turnRight90(ForgeDirection dir) { - return switch (dir) { - case EAST -> ForgeDirection.SOUTH; - case NORTH -> ForgeDirection.EAST; - case SOUTH -> ForgeDirection.WEST; - case WEST -> ForgeDirection.NORTH; - default -> dir; - }; - } - - public static ForgeDirection turnLeft90(ForgeDirection dir) { - return switch (dir) { - case EAST -> ForgeDirection.NORTH; - case NORTH -> ForgeDirection.WEST; - case SOUTH -> ForgeDirection.EAST; - case WEST -> ForgeDirection.SOUTH; - default -> dir; - }; - } - public @Nullable Vector4i checkForBoundary(ForgeDirection starterFacing) { Tuple secondCorner = alignedMarkers.getOrDefault(starterFacing, null); if (secondCorner != null && secondCorner.getKey() != null) { Tuple thirdCorner = Optional - .ofNullable(secondCorner.getKey().alignedMarkers.getOrDefault(turnRight90(starterFacing), null)) - .orElse(secondCorner.getKey().alignedMarkers.getOrDefault(turnLeft90(starterFacing), null)); + .ofNullable( + secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnRight90(starterFacing), null)) + .orElse( + secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(starterFacing), null)); if (thirdCorner != null) { return new Vector4i(this.xCoord, this.zCoord, thirdCorner.getValue().x, thirdCorner.getValue().z); } @@ -75,32 +60,36 @@ public static ForgeDirection turnLeft90(ForgeDirection dir) { } public void checkForAlignedMarkers() { + this.checkForAlignedMarkers(HORIZONTAL_DIRECTIONS, false); + } + + public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate) { ConcurrentHashMap dimRegistry = getRegistryForDimension(); // ArrayList staleMarkers = new ArrayList<>(); -// for (Map.Entry> entry : alignedMarkers -// .entrySet()) { -// if (entry.getValue() -// .getKey() == null) { -// BlockPos alignedMarkerPos = entry.getValue() -// .getValue(); -// worldObj.getChunkProvider() -// .loadChunk(alignedMarkerPos.x >> 4, alignedMarkerPos.z >> 4); -// TileEntity te = worldObj.getTileEntity(alignedMarkerPos.x, alignedMarkerPos.y, alignedMarkerPos.z); -// if (te instanceof TileEntityEnderMarker marker) { -// setAlignedMarker(entry.getKey(), marker); -// marker.setAlignedMarker( -// entry.getKey() -// .getOpposite(), -// this); -// } else { -// removeAlignedMarker(entry.getKey()); -// } -// // staleMarkers.add(entry.getKey()); -// } -// } + for (Map.Entry> entry : alignedMarkers + .entrySet()) { + if (entry.getValue() + .getKey() == null) { + BlockPos alignedMarkerPos = entry.getValue() + .getValue(); + worldObj.getChunkProvider() + .loadChunk(alignedMarkerPos.x >> 4, alignedMarkerPos.z >> 4); + TileEntity te = worldObj.getTileEntity(alignedMarkerPos.x, alignedMarkerPos.y, alignedMarkerPos.z); + if (te instanceof TileEntityEnderMarker marker) { + setAlignedMarker(entry.getKey(), marker); + marker.setAlignedMarker( + entry.getKey() + .getOpposite(), + this); + } else { + removeAlignedMarker(entry.getKey()); + } + // staleMarkers.add(entry.getKey()); + } + } // staleMarkers.forEach(alignedMarkers::remove); - //alignedMarkers.clear(); - for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { + // alignedMarkers.clear(); + for (ForgeDirection dir : dirs) { if (!alignedMarkers.containsKey(dir)) { for (Map.Entry entry : dimRegistry.entrySet()) { if (entry.getValue() != null && entry.getValue() != this @@ -120,9 +109,24 @@ public void checkForAlignedMarkers() { markerDirection = dx > 0 ? ForgeDirection.EAST : ForgeDirection.WEST; } if (markerDirection == dir) { - setAlignedMarker(dir, entry.getValue()); - entry.getValue() - .setAlignedMarker(dir.getOpposite(), this); + if (!onlyValidate) { + setAlignedMarker(dir, entry.getValue()); + entry.getValue() + .setAlignedMarker(dir.getOpposite(), this); + } else if (!entry.getValue().alignedMarkers.containsKey(dir.getOpposite()) + && (worldObj.getBlockMetadata(entry.getKey().x, entry.getKey().y, entry.getKey().z) + & (1 << (dir.getOpposite() + .ordinal() - 2))) + != 0) { + // Does the active store for this marker not contain us & but the metadata does? + // Suggests we reloaded with previous connections, + // and since we would usually link from "this" to "entry" unlink (just from the + // meta) instead + entry.getValue() + .removeAlignedMarker(dir.getOpposite()); + entry.getValue() + .checkForAlignedMarkers(); + } } } } @@ -148,19 +152,37 @@ public void removeAlignedMarker(ForgeDirection dir) { } } + public ForgeDirection[] getActiveDirs() { + List activeDirList = new ArrayList<>(); + for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { + // Is the bit for this direction set? + if ((activeDirections & (1 << (dir.ordinal() - 2))) != 0) { + activeDirList.add(dir); + } + } + return activeDirList.toArray(new ForgeDirection[0]); + } + public void teardownConnections() { + checkForAlignedMarkers(getActiveDirs(), true); getRegistryForDimension().remove(new BlockPos(this.xCoord, this.yCoord, this.zCoord)); for (Map.Entry> alignedMarker : alignedMarkers .entrySet()) { - TileEntityEnderMarker markerTE = alignedMarker.getValue().getKey(); + TileEntityEnderMarker markerTE = alignedMarker.getValue() + .getKey(); if (markerTE != null) { - markerTE.removeAlignedMarker(alignedMarker.getKey().getOpposite()); - markerTE.checkForAlignedMarkers(); + markerTE.removeAlignedMarker( + alignedMarker.getKey() + .getOpposite()); + markerTE.checkForAlignedMarkers(); } else { - BlockPos alignedMarkerPos = alignedMarker.getValue().getValue(); + BlockPos alignedMarkerPos = alignedMarker.getValue() + .getValue(); TileEntity te = worldObj.getTileEntity(alignedMarkerPos.x, alignedMarkerPos.y, alignedMarkerPos.z); if (te instanceof TileEntityEnderMarker marker) { - marker.removeAlignedMarker(alignedMarker.getKey().getOpposite()); + marker.removeAlignedMarker( + alignedMarker.getKey() + .getOpposite()); marker.checkForAlignedMarkers(); } } @@ -198,11 +220,17 @@ public void setFacing(ForgeDirection newFacing) { public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); readFacingFromNBT(compound); + this.activeDirections = compound.getInteger("meta"); + int dim = compound.getInteger("dim"); + registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()) + .put(new BlockPos(xCoord, yCoord, zCoord), this); } @Override public void writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); writeFacingToNBT(compound); + compound.setInteger("meta", this.activeDirections); + compound.setInteger("dim", this.worldObj.provider.dimensionId); } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index e28fa602..3c179eb3 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -10,12 +10,14 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; import net.minecraft.world.ChunkCoordIntPair; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; @@ -30,10 +32,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; +import org.joml.Vector4i; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; +import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; import cofh.api.energy.EnergyStorage; import cofh.api.energy.IEnergyReceiver; @@ -107,11 +111,56 @@ public void setWorkArea(Area2d area) { chunkZ = dz >> 4; } + public void scanForWorkAreaFromMarkers(EntityPlayer player) { + boolean foundMarkers = false; + for (ForgeDirection dir : DirectionUtil.HORIZONTAL_DIRECTIONS) { + TileEntity te = worldObj.getTileEntity(xCoord + dir.offsetX, yCoord, zCoord + dir.offsetZ); + if (te instanceof TileEntityEnderMarker marker) { + @Nullable + Vector4i scanReturn = marker.checkForBoundary(dir); + if (scanReturn != null) { + // Pad work area by one (inwards), so that we don't mine the markers + Vector2i low = new Vector2i( + Math.min(scanReturn.x, scanReturn.z) + 1, + Math.min(scanReturn.y, scanReturn.w) + 1); + Vector2i high = new Vector2i( + Math.max(scanReturn.x, scanReturn.z) - 1, + Math.max(scanReturn.y, scanReturn.w) - 1); + setWorkArea(new Area2d(low, high)); + state = QuarryWorkState.RUNNING; + + int estBlocks = (worldObj.getHeightValue(dx, dy) + 5) * workArea.height * workArea.width; + player.addChatComponentMessage( + new ChatComponentText( + String.format( + "Found ender marker fence boundary, setting up work area from (%d %d) to (%d %d). Should roughly contain %d blocks.", + workArea.low.x, + workArea.low.y, + workArea.high.x, + workArea.high.y, + estBlocks))); + return; + } else { + player.addChatComponentMessage( + new ChatComponentText( + String.format( + "Ender marker at (%d %d %d) failed to set up a fence boundary.", + marker.xCoord, + marker.yCoord, + marker.zCoord))); + foundMarkers = true; + } + } + } + if (!foundMarkers) + player.addChatComponentMessage(new ChatComponentText("Found no ender markers around quarry.")); + } + /** * Are the current dx & dy & dz in work area bounds */ private boolean isInBounds() { - return dy > 1 && this.workArea.isInBounds(dx, dz); + return dy > 0 && this.workArea.isInBounds(dx, dz); } /** @@ -121,7 +170,7 @@ private boolean isInBounds() { */ private boolean stepPos() { dy--; - if (dy <= 1) { + if (dy <= 0) { // stack is done, move back up dy = this.yCoord + 5; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/DirectionUtil.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/DirectionUtil.java new file mode 100644 index 00000000..41ebce93 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/DirectionUtil.java @@ -0,0 +1,253 @@ +// Copied from U25Core +package com.fouristhenumber.utilitiesinexcess.utils; + +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; + +public class DirectionUtil { + + public static final ForgeDirection[] HORIZONTAL_DIRECTIONS = new ForgeDirection[] { ForgeDirection.NORTH, + ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST }; + + public static ForgeDirection turnRight90(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.SOUTH; + case NORTH -> ForgeDirection.EAST; + case SOUTH -> ForgeDirection.WEST; + case WEST -> ForgeDirection.NORTH; + default -> dir; + }; + } + + public static ForgeDirection turnLeft90(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.NORTH; + case NORTH -> ForgeDirection.WEST; + case SOUTH -> ForgeDirection.EAST; + case WEST -> ForgeDirection.SOUTH; + default -> dir; + }; + } + + public static ForgeDirection turn180(ForgeDirection dir) { + return switch (dir) { + case EAST -> ForgeDirection.WEST; + case NORTH -> ForgeDirection.SOUTH; + case SOUTH -> ForgeDirection.NORTH; + case WEST -> ForgeDirection.EAST; + default -> dir; + }; + } + + public static ForgeDirection turnRight270(ForgeDirection dir) { + return turnLeft90(dir); + } + + public static ForgeDirection turnLeft270(ForgeDirection dir) { + return turnRight90(dir); + } + + /** + * Offsets coordinates to the right based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the right + */ + public static BlockPos offsetByRight(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + ForgeDirection rightDir = turnRight90(facing); + return new BlockPos(pos.x + rightDir.offsetX * amount, pos.y + vertical, pos.z + rightDir.offsetZ * amount); + } + + /** + * Offsets coordinates to the right based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the right + */ + public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); + } + + /** + * Offsets coordinates to the right based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the right + */ + public static BlockPos offsetByRight(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByRight(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } + + /** + * Offsets coordinates to the left based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the left + */ + public static BlockPos offsetByLeft(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + ForgeDirection leftDir = turnLeft90(facing); + return new BlockPos(pos.x + leftDir.offsetX * amount, pos.y + vertical, pos.z + leftDir.offsetZ * amount); + } + + /** + * Offsets coordinates to the left based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the left + */ + public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); + } + + /** + * Offsets coordinates to the left based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset to the left + */ + public static BlockPos offsetByLeft(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByLeft(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } + + /** + * Offsets coordinates backwards based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset backwards + */ + public static BlockPos offsetByBack(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + ForgeDirection backDir = facing.getOpposite(); + return new BlockPos(pos.x + backDir.offsetX * amount, pos.y + vertical, pos.z + backDir.offsetZ * amount); + } + + /** + * Offsets coordinates backwards based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset backwards + */ + public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); + } + + /** + * Offsets coordinates backwards based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset backwards + */ + public static BlockPos offsetByBack(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByBack(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } + + /** + * Offsets coordinates forward based on the facing direction + * + * @param pos Current position + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset forward + */ + public static BlockPos offsetByForward(BlockPos pos, ForgeDirection facing, int amount, int vertical) { + return new BlockPos(pos.x + facing.offsetX * amount, pos.y + vertical, pos.z + facing.offsetZ * amount); + } + + /** + * Offsets coordinates forward based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset forward + */ + public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical) { + return offsetByForward(x, y, z, facing, amount, vertical, false); + } + + /** + * Offsets coordinates forward based on the facing direction + * + * @param x Current X coordinate + * @param y Current Y coordinate + * @param z Current Z coordinate + * @param facing The direction being faced + * @param amount Number of blocks to offset + * @param vertical Number of blocks to offset vertically (positive = up, negative = down) + * @return New BlockPos offset forward + */ + public static BlockPos offsetByForward(int x, int y, int z, ForgeDirection facing, int amount, int vertical, + boolean relative) { + BlockPos pos = offsetByForward(new BlockPos(x, y, z), facing, amount, vertical); + if (relative) { + pos.x = x - pos.x; + pos.y = y - pos.y; + pos.z = z - pos.z; + } + return pos; + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java index 3b585c70..e82b2ba5 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/ForgeEventHandler.java @@ -9,8 +9,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; -import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; @@ -25,6 +23,7 @@ import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.WorldEvent; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockSpike; import com.fouristhenumber.utilitiesinexcess.common.items.ItemInvertedIngot; @@ -34,14 +33,15 @@ import com.fouristhenumber.utilitiesinexcess.common.items.tools.ItemGluttonsAxe; import com.fouristhenumber.utilitiesinexcess.common.items.tools.ItemPrecisionShears; import com.fouristhenumber.utilitiesinexcess.common.renderers.XRayRenderer; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker; import com.fouristhenumber.utilitiesinexcess.config.items.unstabletools.AntiParticulateShovelConfig; import com.fouristhenumber.utilitiesinexcess.config.items.unstabletools.DestructionPickaxeConfig; import com.fouristhenumber.utilitiesinexcess.config.items.unstabletools.GluttonsAxeConfig; import com.fouristhenumber.utilitiesinexcess.mixins.early.minecraft.accessors.AccessorEntityLivingBase; +import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; import com.gtnewhorizon.gtnhlib.client.event.LivingEquipmentChangeEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.event.world.WorldEvent; public class ForgeEventHandler { @@ -160,8 +160,8 @@ public void onWorldUnload(WorldEvent.Unload event) { if (event.world.isRemote) return; // Clear the entire dimension registry - ConcurrentHashMap dimRegistry = - TileEntityEnderMarker.registeredMarkers.get(event.world.provider.dimensionId); + ConcurrentHashMap dimRegistry = TileEntityEnderMarker.registeredMarkers + .get(event.world.provider.dimensionId); if (dimRegistry != null) { dimRegistry.clear(); From 044d53b441c9fc11adf214adc3f72564959d5c4e Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Sat, 6 Dec 2025 22:18:55 +0100 Subject: [PATCH 08/23] Add initial version of upgrade models, Add marker modes and start on complex version --- dependencies.gradle | 31 +- .../utilitiesinexcess/ModBlocks.java | 6 +- .../utilitiesinexcess/ModTileEntities.java | 36 +- .../{ => ender_quarry}/BlockEnderMarker.java | 24 +- .../{ => ender_quarry}/BlockEnderQuarry.java | 11 +- .../ender_quarry/BlockEnderQuarryUpgrade.java | 42 + .../ender_quarry/IEnderQuarryUpgrade.java | 6 + .../tileentities/TileEntityEnderMarker.java | 166 ++- .../tileentities/TileEntityEnderQuarry.java | 20 +- .../blockstates/ender_quarry_upgrade.json | 7 + .../models/blocks/upgrade_speed_1.json | 701 +++++++++++++ .../models/blocks/upgrade_speed_2.json | 831 +++++++++++++++ .../models/blocks/upgrade_speed_3.json | 961 ++++++++++++++++++ .../textures/blocks/upgrade_speed_1.png | Bin 0 -> 1376 bytes .../textures/blocks/upgrade_speed_2.png | Bin 0 -> 1553 bytes .../textures/blocks/upgrade_speed_3.png | Bin 0 -> 1604 bytes 16 files changed, 2776 insertions(+), 66 deletions(-) rename src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/{ => ender_quarry}/BlockEnderMarker.java (77%) rename src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/{ => ender_quarry}/BlockEnderQuarry.java (87%) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java create mode 100644 src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_2.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_3.json create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_speed_1.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_speed_2.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_speed_3.png diff --git a/dependencies.gradle b/dependencies.gradle index 4d95a685..71cad413 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -34,25 +34,30 @@ * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph */ dependencies { + compileOnly('org.jetbrains:annotations:26.0.1') + implementation("com.github.GTNewHorizons:GTNHLib:0.8.15:dev") // TODO: remove MUI1 dep when the implicit dependency // TODO: in IItemHandlerModifiable is removed - api("com.github.GTNewHorizons:ModularUI:1.2.20:dev") - api("com.github.GTNewHorizons:ModularUI2:2.2.16-1.7.10:dev") - api("com.github.GTNewHorizons:CraftTweaker:3.4.2:dev") - api('curse.maven:cofh-lib-220333:2388748') - compileOnly("com.github.GTNewHorizons:Baubles-Expanded:2.1.9-GTNH:dev") - compileOnly('thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev') - compileOnly('com.github.GTNewHorizons:Angelica:1.0.0-beta62:dev') + api("com.github.GTNewHorizons:ModularUI:1.2.20:dev") { transitive = false } + api("com.github.GTNewHorizons:ModularUI2:2.2.16-1.7.10:dev") { transitive = false } + api("com.github.GTNewHorizons:CraftTweaker:3.4.2:dev") { transitive = false } + api('curse.maven:cofh-lib-220333:2388748') { transitive = false } + + compileOnly("com.github.GTNewHorizons:Baubles-Expanded:2.1.9-GTNH:dev") { transitive = false } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderCore:0.5.0:dev") { transitive = false } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.2-GTNH:dev") { transitive = false } + compileOnly('thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev') { transitive = false } + compileOnly('com.github.GTNewHorizons:Angelica:1.0.0-beta62:dev') { transitive = false } - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.31-GTNH:dev" ) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-beta71-pre:dev" ) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.17:dev" ) + api("com.github.GTNewHorizons:NotEnoughItems:2.8.31-GTNH:dev" ) { transitive = true } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-beta71-pre:dev" ) { transitive = false } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.17:dev" ) { transitive = false } // For debugging chunkloading runtimeOnlyNonPublishable('curse.maven:chicken-chunks-229316:2233250') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:WAILAPlugins:0.7.2:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.9.15:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.5:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:WAILAPlugins:0.7.2:dev") { transitive = false } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.9.15:dev") { transitive = false } + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.5:dev") { transitive = false } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java index 2223f1d8..0a6677b0 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java @@ -1,5 +1,6 @@ package com.fouristhenumber.utilitiesinexcess; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarryUpgrade; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.item.Item; @@ -13,8 +14,8 @@ import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockCursedEarth; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockDrum; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderLotus; -import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderMarker; -import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderQuarry; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderMarker; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarry; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEtherealGlass; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockFloating; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockInverted; @@ -109,6 +110,7 @@ public enum ModBlocks { END_OF_TIME_PORTAL(BlockConfig.enableEndOfTimePortal && EndOfTimeConfig.enableEndOfTime, new BlockPortalEndOfTime(), BlockPortalEndOfTime.ItemBlockPortalEndOfTime.class, "temporal_gate"), ENDER_QUARRY(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarry(), "ender_quarry"), ENDER_MARKER(EnderQuarryConfig.enableEnderQuarry, new BlockEnderMarker(), "ender_marker"), + ENDER_QUARRY_UPGRADE(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarryUpgrade(), "ender_quarry_upgrade"), ; // leave trailing semicolon // spotless:on diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java index 26e7b2ed..5bfc28b1 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModTileEntities.java @@ -40,31 +40,31 @@ public enum ModTileEntities { REDSTONE_CLOCK(TileEntityRedstoneClock .class , "RedstoneClock"), TRASH_CAN_ITEM(TileEntityTrashCanItem.class , "TrashCanItem"), TRASH_CAN_FLUID(TileEntityTrashCanFluid.class , "TrashCanFluid"), - TRASH_CAN_ENERGY(TileEntityTrashCanEnergy.class , "TrashCanEnergyUIE"), + TRASH_CAN_ENERGY(TileEntityTrashCanEnergy.class , "TrashCanEnergy"), DRUM(TileEntityDrum.class , "Drum"), PURE_LOVE(TileEntityPureLove.class , "PureLove"), CHEST_MAX(TileEntityMarginallyMaximisedChest.class , "MarginallyMaximisedChest"), CHEST_SHRUNK(TileEntitySignificantlyShrunkChest.class , "SignificantlyShrunkChest"), CHEST_SMALL(TileEntityRadicallyReducedChest.class , "RadicallyReducedChest"), - MUFFLER_SOUND(TileEntitySoundMuffler.class , "SoundMufflerUIE"), - MUFFLER_RAIN(TileEntityRainMuffler.class , "RainMufflerUIE"), + MUFFLER_SOUND(TileEntitySoundMuffler.class , "SoundMuffler"), + MUFFLER_RAIN(TileEntityRainMuffler.class , "RainMuffler"), BLOCK_UPDATE_DETECTOR(TileEntityBlockUpdateDetector.class , "BlockUpdateDetector"), CONVEYOR(TileEntityConveyor.class , "Conveyor"), PORTAL_UNDERWORLD(TileEntityPortalUnderWorld.class , "PortalUnderWorld"), - GENERATOR_LOW_TEMP_FURNACE(TileEntityLowTemperatureFurnaceGenerator.class , "LowTemperatureFurnaceGeneratorUIE"), - GENERATOR_FURNACE(TileEntityFurnaceGenerator.class , "FurnaceGeneratorUIE"), - GENERATOR_HIGH_TEMP_FURNACE(TileEntityHighTemperatureFurnaceGenerator.class, "HighTemperatureFurnaceGeneratorUIE"), - GENERATOR_LAVA(TileEntityLavaGenerator.class , "LavaGeneratorUIE"), - GENERATOR_ENDER(TileEntityEnderGenerator.class , "EnderGeneratorUIE"), - GENERATOR_REDSTONE(TileEntityRedstoneGenerator.class , "RedstoneGeneratorUIE"), - GENERATOR_FOOD(TileEntityFoodGenerator.class , "FoodGeneratorUIE"), - GENERATOR_POTION(TileEntityPotionGenerator.class , "PotionGeneratorUIE"), - GENERATOR_SOLAR(TileEntitySolarGenerator.class , "SolarGeneratorUIE"), - GENERATOR_TNT(TileEntityTNTGenerator.class , "TNTGeneratorUIE"), - GENERATOR_PINK(TileEntityPinkGenerator.class , "PinkGeneratorUIE"), - GENERATOR_NETHER_STAR(TileEntityNetherStarGenerator.class , "NetherStarGeneratorUIE"), - ENDER_QUARRY(TileEntityEnderQuarry.class , "EnderQuarryUIE"), - ENDER_MARKER(TileEntityEnderMarker.class , "EnderMarkerUIE"), + GENERATOR_LOW_TEMP_FURNACE(TileEntityLowTemperatureFurnaceGenerator.class , "LowTemperatureFurnaceGenerator"), + GENERATOR_FURNACE(TileEntityFurnaceGenerator.class , "FurnaceGenerator"), + GENERATOR_HIGH_TEMP_FURNACE(TileEntityHighTemperatureFurnaceGenerator.class, "HighTemperatureFurnaceGenerator"), + GENERATOR_LAVA(TileEntityLavaGenerator.class , "LavaGenerator"), + GENERATOR_ENDER(TileEntityEnderGenerator.class , "EnderGenerator"), + GENERATOR_REDSTONE(TileEntityRedstoneGenerator.class , "RedstoneGenerator"), + GENERATOR_FOOD(TileEntityFoodGenerator.class , "FoodGenerator"), + GENERATOR_POTION(TileEntityPotionGenerator.class , "PotionGenerator"), + GENERATOR_SOLAR(TileEntitySolarGenerator.class , "SolarGenerator"), + GENERATOR_TNT(TileEntityTNTGenerator.class , "TNTGenerator"), + GENERATOR_PINK(TileEntityPinkGenerator.class , "PinkGenerator"), + GENERATOR_NETHER_STAR(TileEntityNetherStarGenerator.class , "NetherStarGenerator"), + ENDER_QUARRY(TileEntityEnderQuarry.class , "EnderQuarry"), + ENDER_MARKER(TileEntityEnderMarker.class , "EnderMarker"), ; // spotless:on @@ -89,7 +89,7 @@ public static void init() { ModTileEntities(Boolean enabled, Class clazz, String name) { this.isEnabled = enabled; this.clazz = clazz; - this.name = "TileEntity" + name; + this.name = "TileEntity" + name + "UIE"; } public boolean isEnabled() { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java similarity index 77% rename from src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java rename to src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java index 7a2ca845..c6c69dae 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java @@ -1,4 +1,4 @@ -package com.fouristhenumber.utilitiesinexcess.common.blocks; +package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; @@ -10,9 +10,11 @@ import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.ChatComponentText; import net.minecraft.world.Explosion; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -69,6 +71,26 @@ public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neig } } + @Override + public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, float subY, float subZ) { + if (worldIn.isRemote) return true; + TileEntity te = worldIn.getTileEntity(x, y, z); + if (te instanceof TileEntityEnderMarker marker) { + if (player.isSneaking()) { + if (marker.operationMode == TileEntityEnderMarker.MarkerOperationMode.ARBITRARY_LOOP) { + marker.boundaryForArbitraryLoop(); + } + int newCuboidSize = marker.increaseCuboidSize(); + player.addChatComponentMessage(new ChatComponentText("Increased cuboid size to " + newCuboidSize + ".")); + } else { + marker.rotateMode(); + player.addChatComponentMessage(new ChatComponentText(marker.getMode())); + } + return true; + } + return false; + } + @Override public void randomDisplayTick(World worldIn, int x, int y, int z, Random random) { int meta = worldIn.getBlockMetadata(x, y, z); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java similarity index 87% rename from src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java rename to src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java index 4aab93ea..970beed1 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java @@ -1,4 +1,4 @@ -package com.fouristhenumber.utilitiesinexcess.common.blocks; +package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; @@ -51,11 +51,14 @@ public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase @Override public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, float subY, float subZ) { - if (worldIn.isRemote) { - return true; - } + if (worldIn.isRemote) return true; TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderQuarry quarry) { + if (player.isSneaking() && player.capabilities.isCreativeMode) { + quarry.isCreativeBoosted = !quarry.isCreativeBoosted; + player.addChatComponentMessage(new ChatComponentText((quarry.isCreativeBoosted ? "" : "Un-") + "Creative-Boosted Quarry.")); + return true; + } if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.STOPPED) { quarry.scanForWorkAreaFromMarkers(player); if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.RUNNING) { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java new file mode 100644 index 00000000..3f5acdf3 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java @@ -0,0 +1,42 @@ +package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; + +import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +public class BlockEnderQuarryUpgrade extends Block implements IEnderQuarryUpgrade { + private IIcon[] icons; + + public BlockEnderQuarryUpgrade() { + super(Material.iron); + setHardness(1f); + setLightOpacity(0); + } + + @Override + public boolean isOpaqueCube() { + return false; + } + + @Override + public int getRenderType() { + return JSON_ISBRH_ID; + } + + @Override + public int onBlockPlaced(World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ, int meta) { + return 0; + } + + @Override + public void registerBlockIcons(IIconRegister reg) { + icons = new IIcon[3]; + icons[0] = reg.registerIcon("utilitiesinexcess:upgrade_speed_1"); + icons[1] = reg.registerIcon("utilitiesinexcess:upgrade_speed_2"); + icons[2] = reg.registerIcon("utilitiesinexcess:upgrade_speed_3"); + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java new file mode 100644 index 00000000..423e57d4 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java @@ -0,0 +1,6 @@ +package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; + +public interface IEnderQuarryUpgrade { + + +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 25e7d5da..f42cbdc9 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -2,19 +2,24 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; +import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.joml.Vector4i; - +import org.joml.Vector2i; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.IFacingTE; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; import com.fouristhenumber.utilitiesinexcess.utils.Tuple; @@ -29,6 +34,9 @@ public class TileEntityEnderMarker extends TileEntity implements IFacingTE { public final HashMap> alignedMarkers = new HashMap<>(); private ForgeDirection facing = ForgeDirection.UNKNOWN; private int activeDirections = 0; + public MarkerOperationMode operationMode = MarkerOperationMode.DEFAULT; + private boolean hasChangedMode = false; + private int cuboidSize = 16; @Override public void validate() { @@ -44,7 +52,15 @@ private ConcurrentHashMap getRegistryForDimensi return registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()); } - public @Nullable Vector4i checkForBoundary(ForgeDirection starterFacing) { + public @Nullable List checkForBoundary(ForgeDirection starterFacing) { + return switch (operationMode) { + case DEFAULT -> boundaryFromThree(starterFacing); + case SINGLE -> boundaryForSizedCuboid(starterFacing); + case ARBITRARY_LOOP -> boundaryForArbitraryLoop(); + }; + } + + private @Nullable List boundaryFromThree(ForgeDirection starterFacing) { Tuple secondCorner = alignedMarkers.getOrDefault(starterFacing, null); if (secondCorner != null && secondCorner.getKey() != null) { Tuple thirdCorner = Optional @@ -53,21 +69,95 @@ private ConcurrentHashMap getRegistryForDimensi .orElse( secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(starterFacing), null)); if (thirdCorner != null) { - return new Vector4i(this.xCoord, this.zCoord, thirdCorner.getValue().x, thirdCorner.getValue().z); + return Stream.of(new Vector2i(this.xCoord, this.zCoord), new Vector2i(thirdCorner.getValue().x, thirdCorner.getValue().z)).collect(Collectors.toList()); + } + } + return null; + } + + private List boundaryForSizedCuboid(ForgeDirection facing) { + BlockPos otherCorner = DirectionUtil.offsetByForward(this.xCoord, this.yCoord, this.zCoord, facing, cuboidSize, 0); + otherCorner = DirectionUtil.offsetByRight(otherCorner, facing, cuboidSize, 0); + return Stream.of(new Vector2i(this.xCoord, this.zCoord), new Vector2i(otherCorner.x, otherCorner.z)).collect(Collectors.toList()); + } + + public List boundaryForArbitraryLoop() { + ArrayList stack = new ArrayList<>(); + stack.add(new StackEntry(new LinkedHashMap<>(), this)); + Set markerChain = null; + + searchStack: do { + StackEntry entry = stack.remove(stack.size() - 1); + + if (entry.current.alignedMarkers.isEmpty() + || entry.current.alignedMarkers.size() == 1 + && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) != null + && entry.current.alignedMarkers.get(entry.lastVisited.getValue()).getKey() == entry.lastVisited.getKey()) { + entry.current.checkForAlignedMarkers(); + worldObj.setBlock(entry.current.xCoord, entry.current.yCoord + 3, entry.current.zCoord, Blocks.brick_block); + } + + for (Map.Entry> otherMarker : entry.current.alignedMarkers.entrySet()) { + if (otherMarker.getValue().getKey() != null) { + // Has not already been visited + if (!entry.visitedMarkers.containsKey(otherMarker.getValue().getKey())) { + @SuppressWarnings("unchecked") // Same type + LinkedHashMap visitedMarkers = (LinkedHashMap) entry.visitedMarkers.clone(); + StackEntry stackEntry = new StackEntry(visitedMarkers, otherMarker.getValue().getKey(), entry.current, otherMarker.getKey().getOpposite()); + stack.add(stackEntry); + + worldObj.setBlock(entry.current.xCoord, entry.current.yCoord + 2, entry.current.zCoord, Blocks.tnt); + continue; + } + + // Check if we have completed a loop by arriving at the starter from a different direction + if (otherMarker.getValue().getKey() == this && entry.visitedMarkers.get(otherMarker.getValue().getKey()) != otherMarker.getKey() && !entry.visitedMarkers.isEmpty()) { + // Append the last marker + entry.visitedMarkers.put(entry.current, otherMarker.getKey()); + markerChain = entry.visitedMarkers.keySet(); + break searchStack; + } + } } + } while (!stack.isEmpty()); + + if (markerChain != null) { + UtilitiesInExcess.chat("Completed marker chain with " + markerChain.size() + " entries."); + ArrayList pointChain = new ArrayList<>(markerChain.size()); + markerChain.forEach((e) -> pointChain.add(new Vector2i(e.xCoord, e.zCoord))); + return pointChain; } + UtilitiesInExcess.chat("Failed to complete marker chain."); + return null; } + private static class StackEntry { + // A map of previously visited markers further down the stack & the direction they were visited from + LinkedHashMap visitedMarkers; + TileEntityEnderMarker current; + Tuple lastVisited; + + StackEntry(LinkedHashMap visitedMarkers, TileEntityEnderMarker current) { + this.visitedMarkers = visitedMarkers; + this.current = current; + this.lastVisited = null; + } + + StackEntry(LinkedHashMap visitedMarkers, TileEntityEnderMarker current, TileEntityEnderMarker previous, ForgeDirection previousDir) { + this(visitedMarkers, current); + this.visitedMarkers.put(previous, previousDir); + lastVisited = new Tuple<>(previous, previousDir); + } + } + public void checkForAlignedMarkers() { this.checkForAlignedMarkers(HORIZONTAL_DIRECTIONS, false); } public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate) { ConcurrentHashMap dimRegistry = getRegistryForDimension(); - // ArrayList staleMarkers = new ArrayList<>(); - for (Map.Entry> entry : alignedMarkers - .entrySet()) { + for (Map.Entry> entry : new ArrayList<>(alignedMarkers.entrySet())) { if (entry.getValue() .getKey() == null) { BlockPos alignedMarkerPos = entry.getValue() @@ -84,11 +174,9 @@ public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyV } else { removeAlignedMarker(entry.getKey()); } - // staleMarkers.add(entry.getKey()); } } - // staleMarkers.forEach(alignedMarkers::remove); - // alignedMarkers.clear(); + for (ForgeDirection dir : dirs) { if (!alignedMarkers.containsKey(dir)) { for (Map.Entry entry : dimRegistry.entrySet()) { @@ -190,6 +278,45 @@ public void teardownConnections() { alignedMarkers.clear(); } + public String getMode() { + return switch(operationMode) { + case DEFAULT -> "Marker is set to establish a rectangular fence from the first 3 in chain."; + case SINGLE -> "Marker is set to establish a cuboid of length " + this.cuboidSize + ". Sneak + R-Click to adjust size."; + case ARBITRARY_LOOP -> "Marker is set to establish a full loop of markers that make up a rectilinear polygon back this marker of arbitrary size and shape."; + }; + } + + public void rotateMode() { + // Don't change the mode the first time, want them to be able to read the default + if (!hasChangedMode) { + hasChangedMode = true; + return; + } + int ord = operationMode.ordinal() + 1; + if (ord >= MarkerOperationMode.values().length) { + ord = 0; + } + operationMode = MarkerOperationMode.values()[ord]; + } + + public int increaseCuboidSize() { + int nextSize = cuboidSize * 2; + cuboidSize = nextSize > 512 ? 2 : cuboidSize * 2; + return cuboidSize; + } + + // IFacingTE + @Override + public ForgeDirection getFacing() { + return this.facing; + } + + @Override + public void setFacing(ForgeDirection newFacing) { + this.facing = newFacing; + } + + // TileEntity @Override public void invalidate() { super.invalidate(); @@ -206,24 +333,16 @@ public void onChunkUnload() { } } - @Override - public ForgeDirection getFacing() { - return this.facing; - } - - @Override - public void setFacing(ForgeDirection newFacing) { - this.facing = newFacing; - } - @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); readFacingFromNBT(compound); this.activeDirections = compound.getInteger("meta"); int dim = compound.getInteger("dim"); + // Can't use getRegistryForDimension yet since worldobj is null registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()) .put(new BlockPos(xCoord, yCoord, zCoord), this); + this.operationMode = MarkerOperationMode.values()[compound.getInteger("mode")]; } @Override @@ -232,5 +351,12 @@ public void writeToNBT(NBTTagCompound compound) { writeFacingToNBT(compound); compound.setInteger("meta", this.activeDirections); compound.setInteger("dim", this.worldObj.provider.dimensionId); + compound.setInteger("mode", this.operationMode.ordinal()); + } + + public enum MarkerOperationMode { + DEFAULT, + SINGLE, + ARBITRARY_LOOP } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 3c179eb3..0688b01a 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -47,9 +47,10 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver, IFluidHandler { - public static final int STEPS_PER_TICK = 400; + public static final int BASE_STEPS_PER_TICK = 400; public static final int ITEM_BUFFER_CAPACITY = 256; public static final Block REPLACE_BLOCK = Blocks.dirt; + public boolean isCreativeBoosted = false; private int storedItems; public ForgeDirection facing; private Area2d workArea; @@ -117,15 +118,18 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { TileEntity te = worldObj.getTileEntity(xCoord + dir.offsetX, yCoord, zCoord + dir.offsetZ); if (te instanceof TileEntityEnderMarker marker) { @Nullable - Vector4i scanReturn = marker.checkForBoundary(dir); + List scanReturn = marker.checkForBoundary(dir); if (scanReturn != null) { + Vector2i firstCorner = scanReturn.get(0); + Vector2i secondCorner = scanReturn.get(1); + // Pad work area by one (inwards), so that we don't mine the markers Vector2i low = new Vector2i( - Math.min(scanReturn.x, scanReturn.z) + 1, - Math.min(scanReturn.y, scanReturn.w) + 1); + Math.min(firstCorner.x, secondCorner.x) + 1, + Math.min(firstCorner.y, secondCorner.y) + 1); Vector2i high = new Vector2i( - Math.max(scanReturn.x, scanReturn.z) - 1, - Math.max(scanReturn.y, scanReturn.w) - 1); + Math.max(firstCorner.x, secondCorner.x) - 1, + Math.max(firstCorner.y, secondCorner.y) - 1); setWorkArea(new Area2d(low, high)); state = QuarryWorkState.RUNNING; @@ -562,7 +566,7 @@ public void updateEntity() { } if (state == QuarryWorkState.RUNNING) { - while (brokenBlocksTick < STEPS_PER_TICK && stepPos()) { + while (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) && stepPos()) { // TODO: Remove after this has been tested by others if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { throw new RuntimeException( @@ -582,7 +586,7 @@ public void updateEntity() { if (state != QuarryWorkState.RUNNING) break; } } - if (brokenBlocksTick < STEPS_PER_TICK && state == QuarryWorkState.RUNNING) { + if (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) && state == QuarryWorkState.RUNNING) { state = QuarryWorkState.FINISHED; unloadSelf(); } diff --git a/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json b/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json new file mode 100644 index 00000000..ca5fbcf2 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json @@ -0,0 +1,7 @@ +{ + "variants": { + "meta=0": { "model": "utilitiesinexcess:blocks/upgrade_speed_1" }, + "meta=1": { "model": "utilitiesinexcess:blocks/upgrade_speed_2" }, + "meta=2": { "model": "utilitiesinexcess:blocks/upgrade_speed_3" } + } +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json new file mode 100644 index 00000000..4b111cb3 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json @@ -0,0 +1,701 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:blocks/upgrade_speed_1", + "particle": "utilitiesinexcess:blocks/upgrade_speed_1" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [11.5, 5.5, 11.75, 6], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [6.75, 11.5, 7, 12], "texture": "#0"}, + "up": {"uv": [12, 4, 11.5, 3.75], "texture": "#0"}, + "down": {"uv": [12, 7.25, 11.5, 7.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 11.5, 7.25, 12], "texture": "#0"}, + "east": {"uv": [8.5, 11.5, 8.75, 12], "texture": "#0"}, + "south": {"uv": [8.75, 11.5, 9, 12], "texture": "#0"}, + "west": {"uv": [9, 11.5, 9.25, 12], "texture": "#0"}, + "up": {"uv": [11.75, 8.75, 11.5, 8.5], "texture": "#0"}, + "down": {"uv": [12.5, 3.5, 12.25, 3.75], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9.25, 11.5, 9.5, 12], "texture": "#0"}, + "east": {"uv": [9.5, 11.5, 9.75, 12], "texture": "#0"}, + "south": {"uv": [9.75, 11.5, 10, 12], "texture": "#0"}, + "west": {"uv": [10, 11.5, 10.25, 12], "texture": "#0"}, + "up": {"uv": [12.5, 6.75, 12.25, 6.5], "texture": "#0"}, + "down": {"uv": [12.5, 6.75, 12.25, 7], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 10, 11.75, 10.5], "texture": "#0"}, + "east": {"uv": [10.25, 11.5, 10.5, 12], "texture": "#0"}, + "south": {"uv": [11.5, 10.5, 11.75, 11], "texture": "#0"}, + "west": {"uv": [11.25, 11.5, 11.5, 12], "texture": "#0"}, + "up": {"uv": [12.5, 7.25, 12.25, 7], "texture": "#0"}, + "down": {"uv": [12.5, 7.25, 12.25, 7.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 11.5, 11.75, 12], "texture": "#0"}, + "east": {"uv": [10.25, 8.75, 10.75, 9.25], "texture": "#0"}, + "south": {"uv": [11.75, 1, 12, 1.5], "texture": "#0"}, + "west": {"uv": [3.25, 10.5, 3.75, 11], "texture": "#0"}, + "up": {"uv": [12, 2, 11.75, 1.5], "texture": "#0"}, + "down": {"uv": [12, 2, 11.75, 2.5], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 2.5, 12, 3], "texture": "#0"}, + "east": {"uv": [3, 11.75, 3.25, 12.25], "texture": "#0"}, + "south": {"uv": [11.75, 3, 12, 3.5], "texture": "#0"}, + "west": {"uv": [11.75, 4.5, 12, 5], "texture": "#0"}, + "up": {"uv": [12.5, 7.75, 12.25, 7.5], "texture": "#0"}, + "down": {"uv": [12.5, 7.75, 12.25, 8], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 5, 12, 5.5], "texture": "#0"}, + "east": {"uv": [11.75, 5.5, 12, 6], "texture": "#0"}, + "south": {"uv": [11.75, 8.5, 12, 9], "texture": "#0"}, + "west": {"uv": [11.75, 10, 12, 10.5], "texture": "#0"}, + "up": {"uv": [12.5, 8.25, 12.25, 8], "texture": "#0"}, + "down": {"uv": [12.5, 8.25, 12.25, 8.5], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 10.5, 12, 11], "texture": "#0"}, + "east": {"uv": [11.75, 11.5, 12, 12], "texture": "#0"}, + "south": {"uv": [12, 1, 12.25, 1.5], "texture": "#0"}, + "west": {"uv": [12, 1.5, 12.25, 2], "texture": "#0"}, + "up": {"uv": [12.5, 8.75, 12.25, 8.5], "texture": "#0"}, + "down": {"uv": [12.5, 8.75, 12.25, 9], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 4.5, 11, 5], "texture": "#0"}, + "east": {"uv": [2, 12, 2.25, 12.5], "texture": "#0"}, + "south": {"uv": [10.5, 5, 11, 5.5], "texture": "#0"}, + "west": {"uv": [12, 2, 12.25, 2.5], "texture": "#0"}, + "up": {"uv": [12.25, 3.75, 11.75, 3.5], "texture": "#0"}, + "down": {"uv": [12.25, 9, 11.75, 9.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.25, 12, 2.5, 12.5], "texture": "#0"}, + "east": {"uv": [2.5, 12, 2.75, 12.5], "texture": "#0"}, + "south": {"uv": [12, 2.5, 12.25, 3], "texture": "#0"}, + "west": {"uv": [2.75, 12, 3, 12.5], "texture": "#0"}, + "up": {"uv": [9.25, 12.5, 9, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 9, 12.25, 9.25], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 3, 12.25, 3.5], "texture": "#0"}, + "east": {"uv": [3.25, 12, 3.5, 12.5], "texture": "#0"}, + "south": {"uv": [3.5, 12, 3.75, 12.5], "texture": "#0"}, + "west": {"uv": [4, 12, 4.25, 12.5], "texture": "#0"}, + "up": {"uv": [9.5, 12.5, 9.25, 12.25], "texture": "#0"}, + "down": {"uv": [9.75, 12.25, 9.5, 12.5], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.25, 12, 4.5, 12.5], "texture": "#0"}, + "east": {"uv": [4.5, 12, 4.75, 12.5], "texture": "#0"}, + "south": {"uv": [12, 4.5, 12.25, 5], "texture": "#0"}, + "west": {"uv": [4.75, 12, 5, 12.5], "texture": "#0"}, + "up": {"uv": [10, 12.5, 9.75, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 10, 12.25, 10.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5, 12, 5.25, 12.5], "texture": "#0"}, + "east": {"uv": [10.5, 7.5, 11, 8], "texture": "#0"}, + "south": {"uv": [12, 5, 12.25, 5.5], "texture": "#0"}, + "west": {"uv": [10.5, 8, 11, 8.5], "texture": "#0"}, + "up": {"uv": [12.25, 6, 12, 5.5], "texture": "#0"}, + "down": {"uv": [12.25, 6, 12, 6.5], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 6.5, 12.25, 7], "texture": "#0"}, + "east": {"uv": [6.75, 12, 7, 12.5], "texture": "#0"}, + "south": {"uv": [7, 12, 7.25, 12.5], "texture": "#0"}, + "west": {"uv": [12, 7, 12.25, 7.5], "texture": "#0"}, + "up": {"uv": [12.5, 10.5, 12.25, 10.25], "texture": "#0"}, + "down": {"uv": [11, 12.25, 10.75, 12.5], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 12, 7.5, 12.5], "texture": "#0"}, + "east": {"uv": [7.5, 12, 7.75, 12.5], "texture": "#0"}, + "south": {"uv": [12, 7.5, 12.25, 8], "texture": "#0"}, + "west": {"uv": [7.75, 12, 8, 12.5], "texture": "#0"}, + "up": {"uv": [11.25, 12.5, 11, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 11, 12.25, 11.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 12, 8.25, 12.5], "texture": "#0"}, + "east": {"uv": [12, 8, 12.25, 8.5], "texture": "#0"}, + "south": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "west": {"uv": [8.5, 12, 8.75, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 11.5, 12.25, 11.25], "texture": "#0"}, + "down": {"uv": [12.5, 11.5, 12.25, 11.75], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 10, 11, 10.5], "texture": "#0"}, + "east": {"uv": [12, 8.5, 12.25, 9], "texture": "#0"}, + "south": {"uv": [10.5, 10.5, 11, 11], "texture": "#0"}, + "west": {"uv": [8.75, 12, 9, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 4, 12, 3.75], "texture": "#0"}, + "down": {"uv": [9.5, 12, 9, 12.25], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 9.25, 12.5, 9.5], "texture": "#0"}, + "east": {"uv": [12.25, 11.75, 12.5, 12], "texture": "#0"}, + "south": {"uv": [9.5, 12, 10, 12.25], "texture": "#0"}, + "west": {"uv": [12, 12.25, 12.25, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 9.75, 12, 9.5], "texture": "#0"}, + "down": {"uv": [12.5, 9.75, 12, 10], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 12, 10.25, 12.5], "texture": "#0"}, + "east": {"uv": [10.75, 0, 11.25, 0.5], "texture": "#0"}, + "south": {"uv": [12, 10, 12.25, 10.5], "texture": "#0"}, + "west": {"uv": [10.75, 0.5, 11.25, 1], "texture": "#0"}, + "up": {"uv": [10.5, 12.5, 10.25, 12], "texture": "#0"}, + "down": {"uv": [10.75, 12, 10.5, 12.5], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 12.25, 12.5, 12.5], "texture": "#0"}, + "east": {"uv": [12, 10.5, 12.5, 10.75], "texture": "#0"}, + "south": {"uv": [0, 12.5, 0.25, 12.75], "texture": "#0"}, + "west": {"uv": [10.75, 12, 11.25, 12.25], "texture": "#0"}, + "up": {"uv": [11.5, 12.5, 11.25, 12], "texture": "#0"}, + "down": {"uv": [11.75, 12, 11.5, 12.5], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 1, 11.25, 1.5], "texture": "#0"}, + "east": {"uv": [12, 11.5, 12.25, 12], "texture": "#0"}, + "south": {"uv": [10.75, 1.5, 11.25, 2], "texture": "#0"}, + "west": {"uv": [11.75, 12, 12, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 11, 12, 10.75], "texture": "#0"}, + "down": {"uv": [12.5, 12, 12, 12.25], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 0, 12.75, 0.25], "texture": "#0"}, + "east": {"uv": [0.25, 12.5, 0.5, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 0.25, 12.75, 0.5], "texture": "#0"}, + "west": {"uv": [0.5, 12.5, 0.75, 12.75], "texture": "#0"}, + "up": {"uv": [12.75, 0.75, 12.25, 0.5], "texture": "#0"}, + "down": {"uv": [12.75, 0.75, 12.25, 1], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 1, 12.5, 1.5], "texture": "#0"}, + "east": {"uv": [10.75, 2, 11.25, 2.5], "texture": "#0"}, + "south": {"uv": [12.25, 1.5, 12.5, 2], "texture": "#0"}, + "west": {"uv": [10.75, 2.5, 11.25, 3], "texture": "#0"}, + "up": {"uv": [12.5, 2.5, 12.25, 2], "texture": "#0"}, + "down": {"uv": [12.5, 2.5, 12.25, 3], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.75, 12.5, 1, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 3, 12.75, 3.25], "texture": "#0"}, + "south": {"uv": [1, 12.5, 1.25, 12.75], "texture": "#0"}, + "west": {"uv": [12.25, 3.25, 12.75, 3.5], "texture": "#0"}, + "up": {"uv": [3.25, 12.75, 3, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 4, 12.25, 4.5], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 1, 12.75, 1.25], "texture": "#0"}, + "east": {"uv": [10.5, 3.75, 11.5, 4], "texture": "#0"}, + "south": {"uv": [1.25, 12.5, 1.5, 12.75], "texture": "#0"}, + "west": {"uv": [10.5, 5.5, 11.5, 5.75], "texture": "#0"}, + "up": {"uv": [4, 11.5, 3.75, 10.5], "texture": "#0"}, + "down": {"uv": [3.5, 11, 3.25, 12], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 4.5, 12.5, 5], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [12.25, 5, 12.5, 5.5], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [3.75, 12, 3.5, 11], "texture": "#0"}, + "down": {"uv": [11.25, 4.5, 11, 5.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 8.5, 11.5, 8.75], "texture": "#0"}, + "east": {"uv": [12.5, 1.25, 12.75, 1.5], "texture": "#0"}, + "south": {"uv": [10.75, 3, 11.75, 3.25], "texture": "#0"}, + "west": {"uv": [1.5, 12.5, 1.75, 12.75], "texture": "#0"}, + "up": {"uv": [11.75, 3.5, 10.75, 3.25], "texture": "#0"}, + "down": {"uv": [11.75, 3.5, 10.75, 3.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [5.25, 12.25, 5.5, 12.75], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [5.5, 12.25, 5.75, 12.75], "texture": "#0"}, + "up": {"uv": [11.75, 9, 10.75, 8.75], "texture": "#0"}, + "down": {"uv": [11.75, 9, 10.75, 9.25], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 1.5, 12.75, 1.75], "texture": "#0"}, + "east": {"uv": [11, 7.5, 12, 7.75], "texture": "#0"}, + "south": {"uv": [1.75, 12.5, 2, 12.75], "texture": "#0"}, + "west": {"uv": [11, 7.75, 12, 8], "texture": "#0"}, + "up": {"uv": [11.25, 11, 11, 10], "texture": "#0"}, + "down": {"uv": [10.75, 11, 10.5, 12], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 5.5, 12.5, 6], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [11, 12, 10.75, 11], "texture": "#0"}, + "down": {"uv": [11.25, 11, 11, 12], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11, 8, 12, 8.25], "texture": "#0"}, + "east": {"uv": [12.5, 1.75, 12.75, 2], "texture": "#0"}, + "south": {"uv": [11, 8.25, 12, 8.5], "texture": "#0"}, + "west": {"uv": [2, 12.5, 2.25, 12.75], "texture": "#0"}, + "up": {"uv": [12.25, 0.25, 11.25, 0], "texture": "#0"}, + "down": {"uv": [12.25, 0.25, 11.25, 0.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [12.25, 6, 12.5, 6.5], "texture": "#0"}, + "up": {"uv": [12.25, 0.75, 11.25, 0.5], "texture": "#0"}, + "down": {"uv": [12.25, 0.75, 11.25, 1], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [1, 5.75, 9], + "to": [2, 9.38, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [1.5, 7.75, 9.5]}, + "faces": { + "north": {"uv": [11.25, 1, 11.5, 2], "texture": "#0"}, + "east": {"uv": [11.25, 2, 11.5, 3], "texture": "#0"}, + "south": {"uv": [11.25, 4.5, 11.5, 5.5], "texture": "#0"}, + "west": {"uv": [11.25, 10, 11.5, 11], "texture": "#0"}, + "up": {"uv": [12.75, 2.25, 12.5, 2], "texture": "#0"}, + "down": {"uv": [2.5, 12.5, 2.25, 12.75], "texture": "#0"} + } + }, + { + "from": [1, 5.75, 6], + "to": [2, 10.37, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [1.5, 7.75, 6.5]}, + "faces": { + "north": {"uv": [10.25, 4.5, 10.5, 5.75], "texture": "#0"}, + "east": {"uv": [6.25, 10.25, 6.5, 11.5], "texture": "#0"}, + "south": {"uv": [6.5, 10.25, 6.75, 11.5], "texture": "#0"}, + "west": {"uv": [6.75, 10.25, 7, 11.5], "texture": "#0"}, + "up": {"uv": [12.75, 2.5, 12.5, 2.25], "texture": "#0"}, + "down": {"uv": [2.75, 12.5, 2.5, 12.75], "texture": "#0"} + } + }, + { + "from": [9, 14, 6.62], + "to": [10, 15, 10.25], + "rotation": {"angle": 45, "axis": "y", "origin": [9.5, 14.75, 8.25]}, + "faces": { + "north": {"uv": [12.5, 2.5, 12.75, 2.75], "texture": "#0"}, + "east": {"uv": [11.25, 11, 12.25, 11.25], "texture": "#0"}, + "south": {"uv": [2.75, 12.5, 3, 12.75], "texture": "#0"}, + "west": {"uv": [11.25, 11.25, 12.25, 11.5], "texture": "#0"}, + "up": {"uv": [0.25, 12.5, 0, 11.5], "texture": "#0"}, + "down": {"uv": [0.5, 11.5, 0.25, 12.5], "texture": "#0"} + } + }, + { + "from": [6, 14, 5.63], + "to": [7, 15, 10.25], + "rotation": {"angle": -45, "axis": "y", "origin": [6.5, 14.75, 8.25]}, + "faces": { + "north": {"uv": [12.5, 2.75, 12.75, 3], "texture": "#0"}, + "east": {"uv": [10.25, 5.75, 11.5, 6], "texture": "#0"}, + "south": {"uv": [3.25, 12.5, 3.5, 12.75], "texture": "#0"}, + "west": {"uv": [10.25, 7.25, 11.5, 7.5], "texture": "#0"}, + "up": {"uv": [7.25, 11.5, 7, 10.25], "texture": "#0"}, + "down": {"uv": [10.5, 7.5, 10.25, 8.75], "texture": "#0"} + } + }, + { + "from": [14, 5.75, 6], + "to": [15, 9.38, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [14.5, 7.75, 6.5]}, + "faces": { + "north": {"uv": [0.5, 11.5, 0.75, 12.5], "texture": "#0"}, + "east": {"uv": [0.75, 11.5, 1, 12.5], "texture": "#0"}, + "south": {"uv": [1, 11.5, 1.25, 12.5], "texture": "#0"}, + "west": {"uv": [11.5, 1, 11.75, 2], "texture": "#0"}, + "up": {"uv": [3.75, 12.75, 3.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 3.5, 12.5, 3.75], "texture": "#0"} + } + }, + { + "from": [14, 5.75, 9], + "to": [15, 10.37, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [14.5, 7.75, 9.5]}, + "faces": { + "north": {"uv": [8.5, 10.25, 8.75, 11.5], "texture": "#0"}, + "east": {"uv": [8.75, 10.25, 9, 11.5], "texture": "#0"}, + "south": {"uv": [9, 10.25, 9.25, 11.5], "texture": "#0"}, + "west": {"uv": [9.25, 10.25, 9.5, 11.5], "texture": "#0"}, + "up": {"uv": [4, 12.75, 3.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 3.75, 12.5, 4], "texture": "#0"} + } + }, + { + "from": [6, 5.75, 14], + "to": [7, 10.37, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 7.75, 14.5]}, + "faces": { + "north": {"uv": [9.5, 10.25, 9.75, 11.5], "texture": "#0"}, + "east": {"uv": [9.75, 10.25, 10, 11.5], "texture": "#0"}, + "south": {"uv": [10, 10.25, 10.25, 11.5], "texture": "#0"}, + "west": {"uv": [10.25, 10.25, 10.5, 11.5], "texture": "#0"}, + "up": {"uv": [4.25, 12.75, 4, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 4, 12.5, 4.25], "texture": "#0"} + } + }, + { + "from": [9, 5.75, 14], + "to": [10, 9.38, 15], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 7.75, 14.5]}, + "faces": { + "north": {"uv": [1.25, 11.5, 1.5, 12.5], "texture": "#0"}, + "east": {"uv": [1.5, 11.5, 1.75, 12.5], "texture": "#0"}, + "south": {"uv": [1.75, 11.5, 2, 12.5], "texture": "#0"}, + "west": {"uv": [11.5, 2, 11.75, 3], "texture": "#0"}, + "up": {"uv": [4.5, 12.75, 4.25, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 4.25, 12.5, 4.5], "texture": "#0"} + } + }, + { + "from": [6, 5.75, 1], + "to": [7, 9.38, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 7.75, 1.5]}, + "faces": { + "north": {"uv": [3.75, 11.5, 4, 12.5], "texture": "#0"}, + "east": {"uv": [11.5, 4.5, 11.75, 5.5], "texture": "#0"}, + "south": {"uv": [6.25, 11.5, 6.5, 12.5], "texture": "#0"}, + "west": {"uv": [6.5, 11.5, 6.75, 12.5], "texture": "#0"}, + "up": {"uv": [4.75, 12.75, 4.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 4.5, 12.5, 4.75], "texture": "#0"} + } + }, + { + "from": [9, 5.75, 1], + "to": [10, 10.37, 2], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 7.75, 1.5]}, + "faces": { + "north": {"uv": [10.5, 0, 10.75, 1.25], "texture": "#0"}, + "east": {"uv": [10.5, 1.25, 10.75, 2.5], "texture": "#0"}, + "south": {"uv": [10.5, 2.5, 10.75, 3.75], "texture": "#0"}, + "west": {"uv": [3, 10.5, 3.25, 11.75], "texture": "#0"}, + "up": {"uv": [5, 12.75, 4.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 4.75, 12.5, 5], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [41, 42, 43, 44, 45, 46, 47, 48, 49, 50] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_2.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_2.json new file mode 100644 index 00000000..e729a6b6 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_2.json @@ -0,0 +1,831 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_speed_2", + "particle": "utilitiesinexcess:upgrade_speed_2" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [11.75, 11, 12, 11.5], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [4, 12, 4.25, 12.5], "texture": "#0"}, + "up": {"uv": [11, 4, 10.5, 3.75], "texture": "#0"}, + "down": {"uv": [4.75, 12, 4.25, 12.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 4.5, 12.25, 5], "texture": "#0"}, + "east": {"uv": [4.75, 12, 5, 12.5], "texture": "#0"}, + "south": {"uv": [5, 12, 5.25, 12.5], "texture": "#0"}, + "west": {"uv": [12, 5, 12.25, 5.5], "texture": "#0"}, + "up": {"uv": [11.25, 4, 11, 3.75], "texture": "#0"}, + "down": {"uv": [12.5, 5.5, 12.25, 5.75], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 6, 12.25, 6.5], "texture": "#0"}, + "east": {"uv": [12, 6.5, 12.25, 7], "texture": "#0"}, + "south": {"uv": [7.25, 12, 7.5, 12.5], "texture": "#0"}, + "west": {"uv": [7.5, 12, 7.75, 12.5], "texture": "#0"}, + "up": {"uv": [13, 5.75, 12.75, 5.5], "texture": "#0"}, + "down": {"uv": [6, 12.75, 5.75, 13], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.75, 12, 8, 12.5], "texture": "#0"}, + "east": {"uv": [8, 12, 8.25, 12.5], "texture": "#0"}, + "south": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "west": {"uv": [12, 9.25, 12.25, 9.75], "texture": "#0"}, + "up": {"uv": [13, 6, 12.75, 5.75], "texture": "#0"}, + "down": {"uv": [6.25, 12.75, 6, 13], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 9.75, 12.25, 10.25], "texture": "#0"}, + "east": {"uv": [11.25, 0, 11.75, 0.5], "texture": "#0"}, + "south": {"uv": [12, 10.25, 12.25, 10.75], "texture": "#0"}, + "west": {"uv": [11.25, 0.5, 11.75, 1], "texture": "#0"}, + "up": {"uv": [12.25, 11.25, 12, 10.75], "texture": "#0"}, + "down": {"uv": [12.25, 11.75, 12, 12.25], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 1, 12.5, 1.5], "texture": "#0"}, + "east": {"uv": [12.25, 1.5, 12.5, 2], "texture": "#0"}, + "south": {"uv": [12.25, 2, 12.5, 2.5], "texture": "#0"}, + "west": {"uv": [12.25, 2.5, 12.5, 3], "texture": "#0"}, + "up": {"uv": [13, 6.25, 12.75, 6], "texture": "#0"}, + "down": {"uv": [6.5, 12.75, 6.25, 13], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 3, 12.5, 3.5], "texture": "#0"}, + "east": {"uv": [12.25, 3.5, 12.5, 4], "texture": "#0"}, + "south": {"uv": [12.25, 4, 12.5, 4.5], "texture": "#0"}, + "west": {"uv": [4.25, 12.25, 4.5, 12.75], "texture": "#0"}, + "up": {"uv": [13, 6.5, 12.75, 6.25], "texture": "#0"}, + "down": {"uv": [6.75, 12.75, 6.5, 13], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.5, 12.25, 4.75, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 4.5, 12.5, 5], "texture": "#0"}, + "south": {"uv": [12.25, 5, 12.5, 5.5], "texture": "#0"}, + "west": {"uv": [5.25, 12.25, 5.5, 12.75], "texture": "#0"}, + "up": {"uv": [13, 6.75, 12.75, 6.5], "texture": "#0"}, + "down": {"uv": [7, 12.75, 6.75, 13], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 1, 11.75, 1.5], "texture": "#0"}, + "east": {"uv": [5.5, 12.25, 5.75, 12.75], "texture": "#0"}, + "south": {"uv": [11.25, 1.5, 11.75, 2], "texture": "#0"}, + "west": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "up": {"uv": [12.5, 7.25, 12, 7], "texture": "#0"}, + "down": {"uv": [12.5, 11.25, 12, 11.5], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 6, 12.5, 6.5], "texture": "#0"}, + "south": {"uv": [12.25, 6.5, 12.5, 7], "texture": "#0"}, + "west": {"uv": [12.25, 9.25, 12.5, 9.75], "texture": "#0"}, + "up": {"uv": [13, 7, 12.75, 6.75], "texture": "#0"}, + "down": {"uv": [7.25, 12.75, 7, 13], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 9.75, 12.5, 10.25], "texture": "#0"}, + "east": {"uv": [12.25, 10.25, 12.5, 10.75], "texture": "#0"}, + "south": {"uv": [12.25, 10.75, 12.5, 11.25], "texture": "#0"}, + "west": {"uv": [11, 12.25, 11.25, 12.75], "texture": "#0"}, + "up": {"uv": [7.5, 13, 7.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.25, 12.75, 7.5], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 11.75, 12.5, 12.25], "texture": "#0"}, + "east": {"uv": [12, 12.25, 12.25, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 12.25, 12.5, 12.75], "texture": "#0"}, + "west": {"uv": [0, 12.5, 0.25, 13], "texture": "#0"}, + "up": {"uv": [7.75, 13, 7.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.5, 12.75, 7.75], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.25, 12.5, 0.5, 13], "texture": "#0"}, + "east": {"uv": [11.25, 2, 11.75, 2.5], "texture": "#0"}, + "south": {"uv": [12.5, 1, 12.75, 1.5], "texture": "#0"}, + "west": {"uv": [11.25, 2.5, 11.75, 3], "texture": "#0"}, + "up": {"uv": [1.75, 13, 1.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 1.5, 12.5, 2], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1.75, 12.5, 2, 13], "texture": "#0"}, + "east": {"uv": [12.5, 2, 12.75, 2.5], "texture": "#0"}, + "south": {"uv": [12.5, 2.5, 12.75, 3], "texture": "#0"}, + "west": {"uv": [12.5, 3, 12.75, 3.5], "texture": "#0"}, + "up": {"uv": [8, 13, 7.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.75, 12.75, 8], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 3.5, 12.75, 4], "texture": "#0"}, + "east": {"uv": [4, 12.5, 4.25, 13], "texture": "#0"}, + "south": {"uv": [12.5, 4, 12.75, 4.5], "texture": "#0"}, + "west": {"uv": [12.5, 4.5, 12.75, 5], "texture": "#0"}, + "up": {"uv": [8.25, 13, 8, 12.75], "texture": "#0"}, + "down": {"uv": [13, 8, 12.75, 8.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.75, 12.5, 5, 13], "texture": "#0"}, + "east": {"uv": [5, 12.5, 5.25, 13], "texture": "#0"}, + "south": {"uv": [12.5, 5, 12.75, 5.5], "texture": "#0"}, + "west": {"uv": [12.5, 5.5, 12.75, 6], "texture": "#0"}, + "up": {"uv": [13, 8.5, 12.75, 8.25], "texture": "#0"}, + "down": {"uv": [8.75, 12.75, 8.5, 13], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 3, 11.75, 3.5], "texture": "#0"}, + "east": {"uv": [12.5, 6, 12.75, 6.5], "texture": "#0"}, + "south": {"uv": [11.25, 3.5, 11.75, 4], "texture": "#0"}, + "west": {"uv": [12.5, 6.5, 12.75, 7], "texture": "#0"}, + "up": {"uv": [12.75, 8.5, 12.25, 8.25], "texture": "#0"}, + "down": {"uv": [13, 7, 12.5, 7.25], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 12.5, 7.75, 12.75], "texture": "#0"}, + "east": {"uv": [8.75, 12.75, 9, 13], "texture": "#0"}, + "south": {"uv": [7.75, 12.5, 8.25, 12.75], "texture": "#0"}, + "west": {"uv": [9, 12.75, 9.25, 13], "texture": "#0"}, + "up": {"uv": [13, 8.75, 12.5, 8.5], "texture": "#0"}, + "down": {"uv": [13, 8.75, 12.5, 9], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8.25, 12.5, 8.5, 13], "texture": "#0"}, + "east": {"uv": [11.25, 4.5, 11.75, 5], "texture": "#0"}, + "south": {"uv": [12.5, 9, 12.75, 9.5], "texture": "#0"}, + "west": {"uv": [11.25, 5, 11.75, 5.5], "texture": "#0"}, + "up": {"uv": [12.75, 10, 12.5, 9.5], "texture": "#0"}, + "down": {"uv": [12.75, 10, 12.5, 10.5], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 9, 13, 9.25], "texture": "#0"}, + "east": {"uv": [12.5, 10.5, 13, 10.75], "texture": "#0"}, + "south": {"uv": [9.25, 12.75, 9.5, 13], "texture": "#0"}, + "west": {"uv": [12.5, 10.75, 13, 11], "texture": "#0"}, + "up": {"uv": [12.75, 11.5, 12.5, 11], "texture": "#0"}, + "down": {"uv": [11.5, 12.5, 11.25, 13], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 7.25, 11.75, 7.75], "texture": "#0"}, + "east": {"uv": [12.5, 11.5, 12.75, 12], "texture": "#0"}, + "south": {"uv": [11.25, 7.75, 11.75, 8.25], "texture": "#0"}, + "west": {"uv": [12.5, 12, 12.75, 12.5], "texture": "#0"}, + "up": {"uv": [13, 12.75, 12.5, 12.5], "texture": "#0"}, + "down": {"uv": [13.25, 0, 12.75, 0.25], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 0.25, 13.25, 0.5], "texture": "#0"}, + "east": {"uv": [12.75, 9.25, 13, 9.5], "texture": "#0"}, + "south": {"uv": [12.75, 0.5, 13.25, 0.75], "texture": "#0"}, + "west": {"uv": [9.5, 12.75, 9.75, 13], "texture": "#0"}, + "up": {"uv": [13.25, 1, 12.75, 0.75], "texture": "#0"}, + "down": {"uv": [13.25, 1, 12.75, 1.25], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 1.25, 13, 1.75], "texture": "#0"}, + "east": {"uv": [11.25, 10, 11.75, 10.5], "texture": "#0"}, + "south": {"uv": [12.75, 1.75, 13, 2.25], "texture": "#0"}, + "west": {"uv": [10.5, 11.25, 11, 11.75], "texture": "#0"}, + "up": {"uv": [13, 2.75, 12.75, 2.25], "texture": "#0"}, + "down": {"uv": [13, 2.75, 12.75, 3.25], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 9.5, 13, 9.75], "texture": "#0"}, + "east": {"uv": [3, 12.75, 3.5, 13], "texture": "#0"}, + "south": {"uv": [9.75, 12.75, 10, 13], "texture": "#0"}, + "west": {"uv": [12.75, 3.25, 13.25, 3.5], "texture": "#0"}, + "up": {"uv": [3.75, 13.25, 3.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 3.5, 12.75, 4], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 9.75, 13, 10], "texture": "#0"}, + "east": {"uv": [11.25, 5.5, 12.25, 5.75], "texture": "#0"}, + "south": {"uv": [10, 12.75, 10.25, 13], "texture": "#0"}, + "west": {"uv": [11.25, 8.25, 12.25, 8.5], "texture": "#0"}, + "up": {"uv": [11.5, 11.5, 11.25, 10.5], "texture": "#0"}, + "down": {"uv": [11.25, 11.25, 11, 12.25], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3.75, 12.75, 4, 13.25], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [12.75, 4, 13, 4.5], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [0.25, 12.5, 0, 11.5], "texture": "#0"}, + "down": {"uv": [0.5, 11.5, 0.25, 12.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.5, 11.5, 1.5, 11.75], "texture": "#0"}, + "east": {"uv": [12.75, 10, 13, 10.25], "texture": "#0"}, + "south": {"uv": [11.5, 5.75, 12.5, 6], "texture": "#0"}, + "west": {"uv": [10.25, 12.75, 10.5, 13], "texture": "#0"}, + "up": {"uv": [7.25, 11.75, 6.25, 11.5], "texture": "#0"}, + "down": {"uv": [9.5, 11.5, 8.5, 11.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [4.25, 12.75, 4.5, 13.25], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [4.5, 12.75, 4.75, 13.25], "texture": "#0"}, + "up": {"uv": [12.5, 8.75, 11.5, 8.5], "texture": "#0"}, + "down": {"uv": [12.5, 8.75, 11.5, 9], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 10.25, 13, 10.5], "texture": "#0"}, + "east": {"uv": [11.5, 9, 12.5, 9.25], "texture": "#0"}, + "south": {"uv": [10.5, 12.75, 10.75, 13], "texture": "#0"}, + "west": {"uv": [9.5, 11.5, 10.5, 11.75], "texture": "#0"}, + "up": {"uv": [1.75, 12.5, 1.5, 11.5], "texture": "#0"}, + "down": {"uv": [2, 11.5, 1.75, 12.5], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 4.5, 13, 5], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [12.75, 5, 13, 5.5], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [11.75, 11.5, 11.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.5, 11.5, 11.25, 12.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 11.5, 12.5, 11.75], "texture": "#0"}, + "east": {"uv": [10.75, 12.75, 11, 13], "texture": "#0"}, + "south": {"uv": [11.75, 0, 12.75, 0.25], "texture": "#0"}, + "west": {"uv": [11, 12.75, 11.25, 13], "texture": "#0"}, + "up": {"uv": [12.75, 0.5, 11.75, 0.25], "texture": "#0"}, + "down": {"uv": [1.5, 11.75, 0.5, 12], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [5.25, 12.75, 5.5, 13.25], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [5.5, 12.75, 5.75, 13.25], "texture": "#0"}, + "up": {"uv": [12.75, 0.75, 11.75, 0.5], "texture": "#0"}, + "down": {"uv": [12.75, 0.75, 11.75, 1], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [6, 4.75, 1], + "to": [7, 8.38, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 6.75, 1.5]}, + "faces": { + "north": {"uv": [11.75, 1, 12, 2], "texture": "#0"}, + "east": {"uv": [11.75, 2, 12, 3], "texture": "#0"}, + "south": {"uv": [3, 11.75, 3.25, 12.75], "texture": "#0"}, + "west": {"uv": [11.75, 3, 12, 4], "texture": "#0"}, + "up": {"uv": [13, 11.25, 12.75, 11], "texture": "#0"}, + "down": {"uv": [13, 11.25, 12.75, 11.5], "texture": "#0"} + } + }, + { + "from": [1, 4.75, 6], + "to": [2, 9.37, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [1.5, 6.75, 6.5]}, + "faces": { + "north": {"uv": [10.25, 4.5, 10.5, 5.75], "texture": "#0"}, + "east": {"uv": [6.25, 10.25, 6.5, 11.5], "texture": "#0"}, + "south": {"uv": [6.5, 10.25, 6.75, 11.5], "texture": "#0"}, + "west": {"uv": [6.75, 10.25, 7, 11.5], "texture": "#0"}, + "up": {"uv": [11.75, 13, 11.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 11.5, 12.75, 11.75], "texture": "#0"} + } + }, + { + "from": [1, 6.75, 9], + "to": [2, 10.38, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [1.5, 8.75, 9.5]}, + "faces": { + "north": {"uv": [3.25, 11.75, 3.5, 12.75], "texture": "#0"}, + "east": {"uv": [3.5, 11.75, 3.75, 12.75], "texture": "#0"}, + "south": {"uv": [3.75, 11.75, 4, 12.75], "texture": "#0"}, + "west": {"uv": [11.75, 4.5, 12, 5.5], "texture": "#0"}, + "up": {"uv": [12, 13, 11.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 11.75, 12.75, 12], "texture": "#0"} + } + }, + { + "from": [1, 6.75, 6], + "to": [2, 11.37, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [1.5, 8.75, 6.5]}, + "faces": { + "north": {"uv": [7, 10.25, 7.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 7.25, 10.5, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 10.25, 8.75, 11.5], "texture": "#0"}, + "west": {"uv": [8.75, 10.25, 9, 11.5], "texture": "#0"}, + "up": {"uv": [12.25, 13, 12, 12.75], "texture": "#0"}, + "down": {"uv": [13, 12, 12.75, 12.25], "texture": "#0"} + } + }, + { + "from": [1, 4.75, 9], + "to": [2, 8.38, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [1.5, 6.75, 9.5]}, + "faces": { + "north": {"uv": [6.25, 11.75, 6.5, 12.75], "texture": "#0"}, + "east": {"uv": [6.5, 11.75, 6.75, 12.75], "texture": "#0"}, + "south": {"uv": [6.75, 11.75, 7, 12.75], "texture": "#0"}, + "west": {"uv": [7, 11.75, 7.25, 12.75], "texture": "#0"}, + "up": {"uv": [12.5, 13, 12.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 12.25, 12.75, 12.5], "texture": "#0"} + } + }, + { + "from": [6, 4.75, 14], + "to": [7, 9.37, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 6.75, 14.5]}, + "faces": { + "north": {"uv": [9, 10.25, 9.25, 11.5], "texture": "#0"}, + "east": {"uv": [9.25, 10.25, 9.5, 11.5], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 9.75, 11.5], "texture": "#0"}, + "west": {"uv": [9.75, 10.25, 10, 11.5], "texture": "#0"}, + "up": {"uv": [12.75, 13, 12.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 12.75, 12.75, 13], "texture": "#0"} + } + }, + { + "from": [9, 14, 7.62], + "to": [10, 15, 11.25], + "rotation": {"angle": 45, "axis": "y", "origin": [9.5, 14.75, 9.25]}, + "faces": { + "north": {"uv": [0, 13, 0.25, 13.25], "texture": "#0"}, + "east": {"uv": [11.75, 7.25, 12.75, 7.5], "texture": "#0"}, + "south": {"uv": [0.25, 13, 0.5, 13.25], "texture": "#0"}, + "west": {"uv": [11.75, 7.5, 12.75, 7.75], "texture": "#0"}, + "up": {"uv": [8.75, 12.75, 8.5, 11.75], "texture": "#0"}, + "down": {"uv": [9, 11.75, 8.75, 12.75], "texture": "#0"} + } + }, + { + "from": [6, 14, 6.63], + "to": [7, 15, 11.25], + "rotation": {"angle": -45, "axis": "y", "origin": [6.5, 14.75, 9.25]}, + "faces": { + "north": {"uv": [0.5, 13, 0.75, 13.25], "texture": "#0"}, + "east": {"uv": [10.25, 5.75, 11.5, 6], "texture": "#0"}, + "south": {"uv": [0.75, 13, 1, 13.25], "texture": "#0"}, + "west": {"uv": [10.25, 8.5, 11.5, 8.75], "texture": "#0"}, + "up": {"uv": [10.25, 11.5, 10, 10.25], "texture": "#0"}, + "down": {"uv": [10.5, 10.25, 10.25, 11.5], "texture": "#0"} + } + }, + { + "from": [9, 14, 5.62], + "to": [10, 15, 9.25], + "rotation": {"angle": 45, "axis": "y", "origin": [9.5, 14.75, 7.25]}, + "faces": { + "north": {"uv": [1, 13, 1.25, 13.25], "texture": "#0"}, + "east": {"uv": [11.75, 7.75, 12.75, 8], "texture": "#0"}, + "south": {"uv": [1.25, 13, 1.5, 13.25], "texture": "#0"}, + "west": {"uv": [11.75, 8, 12.75, 8.25], "texture": "#0"}, + "up": {"uv": [9.25, 12.75, 9, 11.75], "texture": "#0"}, + "down": {"uv": [9.5, 11.75, 9.25, 12.75], "texture": "#0"} + } + }, + { + "from": [6, 14, 4.63], + "to": [7, 15, 9.25], + "rotation": {"angle": -45, "axis": "y", "origin": [6.5, 14.75, 7.25]}, + "faces": { + "north": {"uv": [13, 1.25, 13.25, 1.5], "texture": "#0"}, + "east": {"uv": [10.25, 8.75, 11.5, 9], "texture": "#0"}, + "south": {"uv": [1.5, 13, 1.75, 13.25], "texture": "#0"}, + "west": {"uv": [10.25, 9, 11.5, 9.25], "texture": "#0"}, + "up": {"uv": [10.75, 1.25, 10.5, 0], "texture": "#0"}, + "down": {"uv": [10.75, 1.25, 10.5, 2.5], "texture": "#0"} + } + }, + { + "from": [14, 4.75, 6], + "to": [15, 8.38, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [14.5, 6.75, 6.5]}, + "faces": { + "north": {"uv": [9.5, 11.75, 9.75, 12.75], "texture": "#0"}, + "east": {"uv": [9.75, 11.75, 10, 12.75], "texture": "#0"}, + "south": {"uv": [10, 11.75, 10.25, 12.75], "texture": "#0"}, + "west": {"uv": [11.75, 10, 12, 11], "texture": "#0"}, + "up": {"uv": [13.25, 1.75, 13, 1.5], "texture": "#0"}, + "down": {"uv": [2, 13, 1.75, 13.25], "texture": "#0"} + } + }, + { + "from": [14, 4.75, 9], + "to": [15, 9.37, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [14.5, 6.75, 9.5]}, + "faces": { + "north": {"uv": [10.5, 2.5, 10.75, 3.75], "texture": "#0"}, + "east": {"uv": [3, 10.5, 3.25, 11.75], "texture": "#0"}, + "south": {"uv": [3.25, 10.5, 3.5, 11.75], "texture": "#0"}, + "west": {"uv": [3.5, 10.5, 3.75, 11.75], "texture": "#0"}, + "up": {"uv": [13.25, 2, 13, 1.75], "texture": "#0"}, + "down": {"uv": [2.25, 13, 2, 13.25], "texture": "#0"} + } + }, + { + "from": [14, 6.75, 6], + "to": [15, 10.38, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [14.5, 8.75, 6.5]}, + "faces": { + "north": {"uv": [10.25, 11.75, 10.5, 12.75], "texture": "#0"}, + "east": {"uv": [10.5, 11.75, 10.75, 12.75], "texture": "#0"}, + "south": {"uv": [10.75, 11.75, 11, 12.75], "texture": "#0"}, + "west": {"uv": [11.5, 11.75, 11.75, 12.75], "texture": "#0"}, + "up": {"uv": [13.25, 2.25, 13, 2], "texture": "#0"}, + "down": {"uv": [2.5, 13, 2.25, 13.25], "texture": "#0"} + } + }, + { + "from": [14, 6.75, 9], + "to": [15, 11.37, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [14.5, 8.75, 9.5]}, + "faces": { + "north": {"uv": [3.75, 10.5, 4, 11.75], "texture": "#0"}, + "east": {"uv": [10.5, 4.5, 10.75, 5.75], "texture": "#0"}, + "south": {"uv": [10.5, 7.25, 10.75, 8.5], "texture": "#0"}, + "west": {"uv": [10.5, 10, 10.75, 11.25], "texture": "#0"}, + "up": {"uv": [13.25, 2.5, 13, 2.25], "texture": "#0"}, + "down": {"uv": [2.75, 13, 2.5, 13.25], "texture": "#0"} + } + }, + { + "from": [9, 4.75, 14], + "to": [10, 8.38, 15], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 6.75, 14.5]}, + "faces": { + "north": {"uv": [11.75, 11.75, 12, 12.75], "texture": "#0"}, + "east": {"uv": [0.5, 12, 0.75, 13], "texture": "#0"}, + "south": {"uv": [0.75, 12, 1, 13], "texture": "#0"}, + "west": {"uv": [1, 12, 1.25, 13], "texture": "#0"}, + "up": {"uv": [13.25, 2.75, 13, 2.5], "texture": "#0"}, + "down": {"uv": [3, 13, 2.75, 13.25], "texture": "#0"} + } + }, + { + "from": [6, 6.75, 14], + "to": [7, 11.37, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 8.75, 14.5]}, + "faces": { + "north": {"uv": [10.75, 0, 11, 1.25], "texture": "#0"}, + "east": {"uv": [10.75, 1.25, 11, 2.5], "texture": "#0"}, + "south": {"uv": [10.75, 2.5, 11, 3.75], "texture": "#0"}, + "west": {"uv": [10.75, 4.5, 11, 5.75], "texture": "#0"}, + "up": {"uv": [13.25, 3, 13, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 13, 3, 13.25], "texture": "#0"} + } + }, + { + "from": [9, 6.75, 14], + "to": [10, 10.38, 15], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 8.75, 14.5]}, + "faces": { + "north": {"uv": [12, 1, 12.25, 2], "texture": "#0"}, + "east": {"uv": [1.25, 12, 1.5, 13], "texture": "#0"}, + "south": {"uv": [2, 12, 2.25, 13], "texture": "#0"}, + "west": {"uv": [12, 2, 12.25, 3], "texture": "#0"}, + "up": {"uv": [13.25, 3.25, 13, 3], "texture": "#0"}, + "down": {"uv": [3.5, 13, 3.25, 13.25], "texture": "#0"} + } + }, + { + "from": [9, 4.75, 1], + "to": [10, 9.37, 2], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 6.75, 1.5]}, + "faces": { + "north": {"uv": [10.75, 7.25, 11, 8.5], "texture": "#0"}, + "east": {"uv": [10.75, 10, 11, 11.25], "texture": "#0"}, + "south": {"uv": [11, 0, 11.25, 1.25], "texture": "#0"}, + "west": {"uv": [11, 1.25, 11.25, 2.5], "texture": "#0"}, + "up": {"uv": [13.25, 3.75, 13, 3.5], "texture": "#0"}, + "down": {"uv": [13.25, 3.75, 13, 4], "texture": "#0"} + } + }, + { + "from": [6, 6.75, 1], + "to": [7, 10.38, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 8.75, 1.5]}, + "faces": { + "north": {"uv": [2.25, 12, 2.5, 13], "texture": "#0"}, + "east": {"uv": [2.5, 12, 2.75, 13], "texture": "#0"}, + "south": {"uv": [2.75, 12, 3, 13], "texture": "#0"}, + "west": {"uv": [12, 3, 12.25, 4], "texture": "#0"}, + "up": {"uv": [4.25, 13.25, 4, 13], "texture": "#0"}, + "down": {"uv": [13.25, 4, 13, 4.25], "texture": "#0"} + } + }, + { + "from": [9, 6.75, 1], + "to": [10, 11.37, 2], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 8.75, 1.5]}, + "faces": { + "north": {"uv": [11, 2.5, 11.25, 3.75], "texture": "#0"}, + "east": {"uv": [11, 4.5, 11.25, 5.75], "texture": "#0"}, + "south": {"uv": [11, 7.25, 11.25, 8.5], "texture": "#0"}, + "west": {"uv": [11, 10, 11.25, 11.25], "texture": "#0"}, + "up": {"uv": [13.25, 4.5, 13, 4.25], "texture": "#0"}, + "down": {"uv": [13.25, 4.5, 13, 4.75], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_3.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_3.json new file mode 100644 index 00000000..c721e553 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_3.json @@ -0,0 +1,961 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_speed_3", + "particle": "utilitiesinexcess:upgrade_speed_3" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [12, 9.5, 12.25, 10], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [12.25, 5, 12.5, 5.5], "texture": "#0"}, + "up": {"uv": [13, 5.25, 12.5, 5], "texture": "#0"}, + "down": {"uv": [13, 5.25, 12.5, 5.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 9.5, 12.5, 10], "texture": "#0"}, + "east": {"uv": [12.5, 10.5, 12.75, 11], "texture": "#0"}, + "south": {"uv": [10.75, 12.5, 11, 13], "texture": "#0"}, + "west": {"uv": [11, 12.5, 11.25, 13], "texture": "#0"}, + "up": {"uv": [13.5, 0.25, 13.25, 0], "texture": "#0"}, + "down": {"uv": [0.5, 13.25, 0.25, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 11, 12.75, 11.5], "texture": "#0"}, + "east": {"uv": [11.25, 12.5, 11.5, 13], "texture": "#0"}, + "south": {"uv": [11.5, 12.5, 11.75, 13], "texture": "#0"}, + "west": {"uv": [12.5, 11.5, 12.75, 12], "texture": "#0"}, + "up": {"uv": [13.5, 0.5, 13.25, 0.25], "texture": "#0"}, + "down": {"uv": [0.75, 13.25, 0.5, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 12.5, 12, 13], "texture": "#0"}, + "east": {"uv": [12, 12.5, 12.25, 13], "texture": "#0"}, + "south": {"uv": [12.5, 12, 12.75, 12.5], "texture": "#0"}, + "west": {"uv": [12.25, 12.5, 12.5, 13], "texture": "#0"}, + "up": {"uv": [13.5, 0.75, 13.25, 0.5], "texture": "#0"}, + "down": {"uv": [1, 13.25, 0.75, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 12.5, 12.75, 13], "texture": "#0"}, + "east": {"uv": [10.5, 4.75, 11, 5.25], "texture": "#0"}, + "south": {"uv": [0, 12.75, 0.25, 13.25], "texture": "#0"}, + "west": {"uv": [10.5, 5.25, 11, 5.75], "texture": "#0"}, + "up": {"uv": [13, 0.5, 12.75, 0], "texture": "#0"}, + "down": {"uv": [0.5, 12.75, 0.25, 13.25], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.5, 12.75, 0.75, 13.25], "texture": "#0"}, + "east": {"uv": [12.75, 0.5, 13, 1], "texture": "#0"}, + "south": {"uv": [0.75, 12.75, 1, 13.25], "texture": "#0"}, + "west": {"uv": [1, 12.75, 1.25, 13.25], "texture": "#0"}, + "up": {"uv": [13.5, 1, 13.25, 0.75], "texture": "#0"}, + "down": {"uv": [1.25, 13.25, 1, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1.25, 12.75, 1.5, 13.25], "texture": "#0"}, + "east": {"uv": [1.5, 12.75, 1.75, 13.25], "texture": "#0"}, + "south": {"uv": [1.75, 12.75, 2, 13.25], "texture": "#0"}, + "west": {"uv": [12.75, 2, 13, 2.5], "texture": "#0"}, + "up": {"uv": [13.5, 1.25, 13.25, 1], "texture": "#0"}, + "down": {"uv": [1.5, 13.25, 1.25, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 2.5, 13, 3], "texture": "#0"}, + "east": {"uv": [12.75, 3, 13, 3.5], "texture": "#0"}, + "south": {"uv": [12.75, 3.5, 13, 4], "texture": "#0"}, + "west": {"uv": [12.75, 4, 13, 4.5], "texture": "#0"}, + "up": {"uv": [13.5, 1.5, 13.25, 1.25], "texture": "#0"}, + "down": {"uv": [1.75, 13.25, 1.5, 13.5], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11, 4.75, 11.5, 5.25], "texture": "#0"}, + "east": {"uv": [12.75, 4.5, 13, 5], "texture": "#0"}, + "south": {"uv": [11, 5.25, 11.5, 5.75], "texture": "#0"}, + "west": {"uv": [12.75, 5.5, 13, 6], "texture": "#0"}, + "up": {"uv": [13.25, 6.25, 12.75, 6], "texture": "#0"}, + "down": {"uv": [13.25, 6.25, 12.75, 6.5], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 6.5, 13, 7], "texture": "#0"}, + "east": {"uv": [12.75, 7, 13, 7.5], "texture": "#0"}, + "south": {"uv": [12.75, 7.5, 13, 8], "texture": "#0"}, + "west": {"uv": [12.75, 8.25, 13, 8.75], "texture": "#0"}, + "up": {"uv": [13.5, 1.75, 13.25, 1.5], "texture": "#0"}, + "down": {"uv": [2, 13.25, 1.75, 13.5], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 8.75, 13, 9.25], "texture": "#0"}, + "east": {"uv": [12.75, 9.5, 13, 10], "texture": "#0"}, + "south": {"uv": [12.75, 10, 13, 10.5], "texture": "#0"}, + "west": {"uv": [12.75, 10.5, 13, 11], "texture": "#0"}, + "up": {"uv": [13.5, 2, 13.25, 1.75], "texture": "#0"}, + "down": {"uv": [13.5, 2, 13.25, 2.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 11, 13, 11.5], "texture": "#0"}, + "east": {"uv": [12.75, 11.5, 13, 12], "texture": "#0"}, + "south": {"uv": [12.75, 12, 13, 12.5], "texture": "#0"}, + "west": {"uv": [12.75, 12.5, 13, 13], "texture": "#0"}, + "up": {"uv": [13.5, 2.5, 13.25, 2.25], "texture": "#0"}, + "down": {"uv": [13.5, 2.5, 13.25, 2.75], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 0, 13.25, 0.5], "texture": "#0"}, + "east": {"uv": [6.25, 11.5, 6.75, 12], "texture": "#0"}, + "south": {"uv": [13, 0.5, 13.25, 1], "texture": "#0"}, + "west": {"uv": [6.75, 11.5, 7.25, 12], "texture": "#0"}, + "up": {"uv": [13.25, 1.5, 13, 1], "texture": "#0"}, + "down": {"uv": [13.25, 1.5, 13, 2], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 13, 2.25, 13.5], "texture": "#0"}, + "east": {"uv": [13, 2, 13.25, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 13, 2.5, 13.5], "texture": "#0"}, + "west": {"uv": [2.5, 13, 2.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 3, 13.25, 2.75], "texture": "#0"}, + "down": {"uv": [13.5, 3, 13.25, 3.25], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 2.5, 13.25, 3], "texture": "#0"}, + "east": {"uv": [2.75, 13, 3, 13.5], "texture": "#0"}, + "south": {"uv": [3, 13, 3.25, 13.5], "texture": "#0"}, + "west": {"uv": [13, 3, 13.25, 3.5], "texture": "#0"}, + "up": {"uv": [13.5, 3.5, 13.25, 3.25], "texture": "#0"}, + "down": {"uv": [13.5, 3.5, 13.25, 3.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3.25, 13, 3.5, 13.5], "texture": "#0"}, + "east": {"uv": [3.5, 13, 3.75, 13.5], "texture": "#0"}, + "south": {"uv": [13, 3.5, 13.25, 4], "texture": "#0"}, + "west": {"uv": [3.75, 13, 4, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 4, 13.25, 3.75], "texture": "#0"}, + "down": {"uv": [13.5, 4, 13.25, 4.25], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 7.25, 12, 7.75], "texture": "#0"}, + "east": {"uv": [4, 13, 4.25, 13.5], "texture": "#0"}, + "south": {"uv": [11.5, 7.75, 12, 8.25], "texture": "#0"}, + "west": {"uv": [13, 4, 13.25, 4.5], "texture": "#0"}, + "up": {"uv": [4.75, 13.25, 4.25, 13], "texture": "#0"}, + "down": {"uv": [13.5, 4.5, 13, 4.75], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.75, 13, 5.25, 13.25], "texture": "#0"}, + "east": {"uv": [4.25, 13.25, 4.5, 13.5], "texture": "#0"}, + "south": {"uv": [13, 4.75, 13.5, 5], "texture": "#0"}, + "west": {"uv": [13.25, 4.25, 13.5, 4.5], "texture": "#0"}, + "up": {"uv": [13.5, 5.25, 13, 5], "texture": "#0"}, + "down": {"uv": [13.5, 5.25, 13, 5.5], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 5.5, 13.25, 6], "texture": "#0"}, + "east": {"uv": [11.5, 8.25, 12, 8.75], "texture": "#0"}, + "south": {"uv": [6.25, 13, 6.5, 13.5], "texture": "#0"}, + "west": {"uv": [8.5, 11.5, 9, 12], "texture": "#0"}, + "up": {"uv": [13.25, 7, 13, 6.5], "texture": "#0"}, + "down": {"uv": [13.25, 7, 13, 7.5], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.5, 13.25, 4.75, 13.5], "texture": "#0"}, + "east": {"uv": [13, 7.5, 13.5, 7.75], "texture": "#0"}, + "south": {"uv": [4.75, 13.25, 5, 13.5], "texture": "#0"}, + "west": {"uv": [13, 7.75, 13.5, 8], "texture": "#0"}, + "up": {"uv": [7.75, 13.5, 7.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 8, 13, 8.5], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 8.75, 12, 9.25], "texture": "#0"}, + "east": {"uv": [13, 8.5, 13.25, 9], "texture": "#0"}, + "south": {"uv": [9, 11.5, 9.5, 12], "texture": "#0"}, + "west": {"uv": [8.75, 13, 9, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 9.25, 13, 9], "texture": "#0"}, + "down": {"uv": [13.5, 9.25, 13, 9.5], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 9.5, 13.5, 9.75], "texture": "#0"}, + "east": {"uv": [5, 13.25, 5.25, 13.5], "texture": "#0"}, + "south": {"uv": [13, 9.75, 13.5, 10], "texture": "#0"}, + "west": {"uv": [5.25, 13.25, 5.5, 13.5], "texture": "#0"}, + "up": {"uv": [10.5, 13.25, 10, 13], "texture": "#0"}, + "down": {"uv": [13.5, 10, 13, 10.25], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 10.25, 13.25, 10.75], "texture": "#0"}, + "east": {"uv": [9.5, 11.5, 10, 12], "texture": "#0"}, + "south": {"uv": [10.75, 13, 11, 13.5], "texture": "#0"}, + "west": {"uv": [10, 11.5, 10.5, 12], "texture": "#0"}, + "up": {"uv": [13.25, 11.25, 13, 10.75], "texture": "#0"}, + "down": {"uv": [11.25, 13, 11, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.5, 13.25, 5.75, 13.5], "texture": "#0"}, + "east": {"uv": [11.25, 13, 11.75, 13.25], "texture": "#0"}, + "south": {"uv": [13.25, 5.5, 13.5, 5.75], "texture": "#0"}, + "west": {"uv": [13, 11.25, 13.5, 11.5], "texture": "#0"}, + "up": {"uv": [13.25, 12, 13, 11.5], "texture": "#0"}, + "down": {"uv": [12, 13, 11.75, 13.5], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.75, 13.25, 6, 13.5], "texture": "#0"}, + "east": {"uv": [11.5, 10, 12.5, 10.25], "texture": "#0"}, + "south": {"uv": [13.25, 5.75, 13.5, 6], "texture": "#0"}, + "west": {"uv": [11.5, 10.25, 12.5, 10.5], "texture": "#0"}, + "up": {"uv": [11.75, 11.5, 11.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.75, 11.5, 11.5, 12.5], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 13, 12.25, 13.5], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [13, 12, 13.25, 12.5], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [12, 1, 11.75, 0], "texture": "#0"}, + "down": {"uv": [12, 1, 11.75, 2], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 2, 12.75, 2.25], "texture": "#0"}, + "east": {"uv": [6, 13.25, 6.25, 13.5], "texture": "#0"}, + "south": {"uv": [11.75, 2.25, 12.75, 2.5], "texture": "#0"}, + "west": {"uv": [13.25, 6, 13.5, 6.25], "texture": "#0"}, + "up": {"uv": [12.75, 2.75, 11.75, 2.5], "texture": "#0"}, + "down": {"uv": [12.75, 2.75, 11.75, 3], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [12.25, 13, 12.5, 13.5], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [12.5, 13, 12.75, 13.5], "texture": "#0"}, + "up": {"uv": [4, 12, 3, 11.75], "texture": "#0"}, + "down": {"uv": [12.75, 3, 11.75, 3.25], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 6.25, 13.5, 6.5], "texture": "#0"}, + "east": {"uv": [11.75, 3.25, 12.75, 3.5], "texture": "#0"}, + "south": {"uv": [6.5, 13.25, 6.75, 13.5], "texture": "#0"}, + "west": {"uv": [11.75, 3.5, 12.75, 3.75], "texture": "#0"}, + "up": {"uv": [12, 5.5, 11.75, 4.5], "texture": "#0"}, + "down": {"uv": [12, 10.5, 11.75, 11.5], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 12.5, 13.25, 13], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [12.75, 13, 13, 13.5], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [12, 12.5, 11.75, 11.5], "texture": "#0"}, + "down": {"uv": [12.25, 0, 12, 1], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 3.75, 12.75, 4], "texture": "#0"}, + "east": {"uv": [13.25, 6.5, 13.5, 6.75], "texture": "#0"}, + "south": {"uv": [11.75, 5.5, 12.75, 5.75], "texture": "#0"}, + "west": {"uv": [6.75, 13.25, 7, 13.5], "texture": "#0"}, + "up": {"uv": [12.75, 6, 11.75, 5.75], "texture": "#0"}, + "down": {"uv": [13, 1, 12, 1.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [13, 13, 13.25, 13.5], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [0, 13.25, 0.25, 13.75], "texture": "#0"}, + "up": {"uv": [13, 1.5, 12, 1.25], "texture": "#0"}, + "down": {"uv": [13, 1.5, 12, 1.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [6, 5.75, 1], + "to": [7, 9.38, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 7.75, 1.5]}, + "faces": { + "north": {"uv": [2, 12, 2.25, 13], "texture": "#0"}, + "east": {"uv": [2.25, 12, 2.5, 13], "texture": "#0"}, + "south": {"uv": [2.5, 12, 2.75, 13], "texture": "#0"}, + "west": {"uv": [2.75, 12, 3, 13], "texture": "#0"}, + "up": {"uv": [13.5, 7, 13.25, 6.75], "texture": "#0"}, + "down": {"uv": [7.25, 13.25, 7, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 5.75, 6], + "to": [2, 10.37, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [1.5, 7.75, 6.5]}, + "faces": { + "north": {"uv": [10.25, 4.5, 10.5, 5.75], "texture": "#0"}, + "east": {"uv": [6.25, 10.25, 6.5, 11.5], "texture": "#0"}, + "south": {"uv": [6.5, 10.25, 6.75, 11.5], "texture": "#0"}, + "west": {"uv": [6.75, 10.25, 7, 11.5], "texture": "#0"}, + "up": {"uv": [13.5, 7.25, 13.25, 7], "texture": "#0"}, + "down": {"uv": [7.5, 13.25, 7.25, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 7.75, 9], + "to": [2, 11.38, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [1.5, 9.75, 9.5]}, + "faces": { + "north": {"uv": [3, 12, 3.25, 13], "texture": "#0"}, + "east": {"uv": [3.25, 12, 3.5, 13], "texture": "#0"}, + "south": {"uv": [3.5, 12, 3.75, 13], "texture": "#0"}, + "west": {"uv": [3.75, 12, 4, 13], "texture": "#0"}, + "up": {"uv": [13.5, 7.5, 13.25, 7.25], "texture": "#0"}, + "down": {"uv": [8, 13.25, 7.75, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 7.75, 6], + "to": [2, 12.37, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [1.5, 9.75, 6.5]}, + "faces": { + "north": {"uv": [7, 10.25, 7.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 7.25, 10.5, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 10.25, 8.75, 11.5], "texture": "#0"}, + "west": {"uv": [8.75, 10.25, 9, 11.5], "texture": "#0"}, + "up": {"uv": [8.25, 13.5, 8, 13.25], "texture": "#0"}, + "down": {"uv": [13.5, 8, 13.25, 8.25], "texture": "#0"} + } + }, + { + "from": [1, 3.75, 9], + "to": [2, 7.38, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [1.5, 5.75, 9.5]}, + "faces": { + "north": {"uv": [4, 12, 4.25, 13], "texture": "#0"}, + "east": {"uv": [4.25, 12, 4.5, 13], "texture": "#0"}, + "south": {"uv": [4.5, 12, 4.75, 13], "texture": "#0"}, + "west": {"uv": [12, 4.5, 12.25, 5.5], "texture": "#0"}, + "up": {"uv": [8.5, 13.5, 8.25, 13.25], "texture": "#0"}, + "down": {"uv": [13.5, 8.25, 13.25, 8.5], "texture": "#0"} + } + }, + { + "from": [1, 3.75, 6], + "to": [2, 8.37, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [1.5, 5.75, 6.5]}, + "faces": { + "north": {"uv": [9, 10.25, 9.25, 11.5], "texture": "#0"}, + "east": {"uv": [9.25, 10.25, 9.5, 11.5], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 9.75, 11.5], "texture": "#0"}, + "west": {"uv": [9.75, 10.25, 10, 11.5], "texture": "#0"}, + "up": {"uv": [8.75, 13.5, 8.5, 13.25], "texture": "#0"}, + "down": {"uv": [13.5, 8.5, 13.25, 8.75], "texture": "#0"} + } + }, + { + "from": [1, 5.75, 9], + "to": [2, 9.38, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [1.5, 7.75, 9.5]}, + "faces": { + "north": {"uv": [4.75, 12, 5, 13], "texture": "#0"}, + "east": {"uv": [5, 12, 5.25, 13], "texture": "#0"}, + "south": {"uv": [12, 6, 12.25, 7], "texture": "#0"}, + "west": {"uv": [6.25, 12, 6.5, 13], "texture": "#0"}, + "up": {"uv": [13.5, 9, 13.25, 8.75], "texture": "#0"}, + "down": {"uv": [9.25, 13.25, 9, 13.5], "texture": "#0"} + } + }, + { + "from": [6, 5.75, 14], + "to": [7, 10.37, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 7.75, 14.5]}, + "faces": { + "north": {"uv": [10, 10.25, 10.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 10.25, 10.5, 11.5], "texture": "#0"}, + "south": {"uv": [10.5, 0, 10.75, 1.25], "texture": "#0"}, + "west": {"uv": [10.5, 1.25, 10.75, 2.5], "texture": "#0"}, + "up": {"uv": [9.5, 13.5, 9.25, 13.25], "texture": "#0"}, + "down": {"uv": [9.75, 13.25, 9.5, 13.5], "texture": "#0"} + } + }, + { + "from": [9, 14, 6.62], + "to": [10, 15, 10.25], + "rotation": {"angle": 45, "axis": "y", "origin": [9.5, 14.75, 8.25]}, + "faces": { + "north": {"uv": [9.75, 13.25, 10, 13.5], "texture": "#0"}, + "east": {"uv": [12, 1.75, 13, 2], "texture": "#0"}, + "south": {"uv": [10, 13.25, 10.25, 13.5], "texture": "#0"}, + "west": {"uv": [6.5, 12, 7.5, 12.25], "texture": "#0"}, + "up": {"uv": [12.25, 8, 12, 7], "texture": "#0"}, + "down": {"uv": [7.75, 12, 7.5, 13], "texture": "#0"} + } + }, + { + "from": [6, 14, 5.63], + "to": [7, 15, 10.25], + "rotation": {"angle": -45, "axis": "y", "origin": [6.5, 14.75, 8.25]}, + "faces": { + "north": {"uv": [10.25, 13.25, 10.5, 13.5], "texture": "#0"}, + "east": {"uv": [10.25, 5.75, 11.5, 6], "texture": "#0"}, + "south": {"uv": [13.25, 10.25, 13.5, 10.5], "texture": "#0"}, + "west": {"uv": [10.25, 8.5, 11.5, 8.75], "texture": "#0"}, + "up": {"uv": [10.75, 3.75, 10.5, 2.5], "texture": "#0"}, + "down": {"uv": [3.25, 10.5, 3, 11.75], "texture": "#0"} + } + }, + { + "from": [9, 14, 4.62], + "to": [10, 15, 8.25], + "rotation": {"angle": 45, "axis": "y", "origin": [9.5, 14.75, 6.25]}, + "faces": { + "north": {"uv": [13.25, 10.5, 13.5, 10.75], "texture": "#0"}, + "east": {"uv": [7.75, 12, 8.75, 12.25], "texture": "#0"}, + "south": {"uv": [13.25, 10.75, 13.5, 11], "texture": "#0"}, + "west": {"uv": [12, 8, 13, 8.25], "texture": "#0"}, + "up": {"uv": [12.25, 9.25, 12, 8.25], "texture": "#0"}, + "down": {"uv": [9, 12, 8.75, 13], "texture": "#0"} + } + }, + { + "from": [6, 14, 3.63], + "to": [7, 15, 8.25], + "rotation": {"angle": -45, "axis": "y", "origin": [6.5, 14.75, 6.25]}, + "faces": { + "north": {"uv": [13.25, 11, 13.5, 11.25], "texture": "#0"}, + "east": {"uv": [10.25, 8.75, 11.5, 9], "texture": "#0"}, + "south": {"uv": [11.25, 13.25, 11.5, 13.5], "texture": "#0"}, + "west": {"uv": [10.25, 9, 11.5, 9.25], "texture": "#0"}, + "up": {"uv": [3.5, 11.75, 3.25, 10.5], "texture": "#0"}, + "down": {"uv": [3.75, 10.5, 3.5, 11.75], "texture": "#0"} + } + }, + { + "from": [9, 14, 8.62], + "to": [10, 15, 12.25], + "rotation": {"angle": 45, "axis": "y", "origin": [9.5, 14.75, 10.25]}, + "faces": { + "north": {"uv": [11.5, 13.25, 11.75, 13.5], "texture": "#0"}, + "east": {"uv": [9, 12, 10, 12.25], "texture": "#0"}, + "south": {"uv": [13.25, 11.5, 13.5, 11.75], "texture": "#0"}, + "west": {"uv": [12, 9.25, 13, 9.5], "texture": "#0"}, + "up": {"uv": [10.25, 13, 10, 12], "texture": "#0"}, + "down": {"uv": [10.5, 12, 10.25, 13], "texture": "#0"} + } + }, + { + "from": [6, 14, 7.63], + "to": [7, 15, 12.25], + "rotation": {"angle": -45, "axis": "y", "origin": [6.5, 14.75, 10.25]}, + "faces": { + "north": {"uv": [13.25, 11.75, 13.5, 12], "texture": "#0"}, + "east": {"uv": [10.5, 3.75, 11.75, 4], "texture": "#0"}, + "south": {"uv": [13.25, 12, 13.5, 12.25], "texture": "#0"}, + "west": {"uv": [10.5, 4.5, 11.75, 4.75], "texture": "#0"}, + "up": {"uv": [4, 11.75, 3.75, 10.5], "texture": "#0"}, + "down": {"uv": [10.75, 7.25, 10.5, 8.5], "texture": "#0"} + } + }, + { + "from": [14, 5.75, 6], + "to": [15, 9.38, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [14.5, 7.75, 6.5]}, + "faces": { + "north": {"uv": [12, 10.5, 12.25, 11.5], "texture": "#0"}, + "east": {"uv": [12, 11.5, 12.25, 12.5], "texture": "#0"}, + "south": {"uv": [12.25, 0, 12.5, 1], "texture": "#0"}, + "west": {"uv": [12.25, 4, 12.5, 5], "texture": "#0"}, + "up": {"uv": [13.5, 12.5, 13.25, 12.25], "texture": "#0"}, + "down": {"uv": [13.5, 12.5, 13.25, 12.75], "texture": "#0"} + } + }, + { + "from": [14, 5.75, 9], + "to": [15, 10.37, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [14.5, 7.75, 9.5]}, + "faces": { + "north": {"uv": [10.5, 10, 10.75, 11.25], "texture": "#0"}, + "east": {"uv": [10.75, 0, 11, 1.25], "texture": "#0"}, + "south": {"uv": [10.75, 1.25, 11, 2.5], "texture": "#0"}, + "west": {"uv": [10.75, 2.5, 11, 3.75], "texture": "#0"}, + "up": {"uv": [13.5, 13, 13.25, 12.75], "texture": "#0"}, + "down": {"uv": [13.5, 13, 13.25, 13.25], "texture": "#0"} + } + }, + { + "from": [14, 7.75, 6], + "to": [15, 11.38, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [14.5, 9.75, 6.5]}, + "faces": { + "north": {"uv": [5.25, 12.25, 5.5, 13.25], "texture": "#0"}, + "east": {"uv": [5.5, 12.25, 5.75, 13.25], "texture": "#0"}, + "south": {"uv": [5.75, 12.25, 6, 13.25], "texture": "#0"}, + "west": {"uv": [6, 12.25, 6.25, 13.25], "texture": "#0"}, + "up": {"uv": [13.5, 13.5, 13.25, 13.25], "texture": "#0"}, + "down": {"uv": [13.75, 0, 13.5, 0.25], "texture": "#0"} + } + }, + { + "from": [14, 7.75, 9], + "to": [15, 12.37, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [14.5, 9.75, 9.5]}, + "faces": { + "north": {"uv": [10.75, 7.25, 11, 8.5], "texture": "#0"}, + "east": {"uv": [10.75, 10, 11, 11.25], "texture": "#0"}, + "south": {"uv": [11, 0, 11.25, 1.25], "texture": "#0"}, + "west": {"uv": [11, 1.25, 11.25, 2.5], "texture": "#0"}, + "up": {"uv": [0.5, 13.75, 0.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0.25, 13.5, 0.5], "texture": "#0"} + } + }, + { + "from": [14, 3.75, 6], + "to": [15, 7.38, 7], + "rotation": {"angle": 45, "axis": "x", "origin": [14.5, 5.75, 6.5]}, + "faces": { + "north": {"uv": [12.25, 6, 12.5, 7], "texture": "#0"}, + "east": {"uv": [6.5, 12.25, 6.75, 13.25], "texture": "#0"}, + "south": {"uv": [6.75, 12.25, 7, 13.25], "texture": "#0"}, + "west": {"uv": [7, 12.25, 7.25, 13.25], "texture": "#0"}, + "up": {"uv": [0.75, 13.75, 0.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0.5, 13.5, 0.75], "texture": "#0"} + } + }, + { + "from": [14, 3.75, 9], + "to": [15, 8.37, 10], + "rotation": {"angle": -45, "axis": "x", "origin": [14.5, 5.75, 9.5]}, + "faces": { + "north": {"uv": [11, 2.5, 11.25, 3.75], "texture": "#0"}, + "east": {"uv": [11, 7.25, 11.25, 8.5], "texture": "#0"}, + "south": {"uv": [11, 10, 11.25, 11.25], "texture": "#0"}, + "west": {"uv": [11.25, 0, 11.5, 1.25], "texture": "#0"}, + "up": {"uv": [1, 13.75, 0.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0.75, 13.5, 1], "texture": "#0"} + } + }, + { + "from": [9, 5.75, 14], + "to": [10, 9.38, 15], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 7.75, 14.5]}, + "faces": { + "north": {"uv": [12.25, 7, 12.5, 8], "texture": "#0"}, + "east": {"uv": [7.25, 12.25, 7.5, 13.25], "texture": "#0"}, + "south": {"uv": [7.75, 12.25, 8, 13.25], "texture": "#0"}, + "west": {"uv": [8, 12.25, 8.25, 13.25], "texture": "#0"}, + "up": {"uv": [1.25, 13.75, 1, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1, 13.5, 1.25], "texture": "#0"} + } + }, + { + "from": [6, 7.75, 14], + "to": [7, 12.37, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 9.75, 14.5]}, + "faces": { + "north": {"uv": [11.25, 1.25, 11.5, 2.5], "texture": "#0"}, + "east": {"uv": [11.25, 2.5, 11.5, 3.75], "texture": "#0"}, + "south": {"uv": [11.25, 7.25, 11.5, 8.5], "texture": "#0"}, + "west": {"uv": [11.25, 10, 11.5, 11.25], "texture": "#0"}, + "up": {"uv": [1.5, 13.75, 1.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1.25, 13.5, 1.5], "texture": "#0"} + } + }, + { + "from": [9, 7.75, 14], + "to": [10, 11.38, 15], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 9.75, 14.5]}, + "faces": { + "north": {"uv": [8.25, 12.25, 8.5, 13.25], "texture": "#0"}, + "east": {"uv": [12.25, 8.25, 12.5, 9.25], "texture": "#0"}, + "south": {"uv": [8.5, 12.25, 8.75, 13.25], "texture": "#0"}, + "west": {"uv": [9, 12.25, 9.25, 13.25], "texture": "#0"}, + "up": {"uv": [1.75, 13.75, 1.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1.5, 13.5, 1.75], "texture": "#0"} + } + }, + { + "from": [6, 3.75, 14], + "to": [7, 8.37, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 5.75, 14.5]}, + "faces": { + "north": {"uv": [10.5, 11.25, 10.75, 12.5], "texture": "#0"}, + "east": {"uv": [10.75, 11.25, 11, 12.5], "texture": "#0"}, + "south": {"uv": [11, 11.25, 11.25, 12.5], "texture": "#0"}, + "west": {"uv": [11.25, 11.25, 11.5, 12.5], "texture": "#0"}, + "up": {"uv": [2, 13.75, 1.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1.75, 13.5, 2], "texture": "#0"} + } + }, + { + "from": [9, 3.75, 14], + "to": [10, 7.38, 15], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 5.75, 14.5]}, + "faces": { + "north": {"uv": [9.25, 12.25, 9.5, 13.25], "texture": "#0"}, + "east": {"uv": [9.5, 12.25, 9.75, 13.25], "texture": "#0"}, + "south": {"uv": [9.75, 12.25, 10, 13.25], "texture": "#0"}, + "west": {"uv": [12.25, 10.5, 12.5, 11.5], "texture": "#0"}, + "up": {"uv": [2.25, 13.75, 2, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2, 13.5, 2.25], "texture": "#0"} + } + }, + { + "from": [9, 5.75, 1], + "to": [10, 10.37, 2], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 7.75, 1.5]}, + "faces": { + "north": {"uv": [0, 11.5, 0.25, 12.75], "texture": "#0"}, + "east": {"uv": [11.5, 0, 11.75, 1.25], "texture": "#0"}, + "south": {"uv": [0.25, 11.5, 0.5, 12.75], "texture": "#0"}, + "west": {"uv": [0.5, 11.5, 0.75, 12.75], "texture": "#0"}, + "up": {"uv": [2.5, 13.75, 2.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2.25, 13.5, 2.5], "texture": "#0"} + } + }, + { + "from": [6, 7.75, 1], + "to": [7, 11.38, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 9.75, 1.5]}, + "faces": { + "north": {"uv": [12.25, 11.5, 12.5, 12.5], "texture": "#0"}, + "east": {"uv": [12.5, 0, 12.75, 1], "texture": "#0"}, + "south": {"uv": [12.5, 4, 12.75, 5], "texture": "#0"}, + "west": {"uv": [12.5, 6, 12.75, 7], "texture": "#0"}, + "up": {"uv": [2.75, 13.75, 2.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2.5, 13.5, 2.75], "texture": "#0"} + } + }, + { + "from": [9, 7.75, 1], + "to": [10, 12.37, 2], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 9.75, 1.5]}, + "faces": { + "north": {"uv": [0.75, 11.5, 1, 12.75], "texture": "#0"}, + "east": {"uv": [1, 11.5, 1.25, 12.75], "texture": "#0"}, + "south": {"uv": [1.25, 11.5, 1.5, 12.75], "texture": "#0"}, + "west": {"uv": [11.5, 1.25, 11.75, 2.5], "texture": "#0"}, + "up": {"uv": [3, 13.75, 2.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2.75, 13.5, 3], "texture": "#0"} + } + }, + { + "from": [6, 3.75, 1], + "to": [7, 7.38, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [6.5, 5.75, 1.5]}, + "faces": { + "north": {"uv": [12.5, 7, 12.75, 8], "texture": "#0"}, + "east": {"uv": [12.5, 8.25, 12.75, 9.25], "texture": "#0"}, + "south": {"uv": [12.5, 9.5, 12.75, 10.5], "texture": "#0"}, + "west": {"uv": [10.5, 12.5, 10.75, 13.5], "texture": "#0"}, + "up": {"uv": [3.25, 13.75, 3, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 3, 13.5, 3.25], "texture": "#0"} + } + }, + { + "from": [9, 3.75, 1], + "to": [10, 8.37, 2], + "rotation": {"angle": 45, "axis": "z", "origin": [9.5, 5.75, 1.5]}, + "faces": { + "north": {"uv": [1.5, 11.5, 1.75, 12.75], "texture": "#0"}, + "east": {"uv": [1.75, 11.5, 2, 12.75], "texture": "#0"}, + "south": {"uv": [11.5, 2.5, 11.75, 3.75], "texture": "#0"}, + "west": {"uv": [11.5, 4.75, 11.75, 6], "texture": "#0"}, + "up": {"uv": [3.5, 13.75, 3.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 3.25, 13.5, 3.5], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_speed_1.png b/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_speed_1.png new file mode 100644 index 0000000000000000000000000000000000000000..b5f692e53a0f81fec60fd6911f3741e34946928d GIT binary patch literal 1376 zcmV-m1)utfP)|;W2V*y>Cd}E z;xm(%iK3}ffUSKLsp)qqmqR6TR8$7(DD;U<@yuIUqD)$oB-(RG_#B z0kEGoVH#)Z@6MkM{d{BLlW0wGL?iInw>F$Ua$<6ILCkk>^=7wd5gmHIW|g0gh+`Xs z24qhi*A7~p|4zDb_4X%`RK)@8_a|XXeT{lek3$=x#c3R zL!E%r-5RRu5Bi_l2egTp0;W0vyT52sFa^|j!|Mwo!>vqC180GNH3Axi?}TT__}^iw zC`BOzP_LghjZ3n>kHV9s8-^){S6!1zq;aaw+e|;z1>Z-%l)&QN5euL%Q+^S<9tea6 znC74{hCh8Qw14)?57E|J>uUw6i38XWzCD9u|KMhj0#7XuR;$AC>wzE<0x;DH@SsgT?HF+N0!b$@fTbN< zW0ONk_*Awprao*t=+j)ir|KI54n{q*iL~9AM|J#O z7^SR!l~=Xm*GV6Tn35rmW1O!7vJB(YD(uMoSe3p8Yl3~rWPlz7NP%C20YZt4n)i;C(;DRIgNgeO-^;zxna+rqBK5^TC_y9ZHKwIb`GE zf!h8*-n<{)0ahtJxikP%UAo`AV}?EAA4*Fu4KOwGm)iD^H1?tQ%eU{7D}9s`(A)J( z6*~NPF!7lF@`8G~kQ)Q&ptpIv0~m%gz_sFAa-pk3fTMj9NzpznFp8NOB)Vn*y~pAh zTO6{s7iM2IjtzAH`8|(};0^Bu=Nq~p4axwVrzNE0r1rBzzs2r9*nO(Dl8%sY?S0^J zPVG1)Y6}Fn+WtIEj@}@s9hbJf(;!no zGP{t=EC3yV7ke+>+tQSK3qC8uS%BVy;mWNJ}PSfK(@t z0vsCxAPYRoVm-jr8(t|G!;?F=#B(ve>!Ir7+|S>s+6F4od#SQyq70BWPfN#jE8p92 zDTe`%=hjI`8eWp0i9snGtNm_4TcLYXWouc@1)ww^&^);%!O#PwF(_*IUw?{ifBfvF z+Pc>Ix=AjDkRAs>VYW|+pg;qpFNc9l7(l8MAOHgW#+ypwbI26b5+`l|DaMq2v8vYs z^!)&{L}j2GMALiZOeQ?Q7Gqp%e6D0Z2C)0vz;J+AGS>;xka+{tgff>Yv0cf$0VEkb zJ$_YHf~woT-dJ*97f=R}qOdrN3-f&v?c{steDpFr0kRoEljURG^Id-Xe{1;})nG=rz z(p=(lOdWtZHV-&yBo8z(10$EgCWxGB0U#H10rd1C4N`@c>2NE!dDq%s61gydIr#yF zS@)C|M{Yd8wNohy((t<@`#IvTaGEWplg%dHsIZW*=1)dg zeCQ&C!FrvJBu&i<)HSl}5OFfJ6|55?n-q8OkkkyhU|k#RR*lc*fl{wdQD9KXkV!bl z0CWD%c^tLB_x`xVc-14cIYYyR;(0$PP@Ho&<{X{ry{l>qPem})E5ku0n3{gym5wZp z;Rad2sGA&ISD$ecXp2K{*N^A#NCF+gULrk=8Hck0c#K1gq^aq50(b>`27}&jDF{-| z!4Ry`y+|FNBR6unI?U(-2{EJ62x&+m*kvVch>c(xa);J-V(7`QY7;iyql;Oi~cr%)bN+#Rf_^IVCB02USX zR}w5a;eJv{n~96;Cj4vk5B@0m&{fLs?tO`;%(D9Y7UHMYXvV)dsYY)s`0 zHw3qgz7Q9=WB*ZnCw^y5d0izq=OeX+8Xn}%u>?WraeWA(caSb&oa3(RuNcDt9hcS< z`GIh#YX%0qO?*;efz4iD2fx4n>*X+(vZD`I>GIrXe~5(3-qctE$8T@YL3G1P|8sz)*0R8v z^5%yx=4_8$%2pBP|KUNlK!C#ypSyWeJ)D65L()HInap2cr}?HHlx&4l15l51Y5?kS zP7Odk&Zz;Y$LV_#otRTmn% z2t{ZY6|z%sDP6mf3W`{aQl$%n7DT<@eL3;w_P+bmm!x?~zeV4Bx%VaSoqNuA{%*6s zSFe7e4j}h%#ybAKJZ~K@|M!v342@EywnHCok5FNHfM$2kQt9fA#u%>Gs>5_H_ga3w zQMz)19LVC&Z>PSsPI~b271`$bj{lS!GLAp3*5q;YRL(fQ^Ri0qLWX(>OC#%abL{`1 zd$QE$NVNc9=Rk2GLnrBlJij~l4}~B#0s!$N6 zrHePn7g7N1r(JN(bBzy-{%ZVuf9H`ry5a~Ox==rCjmRTRz+@UGWH$|xZ40I-5$;bG zDSx{x39vf8LfNrhy8Et~@z*AXrACw-QKbs00YhUySSQt51f?p;GoOpZr@~2L4fP6R$kj{oePqEo6 zfE@oA|IteT9n%2)1@(u|#oUKF0rGt#>MxA&7y`x-C~XJE0Y?p2oq%7=7puQ~LmQrR zJ0^gG_8P6;g(sJs_rmuh%F`iZgFEHMHL6613Wa_Bv}<0w{5^n|1K4y-0s+`uHcI4$ zETn6LD~8vjF&(k)Xdx;_4Hl^-z;n#L6UW9U*SzkN8sM@)XAJ-I_r~!DkDtq9D{6gg zLTox5_cMI`zK&GK0jIyZ-pDb)1Y9hE)_R)&bpc#;0))7Q@qzkP&Yz6g+cU`^ihFZrPBIRNT! zg-9BaV9%biDx33JV2VWcO&!Yi`#Iz-7;{dw?rNCBYZLRbdwkv%il6`};T~ z>FpuOK_Uergo@)Op%olLgY=Rh3!AZ_xL|J|?k%oc$2Ub{V#hAE3J?sc%S*KGeNWUK zP8i9#s=2N@f=k0pP;-A395H@PemHUtaODy*+Oq)JV7`8i@HnI%2y_Jrx-`%PF0jl_ zo|SEJ;4mfrROABk#lR&QVEhr?jrC&-XOszs5YD{|G*781n6LQoJ6 z3x*#&t_X{OhsV_yl(;`W%C{}xLg6uCMR=tACo^Yq9#{x`%+5`v< zzJ0Y`5@XZia{xkwa6n*O{pD*OKMX?Lb832n&>HLwu(8%`z z`vfx}9RxMpRKyCvhFHzhh!B4AG66OIhLE75%<2#n;&oO#)BgsUSlRTDt1%U1buOp7 z_q$#MLT2f)MN!JtX1z2mp={amub z_sfQ+h~8L$7{@i{;C}E3zFP&p%l1!LAsQgJ$;D-2Hu!Es>vr_SOC-VZKTT1e<5&RH z=QtJs^*N3OKz)wS00030|6r+=kN^Mx21!IgR09Cd_WW_S4s}NW0000 Date: Wed, 10 Dec 2025 05:39:10 +0100 Subject: [PATCH 09/23] First version of arbitrary quarry regions, only semi-stable so far --- .../utilitiesinexcess/ModBlocks.java | 6 +- .../blocks/ender_quarry/BlockEnderMarker.java | 6 +- .../blocks/ender_quarry/BlockEnderQuarry.java | 3 +- .../ender_quarry/BlockEnderQuarryUpgrade.java | 4 +- .../ender_quarry/IEnderQuarryUpgrade.java | 1 - .../tileentities/TileEntityEnderMarker.java | 107 ++++- .../tileentities/TileEntityEnderQuarry.java | 430 ++++++++++++++++-- 7 files changed, 490 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java index 0a6677b0..42b98601 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java @@ -1,6 +1,5 @@ package com.fouristhenumber.utilitiesinexcess; -import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarryUpgrade; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.item.Item; @@ -14,8 +13,6 @@ import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockCursedEarth; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockDrum; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEnderLotus; -import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderMarker; -import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarry; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockEtherealGlass; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockFloating; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockInverted; @@ -35,6 +32,9 @@ import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockTrashCanFluid; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockTrashCanItem; import com.fouristhenumber.utilitiesinexcess.common.blocks.BlockUpdateDetector; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderMarker; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarry; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarryUpgrade; import com.fouristhenumber.utilitiesinexcess.common.blocks.generators.BlockEnderGenerator; import com.fouristhenumber.utilitiesinexcess.common.blocks.generators.BlockFoodGenerator; import com.fouristhenumber.utilitiesinexcess.common.blocks.generators.BlockFurnaceGenerator; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java index c6c69dae..5ab31f1c 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java @@ -72,7 +72,8 @@ public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neig } @Override - public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, float subY, float subZ) { + public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer player, int side, float subX, + float subY, float subZ) { if (worldIn.isRemote) return true; TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderMarker marker) { @@ -81,7 +82,8 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer marker.boundaryForArbitraryLoop(); } int newCuboidSize = marker.increaseCuboidSize(); - player.addChatComponentMessage(new ChatComponentText("Increased cuboid size to " + newCuboidSize + ".")); + player + .addChatComponentMessage(new ChatComponentText("Increased cuboid size to " + newCuboidSize + ".")); } else { marker.rotateMode(); player.addChatComponentMessage(new ChatComponentText(marker.getMode())); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java index 970beed1..5b6da2d3 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java @@ -56,7 +56,8 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer if (te instanceof TileEntityEnderQuarry quarry) { if (player.isSneaking() && player.capabilities.isCreativeMode) { quarry.isCreativeBoosted = !quarry.isCreativeBoosted; - player.addChatComponentMessage(new ChatComponentText((quarry.isCreativeBoosted ? "" : "Un-") + "Creative-Boosted Quarry.")); + player.addChatComponentMessage( + new ChatComponentText((quarry.isCreativeBoosted ? "" : "Un-") + "Creative-Boosted Quarry.")); return true; } if (quarry.state == TileEntityEnderQuarry.QuarryWorkState.STOPPED) { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java index 3f5acdf3..0550fd0b 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java @@ -9,6 +9,7 @@ import net.minecraft.world.World; public class BlockEnderQuarryUpgrade extends Block implements IEnderQuarryUpgrade { + private IIcon[] icons; public BlockEnderQuarryUpgrade() { @@ -28,7 +29,8 @@ public int getRenderType() { } @Override - public int onBlockPlaced(World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ, int meta) { + public int onBlockPlaced(World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ, + int meta) { return 0; } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java index 423e57d4..6a620737 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java @@ -2,5 +2,4 @@ public interface IEnderQuarryUpgrade { - } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index f42cbdc9..259c3489 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -11,7 +12,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; @@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; + +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.IFacingTE; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; import com.fouristhenumber.utilitiesinexcess.utils.Tuple; @@ -52,7 +54,7 @@ private ConcurrentHashMap getRegistryForDimensi return registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()); } - public @Nullable List checkForBoundary(ForgeDirection starterFacing) { + public @Nullable List checkForBoundary(ForgeDirection starterFacing) { return switch (operationMode) { case DEFAULT -> boundaryFromThree(starterFacing); case SINGLE -> boundaryForSizedCuboid(starterFacing); @@ -60,7 +62,7 @@ private ConcurrentHashMap getRegistryForDimensi }; } - private @Nullable List boundaryFromThree(ForgeDirection starterFacing) { + private @Nullable List boundaryFromThree(ForgeDirection starterFacing) { Tuple secondCorner = alignedMarkers.getOrDefault(starterFacing, null); if (secondCorner != null && secondCorner.getKey() != null) { Tuple thirdCorner = Optional @@ -69,19 +71,27 @@ private ConcurrentHashMap getRegistryForDimensi .orElse( secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(starterFacing), null)); if (thirdCorner != null) { - return Stream.of(new Vector2i(this.xCoord, this.zCoord), new Vector2i(thirdCorner.getValue().x, thirdCorner.getValue().z)).collect(Collectors.toList()); + return Stream + .of( + new FacingVector2i(this.xCoord, this.zCoord, ForgeDirection.UNKNOWN), + new FacingVector2i(thirdCorner.getValue().x, thirdCorner.getValue().z, ForgeDirection.UNKNOWN)) + .collect(Collectors.toList()); } } return null; } - private List boundaryForSizedCuboid(ForgeDirection facing) { - BlockPos otherCorner = DirectionUtil.offsetByForward(this.xCoord, this.yCoord, this.zCoord, facing, cuboidSize, 0); + private List boundaryForSizedCuboid(ForgeDirection facing) { + BlockPos otherCorner = DirectionUtil + .offsetByForward(this.xCoord, this.yCoord, this.zCoord, facing, cuboidSize, 0); otherCorner = DirectionUtil.offsetByRight(otherCorner, facing, cuboidSize, 0); - return Stream.of(new Vector2i(this.xCoord, this.zCoord), new Vector2i(otherCorner.x, otherCorner.z)).collect(Collectors.toList()); + return Stream.of(new FacingVector2i(this.xCoord, this.zCoord, ForgeDirection.UNKNOWN), new FacingVector2i(otherCorner.x, otherCorner.z, ForgeDirection.UNKNOWN)) + .collect(Collectors.toList()); } - public List boundaryForArbitraryLoop() { + public List boundaryForArbitraryLoop() { + // TODO: Has issues with markers that have more than 2 connections (uses not boundary not intendeed by player) - + // maybe limit to 2 connections if this mode is used and propagate that other markers in chain? ArrayList stack = new ArrayList<>(); stack.add(new StackEntry(new LinkedHashMap<>(), this)); Set markerChain = null; @@ -89,29 +99,44 @@ public List boundaryForArbitraryLoop() { searchStack: do { StackEntry entry = stack.remove(stack.size() - 1); - if (entry.current.alignedMarkers.isEmpty() - || entry.current.alignedMarkers.size() == 1 + if (entry.current.alignedMarkers.isEmpty() || entry.current.alignedMarkers.size() == 1 && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) != null - && entry.current.alignedMarkers.get(entry.lastVisited.getValue()).getKey() == entry.lastVisited.getKey()) { + && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) + .getKey() == entry.lastVisited.getKey()) { entry.current.checkForAlignedMarkers(); - worldObj.setBlock(entry.current.xCoord, entry.current.yCoord + 3, entry.current.zCoord, Blocks.brick_block); } - for (Map.Entry> otherMarker : entry.current.alignedMarkers.entrySet()) { - if (otherMarker.getValue().getKey() != null) { + for (Map.Entry> otherMarker : entry.current.alignedMarkers + .entrySet()) { + if (otherMarker.getValue() + .getKey() != null) { // Has not already been visited - if (!entry.visitedMarkers.containsKey(otherMarker.getValue().getKey())) { + if (!entry.visitedMarkers.containsKey( + otherMarker.getValue() + .getKey())) { @SuppressWarnings("unchecked") // Same type - LinkedHashMap visitedMarkers = (LinkedHashMap) entry.visitedMarkers.clone(); - StackEntry stackEntry = new StackEntry(visitedMarkers, otherMarker.getValue().getKey(), entry.current, otherMarker.getKey().getOpposite()); + LinkedHashMap visitedMarkers = (LinkedHashMap) entry.visitedMarkers + .clone(); + StackEntry stackEntry = new StackEntry( + visitedMarkers, + otherMarker.getValue() + .getKey(), + entry.current, + otherMarker.getKey() + .getOpposite()); stack.add(stackEntry); - worldObj.setBlock(entry.current.xCoord, entry.current.yCoord + 2, entry.current.zCoord, Blocks.tnt); continue; } // Check if we have completed a loop by arriving at the starter from a different direction - if (otherMarker.getValue().getKey() == this && entry.visitedMarkers.get(otherMarker.getValue().getKey()) != otherMarker.getKey() && !entry.visitedMarkers.isEmpty()) { + if (otherMarker.getValue() + .getKey() == this + && entry.visitedMarkers.get( + otherMarker.getValue() + .getKey()) + != otherMarker.getKey() + && !entry.visitedMarkers.isEmpty()) { // Append the last marker entry.visitedMarkers.put(entry.current, otherMarker.getKey()); markerChain = entry.visitedMarkers.keySet(); @@ -123,8 +148,8 @@ public List boundaryForArbitraryLoop() { if (markerChain != null) { UtilitiesInExcess.chat("Completed marker chain with " + markerChain.size() + " entries."); - ArrayList pointChain = new ArrayList<>(markerChain.size()); - markerChain.forEach((e) -> pointChain.add(new Vector2i(e.xCoord, e.zCoord))); + ArrayList pointChain = new ArrayList<>(markerChain.size()); + markerChain.forEach((e) -> pointChain.add(new FacingVector2i(e.xCoord, e.zCoord, e.alignedMarkers.keySet().stream().collect(Collectors.toList())))); return pointChain; } UtilitiesInExcess.chat("Failed to complete marker chain."); @@ -133,6 +158,7 @@ public List boundaryForArbitraryLoop() { } private static class StackEntry { + // A map of previously visited markers further down the stack & the direction they were visited from LinkedHashMap visitedMarkers; TileEntityEnderMarker current; @@ -144,7 +170,8 @@ private static class StackEntry { this.lastVisited = null; } - StackEntry(LinkedHashMap visitedMarkers, TileEntityEnderMarker current, TileEntityEnderMarker previous, ForgeDirection previousDir) { + StackEntry(LinkedHashMap visitedMarkers, TileEntityEnderMarker current, + TileEntityEnderMarker previous, ForgeDirection previousDir) { this(visitedMarkers, current); this.visitedMarkers.put(previous, previousDir); lastVisited = new Tuple<>(previous, previousDir); @@ -157,7 +184,8 @@ public void checkForAlignedMarkers() { public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate) { ConcurrentHashMap dimRegistry = getRegistryForDimension(); - for (Map.Entry> entry : new ArrayList<>(alignedMarkers.entrySet())) { + for (Map.Entry> entry : new ArrayList<>( + alignedMarkers.entrySet())) { if (entry.getValue() .getKey() == null) { BlockPos alignedMarkerPos = entry.getValue() @@ -279,9 +307,10 @@ public void teardownConnections() { } public String getMode() { - return switch(operationMode) { + return switch (operationMode) { case DEFAULT -> "Marker is set to establish a rectangular fence from the first 3 in chain."; - case SINGLE -> "Marker is set to establish a cuboid of length " + this.cuboidSize + ". Sneak + R-Click to adjust size."; + case SINGLE -> "Marker is set to establish a cuboid of length " + this.cuboidSize + + ". Sneak + R-Click to adjust size."; case ARBITRARY_LOOP -> "Marker is set to establish a full loop of markers that make up a rectilinear polygon back this marker of arbitrary size and shape."; }; } @@ -359,4 +388,32 @@ public enum MarkerOperationMode { SINGLE, ARBITRARY_LOOP } + + public static class FacingVector2i extends Vector2i { + + HashSet facings; + + public FacingVector2i(int x, int z, List facings) { + super(x, z); + this.facings = new HashSet<>(); + this.facings.addAll(facings); + } + + public FacingVector2i(int x, int z, ForgeDirection facing) { + super(x, z); + this.facings = new HashSet<>(); + if (facing != ForgeDirection.UNKNOWN) + this.facings.add(facing); + } + + + boolean hasConnectionTowards(ForgeDirection dir) { + return facings.contains(dir); + } + + @Override + public String toString() { + return String.format("(%d, %d | %s)", x, y, facings.toString()); + } + } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 0688b01a..2ede0118 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -1,10 +1,16 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Queue; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -32,18 +38,19 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; -import org.joml.Vector4i; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker.FacingVector2i; import cofh.api.energy.EnergyStorage; import cofh.api.energy.IEnergyReceiver; import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; +import org.joml.Vector4i; public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver, IFluidHandler { @@ -54,6 +61,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int storedItems; public ForgeDirection facing; private Area2d workArea; + private Queue nextWorkAreas = new LinkedList<>(); public QuarryWorkState state; private int dx; private int dy; @@ -118,32 +126,96 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { TileEntity te = worldObj.getTileEntity(xCoord + dir.offsetX, yCoord, zCoord + dir.offsetZ); if (te instanceof TileEntityEnderMarker marker) { @Nullable - List scanReturn = marker.checkForBoundary(dir); + List scanReturn = marker.checkForBoundary(dir); if (scanReturn != null) { - Vector2i firstCorner = scanReturn.get(0); - Vector2i secondCorner = scanReturn.get(1); - - // Pad work area by one (inwards), so that we don't mine the markers - Vector2i low = new Vector2i( - Math.min(firstCorner.x, secondCorner.x) + 1, - Math.min(firstCorner.y, secondCorner.y) + 1); - Vector2i high = new Vector2i( - Math.max(firstCorner.x, secondCorner.x) - 1, - Math.max(firstCorner.y, secondCorner.y) - 1); - setWorkArea(new Area2d(low, high)); - state = QuarryWorkState.RUNNING; - - int estBlocks = (worldObj.getHeightValue(dx, dy) + 5) * workArea.height * workArea.width; - player.addChatComponentMessage( - new ChatComponentText( - String.format( - "Found ender marker fence boundary, setting up work area from (%d %d) to (%d %d). Should roughly contain %d blocks.", - workArea.low.x, - workArea.low.y, - workArea.high.x, - workArea.high.y, - estBlocks))); - return; + nextWorkAreas.clear(); + // Do we have a simple rectangle as defined by two points + if (scanReturn.size() == 2) { + Vector2i firstCorner = scanReturn.get(0); + Vector2i secondCorner = scanReturn.get(1); + + // Pad work area by one (inwards), so that we don't mine the markers + Vector2i low = new Vector2i( + Math.min(firstCorner.x, secondCorner.x) + 1, + Math.min(firstCorner.y, secondCorner.y) + 1); + Vector2i high = new Vector2i( + Math.max(firstCorner.x, secondCorner.x) - 1, + Math.max(firstCorner.y, secondCorner.y) - 1); + setWorkArea(new Area2d(low, high)); + state = QuarryWorkState.RUNNING; + + int estBlocks = (worldObj.getHeightValue(dx, dy) + 5) * workArea.height * workArea.width; + player.addChatComponentMessage( + new ChatComponentText( + String.format( + "Found ender marker fence boundary, setting up work area from (%d %d) to (%d %d). Should roughly contain %d blocks.", + workArea.low.x, + workArea.low.y, + workArea.high.x, + workArea.high.y, + estBlocks))); + return; + // Or do we have a more complex rectilinear polygon as defined by many points + } else { + // DEBUG: Clear work area of debug blocks + Vector2i low = new Vector2i(Integer.MAX_VALUE); + Vector2i high = new Vector2i(Integer.MIN_VALUE); + for (Vector2i point : scanReturn) { + if (point.x < low.x) { + low.x = point.x; + } + if (point.y < low.y) { + low.y = point.y; + } + if (point.x > high.x) { + high.x = point.x; + } + if (point.y > high.y) { + high.y = point.y; + } + } + LinkedList subRectangles; + + for (int x = low.x; x <= high.x; x++) { + for (int z = low.y; z <= high.y; z++) { + worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); + } + } + + for (int dy = 1; dy < 51; dy++) { + for (int x = low.x; x <= high.x; x++) { + for (int z = low.y; z <= high.y; z++) { + worldObj.setBlock(x, this.yCoord + dy, z, Blocks.air); + } + } + } + + for (Vector2i point : scanReturn) { + worldObj + .setBlock(point.x, this.yCoord + 2, point.y, Blocks.brick_block); + } + + try { + subRectangles = computeRectanglesFromRectilinearPointPolygon(scanReturn); + } catch (RuntimeException e) { + StackTraceElement[] stackTrace = e.getStackTrace(); + StackTraceElement lastElement = stackTrace[0]; + String lastFileAndLine = lastElement.getFileName() + ":" + lastElement.getLineNumber(); + player.addChatComponentMessage( + new ChatComponentText( + String.format( + "Ender marker at (%d %d %d) failed to set up a fence boundary: %s - [%s:%s]", + marker.xCoord, + marker.yCoord, + marker.zCoord, + e.getMessage(), + lastElement.getFileName(), + lastElement.getLineNumber()))); + return; + } + return; + //nextWorkAreas = subRectangles; + } } else { player.addChatComponentMessage( new ChatComponentText( @@ -160,6 +232,257 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { player.addChatComponentMessage(new ChatComponentText("Found no ender markers around quarry.")); } + private LinkedList computeRectanglesFromRectilinearPointPolygon(List points) { + RectilinearPointPoly poly = new RectilinearPointPoly(points); + LinkedList subAreas = new LinkedList<>(); + UtilitiesInExcess.LOG.info("Starting subarea compute from {} markers", points.size()); + int color = 0; + + while (poly.canFormRectangle()) { + FacingVector2i rectMinPoint = poly.shiftPoint(); + + if (worldObj.getBlock(rectMinPoint.x, this.yCoord - 2, rectMinPoint.y) == Blocks.diamond_block) { + UtilitiesInExcess.chat("Point-Info: " + rectMinPoint); + } + + // Is this a bottom right corner? + if (rectMinPoint.hasConnectionTowards(ForgeDirection.EAST) && rectMinPoint.hasConnectionTowards(ForgeDirection.NORTH)) + continue; + + @Nullable + Integer highXBound = null; + @Nullable + Integer highYBound = null; + for (FacingVector2i higherPoint : poly.remainingPoints) { + // Don't include if y is below our minimum y or on the same as what we already see as the lower y bound + if (higherPoint.y < rectMinPoint.y || (highYBound != null && higherPoint.y > highYBound)) continue; + // Do we have an early simple turn left? + if (higherPoint.x == rectMinPoint.x) { + if (higherPoint.hasConnectionTowards(ForgeDirection.EAST)) + highYBound = higherPoint.y; + // Don't include points for x bound on the same x (left line) + continue; + } + + highXBound = higherPoint.x; + + if (higherPoint.hasConnectionTowards(ForgeDirection.SOUTH) || higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + break; + } else { + // Preliminary y bound + highYBound = higherPoint.y; + } + } + + if (highXBound == null) { + throw new RuntimeException( + "Failed to find right side boundary of rectangle starting at x " + rectMinPoint.x); + } + + boolean foundBetterYBound = false; + var iter = poly.remainingPoints.iterator(); + while (iter.hasNext()) { + FacingVector2i higherPoint = iter.next(); + // Don't include points below our minimum y, + // Don't include points which have an x outside the x bounds + // (in theory higherPoint.x >= rectMinPoint.x is redundant since rectMinPoint.x should be the lowest + // available x left, but is there for visibility), + // Don't include points with a y value that is higher than an already present high y bound + if (higherPoint.y <= rectMinPoint.y || higherPoint.x < rectMinPoint.x) + continue; + if (higherPoint.x > highXBound) break; + + if (!foundBetterYBound && (highYBound == null || higherPoint.y > highYBound)) { + highYBound = higherPoint.y; + } + if (foundBetterYBound && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + if ( (!iter.hasNext() && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) || (higherPoint.x == rectMinPoint.x && higherPoint.y > highYBound && higherPoint.hasConnectionTowards(ForgeDirection.EAST))) { + highYBound = higherPoint.y; + } + } + foundBetterYBound = true; + } + + if (highYBound == null) { + throw new RuntimeException( + "Failed to find top side boundary of rectangle starting at " + rectMinPoint); + } + + FacingVector2i rectMaxPoint = new FacingVector2i(highXBound, highYBound, ForgeDirection.UNKNOWN); + + // Finished sub-rectangle, add it to the list and remove all points on the edges of it + Area2d subArea = new Area2d(rectMinPoint, rectMaxPoint, new Vector4i(0, 0, 0, 0)); + + boolean isLineArea = subArea.width <= 1 || subArea.height <= 1; + if (!isLineArea) subAreas.add(subArea); + + HashSet containedPoints = poly.getRemainingPointsInRect(rectMinPoint, rectMaxPoint); + for (FacingVector2i containedPoint : containedPoints) { + // Is point on the top edge and has a connection to something further out that isn't covered by this rectangle? + if (containedPoint.y != rectMaxPoint.y && containedPoint.hasConnectionTowards(ForgeDirection.EAST)) { + // Was point connected to the left boundary that we moved right with this rectangle? + if (containedPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + containedPoint.facings.remove(ForgeDirection.NORTH); + containedPoint.facings.add(ForgeDirection.SOUTH); + } + continue; + } else if (containedPoint.y == rectMaxPoint.y && containedPoint.hasConnectionTowards(ForgeDirection.SOUTH)) { + // Point is on the top edge and has a connection to the right, leave it for later rectangles + continue; + } + poly.removeFromRemainingPoints(containedPoint); + } + + if (!isLineArea) { + // DEBUG: Draw new sub area on floor + for (int x = subArea.low.x; x <= subArea.high.x; x++) { + for (int z = subArea.low.y; z <= subArea.high.y; z++) { + if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.gold_block) { + worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); + //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); + } else { + worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); + } + } + } + } + + // Add the new points on the high x-axis that might be relevant to future areas + if (poly.hasRemaining()) { + List newPoints = new ArrayList<>(); + // Determine facing of top right corner + FacingVector2i topLeftCorner = new FacingVector2i(highXBound, rectMinPoint.y, ForgeDirection.UNKNOWN); + if (containedPoints.contains(topLeftCorner)) { + rectMaxPoint.facings.add(ForgeDirection.NORTH); + } + if (poly.maxY == rectMaxPoint.y) { + rectMaxPoint.facings.add(ForgeDirection.EAST); + } + + + FacingVector2i bottomRightCorner = new FacingVector2i(rectMinPoint.x, highYBound, Stream.of(ForgeDirection.EAST, ForgeDirection.SOUTH).collect(Collectors.toList())); + if (!containedPoints.contains(topLeftCorner)) { + newPoints.add(topLeftCorner); + } + newPoints.add(rectMaxPoint); + poly.unshiftPointsToRemainingPointsIfMissing(newPoints); + + } + + if (!isLineArea) { + // DEBUG: Show points used for area & remaining points one above that + for (Vector2i remainingPoint : poly.remainingPoints) { + worldObj.setBlock(remainingPoint.x, this.yCoord + 2 + subAreas.size() * 2, remainingPoint.y, Blocks.stained_glass, color, 2); + } + worldObj.setBlock(rectMinPoint.x, this.yCoord + 1 + subAreas.size() * 2, rectMinPoint.y, Blocks.wool, color, 2); + worldObj.setBlock(rectMaxPoint.x, this.yCoord + 1 + subAreas.size() * 2, rectMaxPoint.y, Blocks.wool, color, 2); + + color++; + if (color == 16) { + color = 0; + } + } + + UtilitiesInExcess.LOG.info("Added subarea at {}", subArea.toString()); + } + + return subAreas; + } + + private static class RectilinearPointPoly { + + LinkedHashSet remainingPoints = new LinkedHashSet<>(); + ArrayDeque stackPoints = new ArrayDeque<>(); + private int maxY = Integer.MIN_VALUE; + + RectilinearPointPoly(List points) { + points.sort( + Comparator.comparingInt(FacingVector2i::x) + .thenComparingInt(FacingVector2i::y)); + for (FacingVector2i sortedPoint : points) { + this.stackPoints.add(sortedPoint); + this.remainingPoints.add(sortedPoint); + if (sortedPoint.y > maxY) { + maxY = sortedPoint.y; + } + } + } + + void unshiftPointsToRemainingPointsIfMissing(List newPoints) { + @SuppressWarnings("SimplifyStreamApiCallChains") // Not supported by modern java transpiler + List missingPoints = newPoints.stream() + .filter((point) -> !remainingPoints.contains(point)) + .collect(Collectors.toList()); + + if (!missingPoints.isEmpty()) { + List points = new ArrayList<>(stackPoints); + points.addAll(missingPoints); + + stackPoints.clear(); + remainingPoints.clear(); + maxY = Integer.MIN_VALUE; + + // Same logic as in constructor - sort random remaining pool of points by x & y, then add in low to high + // order + points.sort( + Comparator.comparingInt(FacingVector2i::x) + .thenComparingInt(FacingVector2i::y)); + for (FacingVector2i sortedPoint : points) { + this.stackPoints.add(sortedPoint); + this.remainingPoints.add(sortedPoint); + if (sortedPoint.y > maxY) { + maxY = sortedPoint.y; + } + } + } + } + + void removeFromRemainingPoints(FacingVector2i point) { + this.remainingPoints.remove(point); + this.stackPoints.remove(point); + // If we removed the max Y point, recalculate + if (point.y == maxY) { + maxY = remainingPoints.stream() + .mapToInt(p -> p.y) + .max() + .orElse(Integer.MIN_VALUE); + } + } + + HashSet getRemainingPointsInRect(FacingVector2i low, FacingVector2i high) { + HashSet points = new HashSet<>(); + for (FacingVector2i remainingPoint : this.remainingPoints) { + if (remainingPoint.x < low.x || remainingPoint.y < low.y || remainingPoint.y > high.y) + continue; + if (remainingPoint.x > high.x) break; + points.add(remainingPoint); + } + return points; + } + + FacingVector2i shiftPoint() { + FacingVector2i point = stackPoints.removeFirst(); + this.removeFromRemainingPoints(point); + return point; + } + + /** + * Check if we have remaining points to process + * This does not check if there are any remaining points but if there are enough points left to form a rectangle + */ + boolean hasRemaining() { + return !stackPoints.isEmpty(); + } + + boolean canFormRectangle() { + return stackPoints.size() >= 4; + } + + boolean hasPointAt(Vector2i point) { + return remainingPoints.stream().anyMatch(p -> p.x == point.x && p.y == point.y); + } + } + /** * Are the current dx & dy & dz in work area bounds */ @@ -586,9 +909,14 @@ public void updateEntity() { if (state != QuarryWorkState.RUNNING) break; } } - if (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) && state == QuarryWorkState.RUNNING) { - state = QuarryWorkState.FINISHED; - unloadSelf(); + if (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) + && state == QuarryWorkState.RUNNING) { + if (nextWorkAreas.isEmpty()) { + state = QuarryWorkState.FINISHED; + unloadSelf(); + } else { + workArea = nextWorkAreas.remove(); + } } if (brokenBlocksTick > 0) { markDirty(); @@ -615,12 +943,23 @@ public void readFromNBT(NBTTagCompound nbt) { facing = ForgeDirection.getOrientation(nbt.getInteger("facing")); state = QuarryWorkState.values()[nbt.getInteger("state")]; if (state != QuarryWorkState.FINISHED) { + // Current work area workArea = Area2d.fromNBTTag(nbt); dx = nbt.getInteger("dx"); dy = nbt.getInteger("dy"); dz = nbt.getInteger("dz"); chunkX = dx >> 4; chunkZ = dz >> 4; + + // Possible next work areas + NBTTagList possibleNextAreas = nbt.getTagList("nextAreas", Constants.NBT.TAG_COMPOUND); + if (possibleNextAreas.tagCount() > 0) { + nextWorkAreas.clear(); + for (int i = 0; i < possibleNextAreas.tagCount(); i++) { + NBTTagCompound tag = possibleNextAreas.getCompoundTagAt(i); + nextWorkAreas.add(Area2d.fromNBTTag(tag)); + } + } } brokenBlocksTotal = nbt.getInteger("blocks"); energyStorage.readFromNBT(nbt); @@ -656,6 +995,14 @@ public void writeToNBT(NBTTagCompound nbt) { nbt.setInteger("dx", dx); nbt.setInteger("dy", dy); nbt.setInteger("dz", dz); + + NBTTagList areasNBT = new NBTTagList(); + for (Area2d nextArea : nextWorkAreas) { + NBTTagCompound tag = new NBTTagCompound(); + nextArea.writeNBTTag(tag); + areasNBT.appendTag(tag); + } + nbt.setTag("nextAreas", areasNBT); } nbt.setInteger("blocks", brokenBlocksTotal); energyStorage.writeToNBT(nbt); @@ -777,11 +1124,11 @@ public static class Area2d { // The distance to the closest lower x chunk border from the high x bound public final int chunkOffX; - public Area2d(Vector2i first, Vector2i second) { - int lowX = Math.min(first.x, second.x); - int lowZ = Math.min(first.y, second.y); - int highX = Math.max(first.x, second.x); - int highZ = Math.max(first.y, second.y); + public Area2d(Vector2i first, Vector2i second, Vector4i shrinkMatrix) { + int lowX = Math.min(first.x, second.x) + (shrinkMatrix.x); // Side: left + int lowZ = Math.min(first.y, second.y) + (shrinkMatrix.y); // Side: bottom + int highX = Math.max(first.x, second.x) - (shrinkMatrix.z); // Side: right + int highZ = Math.max(first.y, second.y) - (shrinkMatrix.w); // Side: top this.low = new Vector2i(lowX, lowZ); this.high = new Vector2i(highX, highZ); this.width = highX - lowX; @@ -793,6 +1140,16 @@ public Area2d(int x1, int z1, int x2, int z2) { this(new Vector2i(x1, z1), new Vector2i(x2, z2)); } + public Area2d(Vector2i first, Vector2i second) { + // Don't shrink anywhere + this(first, second, new Vector4i(0, 0, 0, 0)); + } + + public Area2d(Vector2i first, Vector2i second, boolean shouldShrink) { + // Shrink equally on all sides if requested + this(first, second, shouldShrink ? new Vector4i(1, 1, 1, 1) : new Vector4i(0, 0, 0, 0)); + } + public boolean isInBounds(int x, int z) { return x >= this.low.x && x <= this.high.x && z >= this.low.y && z <= this.high.y; } @@ -822,6 +1179,11 @@ public boolean equals(Object obj) { if (!(obj instanceof Area2d other)) return false; return low.equals(other.low) && high.equals(other.high); } + + @Override + public String toString() { + return String.format("[(%d, %d), (%d, %d)]", this.low.x, this.low.y, this.high.x, this.high.y); + } } protected static class ItemStackHashStrategy implements Hash.Strategy { From f2d8e922a841a25d473ea98c6887eec498659668 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:15:05 +0100 Subject: [PATCH 10/23] Stop with the current approach, try an edge based one next --- .../tileentities/TileEntityEnderQuarry.java | 184 +++++++++++++----- 1 file changed, 136 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 2ede0118..433be497 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -246,8 +246,16 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List computeRectanglesFromRectilinearPointPolygon(List highYBound)) continue; // Do we have an early simple turn left? if (higherPoint.x == rectMinPoint.x) { - if (higherPoint.hasConnectionTowards(ForgeDirection.EAST)) + if (highYBound == null && (higherPoint.hasConnectionTowards(ForgeDirection.EAST) || higherPoint.hasConnectionTowards(ForgeDirection.WEST))) highYBound = higherPoint.y; // Don't include points for x bound on the same x (left line) continue; } + if (higherPoint.y == rectMinPoint.y && higherPoint.hasConnectionTowards(ForgeDirection.WEST) && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + // Don't take a left turn as the end of this rectangle + continue; + } highXBound = higherPoint.x; + if (worldObj.getBlock(higherPoint.x, this.yCoord - 2, higherPoint.y) == Blocks.diamond_block) { + UtilitiesInExcess.chat("Point-Info for color " + color + ": " + higherPoint); + } - if (higherPoint.hasConnectionTowards(ForgeDirection.SOUTH) || higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + if (higherPoint.hasConnectionTowards(ForgeDirection.SOUTH)) { + if (higherPoint.hasConnectionTowards(ForgeDirection.WEST)) { + if (rectMinPoint.y == higherPoint.y) { + break; + } else if (highYBound == null || highYBound > higherPoint.y) { + highYBound = higherPoint.y; + } + //break; + } else { + break; + } + } + if (higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { break; - } else { - // Preliminary y bound - highYBound = higherPoint.y; } } @@ -279,8 +303,11 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List computeRectanglesFromRectilinearPointPolygon(List highXBound) break; - if (!foundBetterYBound && (highYBound == null || higherPoint.y > highYBound)) { - highYBound = higherPoint.y; + if (worldObj.getBlock(higherPoint.x, this.yCoord - 2, higherPoint.y) == Blocks.diamond_block) { + UtilitiesInExcess.chat("Point-Info for color " + color + ": " + higherPoint); } - if (foundBetterYBound && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - if ( (!iter.hasNext() && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) || (higherPoint.x == rectMinPoint.x && higherPoint.y > highYBound && higherPoint.hasConnectionTowards(ForgeDirection.EAST))) { + + if (higherPoint.hasConnectionTowards(ForgeDirection.WEST) && higherPoint.x > rectMinPoint.x) { + if (highYBound != null) { + if (highYBound > higherPoint.y) { + // Found a boundary that would close the rectangle on the right side + highYBound = higherPoint.y; + } + } else { + highYBound = higherPoint.y; + } + continue; + } + if (higherPoint.hasConnectionTowards(ForgeDirection.EAST) && higherPoint.x <= highXBound) { + if (highYBound != null) { + if (highYBound > higherPoint.y) { + // Found a boundary that would close the rectangle on the right side + highYBound = higherPoint.y; + } + } else { highYBound = higherPoint.y; } + continue; + } + + if (highYBound == null) { + unsafeHighYBound = higherPoint.y; + } + + if (!iter.hasNext() && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + unsafeHighYBound = higherPoint.y; } - foundBetterYBound = true; + } + if (unsafeHighYBound != null && highYBound == null) { + highYBound = unsafeHighYBound; + } } if (highYBound == null) { @@ -309,15 +366,31 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List containedPoints = poly.getRemainingPointsInRect(rectMinPoint, rectMaxPoint); for (FacingVector2i containedPoint : containedPoints) { + // Keep corners on the top side (if not present, will be added later below) + if (containedPoint.x == rectMaxPoint.x && containedPoint.y == rectMaxPoint.y) { + if (containedPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + // We were the connection north, that has now moved up with this rectangle + containedPoint.facings.remove(ForgeDirection.NORTH); + } else if (containedPoint.hasConnectionTowards(ForgeDirection.SOUTH) && containedPoint.hasConnectionTowards(ForgeDirection.EAST)) { + // needed? + containedPoint.facings.remove(ForgeDirection.SOUTH); + } + continue; + } + if (containedPoint.x == topLeftCorner.x && containedPoint.y == topLeftCorner.y) continue; + // Is point on the top edge and has a connection to something further out that isn't covered by this rectangle? if (containedPoint.y != rectMaxPoint.y && containedPoint.hasConnectionTowards(ForgeDirection.EAST)) { // Was point connected to the left boundary that we moved right with this rectangle? @@ -326,23 +399,29 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List rectMinPoint.x && containedPoint.hasConnectionTowards(ForgeDirection.NORTH)) { + // Point is on the left edge and has a connection to a lower rectangle, leave it for later rectangles + continue; + } + if (worldObj.getBlock(containedPoint.x, this.yCoord - 2, containedPoint.y) == Blocks.diamond_block) { + UtilitiesInExcess.chat("Removing point at " + containedPoint + " for subarea " + subArea); + } poly.removeFromRemainingPoints(containedPoint); } - if (!isLineArea) { - // DEBUG: Draw new sub area on floor - for (int x = subArea.low.x; x <= subArea.high.x; x++) { - for (int z = subArea.low.y; z <= subArea.high.y; z++) { - if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.gold_block) { - worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); - //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); - } else { - worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); - } + // DEBUG: Draw new sub area on floor + for (int x = subArea.low.x; x <= subArea.high.x; x++) { + for (int z = subArea.low.y; z <= subArea.high.y; z++) { + if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.gold_block) { + //worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); + //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); + } else { + worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); } } } @@ -351,7 +430,6 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List newPoints = new ArrayList<>(); // Determine facing of top right corner - FacingVector2i topLeftCorner = new FacingVector2i(highXBound, rectMinPoint.y, ForgeDirection.UNKNOWN); if (containedPoints.contains(topLeftCorner)) { rectMaxPoint.facings.add(ForgeDirection.NORTH); } @@ -359,28 +437,22 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List remainingPoints = new LinkedHashSet<>(); ArrayDeque stackPoints = new ArrayDeque<>(); + HashMap> xMarkersByY = new HashMap<>(); private int maxY = Integer.MIN_VALUE; + private int minY = Integer.MAX_VALUE; + RectilinearPointPoly(List points) { points.sort( @@ -402,14 +477,16 @@ private static class RectilinearPointPoly { for (FacingVector2i sortedPoint : points) { this.stackPoints.add(sortedPoint); this.remainingPoints.add(sortedPoint); - if (sortedPoint.y > maxY) { - maxY = sortedPoint.y; - } + this.xMarkersByY.computeIfAbsent(sortedPoint.y, k -> new HashSet<>()).add(sortedPoint.x); + if (sortedPoint.y > maxY) maxY = sortedPoint.y; + if (sortedPoint.y < minY) minY = sortedPoint.y; } } + /** + * Add new points to the remaining points if they are not already present + */ void unshiftPointsToRemainingPointsIfMissing(List newPoints) { - @SuppressWarnings("SimplifyStreamApiCallChains") // Not supported by modern java transpiler List missingPoints = newPoints.stream() .filter((point) -> !remainingPoints.contains(point)) .collect(Collectors.toList()); @@ -420,7 +497,9 @@ void unshiftPointsToRemainingPointsIfMissing(List newPoints) { stackPoints.clear(); remainingPoints.clear(); + xMarkersByY.clear(); maxY = Integer.MIN_VALUE; + minY = Integer.MAX_VALUE; // Same logic as in constructor - sort random remaining pool of points by x & y, then add in low to high // order @@ -430,9 +509,9 @@ void unshiftPointsToRemainingPointsIfMissing(List newPoints) { for (FacingVector2i sortedPoint : points) { this.stackPoints.add(sortedPoint); this.remainingPoints.add(sortedPoint); - if (sortedPoint.y > maxY) { - maxY = sortedPoint.y; - } + this.xMarkersByY.computeIfAbsent(sortedPoint.y, k -> new HashSet<>()).add(sortedPoint.x); + if (sortedPoint.y > maxY) maxY = sortedPoint.y; + if (sortedPoint.y < minY) minY = sortedPoint.y; } } } @@ -440,12 +519,17 @@ void unshiftPointsToRemainingPointsIfMissing(List newPoints) { void removeFromRemainingPoints(FacingVector2i point) { this.remainingPoints.remove(point); this.stackPoints.remove(point); + this.xMarkersByY.get(point.y).remove(point.x); // If we removed the max Y point, recalculate if (point.y == maxY) { maxY = remainingPoints.stream() .mapToInt(p -> p.y) .max() .orElse(Integer.MIN_VALUE); + minY = remainingPoints.stream() + .mapToInt(p -> p.y) + .min() + .orElse(Integer.MAX_VALUE); } } @@ -481,6 +565,10 @@ boolean canFormRectangle() { boolean hasPointAt(Vector2i point) { return remainingPoints.stream().anyMatch(p -> p.x == point.x && p.y == point.y); } + + int getXsAtY(int y) { + return xMarkersByY.getOrDefault(y, new HashSet<>()).size(); + } } /** From 01ea578c6bd2b2cd862414cc0c9aa21bb585c2f2 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Tue, 16 Dec 2025 04:07:36 +0100 Subject: [PATCH 11/23] Move to an edge based sub-area forming approach --- .../tileentities/TileEntityEnderMarker.java | 18 +- .../tileentities/TileEntityEnderQuarry.java | 500 ++++++++---------- 2 files changed, 218 insertions(+), 300 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 259c3489..33597163 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -54,7 +54,7 @@ private ConcurrentHashMap getRegistryForDimensi return registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()); } - public @Nullable List checkForBoundary(ForgeDirection starterFacing) { + public @Nullable List checkForBoundary(ForgeDirection starterFacing) { return switch (operationMode) { case DEFAULT -> boundaryFromThree(starterFacing); case SINGLE -> boundaryForSizedCuboid(starterFacing); @@ -62,7 +62,7 @@ private ConcurrentHashMap getRegistryForDimensi }; } - private @Nullable List boundaryFromThree(ForgeDirection starterFacing) { + private @Nullable List boundaryFromThree(ForgeDirection starterFacing) { Tuple secondCorner = alignedMarkers.getOrDefault(starterFacing, null); if (secondCorner != null && secondCorner.getKey() != null) { Tuple thirdCorner = Optional @@ -73,23 +73,23 @@ private ConcurrentHashMap getRegistryForDimensi if (thirdCorner != null) { return Stream .of( - new FacingVector2i(this.xCoord, this.zCoord, ForgeDirection.UNKNOWN), - new FacingVector2i(thirdCorner.getValue().x, thirdCorner.getValue().z, ForgeDirection.UNKNOWN)) + new Vector2i(this.xCoord, this.zCoord), + new Vector2i(thirdCorner.getValue().x, thirdCorner.getValue().z)) .collect(Collectors.toList()); } } return null; } - private List boundaryForSizedCuboid(ForgeDirection facing) { + private List boundaryForSizedCuboid(ForgeDirection facing) { BlockPos otherCorner = DirectionUtil .offsetByForward(this.xCoord, this.yCoord, this.zCoord, facing, cuboidSize, 0); otherCorner = DirectionUtil.offsetByRight(otherCorner, facing, cuboidSize, 0); - return Stream.of(new FacingVector2i(this.xCoord, this.zCoord, ForgeDirection.UNKNOWN), new FacingVector2i(otherCorner.x, otherCorner.z, ForgeDirection.UNKNOWN)) + return Stream.of(new Vector2i(this.xCoord, this.zCoord), new Vector2i(otherCorner.x, otherCorner.z)) .collect(Collectors.toList()); } - public List boundaryForArbitraryLoop() { + public List boundaryForArbitraryLoop() { // TODO: Has issues with markers that have more than 2 connections (uses not boundary not intendeed by player) - // maybe limit to 2 connections if this mode is used and propagate that other markers in chain? ArrayList stack = new ArrayList<>(); @@ -148,8 +148,8 @@ public List boundaryForArbitraryLoop() { if (markerChain != null) { UtilitiesInExcess.chat("Completed marker chain with " + markerChain.size() + " entries."); - ArrayList pointChain = new ArrayList<>(markerChain.size()); - markerChain.forEach((e) -> pointChain.add(new FacingVector2i(e.xCoord, e.zCoord, e.alignedMarkers.keySet().stream().collect(Collectors.toList())))); + ArrayList pointChain = new ArrayList<>(markerChain.size()); + markerChain.forEach((e) -> pointChain.add(new Vector2i(e.xCoord, e.zCoord))); return pointChain; } UtilitiesInExcess.chat("Failed to complete marker chain."); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 433be497..7673d27b 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -1,16 +1,16 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Comparator; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Queue; +import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,6 +25,8 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.IWorldAccess; +import net.minecraft.world.World; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; @@ -43,7 +45,6 @@ import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderMarker.FacingVector2i; import cofh.api.energy.EnergyStorage; import cofh.api.energy.IEnergyReceiver; @@ -61,7 +62,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int storedItems; public ForgeDirection facing; private Area2d workArea; - private Queue nextWorkAreas = new LinkedList<>(); + private List nextWorkAreas = new LinkedList<>(); public QuarryWorkState state; private int dx; private int dy; @@ -126,7 +127,7 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { TileEntity te = worldObj.getTileEntity(xCoord + dir.offsetX, yCoord, zCoord + dir.offsetZ); if (te instanceof TileEntityEnderMarker marker) { @Nullable - List scanReturn = marker.checkForBoundary(dir); + List scanReturn = marker.checkForBoundary(dir); if (scanReturn != null) { nextWorkAreas.clear(); // Do we have a simple rectangle as defined by two points @@ -174,7 +175,6 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { high.y = point.y; } } - LinkedList subRectangles; for (int x = low.x; x <= high.x; x++) { for (int z = low.y; z <= high.y; z++) { @@ -195,12 +195,12 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { .setBlock(point.x, this.yCoord + 2, point.y, Blocks.brick_block); } + List subRectangles; try { subRectangles = computeRectanglesFromRectilinearPointPolygon(scanReturn); } catch (RuntimeException e) { StackTraceElement[] stackTrace = e.getStackTrace(); StackTraceElement lastElement = stackTrace[0]; - String lastFileAndLine = lastElement.getFileName() + ":" + lastElement.getLineNumber(); player.addChatComponentMessage( new ChatComponentText( String.format( @@ -232,193 +232,88 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { player.addChatComponentMessage(new ChatComponentText("Found no ender markers around quarry.")); } - private LinkedList computeRectanglesFromRectilinearPointPolygon(List points) { - RectilinearPointPoly poly = new RectilinearPointPoly(points); - LinkedList subAreas = new LinkedList<>(); - UtilitiesInExcess.LOG.info("Starting subarea compute from {} markers", points.size()); + private List computeRectanglesFromRectilinearPointPolygon(List points) { + RectilinearEdgePoly poly = new RectilinearEdgePoly(points); + List subAreas = new ArrayList<>(); int color = 0; - while (poly.canFormRectangle()) { - FacingVector2i rectMinPoint = poly.shiftPoint(); + List activeSpans = new ArrayList<>(); - if (worldObj.getBlock(rectMinPoint.x, this.yCoord - 2, rectMinPoint.y) == Blocks.diamond_block) { - UtilitiesInExcess.chat("Point-Info: " + rectMinPoint); - } + // Sweep through y coordinates from bottom to top, skip last since we handle active spans after the loop + for (int i = 0; i < poly.yCoords.size() - 1; i++) { + int y = poly.yCoords.get(i); - // Is this a bottom right corner? - if (rectMinPoint.hasConnectionTowards(ForgeDirection.EAST) && rectMinPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - UtilitiesInExcess.chat("Skipping bottom right corner at " + rectMinPoint); - continue; - } + // Find spans at this Y level by intersecting vertical edges + List currentSpans = poly.findSpansAtY(y); - // Is this a remainder on the lowest level from a new rect? - if (rectMinPoint.y == poly.minY && poly.getXsAtY(rectMinPoint.y) == 0) { - UtilitiesInExcess.chat("Skipping lowest level remainder at " + rectMinPoint); - continue; - } + // Try to merge with active spans (greedy vertical extension) + List newActiveSpans = new ArrayList<>(); + Set mergedIndices = new HashSet<>(); - @Nullable - Integer highXBound = null; - @Nullable - Integer highYBound = null; - for (FacingVector2i higherPoint : poly.remainingPoints) { - // Don't include if y is below our minimum y or on the same as what we already see as the lower y bound - if (higherPoint.y < rectMinPoint.y || (highYBound != null && higherPoint.y > highYBound)) continue; - // Do we have an early simple turn left? - if (higherPoint.x == rectMinPoint.x) { - if (highYBound == null && (higherPoint.hasConnectionTowards(ForgeDirection.EAST) || higherPoint.hasConnectionTowards(ForgeDirection.WEST))) - highYBound = higherPoint.y; - // Don't include points for x bound on the same x (left line) - continue; - } - if (higherPoint.y == rectMinPoint.y && higherPoint.hasConnectionTowards(ForgeDirection.WEST) && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - // Don't take a left turn as the end of this rectangle - continue; - } - - highXBound = higherPoint.x; - if (worldObj.getBlock(higherPoint.x, this.yCoord - 2, higherPoint.y) == Blocks.diamond_block) { - UtilitiesInExcess.chat("Point-Info for color " + color + ": " + higherPoint); - } - - if (higherPoint.hasConnectionTowards(ForgeDirection.SOUTH)) { - if (higherPoint.hasConnectionTowards(ForgeDirection.WEST)) { - if (rectMinPoint.y == higherPoint.y) { - break; - } else if (highYBound == null || highYBound > higherPoint.y) { - highYBound = higherPoint.y; - } - //break; - } else { + for (RectilinearEdgePoly.Span active : activeSpans) { + boolean merged = false; + for (int j = 0; j < currentSpans.size(); j++) { + if (!mergedIndices.contains(j) && active.matches(currentSpans.get(j))) { + // Extend the active span vertically + newActiveSpans.add(new RectilinearEdgePoly.Span(active.x1, active.x2, active.y)); + mergedIndices.add(j); + merged = true; break; } } - if (higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - break; - } - } - if (highXBound == null) { - throw new RuntimeException( - "Failed to find right side boundary of rectangle starting at x " + rectMinPoint.x); - } - - if (highYBound == null) { - - - var iter = poly.remainingPoints.iterator(); - @Nullable Integer unsafeHighYBound = null; - while (iter.hasNext()) { - FacingVector2i higherPoint = iter.next(); - // Don't include points below our minimum y, - // Don't include points which have an x outside the x bounds - // (in theory higherPoint.x >= rectMinPoint.x is redundant since rectMinPoint.x should be the lowest - // available x left, but is there for visibility), - // Don't include points with a y value that is higher than an already present high y bound - if (higherPoint.y <= rectMinPoint.y || higherPoint.x < rectMinPoint.x) - continue; - // Stop if we are outside x bounds - if (higherPoint.x > highXBound) break; - - if (worldObj.getBlock(higherPoint.x, this.yCoord - 2, higherPoint.y) == Blocks.diamond_block) { - UtilitiesInExcess.chat("Point-Info for color " + color + ": " + higherPoint); - } - - if (higherPoint.hasConnectionTowards(ForgeDirection.WEST) && higherPoint.x > rectMinPoint.x) { - if (highYBound != null) { - if (highYBound > higherPoint.y) { - // Found a boundary that would close the rectangle on the right side - highYBound = higherPoint.y; - } - } else { - highYBound = higherPoint.y; + if (!merged) { + // Can't extend this span, output as rectangle + // Check if bottom and top align with actual boundary edges + int lowX = Math.min(active.x1, active.x2) + 1; + int highX = Math.max(active.x1, active.x2) - 1; + boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary( + active.y, lowX, highX, worldObj, color, this.yCoord + 4); + boolean intersectsWithTopBoundary = poly.intersectsWithHorizontalBoundary( + y, lowX, highX, worldObj, color, this.yCoord + 4); + +// subAreas.add(new Area2d(active.x1, active.y, active.x2, y - 1, +// new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, intersectsWithTopBoundary ? 1 : 0))); + subAreas.add(new Area2d(active.x1, active.y, active.x2, y - 1, + new Vector4i(1, 0, 1, 0))); + color++; + if (color == 16) { + color = 0; } - continue; } - if (higherPoint.hasConnectionTowards(ForgeDirection.EAST) && higherPoint.x <= highXBound) { - if (highYBound != null) { - if (highYBound > higherPoint.y) { - // Found a boundary that would close the rectangle on the right side - highYBound = higherPoint.y; - } - } else { - highYBound = higherPoint.y; - } - continue; - } - - if (highYBound == null) { - unsafeHighYBound = higherPoint.y; - } - - if (!iter.hasNext() && higherPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - unsafeHighYBound = higherPoint.y; - } - } - if (unsafeHighYBound != null && highYBound == null) { - highYBound = unsafeHighYBound; - } } - if (highYBound == null) { - throw new RuntimeException( - "Failed to find top side boundary of rectangle starting at " + rectMinPoint); + // Add unmerged current spans as new active spans + for (int j = 0; j < currentSpans.size(); j++) { + if (!mergedIndices.contains(j)) { + newActiveSpans.add(new RectilinearEdgePoly.Span(currentSpans.get(j).x1, currentSpans.get(j).x2, y)); + } } - FacingVector2i rectMaxPoint = new FacingVector2i(highXBound, highYBound, ForgeDirection.UNKNOWN); - FacingVector2i topLeftCorner = new FacingVector2i(highXBound, rectMinPoint.y, ForgeDirection.UNKNOWN); - FacingVector2i bottomRightCorner = new FacingVector2i(rectMinPoint.x, highYBound, Stream.of(ForgeDirection.EAST, ForgeDirection.SOUTH).collect(Collectors.toList())); - - - // Finished sub-rectangle, add it to the list and remove all points on the edges of it - Area2d subArea = new Area2d(rectMinPoint, rectMaxPoint, new Vector4i(0, 0, 0, 0)); + activeSpans = newActiveSpans; + } - boolean isLineArea = subArea.width <= 1 || subArea.height <= 1; - subAreas.add(subArea); + // Output remaining active spans that can't be extended anymore + int lastY = poly.yCoords.get(poly.yCoords.size() - 1); + for (RectilinearEdgePoly.Span span : activeSpans) { + subAreas.add(new Area2d(span.x1, span.y, span.x2, lastY, new Vector4i(1, 1, 1, 1))); + } + color = 0; - HashSet containedPoints = poly.getRemainingPointsInRect(rectMinPoint, rectMaxPoint); - for (FacingVector2i containedPoint : containedPoints) { - // Keep corners on the top side (if not present, will be added later below) - if (containedPoint.x == rectMaxPoint.x && containedPoint.y == rectMaxPoint.y) { - if (containedPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - // We were the connection north, that has now moved up with this rectangle - containedPoint.facings.remove(ForgeDirection.NORTH); - } else if (containedPoint.hasConnectionTowards(ForgeDirection.SOUTH) && containedPoint.hasConnectionTowards(ForgeDirection.EAST)) { - // needed? - containedPoint.facings.remove(ForgeDirection.SOUTH); - } - continue; - } - if (containedPoint.x == topLeftCorner.x && containedPoint.y == topLeftCorner.y) continue; - - // Is point on the top edge and has a connection to something further out that isn't covered by this rectangle? - if (containedPoint.y != rectMaxPoint.y && containedPoint.hasConnectionTowards(ForgeDirection.EAST)) { - // Was point connected to the left boundary that we moved right with this rectangle? - if (containedPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - containedPoint.facings.remove(ForgeDirection.NORTH); - containedPoint.facings.add(ForgeDirection.SOUTH); - } - continue; - } - if (containedPoint.y == rectMaxPoint.y && containedPoint.hasConnectionTowards(ForgeDirection.SOUTH) && !containedPoint.hasConnectionTowards(ForgeDirection.EAST)) { - // Point is on the top edge and has a connection to the right, leave it for later rectangles - continue; - } - if (containedPoint.y == rectMinPoint.y && containedPoint.x > rectMinPoint.x && containedPoint.hasConnectionTowards(ForgeDirection.NORTH)) { - // Point is on the left edge and has a connection to a lower rectangle, leave it for later rectangles - continue; - } - if (worldObj.getBlock(containedPoint.x, this.yCoord - 2, containedPoint.y) == Blocks.diamond_block) { - UtilitiesInExcess.chat("Removing point at " + containedPoint + " for subarea " + subArea); - } - poly.removeFromRemainingPoints(containedPoint); + for (int i = 1; i < subAreas.size() - 1; i++) { + Area2d currentArea = subAreas.get(i); + Area2d nextArea = subAreas.get(i + 1); + if (currentArea.width < nextArea.width) { + //currentArea.applyShrinkMatrix(new Vector4i(0, 0, 0, -1)); } + } - // DEBUG: Draw new sub area on floor +// DEBUG: Draw sub areas on floor + for (Area2d subArea : subAreas) { for (int x = subArea.low.x; x <= subArea.high.x; x++) { for (int z = subArea.low.y; z <= subArea.high.y; z++) { if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.gold_block) { - //worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); + worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); } else { worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); @@ -426,148 +321,157 @@ private LinkedList computeRectanglesFromRectilinearPointPolygon(List newPoints = new ArrayList<>(); - // Determine facing of top right corner - if (containedPoints.contains(topLeftCorner)) { - rectMaxPoint.facings.add(ForgeDirection.NORTH); - } - if (poly.maxY == rectMaxPoint.y) { - rectMaxPoint.facings.add(ForgeDirection.EAST); - } - - newPoints.add(topLeftCorner); - newPoints.add(rectMaxPoint); - poly.unshiftPointsToRemainingPointsIfMissing(newPoints); - - } - - // DEBUG: Show points used for area & remaining points one above that - for (Vector2i remainingPoint : poly.remainingPoints) { - worldObj.setBlock(remainingPoint.x, this.yCoord + 2 + subAreas.size() * 2, remainingPoint.y, Blocks.stained_glass, color, 2); - } - worldObj.setBlock(rectMinPoint.x, this.yCoord + 1 + subAreas.size() * 2, rectMinPoint.y, Blocks.wool, color, 2); - worldObj.setBlock(rectMaxPoint.x, this.yCoord + 1 + subAreas.size() * 2, rectMaxPoint.y, Blocks.wool, color, 2); - color++; if (color == 16) { color = 0; } - - UtilitiesInExcess.LOG.info("Added subarea at {}", subArea.toString()); } return subAreas; } - private static class RectilinearPointPoly { - - LinkedHashSet remainingPoints = new LinkedHashSet<>(); - ArrayDeque stackPoints = new ArrayDeque<>(); - HashMap> xMarkersByY = new HashMap<>(); - private int maxY = Integer.MIN_VALUE; - private int minY = Integer.MAX_VALUE; - - - RectilinearPointPoly(List points) { - points.sort( - Comparator.comparingInt(FacingVector2i::x) - .thenComparingInt(FacingVector2i::y)); - for (FacingVector2i sortedPoint : points) { - this.stackPoints.add(sortedPoint); - this.remainingPoints.add(sortedPoint); - this.xMarkersByY.computeIfAbsent(sortedPoint.y, k -> new HashSet<>()).add(sortedPoint.x); - if (sortedPoint.y > maxY) maxY = sortedPoint.y; - if (sortedPoint.y < minY) minY = sortedPoint.y; + private static class RectilinearEdgePoly { + + List yCoords; + HashMap> verticalEdgesByX; + HashMap> horizontalEdgesByY; + + RectilinearEdgePoly(List points) { + // Build edges from point list + Iterator iter = points.iterator(); + List edges = new ArrayList<>(); + Vector2i current = null; + Vector2i next = null; + while (iter.hasNext()) { + current = next != null ? next : iter.next(); + next = iter.hasNext() ? iter.next() : points.get(0); + edges.add(new Edge(current, next)); + } + // Connect back to start + if (next != null) edges.add(new Edge(next, points.get(0))); + + // Build list of Y coordinates with points sorted by the lovely tree set + Set ySet = new TreeSet<>(); + // Build vertical edge map for x & y lookups + verticalEdgesByX = new HashMap<>(); + horizontalEdgesByY = new HashMap<>(); + + for (Edge edge : edges) { + ySet.add(edge.p1.y); + ySet.add(edge.p2.y); + if (edge.isVertical()) { + verticalEdgesByX.computeIfAbsent(edge.getX(), k -> new ArrayList<>()).add(edge); + } else if (edge.isHorizontal()) { + horizontalEdgesByY.computeIfAbsent(edge.getY(), k -> new ArrayList<>()).add(edge); + } } + yCoords = new ArrayList<>(ySet); } /** - * Add new points to the remaining points if they are not already present + * Find all vertical edges that cross the horizontal line at y */ - void unshiftPointsToRemainingPointsIfMissing(List newPoints) { - List missingPoints = newPoints.stream() - .filter((point) -> !remainingPoints.contains(point)) - .collect(Collectors.toList()); - - if (!missingPoints.isEmpty()) { - List points = new ArrayList<>(stackPoints); - points.addAll(missingPoints); - - stackPoints.clear(); - remainingPoints.clear(); - xMarkersByY.clear(); - maxY = Integer.MIN_VALUE; - minY = Integer.MAX_VALUE; - - // Same logic as in constructor - sort random remaining pool of points by x & y, then add in low to high - // order - points.sort( - Comparator.comparingInt(FacingVector2i::x) - .thenComparingInt(FacingVector2i::y)); - for (FacingVector2i sortedPoint : points) { - this.stackPoints.add(sortedPoint); - this.remainingPoints.add(sortedPoint); - this.xMarkersByY.computeIfAbsent(sortedPoint.y, k -> new HashSet<>()).add(sortedPoint.x); - if (sortedPoint.y > maxY) maxY = sortedPoint.y; - if (sortedPoint.y < minY) minY = sortedPoint.y; + List findSpansAtY(int y) { + // Collect x-coordinates where edges cross + Set xIntersections = new TreeSet<>(); + + for (Map.Entry> entry : verticalEdgesByX.entrySet()) { + for (Edge e : entry.getValue()) { + // Check if this vertical edge crosses y (exclusive on top to avoid double counting) + if (e.getMinY() <= y && e.getMaxY() > y) { + xIntersections.add(entry.getKey()); + } } } - } - void removeFromRemainingPoints(FacingVector2i point) { - this.remainingPoints.remove(point); - this.stackPoints.remove(point); - this.xMarkersByY.get(point.y).remove(point.x); - // If we removed the max Y point, recalculate - if (point.y == maxY) { - maxY = remainingPoints.stream() - .mapToInt(p -> p.y) - .max() - .orElse(Integer.MIN_VALUE); - minY = remainingPoints.stream() - .mapToInt(p -> p.y) - .min() - .orElse(Integer.MAX_VALUE); + // Pair up intersections to form spans (inside regions) + // Assumes even number of intersections (entering/exiting polygon) + List spans = new ArrayList<>(); + List xCoords = new ArrayList<>(xIntersections); + + for (int i = 0; i < xIntersections.size(); i += 2) { + if (i + 1 < xIntersections.size()) { + spans.add(new Span(xCoords.get(i), xCoords.get(i + 1), y)); + } } + + return spans; } - HashSet getRemainingPointsInRect(FacingVector2i low, FacingVector2i high) { - HashSet points = new HashSet<>(); - for (FacingVector2i remainingPoint : this.remainingPoints) { - if (remainingPoint.x < low.x || remainingPoint.y < low.y || remainingPoint.y > high.y) - continue; - if (remainingPoint.x > high.x) break; - points.add(remainingPoint); + boolean intersectsWithHorizontalBoundary(int y, int x1, int x2, World world, int color, int actualY) { + List boundaries = horizontalEdgesByY.getOrDefault(y, Collections.emptyList()); + for (Edge edge : boundaries) { + // Check if [x1, x2] overlaps with [boundary.x1, boundary.x2] + if (x1 <= edge.getMaxX() && x2 >= edge.getMinX()) { + for (int i = edge.getMinX(); i <= edge.getMaxX(); i++) { + world.setBlock(i, actualY, y, Blocks.wool, color, 2); + } + world.setBlock(x1, actualY, y, Blocks.stained_glass, color, 2); + world.setBlock(x2, actualY, y, Blocks.stained_glass, color, 2); + return true; // Our span is contained within a boundary edge + } } - return points; + return false; } - FacingVector2i shiftPoint() { - FacingVector2i point = stackPoints.removeFirst(); - this.removeFromRemainingPoints(point); - return point; - } + static class Edge { + Vector2i p1, p2; - /** - * Check if we have remaining points to process - * This does not check if there are any remaining points but if there are enough points left to form a rectangle - */ - boolean hasRemaining() { - return !stackPoints.isEmpty(); - } + Edge(Vector2i p1, Vector2i p2) { + this.p1 = p1; + this.p2 = p2; + } - boolean canFormRectangle() { - return stackPoints.size() >= 4; - } + boolean isHorizontal() { + return p1.y == p2.y; + } + + boolean isVertical() { + return p1.x == p2.x; + } + + int getY() { + return p1.y; // For horizontal edges + } + + int getX() { + return p1.x; // For vertical edges + } + + int getMinX() { + return Math.min(p1.x, p2.x); + } + + int getMaxX() { + return Math.max(p1.x, p2.x); + } + + int getMinY() { + return Math.min(p1.y, p2.y); + } + + int getMaxY() { + return Math.max(p1.y, p2.y); + } - boolean hasPointAt(Vector2i point) { - return remainingPoints.stream().anyMatch(p -> p.x == point.x && p.y == point.y); + @Override + public String toString() { + return p1 + " -> " + p2; + } } - int getXsAtY(int y) { - return xMarkersByY.getOrDefault(y, new HashSet<>()).size(); + static class Span { + int x1, x2, y; + + Span(int x1, int x2, int y) { + this.x1 = x1; + this.x2 = x2; + this.y = y; + } + + boolean matches(Span other) { + return this.x1 == other.x1 && this.x2 == other.x2; + } } } @@ -1003,7 +907,7 @@ public void updateEntity() { state = QuarryWorkState.FINISHED; unloadSelf(); } else { - workArea = nextWorkAreas.remove(); + workArea = nextWorkAreas.remove(nextWorkAreas.size() - 1); } } if (brokenBlocksTick > 0) { @@ -1206,11 +1110,11 @@ public static class Area2d { // The corner that has the higher x&y public final Vector2i high; // The width of the entire working area - public final int width; + public int width; // The height of the entire working area - public final int height; + public int height; // The distance to the closest lower x chunk border from the high x bound - public final int chunkOffX; + public int chunkOffX; public Area2d(Vector2i first, Vector2i second, Vector4i shrinkMatrix) { int lowX = Math.min(first.x, second.x) + (shrinkMatrix.x); // Side: left @@ -1228,6 +1132,10 @@ public Area2d(int x1, int z1, int x2, int z2) { this(new Vector2i(x1, z1), new Vector2i(x2, z2)); } + public Area2d(int x1, int z1, int x2, int z2, Vector4i shrinkMatrix) { + this(new Vector2i(x1, z1), new Vector2i(x2, z2), shrinkMatrix); + } + public Area2d(Vector2i first, Vector2i second) { // Don't shrink anywhere this(first, second, new Vector4i(0, 0, 0, 0)); @@ -1242,6 +1150,16 @@ public boolean isInBounds(int x, int z) { return x >= this.low.x && x <= this.high.x && z >= this.low.y && z <= this.high.y; } + public void applyShrinkMatrix(Vector4i shrinkMatrix) { + this.low.x += shrinkMatrix.x; + this.low.y += shrinkMatrix.y; + this.high.x -= shrinkMatrix.z; + this.high.y -= shrinkMatrix.w; + this.width = this.high.x - this.low.x; + this.height = this.high.y - this.low.y; + this.chunkOffX = this.high.x - (this.high.x & -16); + } + public void writeNBTTag(NBTTagCompound nbt) { NBTTagCompound tag = new NBTTagCompound(); tag.setInteger("lowX", low.x); From 46ff99d90e83204b2dc066403cba1ce7ad091c15 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Wed, 17 Dec 2025 04:19:02 +0100 Subject: [PATCH 12/23] Arbitrary marker system shrinking, implementing with actual quarry workflow, fixing some bugs --- .../blocks/ender_quarry/BlockEnderMarker.java | 3 - .../tileentities/TileEntityEnderMarker.java | 54 +++-- .../tileentities/TileEntityEnderQuarry.java | 210 ++++++++---------- 3 files changed, 136 insertions(+), 131 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java index 5ab31f1c..5635e648 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderMarker.java @@ -78,9 +78,6 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderMarker marker) { if (player.isSneaking()) { - if (marker.operationMode == TileEntityEnderMarker.MarkerOperationMode.ARBITRARY_LOOP) { - marker.boundaryForArbitraryLoop(); - } int newCuboidSize = marker.increaseCuboidSize(); player .addChatComponentMessage(new ChatComponentText("Increased cuboid size to " + newCuboidSize + ".")); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 33597163..5c6473d9 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -12,9 +12,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import net.minecraft.init.Blocks; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.S2APacketParticles; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.MathHelper; import net.minecraftforge.common.util.ForgeDirection; import org.jetbrains.annotations.NotNull; @@ -54,11 +59,11 @@ private ConcurrentHashMap getRegistryForDimensi return registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()); } - public @Nullable List checkForBoundary(ForgeDirection starterFacing) { + public @Nullable List checkForBoundary(ForgeDirection starterFacing, EntityPlayer player) { return switch (operationMode) { case DEFAULT -> boundaryFromThree(starterFacing); case SINGLE -> boundaryForSizedCuboid(starterFacing); - case ARBITRARY_LOOP -> boundaryForArbitraryLoop(); + case ARBITRARY_LOOP -> boundaryForArbitraryLoop(player); }; } @@ -89,20 +94,25 @@ private List boundaryForSizedCuboid(ForgeDirection facing) { .collect(Collectors.toList()); } - public List boundaryForArbitraryLoop() { - // TODO: Has issues with markers that have more than 2 connections (uses not boundary not intendeed by player) - - // maybe limit to 2 connections if this mode is used and propagate that other markers in chain? + public List boundaryForArbitraryLoop(EntityPlayer player) { + // TODO: Has issues with markers that have more than 2 connections (uses not boundary not intended by player) - + // maybe limit to 2 connections if this mode is used and propagate that to other markers in chain? ArrayList stack = new ArrayList<>(); stack.add(new StackEntry(new LinkedHashMap<>(), this)); + StackEntry lastVisited; Set markerChain = null; searchStack: do { StackEntry entry = stack.remove(stack.size() - 1); + lastVisited = entry; + // If we have no discovered aligned markers yet, or the only discovered marker + // is the one we came from (a single back-connection), refresh discovery so we + // can populate any missing/stale connections (e.g. after chunk reload). if (entry.current.alignedMarkers.isEmpty() || entry.current.alignedMarkers.size() == 1 - && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) != null - && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) - .getKey() == entry.lastVisited.getKey()) { + && (entry.lastVisited == null || (entry.current.alignedMarkers.get(entry.lastVisited.getValue()) != null + && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) + .getKey() == entry.lastVisited.getKey()))) { entry.current.checkForAlignedMarkers(); } @@ -152,7 +162,27 @@ public List boundaryForArbitraryLoop() { markerChain.forEach((e) -> pointChain.add(new Vector2i(e.xCoord, e.zCoord))); return pointChain; } - UtilitiesInExcess.chat("Failed to complete marker chain."); + player.addChatComponentMessage(new ChatComponentText(String.format("Failed to complete marker chain, with last marker at (%d %d %d).", + lastVisited.current.xCoord, lastVisited.current.yCoord, lastVisited.current.zCoord))); + // Spawn particles to show where the chain broke + for (Object obj : worldObj.playerEntities) { + EntityPlayerMP playerMP = (EntityPlayerMP) obj; + double distance = player.getDistanceSq(xCoord, yCoord, zCoord); + if (distance < 1024) { // 32 block range + S2APacketParticles packet = new S2APacketParticles( + "flame", + (float)(lastVisited.current.xCoord + 0.5), + (float)(lastVisited.current.yCoord + 3.25), + (float)(lastVisited.current.zCoord + 0.5), + 0.0F, + 1F, + 0.0F, + 0.04F, // speed + 400 // count + ); + playerMP.playerNetServerHandler.sendPacket(packet); + } + } return null; } @@ -402,11 +432,9 @@ public FacingVector2i(int x, int z, List facings) { public FacingVector2i(int x, int z, ForgeDirection facing) { super(x, z); this.facings = new HashSet<>(); - if (facing != ForgeDirection.UNKNOWN) - this.facings.add(facing); + if (facing != ForgeDirection.UNKNOWN) this.facings.add(facing); } - boolean hasConnectionTowards(ForgeDirection dir) { return facings.contains(dir); } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 7673d27b..b715b198 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -17,16 +17,16 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import net.minecraft.network.play.server.S2APacketParticles; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.world.ChunkCoordIntPair; -import net.minecraft.world.IWorldAccess; -import net.minecraft.world.World; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; @@ -40,6 +40,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; +import org.joml.Vector4i; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; @@ -51,7 +52,6 @@ import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; -import org.joml.Vector4i; public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver, IFluidHandler { @@ -95,7 +95,7 @@ public void resetState() { public String getState() { return switch (state) { case RUNNING -> String - .format("Quarry is currently mining at %d %d %d, has already mined %d", dx, dy, dz, brokenBlocksTotal); + .format("Quarry is currently mining at %d %d %d, has already mined %d blocks", dx, dy, dz, brokenBlocksTotal); case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids"; case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items"; case STOPPED_WAITING_FOR_ENERGY -> "Quarry is missing energy"; @@ -127,7 +127,7 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { TileEntity te = worldObj.getTileEntity(xCoord + dir.offsetX, yCoord, zCoord + dir.offsetZ); if (te instanceof TileEntityEnderMarker marker) { @Nullable - List scanReturn = marker.checkForBoundary(dir); + List scanReturn = marker.checkForBoundary(dir, player); if (scanReturn != null) { nextWorkAreas.clear(); // Do we have a simple rectangle as defined by two points @@ -158,63 +158,35 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { return; // Or do we have a more complex rectilinear polygon as defined by many points } else { - // DEBUG: Clear work area of debug blocks - Vector2i low = new Vector2i(Integer.MAX_VALUE); - Vector2i high = new Vector2i(Integer.MIN_VALUE); - for (Vector2i point : scanReturn) { - if (point.x < low.x) { - low.x = point.x; - } - if (point.y < low.y) { - low.y = point.y; - } - if (point.x > high.x) { - high.x = point.x; - } - if (point.y > high.y) { - high.y = point.y; - } - } - - for (int x = low.x; x <= high.x; x++) { - for (int z = low.y; z <= high.y; z++) { - worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); - } - } - - for (int dy = 1; dy < 51; dy++) { - for (int x = low.x; x <= high.x; x++) { - for (int z = low.y; z <= high.y; z++) { - worldObj.setBlock(x, this.yCoord + dy, z, Blocks.air); - } - } - } - - for (Vector2i point : scanReturn) { - worldObj - .setBlock(point.x, this.yCoord + 2, point.y, Blocks.brick_block); - } - - List subRectangles; - try { - subRectangles = computeRectanglesFromRectilinearPointPolygon(scanReturn); - } catch (RuntimeException e) { - StackTraceElement[] stackTrace = e.getStackTrace(); - StackTraceElement lastElement = stackTrace[0]; - player.addChatComponentMessage( - new ChatComponentText( - String.format( - "Ender marker at (%d %d %d) failed to set up a fence boundary: %s - [%s:%s]", - marker.xCoord, - marker.yCoord, - marker.zCoord, - e.getMessage(), - lastElement.getFileName(), - lastElement.getLineNumber()))); - return; - } + /* + * // DEBUG: Clear work area of debug blocks + * Vector2i low = new Vector2i(Integer.MAX_VALUE); + * Vector2i high = new Vector2i(Integer.MIN_VALUE); + * for (Vector2i point : scanReturn) { + * if (point.x < low.x) { + * low.x = point.x; + * } + * if (point.y < low.y) { + * low.y = point.y; + * } + * if (point.x > high.x) { + * high.x = point.x; + * } + * if (point.y > high.y) { + * high.y = point.y; + * } + * } + * for (int x = low.x; x <= high.x; x++) { + * for (int z = low.y; z <= high.y; z++) { + * worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); + * } + * } + */ + + nextWorkAreas = computeRectanglesFromRectilinearPointPolygon(scanReturn); + setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); + state = QuarryWorkState.RUNNING; return; - //nextWorkAreas = subRectangles; } } else { player.addChatComponentMessage( @@ -235,7 +207,7 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { private List computeRectanglesFromRectilinearPointPolygon(List points) { RectilinearEdgePoly poly = new RectilinearEdgePoly(points); List subAreas = new ArrayList<>(); - int color = 0; + // int color = 0; List activeSpans = new ArrayList<>(); @@ -267,19 +239,22 @@ private List computeRectanglesFromRectilinearPointPolygon(List // Check if bottom and top align with actual boundary edges int lowX = Math.min(active.x1, active.x2) + 1; int highX = Math.max(active.x1, active.x2) - 1; - boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary( - active.y, lowX, highX, worldObj, color, this.yCoord + 4); - boolean intersectsWithTopBoundary = poly.intersectsWithHorizontalBoundary( - y, lowX, highX, worldObj, color, this.yCoord + 4); - -// subAreas.add(new Area2d(active.x1, active.y, active.x2, y - 1, -// new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, intersectsWithTopBoundary ? 1 : 0))); - subAreas.add(new Area2d(active.x1, active.y, active.x2, y - 1, - new Vector4i(1, 0, 1, 0))); - color++; - if (color == 16) { - color = 0; - } + boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary(active.y, lowX, highX) + && (active.y != y); + boolean intersectsWithTopBoundary = poly.intersectsWithHorizontalBoundary(y, lowX, highX) + && (active.y != y); + + subAreas.add( + new Area2d( + active.x1, + active.y, + active.x2, + y, + new Vector4i( + 1, + intersectsWithBottomBoundary ? 1 : 0, + 1, + intersectsWithTopBoundary ? 1 : 0))); } } @@ -296,36 +271,40 @@ private List computeRectanglesFromRectilinearPointPolygon(List // Output remaining active spans that can't be extended anymore int lastY = poly.yCoords.get(poly.yCoords.size() - 1); for (RectilinearEdgePoly.Span span : activeSpans) { - subAreas.add(new Area2d(span.x1, span.y, span.x2, lastY, new Vector4i(1, 1, 1, 1))); + boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary( + span.y, + Math.min(span.x1, span.x2) + 1, + Math.max(span.x1, span.x2) - 1) && (span.y != lastY); + // Always shrink top side on last rectangle + subAreas.add( + new Area2d( + span.x1, + span.y, + span.x2, + lastY, + new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, 1))); } - color = 0; - for (int i = 1; i < subAreas.size() - 1; i++) { - Area2d currentArea = subAreas.get(i); - Area2d nextArea = subAreas.get(i + 1); - if (currentArea.width < nextArea.width) { - //currentArea.applyShrinkMatrix(new Vector4i(0, 0, 0, -1)); - } - } - -// DEBUG: Draw sub areas on floor - for (Area2d subArea : subAreas) { - for (int x = subArea.low.x; x <= subArea.high.x; x++) { - for (int z = subArea.low.y; z <= subArea.high.y; z++) { - if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.gold_block) { - worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); - //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); - } else { - worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); - } - } - } - - color++; - if (color == 16) { - color = 0; - } - } + /* + * // DEBUG: Draw sub areas on floor + * for (Area2d subArea : subAreas) { + * for (int x = subArea.low.x; x <= subArea.high.x; x++) { + * for (int z = subArea.low.y; z <= subArea.high.y; z++) { + * if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == + * Blocks.gold_block) { + * worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); + * //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); + * } else { + * worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); + * } + * } + * } + * color++; + * if (color == 16) { + * color = 0; + * } + * } + */ return subAreas; } @@ -360,9 +339,11 @@ private static class RectilinearEdgePoly { ySet.add(edge.p1.y); ySet.add(edge.p2.y); if (edge.isVertical()) { - verticalEdgesByX.computeIfAbsent(edge.getX(), k -> new ArrayList<>()).add(edge); + verticalEdgesByX.computeIfAbsent(edge.getX(), k -> new ArrayList<>()) + .add(edge); } else if (edge.isHorizontal()) { - horizontalEdgesByY.computeIfAbsent(edge.getY(), k -> new ArrayList<>()).add(edge); + horizontalEdgesByY.computeIfAbsent(edge.getY(), k -> new ArrayList<>()) + .add(edge); } } yCoords = new ArrayList<>(ySet); @@ -398,23 +379,19 @@ List findSpansAtY(int y) { return spans; } - boolean intersectsWithHorizontalBoundary(int y, int x1, int x2, World world, int color, int actualY) { + boolean intersectsWithHorizontalBoundary(int y, int x1, int x2) { List boundaries = horizontalEdgesByY.getOrDefault(y, Collections.emptyList()); for (Edge edge : boundaries) { // Check if [x1, x2] overlaps with [boundary.x1, boundary.x2] if (x1 <= edge.getMaxX() && x2 >= edge.getMinX()) { - for (int i = edge.getMinX(); i <= edge.getMaxX(); i++) { - world.setBlock(i, actualY, y, Blocks.wool, color, 2); - } - world.setBlock(x1, actualY, y, Blocks.stained_glass, color, 2); - world.setBlock(x2, actualY, y, Blocks.stained_glass, color, 2); - return true; // Our span is contained within a boundary edge + return true; // Our span is contained within a boundary edge } } return false; } static class Edge { + Vector2i p1, p2; Edge(Vector2i p1, Vector2i p2) { @@ -461,6 +438,7 @@ public String toString() { } static class Span { + int x1, x2, y; Span(int x1, int x2, int y) { @@ -884,8 +862,10 @@ public void updateEntity() { while (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) && stepPos()) { // TODO: Remove after this has been tested by others if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { - throw new RuntimeException( - String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); + UtilitiesInExcess.LOG.warn("Tried to quarry outside of work area at {} {} {}", dx, dy, dz); + return; + // throw new RuntimeException( + // String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); } boolean[] harvestResult = tryHarvestCurrentBlock(); @@ -907,7 +887,7 @@ public void updateEntity() { state = QuarryWorkState.FINISHED; unloadSelf(); } else { - workArea = nextWorkAreas.remove(nextWorkAreas.size() - 1); + setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); } } if (brokenBlocksTick > 0) { From f3639c432efb8864e21fe0922b66d68ece1e70de Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Thu, 18 Dec 2025 04:35:23 +0100 Subject: [PATCH 13/23] Limit loop markers to 2 links even when restoring --- .../tileentities/TileEntityEnderMarker.java | 170 +++++++++++------- .../tileentities/TileEntityEnderQuarry.java | 10 +- 2 files changed, 110 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 5c6473d9..65912e50 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -12,14 +12,13 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import net.minecraft.entity.effect.EntityLightningBolt; +import com.fouristhenumber.utilitiesinexcess.ModBlocks; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.play.server.S2APacketParticles; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; -import net.minecraft.util.MathHelper; import net.minecraftforge.common.util.ForgeDirection; import org.jetbrains.annotations.NotNull; @@ -68,19 +67,24 @@ private ConcurrentHashMap getRegistryForDimensi } private @Nullable List boundaryFromThree(ForgeDirection starterFacing) { - Tuple secondCorner = alignedMarkers.getOrDefault(starterFacing, null); - if (secondCorner != null && secondCorner.getKey() != null) { - Tuple thirdCorner = Optional - .ofNullable( - secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnRight90(starterFacing), null)) - .orElse( - secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(starterFacing), null)); - if (thirdCorner != null) { - return Stream - .of( - new Vector2i(this.xCoord, this.zCoord), - new Vector2i(thirdCorner.getValue().x, thirdCorner.getValue().z)) - .collect(Collectors.toList()); + for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { + // Don't check the direction back towards the quarry + if (dir == starterFacing.getOpposite()) continue; + + Tuple secondCorner = alignedMarkers.getOrDefault(dir, null); + if (secondCorner != null && secondCorner.getKey() != null) { + Tuple thirdCorner = Optional + .ofNullable( + secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnRight90(dir), null)) + .orElse( + secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(dir), null)); + if (thirdCorner != null) { + return Stream + .of( + new Vector2i(this.xCoord, this.zCoord), + new Vector2i(thirdCorner.getValue().x, thirdCorner.getValue().z)) + .collect(Collectors.toList()); + } } } return null; @@ -106,6 +110,11 @@ public List boundaryForArbitraryLoop(EntityPlayer player) { StackEntry entry = stack.remove(stack.size() - 1); lastVisited = entry; + if (entry.current.operationMode != MarkerOperationMode.ARBITRARY_LOOP) { + // Set to arbitrary loop mode to avoid automatic linking to more than 2 directions + entry.current.operationMode = MarkerOperationMode.ARBITRARY_LOOP; + } + // If we have no discovered aligned markers yet, or the only discovered marker // is the one we came from (a single back-connection), refresh discovery so we // can populate any missing/stale connections (e.g. after chunk reload). @@ -113,17 +122,20 @@ public List boundaryForArbitraryLoop(EntityPlayer player) { && (entry.lastVisited == null || (entry.current.alignedMarkers.get(entry.lastVisited.getValue()) != null && entry.current.alignedMarkers.get(entry.lastVisited.getValue()) .getKey() == entry.lastVisited.getKey()))) { - entry.current.checkForAlignedMarkers(); + entry.current.checkForAlignedMarkers(entry.current.getActiveDirsFromMeta(), false, true); } + int realizedDirections = 0; for (Map.Entry> otherMarker : entry.current.alignedMarkers .entrySet()) { if (otherMarker.getValue() .getKey() != null) { - // Has not already been visited + // Has not already been visited & we have not already queued 2 new directions from this marker + // (avoid markers with >2 connections) if (!entry.visitedMarkers.containsKey( otherMarker.getValue() - .getKey())) { + .getKey()) + && realizedDirections < 2) { @SuppressWarnings("unchecked") // Same type LinkedHashMap visitedMarkers = (LinkedHashMap) entry.visitedMarkers .clone(); @@ -135,7 +147,7 @@ public List boundaryForArbitraryLoop(EntityPlayer player) { otherMarker.getKey() .getOpposite()); stack.add(stackEntry); - + realizedDirections++; continue; } @@ -162,24 +174,30 @@ public List boundaryForArbitraryLoop(EntityPlayer player) { markerChain.forEach((e) -> pointChain.add(new Vector2i(e.xCoord, e.zCoord))); return pointChain; } - player.addChatComponentMessage(new ChatComponentText(String.format("Failed to complete marker chain, with last marker at (%d %d %d).", - lastVisited.current.xCoord, lastVisited.current.yCoord, lastVisited.current.zCoord))); + + player.addChatComponentMessage( + new ChatComponentText( + String.format( + "Failed to complete marker chain, with last marker at (%d %d %d).", + lastVisited.current.xCoord, + lastVisited.current.yCoord, + lastVisited.current.zCoord))); // Spawn particles to show where the chain broke for (Object obj : worldObj.playerEntities) { EntityPlayerMP playerMP = (EntityPlayerMP) obj; double distance = player.getDistanceSq(xCoord, yCoord, zCoord); if (distance < 1024) { // 32 block range - S2APacketParticles packet = new S2APacketParticles( - "flame", - (float)(lastVisited.current.xCoord + 0.5), - (float)(lastVisited.current.yCoord + 3.25), - (float)(lastVisited.current.zCoord + 0.5), - 0.0F, - 1F, - 0.0F, - 0.04F, // speed - 400 // count - ); + S2APacketParticles packet = new S2APacketParticles( + "flame", + (float) (lastVisited.current.xCoord + 0.5), + (float) (lastVisited.current.yCoord + 3.25), + (float) (lastVisited.current.zCoord + 0.5), + 0.0F, + 1F, + 0.0F, + 0.04F, // speed + 400 // count + ); playerMP.playerNetServerHandler.sendPacket(packet); } } @@ -209,11 +227,32 @@ private static class StackEntry { } public void checkForAlignedMarkers() { - this.checkForAlignedMarkers(HORIZONTAL_DIRECTIONS, false); + this.checkForAlignedMarkers(HORIZONTAL_DIRECTIONS, false, false); } - public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate) { + /** + * Checks for and establishes connections to aligned markers in the specified directions. + *

+ * This method performs two main operations: + *

+ * Attempts to resolve any null aligned markers by loading their chunks and retrieving + * the actual {@link TileEntityEnderMarker} instances from saved positions. + * + * Scans the dimension registry for new markers that can be connected in the given directions, + * respecting ARBITRARY_LOOP operation mode constraints. + * + * @param dirs The {@link ForgeDirection}s to check for aligned markers + * @param onlyValidate If true, only validates existing connections without establishing new ones + * @param forceMetaDirections If true, only accepts connections where the target marker's metadata + * indicates a connection back to this marker - meant to be used to restore stale connections + * + * @see #setAlignedMarker(ForgeDirection, TileEntityEnderMarker) + * @see #removeAlignedMarker(ForgeDirection) + */ + public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate, boolean forceMetaDirections) { ConcurrentHashMap dimRegistry = getRegistryForDimension(); + + // First try to resolve any null aligned markers from saved positions for (Map.Entry> entry : new ArrayList<>( alignedMarkers.entrySet())) { if (entry.getValue() @@ -235,9 +274,14 @@ public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyV } } + // Now check for any aligned markers in the given directions for (ForgeDirection dir : dirs) { if (!alignedMarkers.containsKey(dir)) { for (Map.Entry entry : dimRegistry.entrySet()) { + if (operationMode == MarkerOperationMode.ARBITRARY_LOOP && alignedMarkers.size() >= 2) { + // In arbitrary loop mode, only allow 2 connections + break; + } if (entry.getValue() != null && entry.getValue() != this && !entry.getValue().alignedMarkers.containsKey(dir.getOpposite()) && entry.getKey().y == this.yCoord @@ -255,15 +299,24 @@ public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyV markerDirection = dx > 0 ? ForgeDirection.EAST : ForgeDirection.WEST; } if (markerDirection == dir) { - if (!onlyValidate) { + if (forceMetaDirections + && (worldObj.getBlockMetadata(entry.getKey().x, entry.getKey().y, entry.getKey().z) + & (1 << (dir.getOpposite().ordinal() - 2))) == 0) { + // The other marker's metadata does not indicate a (stale) connection to us, skip + continue; + } + // If this other marker is in loop mode only link if it has free connections (less than 2) + if (!onlyValidate && (entry.getValue().operationMode != MarkerOperationMode.ARBITRARY_LOOP || entry.getValue().alignedMarkers.size() < 2)) { setAlignedMarker(dir, entry.getValue()); entry.getValue() .setAlignedMarker(dir.getOpposite(), this); + // If we aren't in loop mode and just linked to a marker in loop mode, switch to loop mode (if we have less than 3 connections) + if (entry.getValue().operationMode == MarkerOperationMode.ARBITRARY_LOOP && this.operationMode != MarkerOperationMode.ARBITRARY_LOOP && this.alignedMarkers.size() < 3) { + this.operationMode = MarkerOperationMode.ARBITRARY_LOOP; + } } else if (!entry.getValue().alignedMarkers.containsKey(dir.getOpposite()) && (worldObj.getBlockMetadata(entry.getKey().x, entry.getKey().y, entry.getKey().z) - & (1 << (dir.getOpposite() - .ordinal() - 2))) - != 0) { + & (1 << (dir.getOpposite().ordinal() - 2))) != 0) { // Does the active store for this marker not contain us & but the metadata does? // Suggests we reloaded with previous connections, // and since we would usually link from "this" to "entry" unlink (just from the @@ -278,6 +331,11 @@ public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyV } } } + + // Finally check if the block metadata matches the active directions + if (worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != this.activeDirections) { + worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, this.activeDirections, 2); + } } public void setAlignedMarker(ForgeDirection dir, TileEntityEnderMarker marker) { @@ -298,7 +356,13 @@ public void removeAlignedMarker(ForgeDirection dir) { } } - public ForgeDirection[] getActiveDirs() { + public ForgeDirection[] getActiveDirsFromMeta() { + // Make sure the loaded meta matches the blocks meta + if (worldObj.getBlock(this.xCoord, this.yCoord, this.zCoord).equals(ModBlocks.ENDER_MARKER.get()) + && worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != this.activeDirections) { + this.activeDirections = worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord); + } + List activeDirList = new ArrayList<>(); for (ForgeDirection dir : HORIZONTAL_DIRECTIONS) { // Is the bit for this direction set? @@ -310,7 +374,7 @@ public ForgeDirection[] getActiveDirs() { } public void teardownConnections() { - checkForAlignedMarkers(getActiveDirs(), true); + checkForAlignedMarkers(getActiveDirsFromMeta(), true, false); getRegistryForDimension().remove(new BlockPos(this.xCoord, this.yCoord, this.zCoord)); for (Map.Entry> alignedMarker : alignedMarkers .entrySet()) { @@ -418,30 +482,4 @@ public enum MarkerOperationMode { SINGLE, ARBITRARY_LOOP } - - public static class FacingVector2i extends Vector2i { - - HashSet facings; - - public FacingVector2i(int x, int z, List facings) { - super(x, z); - this.facings = new HashSet<>(); - this.facings.addAll(facings); - } - - public FacingVector2i(int x, int z, ForgeDirection facing) { - super(x, z); - this.facings = new HashSet<>(); - if (facing != ForgeDirection.UNKNOWN) this.facings.add(facing); - } - - boolean hasConnectionTowards(ForgeDirection dir) { - return facings.contains(dir); - } - - @Override - public String toString() { - return String.format("(%d, %d | %s)", x, y, facings.toString()); - } - } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index b715b198..67395edb 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -17,13 +17,11 @@ import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; -import net.minecraft.network.play.server.S2APacketParticles; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.world.ChunkCoordIntPair; @@ -94,8 +92,12 @@ public void resetState() { public String getState() { return switch (state) { - case RUNNING -> String - .format("Quarry is currently mining at %d %d %d, has already mined %d blocks", dx, dy, dz, brokenBlocksTotal); + case RUNNING -> String.format( + "Quarry is currently mining at %d %d %d, has already mined %d blocks", + dx, + dy, + dz, + brokenBlocksTotal); case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids"; case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items"; case STOPPED_WAITING_FOR_ENERGY -> "Quarry is missing energy"; From 5666f8179a0ca5ee3ab8a6b7a400baacab1f0b9f Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Thu, 18 Dec 2025 19:58:35 +0100 Subject: [PATCH 14/23] Fix inverted sub-areas after shrinking, apply spotless --- .../tileentities/TileEntityEnderMarker.java | 44 ++++++++++------- .../tileentities/TileEntityEnderQuarry.java | 48 +++++++++++-------- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 65912e50..4fe92e53 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -12,7 +11,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.fouristhenumber.utilitiesinexcess.ModBlocks; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; @@ -25,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector2i; +import com.fouristhenumber.utilitiesinexcess.ModBlocks; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.IFacingTE; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; @@ -74,10 +73,8 @@ private ConcurrentHashMap getRegistryForDimensi Tuple secondCorner = alignedMarkers.getOrDefault(dir, null); if (secondCorner != null && secondCorner.getKey() != null) { Tuple thirdCorner = Optional - .ofNullable( - secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnRight90(dir), null)) - .orElse( - secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(dir), null)); + .ofNullable(secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnRight90(dir), null)) + .orElse(secondCorner.getKey().alignedMarkers.getOrDefault(DirectionUtil.turnLeft90(dir), null)); if (thirdCorner != null) { return Stream .of( @@ -236,20 +233,22 @@ public void checkForAlignedMarkers() { * This method performs two main operations: *

* Attempts to resolve any null aligned markers by loading their chunks and retrieving - * the actual {@link TileEntityEnderMarker} instances from saved positions. + * the actual {@link TileEntityEnderMarker} instances from saved positions. * * Scans the dimension registry for new markers that can be connected in the given directions, - * respecting ARBITRARY_LOOP operation mode constraints. + * respecting ARBITRARY_LOOP operation mode constraints. * - * @param dirs The {@link ForgeDirection}s to check for aligned markers - * @param onlyValidate If true, only validates existing connections without establishing new ones + * @param dirs The {@link ForgeDirection}s to check for aligned markers + * @param onlyValidate If true, only validates existing connections without establishing new ones * @param forceMetaDirections If true, only accepts connections where the target marker's metadata - * indicates a connection back to this marker - meant to be used to restore stale connections + * indicates a connection back to this marker - meant to be used to restore stale + * connections * * @see #setAlignedMarker(ForgeDirection, TileEntityEnderMarker) * @see #removeAlignedMarker(ForgeDirection) */ - public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate, boolean forceMetaDirections) { + public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyValidate, + boolean forceMetaDirections) { ConcurrentHashMap dimRegistry = getRegistryForDimension(); // First try to resolve any null aligned markers from saved positions @@ -301,22 +300,30 @@ public void checkForAlignedMarkers(@NotNull ForgeDirection[] dirs, boolean onlyV if (markerDirection == dir) { if (forceMetaDirections && (worldObj.getBlockMetadata(entry.getKey().x, entry.getKey().y, entry.getKey().z) - & (1 << (dir.getOpposite().ordinal() - 2))) == 0) { + & (1 << (dir.getOpposite() + .ordinal() - 2))) + == 0) { // The other marker's metadata does not indicate a (stale) connection to us, skip continue; } // If this other marker is in loop mode only link if it has free connections (less than 2) - if (!onlyValidate && (entry.getValue().operationMode != MarkerOperationMode.ARBITRARY_LOOP || entry.getValue().alignedMarkers.size() < 2)) { + if (!onlyValidate && (entry.getValue().operationMode != MarkerOperationMode.ARBITRARY_LOOP + || entry.getValue().alignedMarkers.size() < 2)) { setAlignedMarker(dir, entry.getValue()); entry.getValue() .setAlignedMarker(dir.getOpposite(), this); - // If we aren't in loop mode and just linked to a marker in loop mode, switch to loop mode (if we have less than 3 connections) - if (entry.getValue().operationMode == MarkerOperationMode.ARBITRARY_LOOP && this.operationMode != MarkerOperationMode.ARBITRARY_LOOP && this.alignedMarkers.size() < 3) { + // If we aren't in loop mode and just linked to a marker in loop mode, switch to loop + // mode (if we have less than 3 connections) + if (entry.getValue().operationMode == MarkerOperationMode.ARBITRARY_LOOP + && this.operationMode != MarkerOperationMode.ARBITRARY_LOOP + && this.alignedMarkers.size() < 3) { this.operationMode = MarkerOperationMode.ARBITRARY_LOOP; } } else if (!entry.getValue().alignedMarkers.containsKey(dir.getOpposite()) && (worldObj.getBlockMetadata(entry.getKey().x, entry.getKey().y, entry.getKey().z) - & (1 << (dir.getOpposite().ordinal() - 2))) != 0) { + & (1 << (dir.getOpposite() + .ordinal() - 2))) + != 0) { // Does the active store for this marker not contain us & but the metadata does? // Suggests we reloaded with previous connections, // and since we would usually link from "this" to "entry" unlink (just from the @@ -358,7 +365,8 @@ public void removeAlignedMarker(ForgeDirection dir) { public ForgeDirection[] getActiveDirsFromMeta() { // Make sure the loaded meta matches the blocks meta - if (worldObj.getBlock(this.xCoord, this.yCoord, this.zCoord).equals(ModBlocks.ENDER_MARKER.get()) + if (worldObj.getBlock(this.xCoord, this.yCoord, this.zCoord) + .equals(ModBlocks.ENDER_MARKER.get()) && worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord) != this.activeDirections) { this.activeDirections = worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord); } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 67395edb..620b1b2a 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -157,11 +157,11 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { workArea.high.x, workArea.high.y, estBlocks))); - return; // Or do we have a more complex rectilinear polygon as defined by many points } else { + + // DEBUG: Clear work area of debug blocks /* - * // DEBUG: Clear work area of debug blocks * Vector2i low = new Vector2i(Integer.MAX_VALUE); * Vector2i high = new Vector2i(Integer.MIN_VALUE); * for (Vector2i point : scanReturn) { @@ -188,8 +188,8 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { nextWorkAreas = computeRectanglesFromRectilinearPointPolygon(scanReturn); setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); state = QuarryWorkState.RUNNING; - return; } + return; } else { player.addChatComponentMessage( new ChatComponentText( @@ -209,7 +209,7 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { private List computeRectanglesFromRectilinearPointPolygon(List points) { RectilinearEdgePoly poly = new RectilinearEdgePoly(points); List subAreas = new ArrayList<>(); - // int color = 0; + // DEBUG: int color = 0; List activeSpans = new ArrayList<>(); @@ -246,17 +246,16 @@ private List computeRectanglesFromRectilinearPointPolygon(List boolean intersectsWithTopBoundary = poly.intersectsWithHorizontalBoundary(y, lowX, highX) && (active.y != y); - subAreas.add( - new Area2d( - active.x1, - active.y, - active.x2, - y, - new Vector4i( - 1, - intersectsWithBottomBoundary ? 1 : 0, - 1, - intersectsWithTopBoundary ? 1 : 0))); + Area2d subArea = new Area2d( + active.x1, + active.y, + active.x2, + y, + new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, intersectsWithTopBoundary ? 1 : 0)); + + // The base area should have a width and height greater than zero, but that might change after + // applying shrinkage + if (subArea.height > 0 && subArea.width > 0) subAreas.add(subArea); } } @@ -287,8 +286,8 @@ private List computeRectanglesFromRectilinearPointPolygon(List new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, 1))); } + // DEBUG: Draw sub areas on floor /* - * // DEBUG: Draw sub areas on floor * for (Area2d subArea : subAreas) { * for (int x = subArea.low.x; x <= subArea.high.x; x++) { * for (int z = subArea.low.y; z <= subArea.high.y; z++) { @@ -864,10 +863,15 @@ public void updateEntity() { while (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) && stepPos()) { // TODO: Remove after this has been tested by others if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { - UtilitiesInExcess.LOG.warn("Tried to quarry outside of work area at {} {} {}", dx, dy, dz); - return; - // throw new RuntimeException( - // String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); + UtilitiesInExcess.LOG.warn( + "Tried to quarry outside of work area at {} {} {} for work area {}", + dx, + dy, + dz, + this.workArea.toString()); + worldObj.setBlock(dx, dy + 8, dz, Blocks.glass); + throw new RuntimeException( + String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); } boolean[] harvestResult = tryHarvestCurrentBlock(); @@ -1103,6 +1107,8 @@ public Area2d(Vector2i first, Vector2i second, Vector4i shrinkMatrix) { int lowZ = Math.min(first.y, second.y) + (shrinkMatrix.y); // Side: bottom int highX = Math.max(first.x, second.x) - (shrinkMatrix.z); // Side: right int highZ = Math.max(first.y, second.y) - (shrinkMatrix.w); // Side: top + // We explicitly do not rerun min & max here since the shrink matrix might have inverted the area, + // But rather leave it to the caller to handle a 0 null width / height this.low = new Vector2i(lowX, lowZ); this.high = new Vector2i(highX, highZ); this.width = highX - lowX; @@ -1133,10 +1139,12 @@ public boolean isInBounds(int x, int z) { } public void applyShrinkMatrix(Vector4i shrinkMatrix) { + // Same here, we do not check a null / inverted area, but rather leave it up to the caller this.low.x += shrinkMatrix.x; this.low.y += shrinkMatrix.y; this.high.x -= shrinkMatrix.z; this.high.y -= shrinkMatrix.w; + this.width = this.high.x - this.low.x; this.height = this.high.y - this.low.y; this.chunkOffX = this.high.x - (this.high.x & -16); From 33fcffab6834942e3aa56874a323196683b32487 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Fri, 19 Dec 2025 05:52:51 +0100 Subject: [PATCH 15/23] Implement upgrade system & finish the rest of the upgrade models --- .../utilitiesinexcess/ModBlocks.java | 2 +- .../ender_quarry/BlockEnderQuarryUpgrade.java | 43 +- .../EnderQuarryUpgradeManager.java | 155 ++ .../ender_quarry/IEnderQuarryUpgrade.java | 5 - .../tileentities/TileEntityEnderMarker.java | 2 - .../tileentities/TileEntityEnderQuarry.java | 57 +- .../blockstates/ender_quarry_upgrade.json | 12 +- .../models/blocks/upgrade_fortune_1.json | 1412 +++++++++++ .../models/blocks/upgrade_fortune_2.json | 1802 ++++++++++++++ .../models/blocks/upgrade_fortune_3.json | 2062 +++++++++++++++++ .../models/blocks/upgrade_pump_fluids.json | 992 ++++++++ .../models/blocks/upgrade_silk_touch.json | 1122 +++++++++ .../models/blocks/upgrade_speed_1.json | 6 +- .../models/blocks/upgrade_world_hole.json | 1382 +++++++++++ .../textures/blocks/upgrade_fortune_1.png | Bin 0 -> 1811 bytes .../textures/blocks/upgrade_fortune_2.png | Bin 0 -> 2029 bytes .../textures/blocks/upgrade_fortune_3.png | Bin 0 -> 2244 bytes .../textures/blocks/upgrade_pump_fluids.png | Bin 0 -> 1682 bytes .../textures/blocks/upgrade_silk_touch.png | Bin 0 -> 1790 bytes .../textures/blocks/upgrade_world_hole.png | Bin 0 -> 1877 bytes 20 files changed, 9019 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java delete mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_1.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_2.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_3.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_pump_fluids.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_silk_touch.json create mode 100644 src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_world_hole.json create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_1.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_2.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_3.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_pump_fluids.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_silk_touch.png create mode 100644 src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_world_hole.png diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java index 42b98601..1a5e171f 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ModBlocks.java @@ -110,7 +110,7 @@ public enum ModBlocks { END_OF_TIME_PORTAL(BlockConfig.enableEndOfTimePortal && EndOfTimeConfig.enableEndOfTime, new BlockPortalEndOfTime(), BlockPortalEndOfTime.ItemBlockPortalEndOfTime.class, "temporal_gate"), ENDER_QUARRY(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarry(), "ender_quarry"), ENDER_MARKER(EnderQuarryConfig.enableEnderQuarry, new BlockEnderMarker(), "ender_marker"), - ENDER_QUARRY_UPGRADE(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarryUpgrade(), "ender_quarry_upgrade"), + ENDER_QUARRY_UPGRADE(EnderQuarryConfig.enableEnderQuarry, new BlockEnderQuarryUpgrade(), BlockEnderQuarryUpgrade.ItemEnderQuarryUpgrade.class, "ender_quarry_upgrade"), ; // leave trailing semicolon // spotless:on diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java index 0550fd0b..e964a0c5 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java @@ -2,19 +2,24 @@ import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; import net.minecraft.util.IIcon; -import net.minecraft.world.World; -public class BlockEnderQuarryUpgrade extends Block implements IEnderQuarryUpgrade { +public class BlockEnderQuarryUpgrade extends Block { + @SideOnly(Side.CLIENT) private IIcon[] icons; public BlockEnderQuarryUpgrade() { super(Material.iron); setHardness(1f); + setBlockName("utilitiesinexcess:ender_quarry_upgrade"); setLightOpacity(0); } @@ -29,16 +34,34 @@ public int getRenderType() { } @Override - public int onBlockPlaced(World worldIn, int x, int y, int z, int side, float subX, float subY, float subZ, - int meta) { - return 0; + public void registerBlockIcons(IIconRegister reg) { + icons = new IIcon[EnderQuarryUpgradeManager.EnderQuarryUpgrade.VALUES.length]; + for (int i = 0; i < EnderQuarryUpgradeManager.EnderQuarryUpgrade.VALUES.length; i++) { + EnderQuarryUpgradeManager.EnderQuarryUpgrade upgrade = EnderQuarryUpgradeManager.EnderQuarryUpgrade.VALUES[i]; + icons[i] = reg.registerIcon(upgrade.getTextureName()); + } } @Override - public void registerBlockIcons(IIconRegister reg) { - icons = new IIcon[3]; - icons[0] = reg.registerIcon("utilitiesinexcess:upgrade_speed_1"); - icons[1] = reg.registerIcon("utilitiesinexcess:upgrade_speed_2"); - icons[2] = reg.registerIcon("utilitiesinexcess:upgrade_speed_3"); + public int damageDropped(int meta) { + return meta; + } + + public static class ItemEnderQuarryUpgrade extends ItemBlock { + public ItemEnderQuarryUpgrade(Block block) { + super(block); + setMaxDamage(0); + setHasSubtypes(true); + } + + @Override + public int getMetadata(int damage) { + return damage; + } + + @Override + public String getUnlocalizedName(final ItemStack stack) { + return this.getUnlocalizedName() + "." + stack.getItemDamage(); + } } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java new file mode 100644 index 00000000..8187331c --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java @@ -0,0 +1,155 @@ +package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; + +import java.util.HashMap; + +import org.jetbrains.annotations.Nullable; + +import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; + +public class EnderQuarryUpgradeManager { + + private final HashMap activeUpgrades = new HashMap<>(); + + // Add an upgrade - keeps highest tier + public void addUpgrade(EnderQuarryUpgrade upgrade) { + if (upgrade.isBoolean()) { + activeUpgrades.put(upgrade.name(), upgrade); + } else { + String baseName = upgrade.getTierGroup(); + EnderQuarryUpgrade current = activeUpgrades.get(baseName); + + // Only set if no current upgrade or new one is higher tier + if (current == null || upgrade.getTier() > current.getTier()) { + activeUpgrades.put(baseName, upgrade); + } + } + } + + // Check if a specific upgrade is active + public boolean has(EnderQuarryUpgrade upgrade) { + if (upgrade.isBoolean()) { + return activeUpgrades.containsKey(upgrade.name()); + } else { + EnderQuarryUpgrade current = activeUpgrades.get(upgrade.getTierGroup()); + return current != null && current.getTier() >= upgrade.getTier(); + } + } + + // Get the active upgrade for a tiered upgrade type + public EnderQuarryUpgrade getActive(TieredEnderQuarryUpgrade tieredUpgrade) { + return activeUpgrades.get(tieredUpgrade.getBaseName()); + } + + // Get value for a tiered upgrade type (returns defaultValue if not present) + public double getValue(TieredEnderQuarryUpgrade tieredUpgrade, double defaultValue) { + EnderQuarryUpgrade upgrade = activeUpgrades.get(tieredUpgrade.getBaseName()); + return upgrade != null ? upgrade.getValue() : defaultValue; + } + + // Check if any tier of an upgrade type is active + public boolean hasAny(TieredEnderQuarryUpgrade tieredUpgrade) { + return activeUpgrades.containsKey(tieredUpgrade.getBaseName()); + } + + public void remove(EnderQuarryUpgrade upgrade) { + if (upgrade.isBoolean()) { + activeUpgrades.remove(upgrade.name()); + } else { + activeUpgrades.remove(upgrade.getTierGroup()); + } + } + + public void clear() { + activeUpgrades.clear(); + } + + public enum EnderQuarryUpgrade { + // Boolean upgrades (presence only) + WORLD_HOLE(1.2, "upgrade_world_hole"), + SILK_TOUCH(8, "upgrade_silk_touch"), + PUMP_FLUIDS(3, "upgrade_pump_fluids"), + + // Tiered upgrades with hardcoded values + SPEED_1(TieredEnderQuarryUpgrade.SPEED, 1, 16, 2.0, "upgrade_speed_1"), + SPEED_2(TieredEnderQuarryUpgrade.SPEED, 2, 32, 4.0, "upgrade_speed_2"), + SPEED_3(TieredEnderQuarryUpgrade.SPEED, 3, 80, 7.0, "upgrade_speed_3"), + + FORTUNE_1(TieredEnderQuarryUpgrade.FORTUNE, 1, 12, 1, "upgrade_fortune_1"), + FORTUNE_2(TieredEnderQuarryUpgrade.FORTUNE, 2, 40, 2, "upgrade_fortune_2"), + FORTUNE_3(TieredEnderQuarryUpgrade.FORTUNE, 3, 100, 3, "upgrade_fortune_3"); + + + public static final EnderQuarryUpgrade[] VALUES = values(); + + private final boolean isBoolean; + private final double value; + private final double cost; + private final @Nullable TieredEnderQuarryUpgrade tierGroup; + private final int tier; + private final String textureName; + + // Constructor for boolean upgrades + EnderQuarryUpgrade(double cost, String textureName) { + this.isBoolean = true; + value = 0.0; + this.cost = cost; + tierGroup = null; + tier = 0; + this.textureName = String.format("%s:%s", UtilitiesInExcess.MODID, textureName); + } + + // Constructor for value-based upgrades + EnderQuarryUpgrade(@Nullable TieredEnderQuarryUpgrade tierGroup, int tier, double cost, double value, + String textureName) { + this.isBoolean = false; + this.value = value; + this.cost = cost; + this.tierGroup = tierGroup; + this.tier = tier; + this.textureName = String.format("%s:%s", UtilitiesInExcess.MODID, textureName); + } + + public boolean isBoolean() { + return isBoolean; + } + + public double getValue() { + if (isBoolean) { + throw new IllegalStateException(this + " is a boolean upgrade and has no value"); + } + return value; + } + + public double getCost() { + return cost; + } + + public String getTierGroup() { + if (isBoolean || tierGroup == null) { + throw new IllegalStateException(this + " is a boolean upgrade and has no tier group"); + } + return tierGroup.getBaseName(); + } + + public int getTier() { + if (isBoolean) { + throw new IllegalStateException(this + " is a boolean upgrade and has no tier"); + } + return tier; + } + + public String getTextureName() { + return textureName; + } + } + + public enum TieredEnderQuarryUpgrade { + + SPEED, + FORTUNE; + + public String getBaseName() { + return this.name(); + } + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java deleted file mode 100644 index 6a620737..00000000 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/IEnderQuarryUpgrade.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; - -public interface IEnderQuarryUpgrade { - -} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 4fe92e53..af46f63a 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -96,8 +96,6 @@ private List boundaryForSizedCuboid(ForgeDirection facing) { } public List boundaryForArbitraryLoop(EntityPlayer player) { - // TODO: Has issues with markers that have more than 2 connections (uses not boundary not intended by player) - - // maybe limit to 2 connections if this mode is used and propagate that to other markers in chain? ArrayList stack = new ArrayList<>(); stack.add(new StackEntry(new LinkedHashMap<>(), this)); StackEntry lastVisited; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 620b1b2a..3c0b2c94 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -19,13 +19,16 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.FakePlayerFactory; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; @@ -41,6 +44,8 @@ import org.joml.Vector4i; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.BlockEnderQuarryUpgrade; +import com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry.EnderQuarryUpgradeManager; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; @@ -70,6 +75,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int brokenBlocksTotal; private final HashMap sidedItemAcceptors = new HashMap<>(); private final HashMap sidedFluidAcceptors = new HashMap<>(); + private final EnderQuarryUpgradeManager upgradeManager = new EnderQuarryUpgradeManager(); protected final EnergyStorage energyStorage = new EnergyStorage(EnderQuarryConfig.enderQuarryEnergyStorage); protected final List fluidStorage = Stream @@ -555,8 +561,7 @@ private boolean tryConsumeEnergy(float hardness, boolean simulate) { private boolean harvestAndStoreBlock(Block block) { try { - // TODO: Use "shouldPumpFluids" or similar from upgrades - if (true) { + if (upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.PUMP_FLUIDS)) { FluidStack fluid = null; if (block == Blocks.water) { fluid = new FluidStack(FluidRegistry.WATER, 1000); @@ -570,18 +575,39 @@ private boolean harvestAndStoreBlock(Block block) { } } - // TODO: Use fake player? Make sure to use fortune upgrade + EntityPlayer fakePlayer = FakePlayerFactory.getMinecraft((WorldServer) worldObj); + @Nullable List drops = null; + int meta = worldObj.getBlockMetadata(dx, dy, dz); + // Try to silk touch if we have the upgrade + if (upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.SILK_TOUCH)) { + if (block.canSilkHarvest(worldObj, fakePlayer, dx, dy, dz, meta)) { + Item item = Item.getItemFromBlock(block); + if (item != null) { + // Set item damage from meta if the BlockItem has subtypes + drops = Collections.singletonList(new ItemStack(item, 1, item.getHasSubtypes() ? meta : 0)); + } + } + } + // If not silk or we failed to resolve to reasonable drops, get normal drops (with fortune if applicable) + if (drops == null) { + drops = block.getDrops( + worldObj, + dx, + dy, + dz, + meta, + (int) upgradeManager.getValue(EnderQuarryUpgradeManager.TieredEnderQuarryUpgrade.FORTUNE, 0)); + } // We can accept that the maximum stored amount is sometimes overrun by fortune - ArrayList drops = block.getDrops(worldObj, dx, dy, dz, worldObj.getBlockMetadata(dx, dy, dz), 0); if (!drops.isEmpty()) { return tryStoreItems(drops); } - // Block just has no drops + // Block probably just has no drops return true; } catch (Exception ignored) { UtilitiesInExcess.LOG - .error("Failed while trying to harvest block {} at {} {} {}.", block.toString(), dx, dy, dz); + .error("EQ Failed while trying to harvest block {} at {} {} {}.", block.toString(), dx, dy, dz); return false; } } @@ -620,7 +646,7 @@ private boolean tryStoreFluid(FluidStack fluid) { * * @return If we could store all the provided items */ - private boolean tryStoreItems(ArrayList items) { + private boolean tryStoreItems(List items) { int toStore = items.stream() .mapToInt((item) -> item != null ? item.stackSize : 0) .sum(); @@ -766,6 +792,7 @@ private boolean ejectStoredToAdjacent() { public void scanSidesForTEs() { sidedFluidAcceptors.clear(); sidedItemAcceptors.clear(); + upgradeManager.clear(); ArrayList loadedAdjacentChunks = new ArrayList<>(); // Make sure the directly adjacent blocks in different chunks are loaded whilst checking @@ -784,14 +811,22 @@ public void scanSidesForTEs() { direction.offsetX + this.xCoord, direction.offsetY + this.yCoord, direction.offsetZ + this.zCoord); + Block block = this.worldObj.getBlock( + direction.offsetX + this.xCoord, + direction.offsetY + this.yCoord, + direction.offsetZ + this.zCoord); + ChunkCoordIntPair chunk = new ChunkCoordIntPair( (direction.offsetX + this.xCoord) >> 4, (direction.offsetZ + this.zCoord) >> 4); - // TODO: Upgrades people - // if (te instanceof IQuarryUpgrade quarryUpgrade) { - // - // } + if (block instanceof BlockEnderQuarryUpgrade) { + int meta = this.worldObj.getBlockMetadata( + direction.offsetX + this.xCoord, + direction.offsetY + this.yCoord, + direction.offsetZ + this.zCoord); + upgradeManager.addUpgrade(EnderQuarryUpgradeManager.EnderQuarryUpgrade.VALUES[meta]); + } if (te instanceof IFluidHandler fluidHandler) { sidedFluidAcceptors.put(direction, fluidHandler); diff --git a/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json b/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json index ca5fbcf2..c18c8f19 100644 --- a/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json +++ b/src/main/resources/assets/utilitiesinexcess/blockstates/ender_quarry_upgrade.json @@ -1,7 +1,13 @@ { "variants": { - "meta=0": { "model": "utilitiesinexcess:blocks/upgrade_speed_1" }, - "meta=1": { "model": "utilitiesinexcess:blocks/upgrade_speed_2" }, - "meta=2": { "model": "utilitiesinexcess:blocks/upgrade_speed_3" } + "meta=0": { "model": "utilitiesinexcess:blocks/upgrade_world_hole" }, + "meta=1": { "model": "utilitiesinexcess:blocks/upgrade_silk_touch" }, + "meta=2": { "model": "utilitiesinexcess:blocks/upgrade_pump_fluids" }, + "meta=3": { "model": "utilitiesinexcess:blocks/upgrade_speed_1" }, + "meta=4": { "model": "utilitiesinexcess:blocks/upgrade_speed_2" }, + "meta=5": { "model": "utilitiesinexcess:blocks/upgrade_speed_3" }, + "meta=6": { "model": "utilitiesinexcess:blocks/upgrade_fortune_1" }, + "meta=7": { "model": "utilitiesinexcess:blocks/upgrade_fortune_2" }, + "meta=8": { "model": "utilitiesinexcess:blocks/upgrade_fortune_3" } } } diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_1.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_1.json new file mode 100644 index 00000000..b26a146b --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_1.json @@ -0,0 +1,1412 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_fortune_1", + "particle": "utilitiesinexcess:upgrade_fortune_1" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [11, 0, 11.25, 0.5], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [11.25, 0, 11.5, 0.5], "texture": "#0"}, + "up": {"uv": [11.25, 6, 10.75, 5.75], "texture": "#0"}, + "down": {"uv": [11.5, 9, 11, 9.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 11.5, 3.25, 12], "texture": "#0"}, + "east": {"uv": [3.25, 11.5, 3.5, 12], "texture": "#0"}, + "south": {"uv": [3.5, 11.5, 3.75, 12], "texture": "#0"}, + "west": {"uv": [3.75, 11.5, 4, 12], "texture": "#0"}, + "up": {"uv": [11.5, 9, 11.25, 8.75], "texture": "#0"}, + "down": {"uv": [11.75, 0.75, 11.5, 1], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 7.25, 11.75, 7.75], "texture": "#0"}, + "east": {"uv": [11.5, 7.75, 11.75, 8.25], "texture": "#0"}, + "south": {"uv": [11.5, 8.25, 11.75, 8.75], "texture": "#0"}, + "west": {"uv": [11.5, 8.75, 11.75, 9.25], "texture": "#0"}, + "up": {"uv": [3.5, 12.75, 3.25, 12.5], "texture": "#0"}, + "down": {"uv": [5.25, 12.5, 5, 12.75], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 10, 11.75, 10.5], "texture": "#0"}, + "east": {"uv": [11.5, 10.5, 11.75, 11], "texture": "#0"}, + "south": {"uv": [11, 11.5, 11.25, 12], "texture": "#0"}, + "west": {"uv": [11.5, 11, 11.75, 11.5], "texture": "#0"}, + "up": {"uv": [5.5, 12.75, 5.25, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 5.25, 12.5, 5.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 11.5, 11.5, 12], "texture": "#0"}, + "east": {"uv": [10.25, 4.5, 10.75, 5], "texture": "#0"}, + "south": {"uv": [11.5, 11.5, 11.75, 12], "texture": "#0"}, + "west": {"uv": [10.25, 5, 10.75, 5.5], "texture": "#0"}, + "up": {"uv": [12, 0.5, 11.75, 0], "texture": "#0"}, + "down": {"uv": [12, 0.5, 11.75, 1], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 1, 12, 1.5], "texture": "#0"}, + "east": {"uv": [11.75, 1.5, 12, 2], "texture": "#0"}, + "south": {"uv": [11.75, 2, 12, 2.5], "texture": "#0"}, + "west": {"uv": [11.75, 2.5, 12, 3], "texture": "#0"}, + "up": {"uv": [5.75, 12.75, 5.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 5.5, 12.5, 5.75], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 3, 12, 3.5], "texture": "#0"}, + "east": {"uv": [11.75, 3.5, 12, 4], "texture": "#0"}, + "south": {"uv": [11.75, 4.5, 12, 5], "texture": "#0"}, + "west": {"uv": [11.75, 5, 12, 5.5], "texture": "#0"}, + "up": {"uv": [12.75, 6, 12.5, 5.75], "texture": "#0"}, + "down": {"uv": [12.75, 6, 12.5, 6.25], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 5.5, 12, 6], "texture": "#0"}, + "east": {"uv": [6.25, 11.75, 6.5, 12.25], "texture": "#0"}, + "south": {"uv": [6.5, 11.75, 6.75, 12.25], "texture": "#0"}, + "west": {"uv": [6.75, 11.75, 7, 12.25], "texture": "#0"}, + "up": {"uv": [6.5, 12.75, 6.25, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 6.25, 12.5, 6.5], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 5.5, 10.75, 6], "texture": "#0"}, + "east": {"uv": [7, 11.75, 7.25, 12.25], "texture": "#0"}, + "south": {"uv": [6.25, 10.25, 6.75, 10.75], "texture": "#0"}, + "west": {"uv": [11.75, 7.25, 12, 7.75], "texture": "#0"}, + "up": {"uv": [11.75, 3.25, 11.25, 3], "texture": "#0"}, + "down": {"uv": [11.75, 5.75, 11.25, 6], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 7.75, 12, 8.25], "texture": "#0"}, + "east": {"uv": [11.75, 8.25, 12, 8.75], "texture": "#0"}, + "south": {"uv": [8.5, 11.75, 8.75, 12.25], "texture": "#0"}, + "west": {"uv": [8.75, 11.75, 9, 12.25], "texture": "#0"}, + "up": {"uv": [6.75, 12.75, 6.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 6.5, 12.5, 6.75], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 8.75, 12, 9.25], "texture": "#0"}, + "east": {"uv": [9, 11.75, 9.25, 12.25], "texture": "#0"}, + "south": {"uv": [9.25, 11.75, 9.5, 12.25], "texture": "#0"}, + "west": {"uv": [9.5, 11.75, 9.75, 12.25], "texture": "#0"}, + "up": {"uv": [7, 12.75, 6.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 6.75, 12.5, 7], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9.75, 11.75, 10, 12.25], "texture": "#0"}, + "east": {"uv": [10, 11.75, 10.25, 12.25], "texture": "#0"}, + "south": {"uv": [11.75, 10, 12, 10.5], "texture": "#0"}, + "west": {"uv": [10.25, 11.75, 10.5, 12.25], "texture": "#0"}, + "up": {"uv": [7.25, 12.75, 7, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7, 12.5, 7.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 11.75, 10.75, 12.25], "texture": "#0"}, + "east": {"uv": [6.75, 10.25, 7.25, 10.75], "texture": "#0"}, + "south": {"uv": [11.75, 10.5, 12, 11], "texture": "#0"}, + "west": {"uv": [10.25, 7.25, 10.75, 7.75], "texture": "#0"}, + "up": {"uv": [11, 12.25, 10.75, 11.75], "texture": "#0"}, + "down": {"uv": [12, 11, 11.75, 11.5], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 11.5, 12, 12], "texture": "#0"}, + "east": {"uv": [12, 0, 12.25, 0.5], "texture": "#0"}, + "south": {"uv": [12, 0.5, 12.25, 1], "texture": "#0"}, + "west": {"uv": [12, 1, 12.25, 1.5], "texture": "#0"}, + "up": {"uv": [7.5, 12.75, 7.25, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7.25, 12.5, 7.5], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 1.5, 12.25, 2], "texture": "#0"}, + "east": {"uv": [2, 12, 2.25, 12.5], "texture": "#0"}, + "south": {"uv": [12, 2, 12.25, 2.5], "texture": "#0"}, + "west": {"uv": [2.25, 12, 2.5, 12.5], "texture": "#0"}, + "up": {"uv": [7.75, 12.75, 7.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7.5, 12.5, 7.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 12, 2.75, 12.5], "texture": "#0"}, + "east": {"uv": [12, 2.5, 12.25, 3], "texture": "#0"}, + "south": {"uv": [2.75, 12, 3, 12.5], "texture": "#0"}, + "west": {"uv": [3, 12, 3.25, 12.5], "texture": "#0"}, + "up": {"uv": [8, 12.75, 7.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7.75, 12.5, 8], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 7.75, 10.75, 8.25], "texture": "#0"}, + "east": {"uv": [12, 3, 12.25, 3.5], "texture": "#0"}, + "south": {"uv": [10.25, 8.25, 10.75, 8.75], "texture": "#0"}, + "west": {"uv": [3.25, 12, 3.5, 12.5], "texture": "#0"}, + "up": {"uv": [4, 12.25, 3.5, 12], "texture": "#0"}, + "down": {"uv": [12.5, 3.5, 12, 3.75], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 3.75, 12.5, 4], "texture": "#0"}, + "east": {"uv": [8, 12.5, 8.25, 12.75], "texture": "#0"}, + "south": {"uv": [4, 12, 4.5, 12.25], "texture": "#0"}, + "west": {"uv": [12.5, 8, 12.75, 8.25], "texture": "#0"}, + "up": {"uv": [5, 12.25, 4.5, 12], "texture": "#0"}, + "down": {"uv": [12.5, 4.5, 12, 4.75], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 4.75, 12.25, 5.25], "texture": "#0"}, + "east": {"uv": [8.5, 10.25, 9, 10.75], "texture": "#0"}, + "south": {"uv": [5, 12, 5.25, 12.5], "texture": "#0"}, + "west": {"uv": [10.25, 8.75, 10.75, 9.25], "texture": "#0"}, + "up": {"uv": [12.25, 5.75, 12, 5.25], "texture": "#0"}, + "down": {"uv": [12.25, 5.75, 12, 6.25], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8.25, 12.5, 8.5, 12.75], "texture": "#0"}, + "east": {"uv": [12, 6.25, 12.5, 6.5], "texture": "#0"}, + "south": {"uv": [12.5, 8.25, 12.75, 8.5], "texture": "#0"}, + "west": {"uv": [12, 6.5, 12.5, 6.75], "texture": "#0"}, + "up": {"uv": [12.25, 7.25, 12, 6.75], "texture": "#0"}, + "down": {"uv": [7.5, 12, 7.25, 12.5], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9, 10.25, 9.5, 10.75], "texture": "#0"}, + "east": {"uv": [12, 7.25, 12.25, 7.75], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 10, 10.75], "texture": "#0"}, + "west": {"uv": [7.5, 12, 7.75, 12.5], "texture": "#0"}, + "up": {"uv": [8.25, 12.25, 7.75, 12], "texture": "#0"}, + "down": {"uv": [12.5, 7.75, 12, 8], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 8, 12.5, 8.25], "texture": "#0"}, + "east": {"uv": [8.5, 12.5, 8.75, 12.75], "texture": "#0"}, + "south": {"uv": [12, 8.25, 12.5, 8.5], "texture": "#0"}, + "west": {"uv": [12.5, 8.5, 12.75, 8.75], "texture": "#0"}, + "up": {"uv": [12.5, 8.75, 12, 8.5], "texture": "#0"}, + "down": {"uv": [12.5, 8.75, 12, 9], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "east": {"uv": [10, 10.25, 10.5, 10.75], "texture": "#0"}, + "south": {"uv": [12, 9, 12.25, 9.5], "texture": "#0"}, + "west": {"uv": [10.5, 0, 11, 0.5], "texture": "#0"}, + "up": {"uv": [12.25, 10, 12, 9.5], "texture": "#0"}, + "down": {"uv": [12.25, 10, 12, 10.5], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8.75, 12.5, 9, 12.75], "texture": "#0"}, + "east": {"uv": [12, 10.5, 12.5, 10.75], "texture": "#0"}, + "south": {"uv": [12.5, 8.75, 12.75, 9], "texture": "#0"}, + "west": {"uv": [12, 10.75, 12.5, 11], "texture": "#0"}, + "up": {"uv": [11.25, 12.5, 11, 12], "texture": "#0"}, + "down": {"uv": [12.25, 11, 12, 11.5], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 9, 12.75, 9.25], "texture": "#0"}, + "east": {"uv": [10.5, 0.5, 11.5, 0.75], "texture": "#0"}, + "south": {"uv": [12.5, 9.25, 12.75, 9.5], "texture": "#0"}, + "west": {"uv": [10.5, 0.75, 11.5, 1], "texture": "#0"}, + "up": {"uv": [10.75, 2, 10.5, 1], "texture": "#0"}, + "down": {"uv": [10.75, 2, 10.5, 3], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 12, 11.5, 12.5], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [11.5, 12, 11.75, 12.5], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [3.25, 11.5, 3, 10.5], "texture": "#0"}, + "down": {"uv": [10.75, 3, 10.5, 4], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 10, 11.5, 10.25], "texture": "#0"}, + "east": {"uv": [12.5, 9.5, 12.75, 9.75], "texture": "#0"}, + "south": {"uv": [10.5, 10.25, 11.5, 10.5], "texture": "#0"}, + "west": {"uv": [12.5, 9.75, 12.75, 10], "texture": "#0"}, + "up": {"uv": [11.5, 10.75, 10.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.75, 1, 10.75, 1.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [12, 11.5, 12.25, 12], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [11.75, 12, 12, 12.5], "texture": "#0"}, + "up": {"uv": [11.75, 1.5, 10.75, 1.25], "texture": "#0"}, + "down": {"uv": [11.75, 1.5, 10.75, 1.75], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 12.5, 10.25, 12.75], "texture": "#0"}, + "east": {"uv": [10.75, 1.75, 11.75, 2], "texture": "#0"}, + "south": {"uv": [10.25, 12.5, 10.5, 12.75], "texture": "#0"}, + "west": {"uv": [10.75, 2, 11.75, 2.25], "texture": "#0"}, + "up": {"uv": [3.5, 11.5, 3.25, 10.5], "texture": "#0"}, + "down": {"uv": [3.75, 10.5, 3.5, 11.5], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 12, 12.25, 12.5], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [0, 12.25, 0.25, 12.75], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [4, 11.5, 3.75, 10.5], "texture": "#0"}, + "down": {"uv": [11, 2.25, 10.75, 3.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 3.25, 11.75, 3.5], "texture": "#0"}, + "east": {"uv": [12.5, 10.5, 12.75, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 3.5, 11.75, 3.75], "texture": "#0"}, + "west": {"uv": [12.5, 10.75, 12.75, 11], "texture": "#0"}, + "up": {"uv": [11.75, 4, 10.75, 3.75], "texture": "#0"}, + "down": {"uv": [11.75, 4.5, 10.75, 4.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [12.25, 0, 12.5, 0.5], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [0.25, 12.25, 0.5, 12.75], "texture": "#0"}, + "up": {"uv": [11.75, 5, 10.75, 4.75], "texture": "#0"}, + "down": {"uv": [11.75, 5, 10.75, 5.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [9.4, 9.4, 14], + "to": [9.9, 9.9, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 9.65, 14.25]}, + "faces": { + "north": {"uv": [11, 12.5, 11.25, 12.75], "texture": "#0"}, + "east": {"uv": [11.25, 12.5, 11.5, 12.75], "texture": "#0"}, + "south": {"uv": [12.5, 11.25, 12.75, 11.5], "texture": "#0"}, + "west": {"uv": [11.5, 12.5, 11.75, 12.75], "texture": "#0"}, + "up": {"uv": [12.75, 11.75, 12.5, 11.5], "texture": "#0"}, + "down": {"uv": [12, 12.5, 11.75, 12.75], "texture": "#0"} + } + }, + { + "from": [9.9, 8.4, 14], + "to": [10.4, 10.9, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 9.65, 14.25]}, + "faces": { + "north": {"uv": [11, 8.25, 11.25, 9], "texture": "#0"}, + "east": {"uv": [11, 10.75, 11.25, 11.5], "texture": "#0"}, + "south": {"uv": [11.25, 2.25, 11.5, 3], "texture": "#0"}, + "west": {"uv": [11.25, 7.25, 11.5, 8], "texture": "#0"}, + "up": {"uv": [12.75, 12, 12.5, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 12.5, 12, 12.75], "texture": "#0"} + } + }, + { + "from": [10.4, 9.4, 14], + "to": [10.9, 9.9, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 9.65, 14.25]}, + "faces": { + "north": {"uv": [12.5, 12, 12.75, 12.25], "texture": "#0"}, + "east": {"uv": [12.25, 12.5, 12.5, 12.75], "texture": "#0"}, + "south": {"uv": [12.5, 12.5, 12.75, 12.75], "texture": "#0"}, + "west": {"uv": [0, 12.75, 0.25, 13], "texture": "#0"}, + "up": {"uv": [0.5, 13, 0.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 0.25, 12.75, 0.5], "texture": "#0"} + } + }, + { + "from": [5.85, 5.7, 14], + "to": [6.35, 7.2, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.1, 4.95, 14.25]}, + "faces": { + "north": {"uv": [0.5, 12.25, 0.75, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 0.5, 12.5, 1], "texture": "#0"}, + "south": {"uv": [0.75, 12.25, 1, 12.75], "texture": "#0"}, + "west": {"uv": [1, 12.25, 1.25, 12.75], "texture": "#0"}, + "up": {"uv": [0.75, 13, 0.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 0.5, 12.75, 0.75], "texture": "#0"} + } + }, + { + "from": [7.75, 5.85, 14], + "to": [8.25, 10.35, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [6.25, 10.75, 6.5, 11.75], "texture": "#0"}, + "east": {"uv": [6.5, 10.75, 6.75, 11.75], "texture": "#0"}, + "south": {"uv": [6.75, 10.75, 7, 11.75], "texture": "#0"}, + "west": {"uv": [7, 10.75, 7.25, 11.75], "texture": "#0"}, + "up": {"uv": [1, 13, 0.75, 12.75], "texture": "#0"}, + "down": {"uv": [1.25, 12.75, 1, 13], "texture": "#0"} + } + }, + { + "from": [7.25, 8.35, 14], + "to": [7.75, 8.85, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [12.75, 1, 13, 1.25], "texture": "#0"}, + "east": {"uv": [12.75, 1.25, 13, 1.5], "texture": "#0"}, + "south": {"uv": [12.75, 1.5, 13, 1.75], "texture": "#0"}, + "west": {"uv": [1.75, 12.75, 2, 13], "texture": "#0"}, + "up": {"uv": [13, 2, 12.75, 1.75], "texture": "#0"}, + "down": {"uv": [2.25, 12.75, 2, 13], "texture": "#0"} + } + }, + { + "from": [7.25, 7.35, 14], + "to": [7.75, 7.85, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [12.75, 2, 13, 2.25], "texture": "#0"}, + "east": {"uv": [2.25, 12.75, 2.5, 13], "texture": "#0"}, + "south": {"uv": [12.75, 2.25, 13, 2.5], "texture": "#0"}, + "west": {"uv": [12.75, 2.5, 13, 2.75], "texture": "#0"}, + "up": {"uv": [3, 13, 2.75, 12.75], "texture": "#0"}, + "down": {"uv": [3.25, 12.75, 3, 13], "texture": "#0"} + } + }, + { + "from": [6.25, 7.85, 14], + "to": [7.75, 8.35, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [12.25, 1, 12.75, 1.25], "texture": "#0"}, + "east": {"uv": [3.25, 12.75, 3.5, 13], "texture": "#0"}, + "south": {"uv": [1.25, 12.25, 1.75, 12.5], "texture": "#0"}, + "west": {"uv": [12.75, 3.25, 13, 3.5], "texture": "#0"}, + "up": {"uv": [12.75, 1.5, 12.25, 1.25], "texture": "#0"}, + "down": {"uv": [12.75, 1.5, 12.25, 1.75], "texture": "#0"} + } + }, + { + "from": [8.25, 7.85, 14], + "to": [9.75, 8.35, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [12.25, 1.75, 12.75, 2], "texture": "#0"}, + "east": {"uv": [3.5, 12.75, 3.75, 13], "texture": "#0"}, + "south": {"uv": [12.25, 2, 12.75, 2.25], "texture": "#0"}, + "west": {"uv": [3.75, 12.75, 4, 13], "texture": "#0"}, + "up": {"uv": [12.75, 2.5, 12.25, 2.25], "texture": "#0"}, + "down": {"uv": [12.75, 2.5, 12.25, 2.75], "texture": "#0"} + } + }, + { + "from": [8.25, 8.35, 14], + "to": [8.75, 8.85, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [4, 12.75, 4.25, 13], "texture": "#0"}, + "east": {"uv": [12.75, 4, 13, 4.25], "texture": "#0"}, + "south": {"uv": [4.25, 12.75, 4.5, 13], "texture": "#0"}, + "west": {"uv": [12.75, 4.25, 13, 4.5], "texture": "#0"}, + "up": {"uv": [4.75, 13, 4.5, 12.75], "texture": "#0"}, + "down": {"uv": [5, 12.75, 4.75, 13], "texture": "#0"} + } + }, + { + "from": [8.25, 7.35, 14], + "to": [8.75, 7.85, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 14.25]}, + "faces": { + "north": {"uv": [12.75, 4.75, 13, 5], "texture": "#0"}, + "east": {"uv": [5, 12.75, 5.25, 13], "texture": "#0"}, + "south": {"uv": [12.75, 5, 13, 5.25], "texture": "#0"}, + "west": {"uv": [5.25, 12.75, 5.5, 13], "texture": "#0"}, + "up": {"uv": [13, 5.5, 12.75, 5.25], "texture": "#0"}, + "down": {"uv": [5.75, 12.75, 5.5, 13], "texture": "#0"} + } + }, + { + "from": [9.5, 9.4, 1.5], + "to": [10, 9.9, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.25, 9.65, 1.75]}, + "faces": { + "north": {"uv": [12.75, 5.5, 13, 5.75], "texture": "#0"}, + "east": {"uv": [5.75, 12.75, 6, 13], "texture": "#0"}, + "south": {"uv": [12.75, 5.75, 13, 6], "texture": "#0"}, + "west": {"uv": [6, 12.75, 6.25, 13], "texture": "#0"}, + "up": {"uv": [13, 6.25, 12.75, 6], "texture": "#0"}, + "down": {"uv": [6.5, 12.75, 6.25, 13], "texture": "#0"} + } + }, + { + "from": [10, 8.4, 1.5], + "to": [10.5, 10.9, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.25, 9.65, 1.75]}, + "faces": { + "north": {"uv": [11.25, 8, 11.5, 8.75], "texture": "#0"}, + "east": {"uv": [11.25, 10.75, 11.5, 11.5], "texture": "#0"}, + "south": {"uv": [0, 11.5, 0.25, 12.25], "texture": "#0"}, + "west": {"uv": [11.5, 0, 11.75, 0.75], "texture": "#0"}, + "up": {"uv": [13, 6.5, 12.75, 6.25], "texture": "#0"}, + "down": {"uv": [6.75, 12.75, 6.5, 13], "texture": "#0"} + } + }, + { + "from": [10.5, 9.4, 1.5], + "to": [11, 9.9, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.25, 9.65, 1.75]}, + "faces": { + "north": {"uv": [12.75, 6.5, 13, 6.75], "texture": "#0"}, + "east": {"uv": [6.75, 12.75, 7, 13], "texture": "#0"}, + "south": {"uv": [12.75, 6.75, 13, 7], "texture": "#0"}, + "west": {"uv": [7, 12.75, 7.25, 13], "texture": "#0"}, + "up": {"uv": [13, 7.25, 12.75, 7], "texture": "#0"}, + "down": {"uv": [7.5, 12.75, 7.25, 13], "texture": "#0"} + } + }, + { + "from": [5.85, 5.7, 1.5], + "to": [6.35, 7.2, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.1, 4.95, 1.75]}, + "faces": { + "north": {"uv": [1.75, 12.25, 2, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 2.75, 12.5, 3.25], "texture": "#0"}, + "south": {"uv": [3.5, 12.25, 3.75, 12.75], "texture": "#0"}, + "west": {"uv": [3.75, 12.25, 4, 12.75], "texture": "#0"}, + "up": {"uv": [13, 7.5, 12.75, 7.25], "texture": "#0"}, + "down": {"uv": [7.75, 12.75, 7.5, 13], "texture": "#0"} + } + }, + { + "from": [7.75, 5.85, 1.5], + "to": [8.25, 10.35, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [10.75, 7.25, 11, 8.25], "texture": "#0"}, + "east": {"uv": [10.75, 8.25, 11, 9.25], "texture": "#0"}, + "south": {"uv": [8.5, 10.75, 8.75, 11.75], "texture": "#0"}, + "west": {"uv": [8.75, 10.75, 9, 11.75], "texture": "#0"}, + "up": {"uv": [13, 7.75, 12.75, 7.5], "texture": "#0"}, + "down": {"uv": [8, 12.75, 7.75, 13], "texture": "#0"} + } + }, + { + "from": [7.25, 8.35, 1.5], + "to": [7.75, 8.85, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [12.75, 7.75, 13, 8], "texture": "#0"}, + "east": {"uv": [8, 12.75, 8.25, 13], "texture": "#0"}, + "south": {"uv": [12.75, 8, 13, 8.25], "texture": "#0"}, + "west": {"uv": [8.25, 12.75, 8.5, 13], "texture": "#0"}, + "up": {"uv": [13, 8.5, 12.75, 8.25], "texture": "#0"}, + "down": {"uv": [8.75, 12.75, 8.5, 13], "texture": "#0"} + } + }, + { + "from": [7.25, 7.35, 1.5], + "to": [7.75, 7.85, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [12.75, 8.5, 13, 8.75], "texture": "#0"}, + "east": {"uv": [8.75, 12.75, 9, 13], "texture": "#0"}, + "south": {"uv": [12.75, 8.75, 13, 9], "texture": "#0"}, + "west": {"uv": [9, 12.75, 9.25, 13], "texture": "#0"}, + "up": {"uv": [13, 9.25, 12.75, 9], "texture": "#0"}, + "down": {"uv": [9.5, 12.75, 9.25, 13], "texture": "#0"} + } + }, + { + "from": [6.25, 7.85, 1.5], + "to": [7.75, 8.35, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [12.25, 3.25, 12.75, 3.5], "texture": "#0"}, + "east": {"uv": [12.75, 9.25, 13, 9.5], "texture": "#0"}, + "south": {"uv": [4, 12.25, 4.5, 12.5], "texture": "#0"}, + "west": {"uv": [9.5, 12.75, 9.75, 13], "texture": "#0"}, + "up": {"uv": [12.75, 4.25, 12.25, 4], "texture": "#0"}, + "down": {"uv": [12.75, 4.25, 12.25, 4.5], "texture": "#0"} + } + }, + { + "from": [8.25, 7.85, 1.5], + "to": [9.75, 8.35, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [4.5, 12.25, 5, 12.5], "texture": "#0"}, + "east": {"uv": [12.75, 9.5, 13, 9.75], "texture": "#0"}, + "south": {"uv": [12.25, 4.75, 12.75, 5], "texture": "#0"}, + "west": {"uv": [9.75, 12.75, 10, 13], "texture": "#0"}, + "up": {"uv": [12.75, 5.25, 12.25, 5], "texture": "#0"}, + "down": {"uv": [5.75, 12.25, 5.25, 12.5], "texture": "#0"} + } + }, + { + "from": [8.25, 8.35, 1.5], + "to": [8.75, 8.85, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [12.75, 9.75, 13, 10], "texture": "#0"}, + "east": {"uv": [10, 12.75, 10.25, 13], "texture": "#0"}, + "south": {"uv": [12.75, 10, 13, 10.25], "texture": "#0"}, + "west": {"uv": [10.25, 12.75, 10.5, 13], "texture": "#0"}, + "up": {"uv": [13, 10.5, 12.75, 10.25], "texture": "#0"}, + "down": {"uv": [10.75, 12.75, 10.5, 13], "texture": "#0"} + } + }, + { + "from": [8.25, 7.35, 1.5], + "to": [8.75, 7.85, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.1, 1.75]}, + "faces": { + "north": {"uv": [12.75, 10.5, 13, 10.75], "texture": "#0"}, + "east": {"uv": [10.75, 12.75, 11, 13], "texture": "#0"}, + "south": {"uv": [12.75, 10.75, 13, 11], "texture": "#0"}, + "west": {"uv": [11, 12.75, 11.25, 13], "texture": "#0"}, + "up": {"uv": [13, 11.25, 12.75, 11], "texture": "#0"}, + "down": {"uv": [11.5, 12.75, 11.25, 13], "texture": "#0"} + } + }, + { + "from": [14, 9.4, 6.15], + "to": [14.5, 9.9, 6.65], + "rotation": {"angle": 0, "axis": "y", "origin": [18.35, 9.65, 5.9]}, + "faces": { + "north": {"uv": [12.75, 11.25, 13, 11.5], "texture": "#0"}, + "east": {"uv": [11.5, 12.75, 11.75, 13], "texture": "#0"}, + "south": {"uv": [12.75, 11.5, 13, 11.75], "texture": "#0"}, + "west": {"uv": [11.75, 12.75, 12, 13], "texture": "#0"}, + "up": {"uv": [13, 12, 12.75, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 12.75, 12, 13], "texture": "#0"} + } + }, + { + "from": [14, 8.4, 5.65], + "to": [14.5, 10.9, 6.15], + "rotation": {"angle": 0, "axis": "y", "origin": [18.35, 9.65, 5.9]}, + "faces": { + "north": {"uv": [0.25, 11.5, 0.5, 12.25], "texture": "#0"}, + "east": {"uv": [0.5, 11.5, 0.75, 12.25], "texture": "#0"}, + "south": {"uv": [0.75, 11.5, 1, 12.25], "texture": "#0"}, + "west": {"uv": [1, 11.5, 1.25, 12.25], "texture": "#0"}, + "up": {"uv": [13, 12.25, 12.75, 12], "texture": "#0"}, + "down": {"uv": [12.5, 12.75, 12.25, 13], "texture": "#0"} + } + }, + { + "from": [14, 9.4, 5.15], + "to": [14.5, 9.9, 5.65], + "rotation": {"angle": 0, "axis": "y", "origin": [18.35, 9.65, 5.9]}, + "faces": { + "north": {"uv": [12.75, 12.25, 13, 12.5], "texture": "#0"}, + "east": {"uv": [12.5, 12.75, 12.75, 13], "texture": "#0"}, + "south": {"uv": [12.75, 12.5, 13, 12.75], "texture": "#0"}, + "west": {"uv": [12.75, 12.75, 13, 13], "texture": "#0"}, + "up": {"uv": [0.25, 13.25, 0, 13], "texture": "#0"}, + "down": {"uv": [13.25, 0, 13, 0.25], "texture": "#0"} + } + }, + { + "from": [14, 5.7, 9.7], + "to": [14.5, 7.2, 10.2], + "rotation": {"angle": 0, "axis": "y", "origin": [14.2, 4.95, 9.95]}, + "faces": { + "north": {"uv": [12.25, 5.25, 12.5, 5.75], "texture": "#0"}, + "east": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 5.75, 12.5, 6.25], "texture": "#0"}, + "west": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "up": {"uv": [0.5, 13.25, 0.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 0.25, 13, 0.5], "texture": "#0"} + } + }, + { + "from": [14, 5.85, 7.8], + "to": [14.5, 10.35, 8.3], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [9, 10.75, 9.25, 11.75], "texture": "#0"}, + "east": {"uv": [9.25, 10.75, 9.5, 11.75], "texture": "#0"}, + "south": {"uv": [9.5, 10.75, 9.75, 11.75], "texture": "#0"}, + "west": {"uv": [9.75, 10.75, 10, 11.75], "texture": "#0"}, + "up": {"uv": [0.75, 13.25, 0.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 0.5, 13, 0.75], "texture": "#0"} + } + }, + { + "from": [14, 8.35, 8.3], + "to": [14.5, 8.85, 8.8], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [0.75, 13, 1, 13.25], "texture": "#0"}, + "east": {"uv": [13, 0.75, 13.25, 1], "texture": "#0"}, + "south": {"uv": [1, 13, 1.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 1, 13.25, 1.25], "texture": "#0"}, + "up": {"uv": [1.5, 13.25, 1.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 1.25, 13, 1.5], "texture": "#0"} + } + }, + { + "from": [14, 7.35, 8.3], + "to": [14.5, 7.85, 8.8], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [1.5, 13, 1.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 1.5, 13.25, 1.75], "texture": "#0"}, + "south": {"uv": [1.75, 13, 2, 13.25], "texture": "#0"}, + "west": {"uv": [13, 1.75, 13.25, 2], "texture": "#0"}, + "up": {"uv": [2.25, 13.25, 2, 13], "texture": "#0"}, + "down": {"uv": [13.25, 2, 13, 2.25], "texture": "#0"} + } + }, + { + "from": [14, 7.85, 8.3], + "to": [14.5, 8.35, 9.8], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [2.25, 13, 2.5, 13.25], "texture": "#0"}, + "east": {"uv": [6.25, 12.25, 6.75, 12.5], "texture": "#0"}, + "south": {"uv": [13, 2.25, 13.25, 2.5], "texture": "#0"}, + "west": {"uv": [6.75, 12.25, 7.25, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 7.25, 12.25, 6.75], "texture": "#0"}, + "down": {"uv": [12.5, 7.25, 12.25, 7.75], "texture": "#0"} + } + }, + { + "from": [14, 7.85, 6.3], + "to": [14.5, 8.35, 7.8], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [2.5, 13, 2.75, 13.25], "texture": "#0"}, + "east": {"uv": [7.75, 12.25, 8.25, 12.5], "texture": "#0"}, + "south": {"uv": [13, 2.5, 13.25, 2.75], "texture": "#0"}, + "west": {"uv": [8.5, 12.25, 9, 12.5], "texture": "#0"}, + "up": {"uv": [9.25, 12.75, 9, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 9, 12.25, 9.5], "texture": "#0"} + } + }, + { + "from": [14, 8.35, 7.3], + "to": [14.5, 8.85, 7.8], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [2.75, 13, 3, 13.25], "texture": "#0"}, + "east": {"uv": [13, 2.75, 13.25, 3], "texture": "#0"}, + "south": {"uv": [3, 13, 3.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 3, 13.25, 3.25], "texture": "#0"}, + "up": {"uv": [3.5, 13.25, 3.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 3.25, 13, 3.5], "texture": "#0"} + } + }, + { + "from": [14, 7.35, 7.3], + "to": [14.5, 7.85, 7.8], + "rotation": {"angle": 0, "axis": "y", "origin": [16.1, 8.1, 8.05]}, + "faces": { + "north": {"uv": [3.5, 13, 3.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 3.5, 13.25, 3.75], "texture": "#0"}, + "south": {"uv": [3.75, 13, 4, 13.25], "texture": "#0"}, + "west": {"uv": [13, 3.75, 13.25, 4], "texture": "#0"}, + "up": {"uv": [4.25, 13.25, 4, 13], "texture": "#0"}, + "down": {"uv": [13.25, 4, 13, 4.25], "texture": "#0"} + } + }, + { + "from": [1.5, 9.4, 6.15], + "to": [2, 9.9, 6.65], + "rotation": {"angle": 0, "axis": "y", "origin": [5.85, 9.65, 5.9]}, + "faces": { + "north": {"uv": [4.25, 13, 4.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 4.25, 13.25, 4.5], "texture": "#0"}, + "south": {"uv": [4.5, 13, 4.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 4.5, 13.25, 4.75], "texture": "#0"}, + "up": {"uv": [5, 13.25, 4.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 4.75, 13, 5], "texture": "#0"} + } + }, + { + "from": [1.5, 8.4, 5.65], + "to": [2, 10.9, 6.15], + "rotation": {"angle": 0, "axis": "y", "origin": [5.85, 9.65, 5.9]}, + "faces": { + "north": {"uv": [1.25, 11.5, 1.5, 12.25], "texture": "#0"}, + "east": {"uv": [1.5, 11.5, 1.75, 12.25], "texture": "#0"}, + "south": {"uv": [1.75, 11.5, 2, 12.25], "texture": "#0"}, + "west": {"uv": [11.5, 2.25, 11.75, 3], "texture": "#0"}, + "up": {"uv": [5.25, 13.25, 5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 5, 13, 5.25], "texture": "#0"} + } + }, + { + "from": [1.5, 9.4, 5.15], + "to": [2, 9.9, 5.65], + "rotation": {"angle": 0, "axis": "y", "origin": [5.85, 9.65, 5.9]}, + "faces": { + "north": {"uv": [5.25, 13, 5.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 5.25, 13.25, 5.5], "texture": "#0"}, + "south": {"uv": [5.5, 13, 5.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 5.5, 13.25, 5.75], "texture": "#0"}, + "up": {"uv": [6, 13.25, 5.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 5.75, 13, 6], "texture": "#0"} + } + }, + { + "from": [1.5, 5.7, 9.7], + "to": [2, 7.2, 10.2], + "rotation": {"angle": 0, "axis": "y", "origin": [1.7, 4.95, 9.95]}, + "faces": { + "north": {"uv": [9.25, 12.25, 9.5, 12.75], "texture": "#0"}, + "east": {"uv": [9.5, 12.25, 9.75, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 9.5, 12.5, 10], "texture": "#0"}, + "west": {"uv": [9.75, 12.25, 10, 12.75], "texture": "#0"}, + "up": {"uv": [6.25, 13.25, 6, 13], "texture": "#0"}, + "down": {"uv": [13.25, 6, 13, 6.25], "texture": "#0"} + } + }, + { + "from": [1.5, 5.85, 7.8], + "to": [2, 10.35, 8.3], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [10, 10.75, 10.25, 11.75], "texture": "#0"}, + "east": {"uv": [10.25, 10.75, 10.5, 11.75], "texture": "#0"}, + "south": {"uv": [10.5, 10.75, 10.75, 11.75], "texture": "#0"}, + "west": {"uv": [10.75, 10.75, 11, 11.75], "texture": "#0"}, + "up": {"uv": [6.5, 13.25, 6.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 6.25, 13, 6.5], "texture": "#0"} + } + }, + { + "from": [1.5, 8.35, 8.3], + "to": [2, 8.85, 8.8], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [6.5, 13, 6.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 6.5, 13.25, 6.75], "texture": "#0"}, + "south": {"uv": [6.75, 13, 7, 13.25], "texture": "#0"}, + "west": {"uv": [13, 6.75, 13.25, 7], "texture": "#0"}, + "up": {"uv": [7.25, 13.25, 7, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7, 13, 7.25], "texture": "#0"} + } + }, + { + "from": [1.5, 7.35, 8.3], + "to": [2, 7.85, 8.8], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [7.25, 13, 7.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 7.25, 13.25, 7.5], "texture": "#0"}, + "south": {"uv": [7.5, 13, 7.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 7.5, 13.25, 7.75], "texture": "#0"}, + "up": {"uv": [8, 13.25, 7.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7.75, 13, 8], "texture": "#0"} + } + }, + { + "from": [1.5, 7.85, 8.3], + "to": [2, 8.35, 9.8], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [8, 13, 8.25, 13.25], "texture": "#0"}, + "east": {"uv": [10, 12.25, 10.5, 12.5], "texture": "#0"}, + "south": {"uv": [13, 8, 13.25, 8.25], "texture": "#0"}, + "west": {"uv": [12.25, 10, 12.75, 10.25], "texture": "#0"}, + "up": {"uv": [10.75, 12.75, 10.5, 12.25], "texture": "#0"}, + "down": {"uv": [11, 12.25, 10.75, 12.75], "texture": "#0"} + } + }, + { + "from": [1.5, 7.85, 6.3], + "to": [2, 8.35, 7.8], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [8.25, 13, 8.5, 13.25], "texture": "#0"}, + "east": {"uv": [12.25, 10.25, 12.75, 10.5], "texture": "#0"}, + "south": {"uv": [13, 8.25, 13.25, 8.5], "texture": "#0"}, + "west": {"uv": [12.25, 11, 12.75, 11.25], "texture": "#0"}, + "up": {"uv": [12.5, 11.75, 12.25, 11.25], "texture": "#0"}, + "down": {"uv": [12.5, 11.75, 12.25, 12.25], "texture": "#0"} + } + }, + { + "from": [1.5, 8.35, 7.3], + "to": [2, 8.85, 7.8], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [8.5, 13, 8.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 8.5, 13.25, 8.75], "texture": "#0"}, + "south": {"uv": [8.75, 13, 9, 13.25], "texture": "#0"}, + "west": {"uv": [13, 8.75, 13.25, 9], "texture": "#0"}, + "up": {"uv": [9.25, 13.25, 9, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9, 13, 9.25], "texture": "#0"} + } + }, + { + "from": [1.5, 7.35, 7.3], + "to": [2, 7.85, 7.8], + "rotation": {"angle": 0, "axis": "y", "origin": [3.6, 8.1, 8.05]}, + "faces": { + "north": {"uv": [9.25, 13, 9.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 9.25, 13.25, 9.5], "texture": "#0"}, + "south": {"uv": [9.5, 13, 9.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 9.5, 13.25, 9.75], "texture": "#0"}, + "up": {"uv": [10, 13.25, 9.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.75, 13, 10], "texture": "#0"} + } + }, + { + "from": [6.125, 14.05, 6.225], + "to": [6.625, 14.55, 6.725], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [10, 13, 10.25, 13.25], "texture": "#0"}, + "east": {"uv": [13, 10, 13.25, 10.25], "texture": "#0"}, + "south": {"uv": [10.25, 13, 10.5, 13.25], "texture": "#0"}, + "west": {"uv": [13, 10.25, 13.25, 10.5], "texture": "#0"}, + "up": {"uv": [10.75, 13.25, 10.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 10.5, 13, 10.75], "texture": "#0"} + } + }, + { + "from": [5.625, 14.05, 5.225], + "to": [6.125, 14.55, 7.725], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [10.75, 13, 11, 13.25], "texture": "#0"}, + "east": {"uv": [12.25, 12.25, 12.75, 12.5], "texture": "#0"}, + "south": {"uv": [13, 10.75, 13.25, 11], "texture": "#0"}, + "west": {"uv": [12.5, 0, 13, 0.25], "texture": "#0"}, + "up": {"uv": [12.75, 0.75, 12.5, 0.25], "texture": "#0"}, + "down": {"uv": [1.5, 12.5, 1.25, 13], "texture": "#0"} + } + }, + { + "from": [5.125, 14.05, 6.225], + "to": [5.625, 14.55, 6.725], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [11, 13, 11.25, 13.25], "texture": "#0"}, + "east": {"uv": [13, 11, 13.25, 11.25], "texture": "#0"}, + "south": {"uv": [11.25, 13, 11.5, 13.25], "texture": "#0"}, + "west": {"uv": [13, 11.25, 13.25, 11.5], "texture": "#0"}, + "up": {"uv": [11.75, 13.25, 11.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 11.5, 13, 11.75], "texture": "#0"} + } + }, + { + "from": [9.675, 14.05, 8.925], + "to": [10.175, 14.55, 10.425], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [11.75, 13, 12, 13.25], "texture": "#0"}, + "east": {"uv": [12.5, 0.75, 13, 1], "texture": "#0"}, + "south": {"uv": [13, 11.75, 13.25, 12], "texture": "#0"}, + "west": {"uv": [2, 12.5, 2.5, 12.75], "texture": "#0"}, + "up": {"uv": [1.75, 13, 1.5, 12.5], "texture": "#0"}, + "down": {"uv": [2.75, 12.5, 2.5, 13], "texture": "#0"} + } + }, + { + "from": [7.775, 14.05, 5.775], + "to": [8.275, 14.55, 10.275], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [12, 13, 12.25, 13.25], "texture": "#0"}, + "east": {"uv": [10.75, 5.25, 11.75, 5.5], "texture": "#0"}, + "south": {"uv": [13, 12, 13.25, 12.25], "texture": "#0"}, + "west": {"uv": [10.75, 5.5, 11.75, 5.75], "texture": "#0"}, + "up": {"uv": [11.25, 3.25, 11, 2.25], "texture": "#0"}, + "down": {"uv": [11.25, 7.25, 11, 8.25], "texture": "#0"} + } + }, + { + "from": [8.275, 14.05, 7.275], + "to": [8.775, 14.55, 7.775], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [12.25, 13, 12.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 12.25, 13.25, 12.5], "texture": "#0"}, + "south": {"uv": [12.5, 13, 12.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 12.5, 13.25, 12.75], "texture": "#0"}, + "up": {"uv": [13, 13.25, 12.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 12.75, 13, 13], "texture": "#0"} + } + }, + { + "from": [8.275, 14.05, 8.275], + "to": [8.775, 14.55, 8.775], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [13, 13, 13.25, 13.25], "texture": "#0"}, + "east": {"uv": [0, 13.25, 0.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 0, 13.5, 0.25], "texture": "#0"}, + "west": {"uv": [0.25, 13.25, 0.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 0.5, 13.25, 0.25], "texture": "#0"}, + "down": {"uv": [0.75, 13.25, 0.5, 13.5], "texture": "#0"} + } + }, + { + "from": [8.275, 14.05, 7.775], + "to": [9.775, 14.55, 8.275], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [2.75, 12.5, 3.25, 12.75], "texture": "#0"}, + "east": {"uv": [13.25, 0.5, 13.5, 0.75], "texture": "#0"}, + "south": {"uv": [12.5, 2.75, 13, 3], "texture": "#0"}, + "west": {"uv": [0.75, 13.25, 1, 13.5], "texture": "#0"}, + "up": {"uv": [13, 3.25, 12.5, 3], "texture": "#0"}, + "down": {"uv": [13, 3.5, 12.5, 3.75], "texture": "#0"} + } + }, + { + "from": [6.275, 14.05, 7.775], + "to": [7.775, 14.55, 8.275], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [12.5, 3.75, 13, 4], "texture": "#0"}, + "east": {"uv": [13.25, 0.75, 13.5, 1], "texture": "#0"}, + "south": {"uv": [4, 12.5, 4.5, 12.75], "texture": "#0"}, + "west": {"uv": [1, 13.25, 1.25, 13.5], "texture": "#0"}, + "up": {"uv": [5, 12.75, 4.5, 12.5], "texture": "#0"}, + "down": {"uv": [13, 4.5, 12.5, 4.75], "texture": "#0"} + } + }, + { + "from": [7.275, 14.05, 7.275], + "to": [7.775, 14.55, 7.775], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [13.25, 1, 13.5, 1.25], "texture": "#0"}, + "east": {"uv": [1.25, 13.25, 1.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 1.25, 13.5, 1.5], "texture": "#0"}, + "west": {"uv": [1.5, 13.25, 1.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 1.75, 13.25, 1.5], "texture": "#0"}, + "down": {"uv": [2, 13.25, 1.75, 13.5], "texture": "#0"} + } + }, + { + "from": [7.275, 14.05, 8.275], + "to": [7.775, 14.55, 8.775], + "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 14.3, 8.075]}, + "faces": { + "north": {"uv": [13.25, 1.75, 13.5, 2], "texture": "#0"}, + "east": {"uv": [2, 13.25, 2.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 2, 13.5, 2.25], "texture": "#0"}, + "west": {"uv": [2.25, 13.25, 2.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 2.5, 13.25, 2.25], "texture": "#0"}, + "down": {"uv": [2.75, 13.25, 2.5, 13.5], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [ + { + "name": "side1", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [41, 42, 43] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [44] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [45, 46, 47, 48, 49, 50, 51] + } + ] + }, + { + "name": "side2", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [52, 53, 54] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [55] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [56, 57, 58, 59, 60, 61, 62] + } + ] + }, + { + "name": "side3", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [63, 64, 65] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [66] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [67, 68, 69, 70, 71, 72, 73] + } + ] + }, + { + "name": "side4", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [74, 75, 76] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [77] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [78, 79, 80, 81, 82, 83, 84] + } + ] + }, + { + "name": "top", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [85, 86, 87] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [88] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [89, 90, 91, 92, 93, 94, 95] + } + ] + } + ] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_2.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_2.json new file mode 100644 index 00000000..1b4cc66f --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_2.json @@ -0,0 +1,1802 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_fortune_2", + "particle": "utilitiesinexcess:upgrade_fortune_2" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 4.5, 11, 5], "texture": "#0"}, + "east": {"uv": [11.75, 4.5, 12, 5], "texture": "#0"}, + "south": {"uv": [10.5, 5, 11, 5.5], "texture": "#0"}, + "west": {"uv": [11.75, 5, 12, 5.5], "texture": "#0"}, + "up": {"uv": [12.25, 7.5, 11.75, 7.25], "texture": "#0"}, + "down": {"uv": [12.25, 7.5, 11.75, 7.75], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 7.75, 12, 8.25], "texture": "#0"}, + "east": {"uv": [11.75, 8.25, 12, 8.75], "texture": "#0"}, + "south": {"uv": [11.75, 8.75, 12, 9.25], "texture": "#0"}, + "west": {"uv": [11.75, 11, 12, 11.5], "texture": "#0"}, + "up": {"uv": [10.75, 4, 10.5, 3.75], "texture": "#0"}, + "down": {"uv": [9.25, 12.75, 9, 13], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 11.75, 11.75, 12.25], "texture": "#0"}, + "east": {"uv": [11.75, 11.5, 12, 12], "texture": "#0"}, + "south": {"uv": [12, 1.75, 12.25, 2.25], "texture": "#0"}, + "west": {"uv": [2, 12, 2.25, 12.5], "texture": "#0"}, + "up": {"uv": [13, 9.25, 12.75, 9], "texture": "#0"}, + "down": {"uv": [9.5, 12.75, 9.25, 13], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.25, 12, 2.5, 12.5], "texture": "#0"}, + "east": {"uv": [12, 2.25, 12.25, 2.75], "texture": "#0"}, + "south": {"uv": [2.5, 12, 2.75, 12.5], "texture": "#0"}, + "west": {"uv": [2.75, 12, 3, 12.5], "texture": "#0"}, + "up": {"uv": [13, 9.5, 12.75, 9.25], "texture": "#0"}, + "down": {"uv": [13, 9.75, 12.75, 10], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 8.75, 11.75, 9.25], "texture": "#0"}, + "east": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "south": {"uv": [3.25, 11.75, 3.5, 12.25], "texture": "#0"}, + "west": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "up": {"uv": [12, 3.75, 11.75, 3.25], "texture": "#0"}, + "down": {"uv": [3.75, 11.75, 3.5, 12.25], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 2.75, 12.25, 3.25], "texture": "#0"}, + "east": {"uv": [12, 3.25, 12.25, 3.75], "texture": "#0"}, + "south": {"uv": [4, 12, 4.25, 12.5], "texture": "#0"}, + "west": {"uv": [4.25, 12, 4.5, 12.5], "texture": "#0"}, + "up": {"uv": [10.25, 13, 10, 12.75], "texture": "#0"}, + "down": {"uv": [10.5, 12.75, 10.25, 13], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.5, 12, 4.75, 12.5], "texture": "#0"}, + "east": {"uv": [12, 4.5, 12.25, 5], "texture": "#0"}, + "south": {"uv": [4.75, 12, 5, 12.5], "texture": "#0"}, + "west": {"uv": [5, 12, 5.25, 12.5], "texture": "#0"}, + "up": {"uv": [10.75, 13, 10.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 10.5, 12.75, 10.75], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 5, 12.25, 5.5], "texture": "#0"}, + "east": {"uv": [12, 6, 12.25, 6.5], "texture": "#0"}, + "south": {"uv": [12, 6.5, 12.25, 7], "texture": "#0"}, + "west": {"uv": [7.25, 12, 7.5, 12.5], "texture": "#0"}, + "up": {"uv": [13, 11, 12.75, 10.75], "texture": "#0"}, + "down": {"uv": [11.25, 12.75, 11, 13], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 7.25, 11, 7.75], "texture": "#0"}, + "east": {"uv": [7.5, 12, 7.75, 12.5], "texture": "#0"}, + "south": {"uv": [10.5, 7.75, 11, 8.25], "texture": "#0"}, + "west": {"uv": [7.75, 12, 8, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 7.25, 12, 7], "texture": "#0"}, + "down": {"uv": [12.5, 7.75, 12, 8], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 12, 8.25, 12.5], "texture": "#0"}, + "east": {"uv": [12, 8, 12.25, 8.5], "texture": "#0"}, + "south": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "west": {"uv": [12, 8.5, 12.25, 9], "texture": "#0"}, + "up": {"uv": [13, 11.25, 12.75, 11], "texture": "#0"}, + "down": {"uv": [11.5, 12.75, 11.25, 13], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 9, 12.25, 9.5], "texture": "#0"}, + "east": {"uv": [12, 9.5, 12.25, 10], "texture": "#0"}, + "south": {"uv": [12, 11, 12.25, 11.5], "texture": "#0"}, + "west": {"uv": [12, 11.5, 12.25, 12], "texture": "#0"}, + "up": {"uv": [13, 11.5, 12.75, 11.25], "texture": "#0"}, + "down": {"uv": [13, 11.5, 12.75, 11.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 12, 12, 12.5], "texture": "#0"}, + "east": {"uv": [12, 12, 12.25, 12.5], "texture": "#0"}, + "south": {"uv": [12.25, 0, 12.5, 0.5], "texture": "#0"}, + "west": {"uv": [12.25, 0.5, 12.5, 1], "texture": "#0"}, + "up": {"uv": [12, 13, 11.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 11.75, 12.75, 12], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 1, 12.5, 1.5], "texture": "#0"}, + "east": {"uv": [10.5, 10, 11, 10.5], "texture": "#0"}, + "south": {"uv": [12.25, 1.5, 12.5, 2], "texture": "#0"}, + "west": {"uv": [10.5, 10.5, 11, 11], "texture": "#0"}, + "up": {"uv": [12.5, 2.5, 12.25, 2], "texture": "#0"}, + "down": {"uv": [12.5, 2.5, 12.25, 3], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 3, 12.5, 3.5], "texture": "#0"}, + "east": {"uv": [3.25, 12.25, 3.5, 12.75], "texture": "#0"}, + "south": {"uv": [3.5, 12.25, 3.75, 12.75], "texture": "#0"}, + "west": {"uv": [12.25, 3.5, 12.5, 4], "texture": "#0"}, + "up": {"uv": [12.25, 13, 12, 12.75], "texture": "#0"}, + "down": {"uv": [13, 12.25, 12.75, 12.5], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 4, 12.5, 4.5], "texture": "#0"}, + "east": {"uv": [12.25, 4.5, 12.5, 5], "texture": "#0"}, + "south": {"uv": [12.25, 5, 12.5, 5.5], "texture": "#0"}, + "west": {"uv": [5.25, 12.25, 5.5, 12.75], "texture": "#0"}, + "up": {"uv": [12.75, 13, 12.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 12.5, 12.75, 12.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.5, 12.25, 5.75, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 5.5, 12.5, 6], "texture": "#0"}, + "south": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "west": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "up": {"uv": [13, 13, 12.75, 12.75], "texture": "#0"}, + "down": {"uv": [0.25, 13, 0, 13.25], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 0, 11.25, 0.5], "texture": "#0"}, + "east": {"uv": [12.25, 6, 12.5, 6.5], "texture": "#0"}, + "south": {"uv": [10.75, 0.5, 11.25, 1], "texture": "#0"}, + "west": {"uv": [12.25, 6.5, 12.5, 7], "texture": "#0"}, + "up": {"uv": [12.75, 7.5, 12.25, 7.25], "texture": "#0"}, + "down": {"uv": [12.75, 7.5, 12.25, 7.75], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 8, 12.75, 8.25], "texture": "#0"}, + "east": {"uv": [0.25, 13, 0.5, 13.25], "texture": "#0"}, + "south": {"uv": [12.25, 8.25, 12.75, 8.5], "texture": "#0"}, + "west": {"uv": [0.5, 13, 0.75, 13.25], "texture": "#0"}, + "up": {"uv": [9, 12.5, 8.5, 12.25], "texture": "#0"}, + "down": {"uv": [12.75, 8.5, 12.25, 8.75], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 8.75, 12.5, 9.25], "texture": "#0"}, + "east": {"uv": [10.75, 1, 11.25, 1.5], "texture": "#0"}, + "south": {"uv": [9, 12.25, 9.25, 12.75], "texture": "#0"}, + "west": {"uv": [10.75, 1.5, 11.25, 2], "texture": "#0"}, + "up": {"uv": [9.5, 12.75, 9.25, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 9.25, 12.25, 9.75], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 0.5, 13.25, 0.75], "texture": "#0"}, + "east": {"uv": [9.5, 12.25, 10, 12.5], "texture": "#0"}, + "south": {"uv": [0.75, 13, 1, 13.25], "texture": "#0"}, + "west": {"uv": [12.25, 9.75, 12.75, 10], "texture": "#0"}, + "up": {"uv": [10.25, 12.75, 10, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 10, 12.25, 10.5], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 2, 11.25, 2.5], "texture": "#0"}, + "east": {"uv": [10.25, 12.25, 10.5, 12.75], "texture": "#0"}, + "south": {"uv": [10.75, 2.5, 11.25, 3], "texture": "#0"}, + "west": {"uv": [10.5, 12.25, 10.75, 12.75], "texture": "#0"}, + "up": {"uv": [12.75, 10.75, 12.25, 10.5], "texture": "#0"}, + "down": {"uv": [11.25, 12.25, 10.75, 12.5], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 10.75, 12.75, 11], "texture": "#0"}, + "east": {"uv": [13, 0.75, 13.25, 1], "texture": "#0"}, + "south": {"uv": [12.25, 11, 12.75, 11.25], "texture": "#0"}, + "west": {"uv": [1, 13, 1.25, 13.25], "texture": "#0"}, + "up": {"uv": [11.75, 12.5, 11.25, 12.25], "texture": "#0"}, + "down": {"uv": [12.75, 11.25, 12.25, 11.5], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 11.5, 12.5, 12], "texture": "#0"}, + "east": {"uv": [10.75, 3, 11.25, 3.5], "texture": "#0"}, + "south": {"uv": [12.25, 12, 12.5, 12.5], "texture": "#0"}, + "west": {"uv": [10.75, 3.5, 11.25, 4], "texture": "#0"}, + "up": {"uv": [0.25, 13, 0, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 0, 12.5, 0.5], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 1, 13.25, 1.25], "texture": "#0"}, + "east": {"uv": [0.25, 12.5, 0.75, 12.75], "texture": "#0"}, + "south": {"uv": [1.25, 13, 1.5, 13.25], "texture": "#0"}, + "west": {"uv": [12.5, 0.5, 13, 0.75], "texture": "#0"}, + "up": {"uv": [1, 13, 0.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 0.75, 12.5, 1.25], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [13, 1.25, 13.25, 1.5], "texture": "#0"}, + "east": {"uv": [10.5, 5.5, 11.5, 5.75], "texture": "#0"}, + "south": {"uv": [1.5, 13, 1.75, 13.25], "texture": "#0"}, + "west": {"uv": [10.5, 8.25, 11.5, 8.5], "texture": "#0"}, + "up": {"uv": [4, 11.5, 3.75, 10.5], "texture": "#0"}, + "down": {"uv": [11.25, 4.5, 11, 5.5], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1, 12.5, 1.25, 13], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [1.25, 12.5, 1.5, 13], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [11.25, 8.25, 11, 7.25], "texture": "#0"}, + "down": {"uv": [11.25, 10, 11, 11], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 11, 11.5, 11.25], "texture": "#0"}, + "east": {"uv": [13, 1.5, 13.25, 1.75], "texture": "#0"}, + "south": {"uv": [11.25, 0, 12.25, 0.25], "texture": "#0"}, + "west": {"uv": [1.75, 13, 2, 13.25], "texture": "#0"}, + "up": {"uv": [12.25, 0.5, 11.25, 0.25], "texture": "#0"}, + "down": {"uv": [12.25, 0.5, 11.25, 0.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [12.5, 1.25, 12.75, 1.75], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [1.5, 12.5, 1.75, 13], "texture": "#0"}, + "up": {"uv": [12.25, 1, 11.25, 0.75], "texture": "#0"}, + "down": {"uv": [12.25, 1, 11.25, 1.25], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 13, 2.25, 13.25], "texture": "#0"}, + "east": {"uv": [11.25, 1.25, 12.25, 1.5], "texture": "#0"}, + "south": {"uv": [2.25, 13, 2.5, 13.25], "texture": "#0"}, + "west": {"uv": [11.25, 1.5, 12.25, 1.75], "texture": "#0"}, + "up": {"uv": [11.5, 2.75, 11.25, 1.75], "texture": "#0"}, + "down": {"uv": [11.5, 2.75, 11.25, 3.75], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1.75, 12.5, 2, 13], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [12.5, 1.75, 12.75, 2.25], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [11.5, 5.5, 11.25, 4.5], "texture": "#0"}, + "down": {"uv": [11.5, 7.25, 11.25, 8.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 3.75, 12.25, 4], "texture": "#0"}, + "east": {"uv": [13, 2.25, 13.25, 2.5], "texture": "#0"}, + "south": {"uv": [11.25, 10, 12.25, 10.25], "texture": "#0"}, + "west": {"uv": [13, 2.5, 13.25, 2.75], "texture": "#0"}, + "up": {"uv": [12.25, 10.5, 11.25, 10.25], "texture": "#0"}, + "down": {"uv": [11.5, 11.25, 10.5, 11.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [2, 12.5, 2.25, 13], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [2.25, 12.5, 2.5, 13], "texture": "#0"}, + "up": {"uv": [12.25, 10.75, 11.25, 10.5], "texture": "#0"}, + "down": {"uv": [12.25, 10.75, 11.25, 11], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [7.15, 5.75, 14], + "to": [7.65, 6.25, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [13, 2.75, 13.25, 3], "texture": "#0"}, + "east": {"uv": [3, 13, 3.25, 13.25], "texture": "#0"}, + "south": {"uv": [13, 3, 13.25, 3.25], "texture": "#0"}, + "west": {"uv": [3.25, 13, 3.5, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 3.5, 13, 3.25], "texture": "#0"}, + "down": {"uv": [3.75, 13, 3.5, 13.25], "texture": "#0"} + } + }, + { + "from": [7.65, 4.75, 14], + "to": [8.15, 7.25, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [11.5, 7.25, 11.75, 8], "texture": "#0"}, + "east": {"uv": [11.5, 8, 11.75, 8.75], "texture": "#0"}, + "south": {"uv": [8.5, 11.5, 8.75, 12.25], "texture": "#0"}, + "west": {"uv": [8.75, 11.5, 9, 12.25], "texture": "#0"}, + "up": {"uv": [13.25, 3.75, 13, 3.5], "texture": "#0"}, + "down": {"uv": [4, 13, 3.75, 13.25], "texture": "#0"} + } + }, + { + "from": [8.15, 5.75, 14], + "to": [8.65, 6.25, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [13, 3.75, 13.25, 4], "texture": "#0"}, + "east": {"uv": [4, 13, 4.25, 13.25], "texture": "#0"}, + "south": {"uv": [13, 4, 13.25, 4.25], "texture": "#0"}, + "west": {"uv": [4.25, 13, 4.5, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 4.5, 13, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13, 4.5, 13.25], "texture": "#0"} + } + }, + { + "from": [6.45, 8.95, 14], + "to": [7.95, 9.45, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [13, 4.5, 13.25, 4.75], "texture": "#0"}, + "east": {"uv": [4.75, 13, 5, 13.25], "texture": "#0"}, + "south": {"uv": [13, 4.75, 13.25, 5], "texture": "#0"}, + "west": {"uv": [5, 13, 5.25, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 5.25, 13, 5], "texture": "#0"}, + "down": {"uv": [5.5, 13, 5.25, 13.25], "texture": "#0"} + } + }, + { + "from": [5.95, 6.95, 14], + "to": [6.45, 11.45, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [0, 11.5, 0.25, 12.5], "texture": "#0"}, + "east": {"uv": [0.25, 11.5, 0.5, 12.5], "texture": "#0"}, + "south": {"uv": [0.5, 11.5, 0.75, 12.5], "texture": "#0"}, + "west": {"uv": [0.75, 11.5, 1, 12.5], "texture": "#0"}, + "up": {"uv": [13.25, 5.5, 13, 5.25], "texture": "#0"}, + "down": {"uv": [5.75, 13, 5.5, 13.25], "texture": "#0"} + } + }, + { + "from": [4.45, 8.95, 14], + "to": [5.95, 9.45, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [12.5, 2.25, 13, 2.5], "texture": "#0"}, + "east": {"uv": [13, 5.5, 13.25, 5.75], "texture": "#0"}, + "south": {"uv": [2.5, 12.5, 3, 12.75], "texture": "#0"}, + "west": {"uv": [5.75, 13, 6, 13.25], "texture": "#0"}, + "up": {"uv": [13, 2.75, 12.5, 2.5], "texture": "#0"}, + "down": {"uv": [13, 2.75, 12.5, 3], "texture": "#0"} + } + }, + { + "from": [5.45, 9.45, 14], + "to": [5.95, 9.95, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [13, 5.75, 13.25, 6], "texture": "#0"}, + "east": {"uv": [6, 13, 6.25, 13.25], "texture": "#0"}, + "south": {"uv": [13, 6, 13.25, 6.25], "texture": "#0"}, + "west": {"uv": [6.25, 13, 6.5, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 6.5, 13, 6.25], "texture": "#0"}, + "down": {"uv": [6.75, 13, 6.5, 13.25], "texture": "#0"} + } + }, + { + "from": [6.45, 9.45, 14], + "to": [6.95, 9.95, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [13, 6.5, 13.25, 6.75], "texture": "#0"}, + "east": {"uv": [6.75, 13, 7, 13.25], "texture": "#0"}, + "south": {"uv": [13, 6.75, 13.25, 7], "texture": "#0"}, + "west": {"uv": [7, 13, 7.25, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 7.25, 13, 7], "texture": "#0"}, + "down": {"uv": [7.5, 13, 7.25, 13.25], "texture": "#0"} + } + }, + { + "from": [5.45, 8.45, 14], + "to": [5.95, 8.95, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [7.5, 13, 7.75, 13.25], "texture": "#0"}, + "east": {"uv": [7.75, 13, 8, 13.25], "texture": "#0"}, + "south": {"uv": [13, 7.75, 13.25, 8], "texture": "#0"}, + "west": {"uv": [8, 13, 8.25, 13.25], "texture": "#0"}, + "up": {"uv": [8.5, 13.25, 8.25, 13], "texture": "#0"}, + "down": {"uv": [8.75, 13, 8.5, 13.25], "texture": "#0"} + } + }, + { + "from": [6.45, 8.45, 14], + "to": [6.95, 8.95, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [8.75, 13, 9, 13.25], "texture": "#0"}, + "east": {"uv": [13, 8.75, 13.25, 9], "texture": "#0"}, + "south": {"uv": [9, 13, 9.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 9, 13.25, 9.25], "texture": "#0"}, + "up": {"uv": [9.5, 13.25, 9.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.25, 13, 9.5], "texture": "#0"} + } + }, + { + "from": [9.65, 6.15, 14], + "to": [10.15, 10.65, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [10.25, 4.5, 10.5, 5.75], "texture": "#0"}, + "east": {"uv": [6.25, 10.25, 6.5, 11.5], "texture": "#0"}, + "south": {"uv": [6.5, 10.25, 6.75, 11.5], "texture": "#0"}, + "west": {"uv": [6.75, 10.25, 7, 11.5], "texture": "#0"}, + "up": {"uv": [9.75, 13.25, 9.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.5, 13, 9.75], "texture": "#0"} + } + }, + { + "from": [9.15, 8.65, 14], + "to": [9.65, 9.15, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [9.75, 13, 10, 13.25], "texture": "#0"}, + "east": {"uv": [13, 9.75, 13.25, 10], "texture": "#0"}, + "south": {"uv": [10, 13, 10.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 10, 13.25, 10.25], "texture": "#0"}, + "up": {"uv": [10.5, 13.25, 10.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 10.25, 13, 10.5], "texture": "#0"} + } + }, + { + "from": [9.15, 7.65, 14], + "to": [9.65, 8.15, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [10.5, 13, 10.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 10.5, 13.25, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 13, 11, 13.25], "texture": "#0"}, + "west": {"uv": [13, 10.75, 13.25, 11], "texture": "#0"}, + "up": {"uv": [11.25, 13.25, 11, 13], "texture": "#0"}, + "down": {"uv": [13.25, 11, 13, 11.25], "texture": "#0"} + } + }, + { + "from": [8.15, 8.15, 14], + "to": [9.65, 8.65, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [12.5, 3, 13, 3.25], "texture": "#0"}, + "east": {"uv": [11.25, 13, 11.5, 13.25], "texture": "#0"}, + "south": {"uv": [12.5, 3.25, 13, 3.5], "texture": "#0"}, + "west": {"uv": [13, 11.25, 13.25, 11.5], "texture": "#0"}, + "up": {"uv": [13, 3.75, 12.5, 3.5], "texture": "#0"}, + "down": {"uv": [4.25, 12.5, 3.75, 12.75], "texture": "#0"} + } + }, + { + "from": [10.15, 8.15, 14], + "to": [11.65, 8.65, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [12.5, 3.75, 13, 4], "texture": "#0"}, + "east": {"uv": [11.5, 13, 11.75, 13.25], "texture": "#0"}, + "south": {"uv": [12.5, 4, 13, 4.25], "texture": "#0"}, + "west": {"uv": [13, 11.5, 13.25, 11.75], "texture": "#0"}, + "up": {"uv": [4.75, 12.75, 4.25, 12.5], "texture": "#0"}, + "down": {"uv": [13, 4.25, 12.5, 4.5], "texture": "#0"} + } + }, + { + "from": [10.15, 8.65, 14], + "to": [10.65, 9.15, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [11.75, 13, 12, 13.25], "texture": "#0"}, + "east": {"uv": [13, 11.75, 13.25, 12], "texture": "#0"}, + "south": {"uv": [12, 13, 12.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 12, 13.25, 12.25], "texture": "#0"}, + "up": {"uv": [12.5, 13.25, 12.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 12.25, 13, 12.5], "texture": "#0"} + } + }, + { + "from": [10.15, 7.65, 14], + "to": [10.65, 8.15, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 14.25]}, + "faces": { + "north": {"uv": [12.5, 13, 12.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 12.5, 13.25, 12.75], "texture": "#0"}, + "south": {"uv": [12.75, 13, 13, 13.25], "texture": "#0"}, + "west": {"uv": [13, 12.75, 13.25, 13], "texture": "#0"}, + "up": {"uv": [13.25, 13.25, 13, 13], "texture": "#0"}, + "down": {"uv": [0.25, 13.25, 0, 13.5], "texture": "#0"} + } + }, + { + "from": [7.15, 5.75, 1.5], + "to": [7.65, 6.25, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 0, 13.5, 0.25], "texture": "#0"}, + "east": {"uv": [0.25, 13.25, 0.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 0.25, 13.5, 0.5], "texture": "#0"}, + "west": {"uv": [0.5, 13.25, 0.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 0.75, 13.25, 0.5], "texture": "#0"}, + "down": {"uv": [1, 13.25, 0.75, 13.5], "texture": "#0"} + } + }, + { + "from": [7.65, 4.75, 1.5], + "to": [8.15, 7.25, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [9, 11.5, 9.25, 12.25], "texture": "#0"}, + "east": {"uv": [9.25, 11.5, 9.5, 12.25], "texture": "#0"}, + "south": {"uv": [9.5, 11.5, 9.75, 12.25], "texture": "#0"}, + "west": {"uv": [9.75, 11.5, 10, 12.25], "texture": "#0"}, + "up": {"uv": [13.5, 1, 13.25, 0.75], "texture": "#0"}, + "down": {"uv": [1.25, 13.25, 1, 13.5], "texture": "#0"} + } + }, + { + "from": [8.15, 5.75, 1.5], + "to": [8.65, 6.25, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 1, 13.5, 1.25], "texture": "#0"}, + "east": {"uv": [1.25, 13.25, 1.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 1.25, 13.5, 1.5], "texture": "#0"}, + "west": {"uv": [1.5, 13.25, 1.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 1.75, 13.25, 1.5], "texture": "#0"}, + "down": {"uv": [2, 13.25, 1.75, 13.5], "texture": "#0"} + } + }, + { + "from": [6.45, 8.95, 1.5], + "to": [7.95, 9.45, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 1.75, 13.5, 2], "texture": "#0"}, + "east": {"uv": [2, 13.25, 2.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 2, 13.5, 2.25], "texture": "#0"}, + "west": {"uv": [2.25, 13.25, 2.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 2.5, 13.25, 2.25], "texture": "#0"}, + "down": {"uv": [2.75, 13.25, 2.5, 13.5], "texture": "#0"} + } + }, + { + "from": [5.95, 6.95, 1.5], + "to": [6.45, 11.45, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [1, 11.5, 1.25, 12.5], "texture": "#0"}, + "east": {"uv": [1.25, 11.5, 1.5, 12.5], "texture": "#0"}, + "south": {"uv": [1.5, 11.5, 1.75, 12.5], "texture": "#0"}, + "west": {"uv": [1.75, 11.5, 2, 12.5], "texture": "#0"}, + "up": {"uv": [13.5, 2.75, 13.25, 2.5], "texture": "#0"}, + "down": {"uv": [3, 13.25, 2.75, 13.5], "texture": "#0"} + } + }, + { + "from": [4.45, 8.95, 1.5], + "to": [5.95, 9.45, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [12.5, 4.5, 13, 4.75], "texture": "#0"}, + "east": {"uv": [13.25, 2.75, 13.5, 3], "texture": "#0"}, + "south": {"uv": [4.75, 12.5, 5.25, 12.75], "texture": "#0"}, + "west": {"uv": [3, 13.25, 3.25, 13.5], "texture": "#0"}, + "up": {"uv": [13, 5, 12.5, 4.75], "texture": "#0"}, + "down": {"uv": [13, 5, 12.5, 5.25], "texture": "#0"} + } + }, + { + "from": [5.45, 9.45, 1.5], + "to": [5.95, 9.95, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 3, 13.5, 3.25], "texture": "#0"}, + "east": {"uv": [3.25, 13.25, 3.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 3.25, 13.5, 3.5], "texture": "#0"}, + "west": {"uv": [3.5, 13.25, 3.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 3.75, 13.25, 3.5], "texture": "#0"}, + "down": {"uv": [4, 13.25, 3.75, 13.5], "texture": "#0"} + } + }, + { + "from": [6.45, 9.45, 1.5], + "to": [6.95, 9.95, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 3.75, 13.5, 4], "texture": "#0"}, + "east": {"uv": [4, 13.25, 4.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 4, 13.5, 4.25], "texture": "#0"}, + "west": {"uv": [4.25, 13.25, 4.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 4.5, 13.25, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13.25, 4.5, 13.5], "texture": "#0"} + } + }, + { + "from": [5.45, 8.45, 1.5], + "to": [5.95, 8.95, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 4.5, 13.5, 4.75], "texture": "#0"}, + "east": {"uv": [4.75, 13.25, 5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 4.75, 13.5, 5], "texture": "#0"}, + "west": {"uv": [5, 13.25, 5.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 5.25, 13.25, 5], "texture": "#0"}, + "down": {"uv": [5.5, 13.25, 5.25, 13.5], "texture": "#0"} + } + }, + { + "from": [6.45, 8.45, 1.5], + "to": [6.95, 8.95, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 5.25, 13.5, 5.5], "texture": "#0"}, + "east": {"uv": [5.5, 13.25, 5.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 5.5, 13.5, 5.75], "texture": "#0"}, + "west": {"uv": [5.75, 13.25, 6, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 6, 13.25, 5.75], "texture": "#0"}, + "down": {"uv": [6.25, 13.25, 6, 13.5], "texture": "#0"} + } + }, + { + "from": [9.65, 6.15, 1.5], + "to": [10.15, 10.65, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [7, 10.25, 7.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 7.25, 10.5, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 10.25, 8.75, 11.5], "texture": "#0"}, + "west": {"uv": [8.75, 10.25, 9, 11.5], "texture": "#0"}, + "up": {"uv": [13.5, 6.25, 13.25, 6], "texture": "#0"}, + "down": {"uv": [6.5, 13.25, 6.25, 13.5], "texture": "#0"} + } + }, + { + "from": [9.15, 8.65, 1.5], + "to": [9.65, 9.15, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 6.25, 13.5, 6.5], "texture": "#0"}, + "east": {"uv": [6.5, 13.25, 6.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 6.5, 13.5, 6.75], "texture": "#0"}, + "west": {"uv": [6.75, 13.25, 7, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 7, 13.25, 6.75], "texture": "#0"}, + "down": {"uv": [7.25, 13.25, 7, 13.5], "texture": "#0"} + } + }, + { + "from": [9.15, 7.65, 1.5], + "to": [9.65, 8.15, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 7, 13.5, 7.25], "texture": "#0"}, + "east": {"uv": [7.25, 13.25, 7.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 7.25, 13.5, 7.5], "texture": "#0"}, + "west": {"uv": [7.5, 13.25, 7.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 7.75, 13.25, 7.5], "texture": "#0"}, + "down": {"uv": [8, 13.25, 7.75, 13.5], "texture": "#0"} + } + }, + { + "from": [8.15, 8.15, 1.5], + "to": [9.65, 8.65, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [12.5, 5.25, 13, 5.5], "texture": "#0"}, + "east": {"uv": [13.25, 7.75, 13.5, 8], "texture": "#0"}, + "south": {"uv": [12.5, 5.5, 13, 5.75], "texture": "#0"}, + "west": {"uv": [8, 13.25, 8.25, 13.5], "texture": "#0"}, + "up": {"uv": [13, 6, 12.5, 5.75], "texture": "#0"}, + "down": {"uv": [13, 6, 12.5, 6.25], "texture": "#0"} + } + }, + { + "from": [10.15, 8.15, 1.5], + "to": [11.65, 8.65, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [6.25, 12.5, 6.75, 12.75], "texture": "#0"}, + "east": {"uv": [13.25, 8, 13.5, 8.25], "texture": "#0"}, + "south": {"uv": [12.5, 6.25, 13, 6.5], "texture": "#0"}, + "west": {"uv": [8.25, 13.25, 8.5, 13.5], "texture": "#0"}, + "up": {"uv": [13, 6.75, 12.5, 6.5], "texture": "#0"}, + "down": {"uv": [7.25, 12.5, 6.75, 12.75], "texture": "#0"} + } + }, + { + "from": [10.15, 8.65, 1.5], + "to": [10.65, 9.15, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 8.25, 13.5, 8.5], "texture": "#0"}, + "east": {"uv": [8.5, 13.25, 8.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 8.5, 13.5, 8.75], "texture": "#0"}, + "west": {"uv": [8.75, 13.25, 9, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 9, 13.25, 8.75], "texture": "#0"}, + "down": {"uv": [9.25, 13.25, 9, 13.5], "texture": "#0"} + } + }, + { + "from": [10.15, 7.65, 1.5], + "to": [10.65, 8.15, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 7.85, 1.75]}, + "faces": { + "north": {"uv": [13.25, 9, 13.5, 9.25], "texture": "#0"}, + "east": {"uv": [9.25, 13.25, 9.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 9.25, 13.5, 9.5], "texture": "#0"}, + "west": {"uv": [9.5, 13.25, 9.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 9.75, 13.25, 9.5], "texture": "#0"}, + "down": {"uv": [10, 13.25, 9.75, 13.5], "texture": "#0"} + } + }, + { + "from": [1.55, 5.75, 8.4], + "to": [2.05, 6.25, 8.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 9.75, 13.5, 10], "texture": "#0"}, + "east": {"uv": [10, 13.25, 10.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 10, 13.5, 10.25], "texture": "#0"}, + "west": {"uv": [10.25, 13.25, 10.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 10.5, 13.25, 10.25], "texture": "#0"}, + "down": {"uv": [10.75, 13.25, 10.5, 13.5], "texture": "#0"} + } + }, + { + "from": [1.55, 4.75, 7.9], + "to": [2.05, 7.25, 8.4], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [10, 11.5, 10.25, 12.25], "texture": "#0"}, + "east": {"uv": [10.25, 11.5, 10.5, 12.25], "texture": "#0"}, + "south": {"uv": [10.5, 11.5, 10.75, 12.25], "texture": "#0"}, + "west": {"uv": [10.75, 11.5, 11, 12.25], "texture": "#0"}, + "up": {"uv": [13.5, 10.75, 13.25, 10.5], "texture": "#0"}, + "down": {"uv": [11, 13.25, 10.75, 13.5], "texture": "#0"} + } + }, + { + "from": [1.55, 5.75, 7.4], + "to": [2.05, 6.25, 7.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 10.75, 13.5, 11], "texture": "#0"}, + "east": {"uv": [11, 13.25, 11.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 11, 13.5, 11.25], "texture": "#0"}, + "west": {"uv": [11.25, 13.25, 11.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 11.5, 13.25, 11.25], "texture": "#0"}, + "down": {"uv": [11.75, 13.25, 11.5, 13.5], "texture": "#0"} + } + }, + { + "from": [1.55, 8.95, 8.1], + "to": [2.05, 9.45, 9.6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 11.5, 13.5, 11.75], "texture": "#0"}, + "east": {"uv": [12.5, 6.75, 13, 7], "texture": "#0"}, + "south": {"uv": [11.75, 13.25, 12, 13.5], "texture": "#0"}, + "west": {"uv": [12.5, 7, 13, 7.25], "texture": "#0"}, + "up": {"uv": [3.25, 13, 3, 12.5], "texture": "#0"}, + "down": {"uv": [7.5, 12.5, 7.25, 13], "texture": "#0"} + } + }, + { + "from": [1.55, 6.95, 9.6], + "to": [2.05, 11.45, 10.1], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [11.5, 1.75, 11.75, 2.75], "texture": "#0"}, + "east": {"uv": [11.5, 2.75, 11.75, 3.75], "texture": "#0"}, + "south": {"uv": [3.75, 11.5, 4, 12.5], "texture": "#0"}, + "west": {"uv": [11.5, 4.5, 11.75, 5.5], "texture": "#0"}, + "up": {"uv": [13.5, 12, 13.25, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 13.25, 12, 13.5], "texture": "#0"} + } + }, + { + "from": [1.55, 8.95, 10.1], + "to": [2.05, 9.45, 11.6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 12, 13.5, 12.25], "texture": "#0"}, + "east": {"uv": [7.5, 12.5, 8, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 13.25, 12.5, 13.5], "texture": "#0"}, + "west": {"uv": [12.5, 7.75, 13, 8], "texture": "#0"}, + "up": {"uv": [8.25, 13, 8, 12.5], "texture": "#0"}, + "down": {"uv": [8.5, 12.5, 8.25, 13], "texture": "#0"} + } + }, + { + "from": [1.55, 9.45, 10.1], + "to": [2.05, 9.95, 10.6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 12.25, 13.5, 12.5], "texture": "#0"}, + "east": {"uv": [12.5, 13.25, 12.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 12.5, 13.5, 12.75], "texture": "#0"}, + "west": {"uv": [12.75, 13.25, 13, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 13, 13.25, 12.75], "texture": "#0"}, + "down": {"uv": [13.25, 13.25, 13, 13.5], "texture": "#0"} + } + }, + { + "from": [1.55, 9.45, 9.1], + "to": [2.05, 9.95, 9.6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 13, 13.5, 13.25], "texture": "#0"}, + "east": {"uv": [13.25, 13.25, 13.5, 13.5], "texture": "#0"}, + "south": {"uv": [0, 13.5, 0.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 0, 13.75, 0.25], "texture": "#0"}, + "up": {"uv": [0.5, 13.75, 0.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0.25, 13.5, 0.5], "texture": "#0"} + } + }, + { + "from": [1.55, 8.45, 10.1], + "to": [2.05, 8.95, 10.6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [0.5, 13.5, 0.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 0.5, 13.75, 0.75], "texture": "#0"}, + "south": {"uv": [0.75, 13.5, 1, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 0.75, 13.75, 1], "texture": "#0"}, + "up": {"uv": [1.25, 13.75, 1, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1, 13.5, 1.25], "texture": "#0"} + } + }, + { + "from": [1.55, 8.45, 9.1], + "to": [2.05, 8.95, 9.6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [1.25, 13.5, 1.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 1.25, 13.75, 1.5], "texture": "#0"}, + "south": {"uv": [1.5, 13.5, 1.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 1.5, 13.75, 1.75], "texture": "#0"}, + "up": {"uv": [2, 13.75, 1.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1.75, 13.5, 2], "texture": "#0"} + } + }, + { + "from": [1.55, 6.15, 5.9], + "to": [2.05, 10.65, 6.4], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [9, 10.25, 9.25, 11.5], "texture": "#0"}, + "east": {"uv": [9.25, 10.25, 9.5, 11.5], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 9.75, 11.5], "texture": "#0"}, + "west": {"uv": [9.75, 10.25, 10, 11.5], "texture": "#0"}, + "up": {"uv": [2.25, 13.75, 2, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2, 13.5, 2.25], "texture": "#0"} + } + }, + { + "from": [1.55, 8.65, 6.4], + "to": [2.05, 9.15, 6.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [2.25, 13.5, 2.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 2.25, 13.75, 2.5], "texture": "#0"}, + "south": {"uv": [2.5, 13.5, 2.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 2.5, 13.75, 2.75], "texture": "#0"}, + "up": {"uv": [3, 13.75, 2.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2.75, 13.5, 3], "texture": "#0"} + } + }, + { + "from": [1.55, 7.65, 6.4], + "to": [2.05, 8.15, 6.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [3, 13.5, 3.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 3, 13.75, 3.25], "texture": "#0"}, + "south": {"uv": [3.25, 13.5, 3.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 3.25, 13.75, 3.5], "texture": "#0"}, + "up": {"uv": [3.75, 13.75, 3.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 3.5, 13.5, 3.75], "texture": "#0"} + } + }, + { + "from": [1.55, 8.15, 6.4], + "to": [2.05, 8.65, 7.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [3.75, 13.5, 4, 13.75], "texture": "#0"}, + "east": {"uv": [8.5, 12.5, 9, 12.75], "texture": "#0"}, + "south": {"uv": [13.5, 3.75, 13.75, 4], "texture": "#0"}, + "west": {"uv": [12.5, 8.75, 13, 9], "texture": "#0"}, + "up": {"uv": [12.75, 9.5, 12.5, 9], "texture": "#0"}, + "down": {"uv": [9.75, 12.5, 9.5, 13], "texture": "#0"} + } + }, + { + "from": [1.55, 8.15, 4.4], + "to": [2.05, 8.65, 5.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [4, 13.5, 4.25, 13.75], "texture": "#0"}, + "east": {"uv": [12.5, 9.5, 13, 9.75], "texture": "#0"}, + "south": {"uv": [13.5, 4, 13.75, 4.25], "texture": "#0"}, + "west": {"uv": [12.5, 10, 13, 10.25], "texture": "#0"}, + "up": {"uv": [10, 13, 9.75, 12.5], "texture": "#0"}, + "down": {"uv": [11, 12.5, 10.75, 13], "texture": "#0"} + } + }, + { + "from": [1.55, 8.65, 5.4], + "to": [2.05, 9.15, 5.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [4.25, 13.5, 4.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 4.25, 13.75, 4.5], "texture": "#0"}, + "south": {"uv": [4.5, 13.5, 4.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 4.5, 13.75, 4.75], "texture": "#0"}, + "up": {"uv": [5, 13.75, 4.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 4.75, 13.5, 5], "texture": "#0"} + } + }, + { + "from": [1.55, 7.65, 5.4], + "to": [2.05, 8.15, 5.9], + "rotation": {"angle": 0, "axis": "y", "origin": [1.8, 7.85, 8]}, + "faces": { + "north": {"uv": [5, 13.5, 5.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 5, 13.75, 5.25], "texture": "#0"}, + "south": {"uv": [5.25, 13.5, 5.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 5.25, 13.75, 5.5], "texture": "#0"}, + "up": {"uv": [5.75, 13.75, 5.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 5.5, 13.5, 5.75], "texture": "#0"} + } + }, + { + "from": [14.05, 5.75, 8.4], + "to": [14.55, 6.25, 8.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [5.75, 13.5, 6, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 5.75, 13.75, 6], "texture": "#0"}, + "south": {"uv": [6, 13.5, 6.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 6, 13.75, 6.25], "texture": "#0"}, + "up": {"uv": [6.5, 13.75, 6.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 6.25, 13.5, 6.5], "texture": "#0"} + } + }, + { + "from": [14.05, 4.75, 7.9], + "to": [14.55, 7.25, 8.4], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [11, 11.5, 11.25, 12.25], "texture": "#0"}, + "east": {"uv": [11.5, 11, 11.75, 11.75], "texture": "#0"}, + "south": {"uv": [11.25, 11.5, 11.5, 12.25], "texture": "#0"}, + "west": {"uv": [11.75, 1.75, 12, 2.5], "texture": "#0"}, + "up": {"uv": [6.75, 13.75, 6.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 6.5, 13.5, 6.75], "texture": "#0"} + } + }, + { + "from": [14.05, 5.75, 7.4], + "to": [14.55, 6.25, 7.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [6.75, 13.5, 7, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 6.75, 13.75, 7], "texture": "#0"}, + "south": {"uv": [7, 13.5, 7.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 7, 13.75, 7.25], "texture": "#0"}, + "up": {"uv": [7.5, 13.75, 7.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 7.25, 13.5, 7.5], "texture": "#0"} + } + }, + { + "from": [14.05, 8.95, 8.1], + "to": [14.55, 9.45, 9.6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [7.5, 13.5, 7.75, 13.75], "texture": "#0"}, + "east": {"uv": [12.5, 10.25, 13, 10.5], "texture": "#0"}, + "south": {"uv": [13.5, 7.5, 13.75, 7.75], "texture": "#0"}, + "west": {"uv": [11, 12.5, 11.5, 12.75], "texture": "#0"}, + "up": {"uv": [11.75, 13, 11.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 11.5, 12.5, 12], "texture": "#0"} + } + }, + { + "from": [14.05, 6.95, 9.6], + "to": [14.55, 11.45, 10.1], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [6.25, 11.5, 6.5, 12.5], "texture": "#0"}, + "east": {"uv": [6.5, 11.5, 6.75, 12.5], "texture": "#0"}, + "south": {"uv": [6.75, 11.5, 7, 12.5], "texture": "#0"}, + "west": {"uv": [7, 11.5, 7.25, 12.5], "texture": "#0"}, + "up": {"uv": [8, 13.75, 7.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 7.75, 13.5, 8], "texture": "#0"} + } + }, + { + "from": [14.05, 8.95, 10.1], + "to": [14.55, 9.45, 11.6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [8, 13.5, 8.25, 13.75], "texture": "#0"}, + "east": {"uv": [11.75, 12.5, 12.25, 12.75], "texture": "#0"}, + "south": {"uv": [13.5, 8, 13.75, 8.25], "texture": "#0"}, + "west": {"uv": [12.5, 12, 13, 12.25], "texture": "#0"}, + "up": {"uv": [12.5, 13, 12.25, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 12.25, 12.5, 12.75], "texture": "#0"} + } + }, + { + "from": [14.05, 9.45, 10.1], + "to": [14.55, 9.95, 10.6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [8.25, 13.5, 8.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 8.25, 13.75, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 13.5, 8.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 8.5, 13.75, 8.75], "texture": "#0"}, + "up": {"uv": [9, 13.75, 8.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 8.75, 13.5, 9], "texture": "#0"} + } + }, + { + "from": [14.05, 9.45, 9.1], + "to": [14.55, 9.95, 9.6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [9, 13.5, 9.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 9, 13.75, 9.25], "texture": "#0"}, + "south": {"uv": [9.25, 13.5, 9.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 9.25, 13.75, 9.5], "texture": "#0"}, + "up": {"uv": [9.75, 13.75, 9.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 9.5, 13.5, 9.75], "texture": "#0"} + } + }, + { + "from": [14.05, 8.45, 10.1], + "to": [14.55, 8.95, 10.6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [9.75, 13.5, 10, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 9.75, 13.75, 10], "texture": "#0"}, + "south": {"uv": [10, 13.5, 10.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 10, 13.75, 10.25], "texture": "#0"}, + "up": {"uv": [10.5, 13.75, 10.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 10.25, 13.5, 10.5], "texture": "#0"} + } + }, + { + "from": [14.05, 8.45, 9.1], + "to": [14.55, 8.95, 9.6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [10.5, 13.5, 10.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 10.5, 13.75, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 13.5, 11, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 10.75, 13.75, 11], "texture": "#0"}, + "up": {"uv": [11.25, 13.75, 11, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 11, 13.5, 11.25], "texture": "#0"} + } + }, + { + "from": [14.05, 6.15, 5.9], + "to": [14.55, 10.65, 6.4], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [10, 10.25, 10.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 10.25, 10.5, 11.5], "texture": "#0"}, + "south": {"uv": [10.5, 0, 10.75, 1.25], "texture": "#0"}, + "west": {"uv": [10.5, 1.25, 10.75, 2.5], "texture": "#0"}, + "up": {"uv": [11.5, 13.75, 11.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 11.25, 13.5, 11.5], "texture": "#0"} + } + }, + { + "from": [14.05, 8.65, 6.4], + "to": [14.55, 9.15, 6.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [11.5, 13.5, 11.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 11.5, 13.75, 11.75], "texture": "#0"}, + "south": {"uv": [11.75, 13.5, 12, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 11.75, 13.75, 12], "texture": "#0"}, + "up": {"uv": [12.25, 13.75, 12, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 12, 13.5, 12.25], "texture": "#0"} + } + }, + { + "from": [14.05, 7.65, 6.4], + "to": [14.55, 8.15, 6.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [12.25, 13.5, 12.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 12.25, 13.75, 12.5], "texture": "#0"}, + "south": {"uv": [12.5, 13.5, 12.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 12.5, 13.75, 12.75], "texture": "#0"}, + "up": {"uv": [13, 13.75, 12.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 12.75, 13.5, 13], "texture": "#0"} + } + }, + { + "from": [14.05, 8.15, 6.4], + "to": [14.55, 8.65, 7.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [13, 13.5, 13.25, 13.75], "texture": "#0"}, + "east": {"uv": [12.75, 0, 13.25, 0.25], "texture": "#0"}, + "south": {"uv": [13.5, 13, 13.75, 13.25], "texture": "#0"}, + "west": {"uv": [0.25, 12.75, 0.75, 13], "texture": "#0"}, + "up": {"uv": [13, 1.25, 12.75, 0.75], "texture": "#0"}, + "down": {"uv": [13, 1.25, 12.75, 1.75], "texture": "#0"} + } + }, + { + "from": [14.05, 8.15, 4.4], + "to": [14.55, 8.65, 5.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [13.25, 13.5, 13.5, 13.75], "texture": "#0"}, + "east": {"uv": [12.75, 0.25, 13.25, 0.5], "texture": "#0"}, + "south": {"uv": [13.5, 13.25, 13.75, 13.5], "texture": "#0"}, + "west": {"uv": [12.75, 1.75, 13.25, 2], "texture": "#0"}, + "up": {"uv": [2.75, 13.25, 2.5, 12.75], "texture": "#0"}, + "down": {"uv": [3, 12.75, 2.75, 13.25], "texture": "#0"} + } + }, + { + "from": [14.05, 8.65, 5.4], + "to": [14.55, 9.15, 5.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [13.5, 13.5, 13.75, 13.75], "texture": "#0"}, + "east": {"uv": [0, 13.75, 0.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 0, 14, 0.25], "texture": "#0"}, + "west": {"uv": [0.25, 13.75, 0.5, 14], "texture": "#0"}, + "up": {"uv": [14, 0.5, 13.75, 0.25], "texture": "#0"}, + "down": {"uv": [0.75, 13.75, 0.5, 14], "texture": "#0"} + } + }, + { + "from": [14.05, 7.65, 5.4], + "to": [14.55, 8.15, 5.9], + "rotation": {"angle": 0, "axis": "y", "origin": [14.3, 7.85, 8]}, + "faces": { + "north": {"uv": [13.75, 0.5, 14, 0.75], "texture": "#0"}, + "east": {"uv": [0.75, 13.75, 1, 14], "texture": "#0"}, + "south": {"uv": [13.75, 0.75, 14, 1], "texture": "#0"}, + "west": {"uv": [1, 13.75, 1.25, 14], "texture": "#0"}, + "up": {"uv": [14, 1.25, 13.75, 1], "texture": "#0"}, + "down": {"uv": [1.5, 13.75, 1.25, 14], "texture": "#0"} + } + }, + { + "from": [7.15, 14.1, 9.85], + "to": [7.65, 14.6, 10.35], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 1.25, 14, 1.5], "texture": "#0"}, + "east": {"uv": [1.5, 13.75, 1.75, 14], "texture": "#0"}, + "south": {"uv": [13.75, 1.5, 14, 1.75], "texture": "#0"}, + "west": {"uv": [1.75, 13.75, 2, 14], "texture": "#0"}, + "up": {"uv": [14, 2, 13.75, 1.75], "texture": "#0"}, + "down": {"uv": [2.25, 13.75, 2, 14], "texture": "#0"} + } + }, + { + "from": [7.65, 14.1, 8.85], + "to": [8.15, 14.6, 11.35], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 2, 14, 2.25], "texture": "#0"}, + "east": {"uv": [11.5, 5.5, 12.25, 5.75], "texture": "#0"}, + "south": {"uv": [2.25, 13.75, 2.5, 14], "texture": "#0"}, + "west": {"uv": [11.5, 5.75, 12.25, 6], "texture": "#0"}, + "up": {"uv": [12, 3.25, 11.75, 2.5], "texture": "#0"}, + "down": {"uv": [3.25, 11.75, 3, 12.5], "texture": "#0"} + } + }, + { + "from": [8.15, 14.1, 9.85], + "to": [8.65, 14.6, 10.35], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 2.25, 14, 2.5], "texture": "#0"}, + "east": {"uv": [2.5, 13.75, 2.75, 14], "texture": "#0"}, + "south": {"uv": [13.75, 2.5, 14, 2.75], "texture": "#0"}, + "west": {"uv": [2.75, 13.75, 3, 14], "texture": "#0"}, + "up": {"uv": [14, 3, 13.75, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 13.75, 3, 14], "texture": "#0"} + } + }, + { + "from": [6.45, 14.1, 6.65], + "to": [7.95, 14.6, 7.15], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [12.75, 2, 13.25, 2.25], "texture": "#0"}, + "east": {"uv": [13.75, 3, 14, 3.25], "texture": "#0"}, + "south": {"uv": [3.25, 12.75, 3.75, 13], "texture": "#0"}, + "west": {"uv": [3.25, 13.75, 3.5, 14], "texture": "#0"}, + "up": {"uv": [4.25, 13, 3.75, 12.75], "texture": "#0"}, + "down": {"uv": [4.75, 12.75, 4.25, 13], "texture": "#0"} + } + }, + { + "from": [5.95, 14.1, 4.65], + "to": [6.45, 14.6, 9.15], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 3.25, 14, 3.5], "texture": "#0"}, + "east": {"uv": [10.25, 5.75, 11.5, 6], "texture": "#0"}, + "south": {"uv": [3.5, 13.75, 3.75, 14], "texture": "#0"}, + "west": {"uv": [10.25, 8.5, 11.5, 8.75], "texture": "#0"}, + "up": {"uv": [10.75, 3.75, 10.5, 2.5], "texture": "#0"}, + "down": {"uv": [3.25, 10.5, 3, 11.75], "texture": "#0"} + } + }, + { + "from": [4.45, 14.1, 6.65], + "to": [5.95, 14.6, 7.15], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [4.75, 12.75, 5.25, 13], "texture": "#0"}, + "east": {"uv": [13.75, 3.5, 14, 3.75], "texture": "#0"}, + "south": {"uv": [5.25, 12.75, 5.75, 13], "texture": "#0"}, + "west": {"uv": [3.75, 13.75, 4, 14], "texture": "#0"}, + "up": {"uv": [6.25, 13, 5.75, 12.75], "texture": "#0"}, + "down": {"uv": [6.75, 12.75, 6.25, 13], "texture": "#0"} + } + }, + { + "from": [5.45, 14.1, 6.15], + "to": [5.95, 14.6, 6.65], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 3.75, 14, 4], "texture": "#0"}, + "east": {"uv": [4, 13.75, 4.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 4, 14, 4.25], "texture": "#0"}, + "west": {"uv": [4.25, 13.75, 4.5, 14], "texture": "#0"}, + "up": {"uv": [14, 4.5, 13.75, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13.75, 4.5, 14], "texture": "#0"} + } + }, + { + "from": [6.45, 14.1, 6.15], + "to": [6.95, 14.6, 6.65], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 4.5, 14, 4.75], "texture": "#0"}, + "east": {"uv": [4.75, 13.75, 5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 4.75, 14, 5], "texture": "#0"}, + "west": {"uv": [5, 13.75, 5.25, 14], "texture": "#0"}, + "up": {"uv": [14, 5.25, 13.75, 5], "texture": "#0"}, + "down": {"uv": [5.5, 13.75, 5.25, 14], "texture": "#0"} + } + }, + { + "from": [5.45, 14.1, 7.15], + "to": [5.95, 14.6, 7.65], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 5.25, 14, 5.5], "texture": "#0"}, + "east": {"uv": [5.5, 13.75, 5.75, 14], "texture": "#0"}, + "south": {"uv": [13.75, 5.5, 14, 5.75], "texture": "#0"}, + "west": {"uv": [5.75, 13.75, 6, 14], "texture": "#0"}, + "up": {"uv": [14, 6, 13.75, 5.75], "texture": "#0"}, + "down": {"uv": [6.25, 13.75, 6, 14], "texture": "#0"} + } + }, + { + "from": [6.45, 14.1, 7.15], + "to": [6.95, 14.6, 7.65], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 6, 14, 6.25], "texture": "#0"}, + "east": {"uv": [6.25, 13.75, 6.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 6.25, 14, 6.5], "texture": "#0"}, + "west": {"uv": [6.5, 13.75, 6.75, 14], "texture": "#0"}, + "up": {"uv": [14, 6.75, 13.75, 6.5], "texture": "#0"}, + "down": {"uv": [7, 13.75, 6.75, 14], "texture": "#0"} + } + }, + { + "from": [9.65, 14.1, 5.45], + "to": [10.15, 14.6, 9.95], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 6.75, 14, 7], "texture": "#0"}, + "east": {"uv": [10.25, 8.75, 11.5, 9], "texture": "#0"}, + "south": {"uv": [7, 13.75, 7.25, 14], "texture": "#0"}, + "west": {"uv": [10.25, 9, 11.5, 9.25], "texture": "#0"}, + "up": {"uv": [3.5, 11.75, 3.25, 10.5], "texture": "#0"}, + "down": {"uv": [3.75, 10.5, 3.5, 11.75], "texture": "#0"} + } + }, + { + "from": [9.15, 14.1, 6.95], + "to": [9.65, 14.6, 7.45], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 7, 14, 7.25], "texture": "#0"}, + "east": {"uv": [7.25, 13.75, 7.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 7.25, 14, 7.5], "texture": "#0"}, + "west": {"uv": [7.5, 13.75, 7.75, 14], "texture": "#0"}, + "up": {"uv": [14, 7.75, 13.75, 7.5], "texture": "#0"}, + "down": {"uv": [8, 13.75, 7.75, 14], "texture": "#0"} + } + }, + { + "from": [9.15, 14.1, 7.95], + "to": [9.65, 14.6, 8.45], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 7.75, 14, 8], "texture": "#0"}, + "east": {"uv": [8, 13.75, 8.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 8, 14, 8.25], "texture": "#0"}, + "west": {"uv": [8.25, 13.75, 8.5, 14], "texture": "#0"}, + "up": {"uv": [14, 8.5, 13.75, 8.25], "texture": "#0"}, + "down": {"uv": [8.75, 13.75, 8.5, 14], "texture": "#0"} + } + }, + { + "from": [8.15, 14.1, 7.45], + "to": [9.65, 14.6, 7.95], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [6.75, 12.75, 7.25, 13], "texture": "#0"}, + "east": {"uv": [13.75, 8.5, 14, 8.75], "texture": "#0"}, + "south": {"uv": [12.75, 7.25, 13.25, 7.5], "texture": "#0"}, + "west": {"uv": [8.75, 13.75, 9, 14], "texture": "#0"}, + "up": {"uv": [8, 13, 7.5, 12.75], "texture": "#0"}, + "down": {"uv": [13.25, 7.5, 12.75, 7.75], "texture": "#0"} + } + }, + { + "from": [10.15, 14.1, 7.45], + "to": [11.65, 14.6, 7.95], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [12.75, 8, 13.25, 8.25], "texture": "#0"}, + "east": {"uv": [13.75, 8.75, 14, 9], "texture": "#0"}, + "south": {"uv": [12.75, 8.25, 13.25, 8.5], "texture": "#0"}, + "west": {"uv": [9, 13.75, 9.25, 14], "texture": "#0"}, + "up": {"uv": [9, 13, 8.5, 12.75], "texture": "#0"}, + "down": {"uv": [13.25, 8.5, 12.75, 8.75], "texture": "#0"} + } + }, + { + "from": [10.15, 14.1, 6.95], + "to": [10.65, 14.6, 7.45], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 9, 14, 9.25], "texture": "#0"}, + "east": {"uv": [9.25, 13.75, 9.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 9.25, 14, 9.5], "texture": "#0"}, + "west": {"uv": [9.5, 13.75, 9.75, 14], "texture": "#0"}, + "up": {"uv": [14, 9.75, 13.75, 9.5], "texture": "#0"}, + "down": {"uv": [10, 13.75, 9.75, 14], "texture": "#0"} + } + }, + { + "from": [10.15, 14.1, 7.95], + "to": [10.65, 14.6, 8.45], + "rotation": {"angle": 0, "axis": "y", "origin": [8.05, 14.35, 8.25]}, + "faces": { + "north": {"uv": [13.75, 9.75, 14, 10], "texture": "#0"}, + "east": {"uv": [10, 13.75, 10.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 10, 14, 10.25], "texture": "#0"}, + "west": {"uv": [10.25, 13.75, 10.5, 14], "texture": "#0"}, + "up": {"uv": [14, 10.5, 13.75, 10.25], "texture": "#0"}, + "down": {"uv": [10.75, 13.75, 10.5, 14], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [ + { + "name": "side1", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [41, 42, 43] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [44, 45, 46, 47, 48, 49, 50] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [51, 52, 53, 54, 55, 56, 57] + } + ] + }, + { + "name": "side2", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [58, 59, 60] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [61, 62, 63, 64, 65, 66, 67] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [68, 69, 70, 71, 72, 73, 74] + } + ] + }, + { + "name": "side3", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [75, 76, 77] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [78, 79, 80, 81, 82, 83, 84] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [85, 86, 87, 88, 89, 90, 91] + } + ] + }, + { + "name": "side4", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [92, 93, 94] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [95, 96, 97, 98, 99, 100, 101] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [102, 103, 104, 105, 106, 107, 108] + } + ] + }, + { + "name": "top", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [109, 110, 111] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [112, 113, 114, 115, 116, 117, 118] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [119, 120, 121, 122, 123, 124, 125] + } + ] + } + ] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_3.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_3.json new file mode 100644 index 00000000..3528cc9f --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_fortune_3.json @@ -0,0 +1,2062 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_fortune_3", + "particle": "utilitiesinexcess:upgrade_fortune_3" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [0.5, 12, 0.75, 12.5], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [0.75, 12, 1, 12.5], "texture": "#0"}, + "up": {"uv": [11, 4, 10.5, 3.75], "texture": "#0"}, + "down": {"uv": [12.25, 11.25, 11.75, 11.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1, 12, 1.25, 12.5], "texture": "#0"}, + "east": {"uv": [12, 1, 12.25, 1.5], "texture": "#0"}, + "south": {"uv": [1.25, 12, 1.5, 12.5], "texture": "#0"}, + "west": {"uv": [12, 1.5, 12.25, 2], "texture": "#0"}, + "up": {"uv": [13.25, 2, 13, 1.75], "texture": "#0"}, + "down": {"uv": [2.25, 13, 2, 13.25], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 12, 2.25, 12.5], "texture": "#0"}, + "east": {"uv": [12, 2, 12.25, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 12, 2.5, 12.5], "texture": "#0"}, + "west": {"uv": [2.5, 12, 2.75, 12.5], "texture": "#0"}, + "up": {"uv": [2.5, 13.25, 2.25, 13], "texture": "#0"}, + "down": {"uv": [2.75, 13, 2.5, 13.25], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 2.5, 12.25, 3], "texture": "#0"}, + "east": {"uv": [2.75, 12, 3, 12.5], "texture": "#0"}, + "south": {"uv": [12, 3, 12.25, 3.5], "texture": "#0"}, + "west": {"uv": [12, 3.5, 12.25, 4], "texture": "#0"}, + "up": {"uv": [13.25, 2.75, 13, 2.5], "texture": "#0"}, + "down": {"uv": [3, 13, 2.75, 13.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4, 12, 4.25, 12.5], "texture": "#0"}, + "east": {"uv": [11.25, 0, 11.75, 0.5], "texture": "#0"}, + "south": {"uv": [4.25, 12, 4.5, 12.5], "texture": "#0"}, + "west": {"uv": [11.25, 0.5, 11.75, 1], "texture": "#0"}, + "up": {"uv": [4.75, 12.5, 4.5, 12], "texture": "#0"}, + "down": {"uv": [12.25, 4.5, 12, 5], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.75, 12, 5, 12.5], "texture": "#0"}, + "east": {"uv": [5, 12, 5.25, 12.5], "texture": "#0"}, + "south": {"uv": [12, 5, 12.25, 5.5], "texture": "#0"}, + "west": {"uv": [12, 6, 12.25, 6.5], "texture": "#0"}, + "up": {"uv": [13.25, 3, 13, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 13, 3, 13.25], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 6.5, 12.25, 7], "texture": "#0"}, + "east": {"uv": [12, 7, 12.25, 7.5], "texture": "#0"}, + "south": {"uv": [7.25, 12, 7.5, 12.5], "texture": "#0"}, + "west": {"uv": [7.5, 12, 7.75, 12.5], "texture": "#0"}, + "up": {"uv": [3.5, 13.25, 3.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 3.5, 13, 3.75], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 7.5, 12.25, 8], "texture": "#0"}, + "east": {"uv": [7.75, 12, 8, 12.5], "texture": "#0"}, + "south": {"uv": [8, 12, 8.25, 12.5], "texture": "#0"}, + "west": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "up": {"uv": [13.25, 4, 13, 3.75], "texture": "#0"}, + "down": {"uv": [4.25, 13, 4, 13.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 1, 11.75, 1.5], "texture": "#0"}, + "east": {"uv": [9.25, 12, 9.5, 12.5], "texture": "#0"}, + "south": {"uv": [11.25, 1.5, 11.75, 2], "texture": "#0"}, + "west": {"uv": [12, 9.25, 12.25, 9.75], "texture": "#0"}, + "up": {"uv": [12.5, 8.25, 12, 8], "texture": "#0"}, + "down": {"uv": [10, 12, 9.5, 12.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 12, 10.25, 12.5], "texture": "#0"}, + "east": {"uv": [12, 10.25, 12.25, 10.75], "texture": "#0"}, + "south": {"uv": [12, 10.75, 12.25, 11.25], "texture": "#0"}, + "west": {"uv": [12, 11.75, 12.25, 12.25], "texture": "#0"}, + "up": {"uv": [4.5, 13.25, 4.25, 13], "texture": "#0"}, + "down": {"uv": [4.75, 13, 4.5, 13.25], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 11.75, 10.75, 12.25], "texture": "#0"}, + "east": {"uv": [10.75, 11.75, 11, 12.25], "texture": "#0"}, + "south": {"uv": [11.5, 11.75, 11.75, 12.25], "texture": "#0"}, + "west": {"uv": [11.75, 11.75, 12, 12.25], "texture": "#0"}, + "up": {"uv": [11.25, 4, 11, 3.75], "texture": "#0"}, + "down": {"uv": [11.25, 12.75, 11, 13], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 1, 12.5, 1.5], "texture": "#0"}, + "east": {"uv": [12.25, 1.5, 12.5, 2], "texture": "#0"}, + "south": {"uv": [12.25, 2, 12.5, 2.5], "texture": "#0"}, + "west": {"uv": [12.25, 2.5, 12.5, 3], "texture": "#0"}, + "up": {"uv": [13.25, 4.75, 13, 4.5], "texture": "#0"}, + "down": {"uv": [5, 13, 4.75, 13.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 3, 12.5, 3.5], "texture": "#0"}, + "east": {"uv": [11.25, 2, 11.75, 2.5], "texture": "#0"}, + "south": {"uv": [12.25, 3.5, 12.5, 4], "texture": "#0"}, + "west": {"uv": [11.25, 2.5, 11.75, 3], "texture": "#0"}, + "up": {"uv": [12.5, 4.5, 12.25, 4], "texture": "#0"}, + "down": {"uv": [12.5, 4.5, 12.25, 5], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 5, 12.5, 5.5], "texture": "#0"}, + "east": {"uv": [5.25, 12.25, 5.5, 12.75], "texture": "#0"}, + "south": {"uv": [5.5, 12.25, 5.75, 12.75], "texture": "#0"}, + "west": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "up": {"uv": [13.25, 5, 13, 4.75], "texture": "#0"}, + "down": {"uv": [5.25, 13, 5, 13.25], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 6, 12.5, 6.5], "texture": "#0"}, + "south": {"uv": [12.25, 6.5, 12.5, 7], "texture": "#0"}, + "west": {"uv": [12.25, 7, 12.5, 7.5], "texture": "#0"}, + "up": {"uv": [13.25, 5.25, 13, 5], "texture": "#0"}, + "down": {"uv": [5.5, 13, 5.25, 13.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 7.5, 12.5, 8], "texture": "#0"}, + "east": {"uv": [12.25, 9.25, 12.5, 9.75], "texture": "#0"}, + "south": {"uv": [9.5, 12.25, 9.75, 12.75], "texture": "#0"}, + "west": {"uv": [9.75, 12.25, 10, 12.75], "texture": "#0"}, + "up": {"uv": [13.25, 5.5, 13, 5.25], "texture": "#0"}, + "down": {"uv": [5.75, 13, 5.5, 13.25], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 3, 11.75, 3.5], "texture": "#0"}, + "east": {"uv": [12.25, 10.25, 12.5, 10.75], "texture": "#0"}, + "south": {"uv": [11.25, 3.5, 11.75, 4], "texture": "#0"}, + "west": {"uv": [10.5, 12.25, 10.75, 12.75], "texture": "#0"}, + "up": {"uv": [12.5, 10, 12, 9.75], "texture": "#0"}, + "down": {"uv": [12.75, 5.5, 12.25, 5.75], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 8.25, 12.75, 8.5], "texture": "#0"}, + "east": {"uv": [13, 5.75, 13.25, 6], "texture": "#0"}, + "south": {"uv": [10.75, 12.25, 11.25, 12.5], "texture": "#0"}, + "west": {"uv": [13, 6, 13.25, 6.25], "texture": "#0"}, + "up": {"uv": [12.75, 11, 12.25, 10.75], "texture": "#0"}, + "down": {"uv": [12.75, 11, 12.25, 11.25], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 12.25, 11.75, 12.75], "texture": "#0"}, + "east": {"uv": [11.25, 4.5, 11.75, 5], "texture": "#0"}, + "south": {"uv": [11.75, 12.25, 12, 12.75], "texture": "#0"}, + "west": {"uv": [11.25, 5, 11.75, 5.5], "texture": "#0"}, + "up": {"uv": [12.5, 12.25, 12.25, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 12.25, 12, 12.75], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.25, 13, 6.5, 13.25], "texture": "#0"}, + "east": {"uv": [12.25, 11.25, 12.75, 11.5], "texture": "#0"}, + "south": {"uv": [13, 6.25, 13.25, 6.5], "texture": "#0"}, + "west": {"uv": [12.25, 12.25, 12.75, 12.5], "texture": "#0"}, + "up": {"uv": [0.25, 13, 0, 12.5], "texture": "#0"}, + "down": {"uv": [0.5, 12.5, 0.25, 13], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 7.25, 11.75, 7.75], "texture": "#0"}, + "east": {"uv": [0.5, 12.5, 0.75, 13], "texture": "#0"}, + "south": {"uv": [11.25, 7.75, 11.75, 8.25], "texture": "#0"}, + "west": {"uv": [0.75, 12.5, 1, 13], "texture": "#0"}, + "up": {"uv": [1.5, 12.75, 1, 12.5], "texture": "#0"}, + "down": {"uv": [13, 1, 12.5, 1.25], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 1.25, 13, 1.5], "texture": "#0"}, + "east": {"uv": [6.5, 13, 6.75, 13.25], "texture": "#0"}, + "south": {"uv": [1.5, 12.5, 2, 12.75], "texture": "#0"}, + "west": {"uv": [13, 6.5, 13.25, 6.75], "texture": "#0"}, + "up": {"uv": [13, 1.75, 12.5, 1.5], "texture": "#0"}, + "down": {"uv": [13, 1.75, 12.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 12.5, 2.25, 13], "texture": "#0"}, + "east": {"uv": [11.25, 10, 11.75, 10.5], "texture": "#0"}, + "south": {"uv": [12.5, 2, 12.75, 2.5], "texture": "#0"}, + "west": {"uv": [10.5, 11.25, 11, 11.75], "texture": "#0"}, + "up": {"uv": [2.5, 13, 2.25, 12.5], "texture": "#0"}, + "down": {"uv": [2.75, 12.5, 2.5, 13], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.75, 13, 7, 13.25], "texture": "#0"}, + "east": {"uv": [12.5, 2.5, 13, 2.75], "texture": "#0"}, + "south": {"uv": [13, 6.75, 13.25, 7], "texture": "#0"}, + "west": {"uv": [12.5, 2.75, 13, 3], "texture": "#0"}, + "up": {"uv": [3, 13, 2.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 3, 12.5, 3.5], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 13, 7.25, 13.25], "texture": "#0"}, + "east": {"uv": [11.25, 5.5, 12.25, 5.75], "texture": "#0"}, + "south": {"uv": [13, 7, 13.25, 7.25], "texture": "#0"}, + "west": {"uv": [11.25, 8.25, 12.25, 8.5], "texture": "#0"}, + "up": {"uv": [11.5, 11.5, 11.25, 10.5], "texture": "#0"}, + "down": {"uv": [11.25, 11.25, 11, 12.25], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 3.5, 12.75, 4], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [4, 12.5, 4.25, 13], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [0.25, 12.5, 0, 11.5], "texture": "#0"}, + "down": {"uv": [0.5, 11.5, 0.25, 12.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.5, 11.5, 1.5, 11.75], "texture": "#0"}, + "east": {"uv": [13, 7.25, 13.25, 7.5], "texture": "#0"}, + "south": {"uv": [11.5, 5.75, 12.5, 6], "texture": "#0"}, + "west": {"uv": [13, 7.5, 13.25, 7.75], "texture": "#0"}, + "up": {"uv": [7.25, 11.75, 6.25, 11.5], "texture": "#0"}, + "down": {"uv": [9.5, 11.5, 8.5, 11.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [12.5, 4, 12.75, 4.5], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [4.25, 12.5, 4.5, 13], "texture": "#0"}, + "up": {"uv": [12.5, 8.75, 11.5, 8.5], "texture": "#0"}, + "down": {"uv": [12.5, 8.75, 11.5, 9], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.75, 13, 8, 13.25], "texture": "#0"}, + "east": {"uv": [11.5, 9, 12.5, 9.25], "texture": "#0"}, + "south": {"uv": [13, 7.75, 13.25, 8], "texture": "#0"}, + "west": {"uv": [9.5, 11.5, 10.5, 11.75], "texture": "#0"}, + "up": {"uv": [1.75, 12.5, 1.5, 11.5], "texture": "#0"}, + "down": {"uv": [2, 11.5, 1.75, 12.5], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.5, 12.5, 4.75, 13], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [12.5, 4.5, 12.75, 5], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [11.75, 11.5, 11.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.5, 11.5, 11.25, 12.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 11.5, 12.5, 11.75], "texture": "#0"}, + "east": {"uv": [8, 13, 8.25, 13.25], "texture": "#0"}, + "south": {"uv": [11.75, 0, 12.75, 0.25], "texture": "#0"}, + "west": {"uv": [13, 8, 13.25, 8.25], "texture": "#0"}, + "up": {"uv": [12.75, 0.5, 11.75, 0.25], "texture": "#0"}, + "down": {"uv": [1.5, 11.75, 0.5, 12], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [4.75, 12.5, 5, 13], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [5, 12.5, 5.25, 13], "texture": "#0"}, + "up": {"uv": [12.75, 0.75, 11.75, 0.5], "texture": "#0"}, + "down": {"uv": [12.75, 0.75, 11.75, 1], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [6.9, 6, 14], + "to": [7.9, 6.5, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.15, 6.25, 14.25]}, + "faces": { + "north": {"uv": [8.25, 13, 8.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 8.5, 13.25, 8.75], "texture": "#0"}, + "south": {"uv": [13, 8.75, 13.25, 9], "texture": "#0"}, + "west": {"uv": [13, 9, 13.25, 9.25], "texture": "#0"}, + "up": {"uv": [9.5, 13.25, 9.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.25, 13, 9.5], "texture": "#0"} + } + }, + { + "from": [7.9, 4.5, 14], + "to": [8.4, 8, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.15, 6.25, 14.25]}, + "faces": { + "north": {"uv": [11.75, 1, 12, 2], "texture": "#0"}, + "east": {"uv": [11.75, 2, 12, 3], "texture": "#0"}, + "south": {"uv": [3, 11.75, 3.25, 12.75], "texture": "#0"}, + "west": {"uv": [11.75, 3, 12, 4], "texture": "#0"}, + "up": {"uv": [9.75, 13.25, 9.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.5, 13, 9.75], "texture": "#0"} + } + }, + { + "from": [8.4, 6, 14], + "to": [9.4, 6.5, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8.15, 6.25, 14.25]}, + "faces": { + "north": {"uv": [9.75, 13, 10, 13.25], "texture": "#0"}, + "east": {"uv": [13, 9.75, 13.25, 10], "texture": "#0"}, + "south": {"uv": [10, 13, 10.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 10.25, 13.25, 10.5], "texture": "#0"}, + "up": {"uv": [10.75, 13.25, 10.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 10.5, 13, 10.75], "texture": "#0"} + } + }, + { + "from": [6.7, 9.2, 14], + "to": [8.7, 9.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [12.5, 5, 13, 5.25], "texture": "#0"}, + "east": {"uv": [10.75, 13, 11, 13.25], "texture": "#0"}, + "south": {"uv": [12.5, 5.25, 13, 5.5], "texture": "#0"}, + "west": {"uv": [11, 13, 11.25, 13.25], "texture": "#0"}, + "up": {"uv": [13, 6, 12.5, 5.75], "texture": "#0"}, + "down": {"uv": [13, 6, 12.5, 6.25], "texture": "#0"} + } + }, + { + "from": [6.2, 6.7, 14], + "to": [6.7, 11.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [10.25, 4.5, 10.5, 5.75], "texture": "#0"}, + "east": {"uv": [6.25, 10.25, 6.5, 11.5], "texture": "#0"}, + "south": {"uv": [6.5, 10.25, 6.75, 11.5], "texture": "#0"}, + "west": {"uv": [6.75, 10.25, 7, 11.5], "texture": "#0"}, + "up": {"uv": [11.5, 13.25, 11.25, 13], "texture": "#0"}, + "down": {"uv": [11.75, 13, 11.5, 13.25], "texture": "#0"} + } + }, + { + "from": [4.2, 9.2, 14], + "to": [6.2, 9.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [12.5, 6.25, 13, 6.5], "texture": "#0"}, + "east": {"uv": [13, 11.5, 13.25, 11.75], "texture": "#0"}, + "south": {"uv": [12.5, 6.5, 13, 6.75], "texture": "#0"}, + "west": {"uv": [11.75, 13, 12, 13.25], "texture": "#0"}, + "up": {"uv": [13, 7, 12.5, 6.75], "texture": "#0"}, + "down": {"uv": [13, 7, 12.5, 7.25], "texture": "#0"} + } + }, + { + "from": [5.7, 9.7, 14], + "to": [6.2, 10.2, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [13, 11.75, 13.25, 12], "texture": "#0"}, + "east": {"uv": [12, 13, 12.25, 13.25], "texture": "#0"}, + "south": {"uv": [13, 12, 13.25, 12.25], "texture": "#0"}, + "west": {"uv": [12.25, 13, 12.5, 13.25], "texture": "#0"}, + "up": {"uv": [12.75, 13.25, 12.5, 13], "texture": "#0"}, + "down": {"uv": [13, 13, 12.75, 13.25], "texture": "#0"} + } + }, + { + "from": [6.7, 9.7, 14], + "to": [7.2, 10.2, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [13, 12.75, 13.25, 13], "texture": "#0"}, + "east": {"uv": [13, 13, 13.25, 13.25], "texture": "#0"}, + "south": {"uv": [0, 13.25, 0.25, 13.5], "texture": "#0"}, + "west": {"uv": [13.25, 0, 13.5, 0.25], "texture": "#0"}, + "up": {"uv": [0.5, 13.5, 0.25, 13.25], "texture": "#0"}, + "down": {"uv": [13.5, 0.25, 13.25, 0.5], "texture": "#0"} + } + }, + { + "from": [5.7, 8.7, 14], + "to": [6.2, 9.2, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [0.5, 13.25, 0.75, 13.5], "texture": "#0"}, + "east": {"uv": [13.25, 0.5, 13.5, 0.75], "texture": "#0"}, + "south": {"uv": [0.75, 13.25, 1, 13.5], "texture": "#0"}, + "west": {"uv": [13.25, 0.75, 13.5, 1], "texture": "#0"}, + "up": {"uv": [1.25, 13.5, 1, 13.25], "texture": "#0"}, + "down": {"uv": [1.5, 13.25, 1.25, 13.5], "texture": "#0"} + } + }, + { + "from": [6.7, 8.7, 14], + "to": [7.2, 9.2, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [1.5, 13.25, 1.75, 13.5], "texture": "#0"}, + "east": {"uv": [1.75, 13.25, 2, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 1.75, 13.5, 2], "texture": "#0"}, + "west": {"uv": [2, 13.25, 2.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 2.25, 13.25, 2], "texture": "#0"}, + "down": {"uv": [2.5, 13.25, 2.25, 13.5], "texture": "#0"} + } + }, + { + "from": [5.2, 10.2, 14], + "to": [5.7, 10.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [13.25, 2.25, 13.5, 2.5], "texture": "#0"}, + "east": {"uv": [2.5, 13.25, 2.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 2.5, 13.5, 2.75], "texture": "#0"}, + "west": {"uv": [2.75, 13.25, 3, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 3, 13.25, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 13.25, 3, 13.5], "texture": "#0"} + } + }, + { + "from": [5.2, 8.2, 14], + "to": [5.7, 8.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [13.25, 3, 13.5, 3.25], "texture": "#0"}, + "east": {"uv": [3.25, 13.25, 3.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 3.25, 13.5, 3.5], "texture": "#0"}, + "west": {"uv": [3.5, 13.25, 3.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 3.75, 13.25, 3.5], "texture": "#0"}, + "down": {"uv": [4, 13.25, 3.75, 13.5], "texture": "#0"} + } + }, + { + "from": [7.2, 8.2, 14], + "to": [7.7, 8.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [13.25, 3.75, 13.5, 4], "texture": "#0"}, + "east": {"uv": [4, 13.25, 4.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 4, 13.5, 4.25], "texture": "#0"}, + "west": {"uv": [4.25, 13.25, 4.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 4.5, 13.25, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13.25, 4.5, 13.5], "texture": "#0"} + } + }, + { + "from": [7.2, 10.2, 14], + "to": [7.7, 10.7, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 14.25]}, + "faces": { + "north": {"uv": [13.25, 4.5, 13.5, 4.75], "texture": "#0"}, + "east": {"uv": [4.75, 13.25, 5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 4.75, 13.5, 5], "texture": "#0"}, + "west": {"uv": [5, 13.25, 5.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 5.25, 13.25, 5], "texture": "#0"}, + "down": {"uv": [5.5, 13.25, 5.25, 13.5], "texture": "#0"} + } + }, + { + "from": [9.9, 6.4, 14], + "to": [10.4, 10.9, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [7, 10.25, 7.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 7.25, 10.5, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 10.25, 8.75, 11.5], "texture": "#0"}, + "west": {"uv": [8.75, 10.25, 9, 11.5], "texture": "#0"}, + "up": {"uv": [13.5, 5.5, 13.25, 5.25], "texture": "#0"}, + "down": {"uv": [5.75, 13.25, 5.5, 13.5], "texture": "#0"} + } + }, + { + "from": [9.4, 8.9, 14], + "to": [9.9, 9.4, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [13.25, 5.5, 13.5, 5.75], "texture": "#0"}, + "east": {"uv": [5.75, 13.25, 6, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 5.75, 13.5, 6], "texture": "#0"}, + "west": {"uv": [6, 13.25, 6.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 6.25, 13.25, 6], "texture": "#0"}, + "down": {"uv": [6.5, 13.25, 6.25, 13.5], "texture": "#0"} + } + }, + { + "from": [9.4, 7.9, 14], + "to": [9.9, 8.4, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [13.25, 6.25, 13.5, 6.5], "texture": "#0"}, + "east": {"uv": [6.5, 13.25, 6.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 6.5, 13.5, 6.75], "texture": "#0"}, + "west": {"uv": [6.75, 13.25, 7, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 7, 13.25, 6.75], "texture": "#0"}, + "down": {"uv": [7.25, 13.25, 7, 13.5], "texture": "#0"} + } + }, + { + "from": [8.4, 8.4, 14], + "to": [9.9, 8.9, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [7.25, 12.5, 7.75, 12.75], "texture": "#0"}, + "east": {"uv": [13.25, 7, 13.5, 7.25], "texture": "#0"}, + "south": {"uv": [12.5, 7.25, 13, 7.5], "texture": "#0"}, + "west": {"uv": [7.25, 13.25, 7.5, 13.5], "texture": "#0"}, + "up": {"uv": [13, 7.75, 12.5, 7.5], "texture": "#0"}, + "down": {"uv": [8.25, 12.5, 7.75, 12.75], "texture": "#0"} + } + }, + { + "from": [10.4, 8.4, 14], + "to": [11.9, 8.9, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [12.5, 7.75, 13, 8], "texture": "#0"}, + "east": {"uv": [13.25, 7.25, 13.5, 7.5], "texture": "#0"}, + "south": {"uv": [12.5, 8, 13, 8.25], "texture": "#0"}, + "west": {"uv": [7.5, 13.25, 7.75, 13.5], "texture": "#0"}, + "up": {"uv": [13, 8.75, 12.5, 8.5], "texture": "#0"}, + "down": {"uv": [13, 8.75, 12.5, 9], "texture": "#0"} + } + }, + { + "from": [10.4, 8.9, 14], + "to": [10.9, 9.4, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [13.25, 7.5, 13.5, 7.75], "texture": "#0"}, + "east": {"uv": [7.75, 13.25, 8, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 7.75, 13.5, 8], "texture": "#0"}, + "west": {"uv": [8, 13.25, 8.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 8.25, 13.25, 8], "texture": "#0"}, + "down": {"uv": [8.5, 13.25, 8.25, 13.5], "texture": "#0"} + } + }, + { + "from": [10.4, 7.9, 14], + "to": [10.9, 8.4, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 14.25]}, + "faces": { + "north": {"uv": [13.25, 8.25, 13.5, 8.5], "texture": "#0"}, + "east": {"uv": [8.5, 13.25, 8.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 8.5, 13.5, 8.75], "texture": "#0"}, + "west": {"uv": [8.75, 13.25, 9, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 9, 13.25, 8.75], "texture": "#0"}, + "down": {"uv": [9.25, 13.25, 9, 13.5], "texture": "#0"} + } + }, + { + "from": [6.9, 6, 1.5], + "to": [7.9, 6.5, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.15, 6.25, 1.75]}, + "faces": { + "north": {"uv": [13.25, 9, 13.5, 9.25], "texture": "#0"}, + "east": {"uv": [9.25, 13.25, 9.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 9.25, 13.5, 9.5], "texture": "#0"}, + "west": {"uv": [9.5, 13.25, 9.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 9.75, 13.25, 9.5], "texture": "#0"}, + "down": {"uv": [10, 13.25, 9.75, 13.5], "texture": "#0"} + } + }, + { + "from": [7.9, 4.5, 1.5], + "to": [8.4, 8, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.15, 6.25, 1.75]}, + "faces": { + "north": {"uv": [3.25, 11.75, 3.5, 12.75], "texture": "#0"}, + "east": {"uv": [3.5, 11.75, 3.75, 12.75], "texture": "#0"}, + "south": {"uv": [3.75, 11.75, 4, 12.75], "texture": "#0"}, + "west": {"uv": [11.75, 4.5, 12, 5.5], "texture": "#0"}, + "up": {"uv": [13.5, 10, 13.25, 9.75], "texture": "#0"}, + "down": {"uv": [10.25, 13.25, 10, 13.5], "texture": "#0"} + } + }, + { + "from": [8.4, 6, 1.5], + "to": [9.4, 6.5, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8.15, 6.25, 1.75]}, + "faces": { + "north": {"uv": [13.25, 10, 13.5, 10.25], "texture": "#0"}, + "east": {"uv": [10.25, 13.25, 10.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 10.25, 13.5, 10.5], "texture": "#0"}, + "west": {"uv": [10.5, 13.25, 10.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 10.75, 13.25, 10.5], "texture": "#0"}, + "down": {"uv": [11, 13.25, 10.75, 13.5], "texture": "#0"} + } + }, + { + "from": [6.7, 9.2, 1.5], + "to": [8.7, 9.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [12.5, 9, 13, 9.25], "texture": "#0"}, + "east": {"uv": [13.25, 10.75, 13.5, 11], "texture": "#0"}, + "south": {"uv": [12.5, 9.25, 13, 9.5], "texture": "#0"}, + "west": {"uv": [11, 13.25, 11.25, 13.5], "texture": "#0"}, + "up": {"uv": [13, 9.75, 12.5, 9.5], "texture": "#0"}, + "down": {"uv": [13, 9.75, 12.5, 10], "texture": "#0"} + } + }, + { + "from": [6.2, 6.7, 1.5], + "to": [6.7, 11.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [9, 10.25, 9.25, 11.5], "texture": "#0"}, + "east": {"uv": [9.25, 10.25, 9.5, 11.5], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 9.75, 11.5], "texture": "#0"}, + "west": {"uv": [9.75, 10.25, 10, 11.5], "texture": "#0"}, + "up": {"uv": [13.5, 11.25, 13.25, 11], "texture": "#0"}, + "down": {"uv": [11.5, 13.25, 11.25, 13.5], "texture": "#0"} + } + }, + { + "from": [4.2, 9.2, 1.5], + "to": [6.2, 9.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [12.5, 10.25, 13, 10.5], "texture": "#0"}, + "east": {"uv": [13.25, 11.25, 13.5, 11.5], "texture": "#0"}, + "south": {"uv": [12.5, 10.5, 13, 10.75], "texture": "#0"}, + "west": {"uv": [11.5, 13.25, 11.75, 13.5], "texture": "#0"}, + "up": {"uv": [11.25, 12.75, 10.75, 12.5], "texture": "#0"}, + "down": {"uv": [13, 11.5, 12.5, 11.75], "texture": "#0"} + } + }, + { + "from": [5.7, 9.7, 1.5], + "to": [6.2, 10.2, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [13.25, 11.5, 13.5, 11.75], "texture": "#0"}, + "east": {"uv": [11.75, 13.25, 12, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 11.75, 13.5, 12], "texture": "#0"}, + "west": {"uv": [12, 13.25, 12.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 12.25, 13.25, 12], "texture": "#0"}, + "down": {"uv": [12.5, 13.25, 12.25, 13.5], "texture": "#0"} + } + }, + { + "from": [6.7, 9.7, 1.5], + "to": [7.2, 10.2, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [13.25, 12.25, 13.5, 12.5], "texture": "#0"}, + "east": {"uv": [12.5, 13.25, 12.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 12.5, 13.5, 12.75], "texture": "#0"}, + "west": {"uv": [12.75, 13.25, 13, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 13, 13.25, 12.75], "texture": "#0"}, + "down": {"uv": [13.25, 13.25, 13, 13.5], "texture": "#0"} + } + }, + { + "from": [5.7, 8.7, 1.5], + "to": [6.2, 9.2, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [13.25, 13, 13.5, 13.25], "texture": "#0"}, + "east": {"uv": [13.25, 13.25, 13.5, 13.5], "texture": "#0"}, + "south": {"uv": [0, 13.5, 0.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 0, 13.75, 0.25], "texture": "#0"}, + "up": {"uv": [0.5, 13.75, 0.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0.25, 13.5, 0.5], "texture": "#0"} + } + }, + { + "from": [6.7, 8.7, 1.5], + "to": [7.2, 9.2, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [0.5, 13.5, 0.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 0.5, 13.75, 0.75], "texture": "#0"}, + "south": {"uv": [0.75, 13.5, 1, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 0.75, 13.75, 1], "texture": "#0"}, + "up": {"uv": [1.25, 13.75, 1, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1, 13.5, 1.25], "texture": "#0"} + } + }, + { + "from": [5.2, 10.2, 1.5], + "to": [5.7, 10.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [1.25, 13.5, 1.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 1.25, 13.75, 1.5], "texture": "#0"}, + "south": {"uv": [1.5, 13.5, 1.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 1.5, 13.75, 1.75], "texture": "#0"}, + "up": {"uv": [2, 13.75, 1.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1.75, 13.5, 2], "texture": "#0"} + } + }, + { + "from": [5.2, 8.2, 1.5], + "to": [5.7, 8.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [2, 13.5, 2.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 2, 13.75, 2.25], "texture": "#0"}, + "south": {"uv": [2.25, 13.5, 2.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 2.25, 13.75, 2.5], "texture": "#0"}, + "up": {"uv": [2.75, 13.75, 2.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 2.5, 13.5, 2.75], "texture": "#0"} + } + }, + { + "from": [7.2, 8.2, 1.5], + "to": [7.7, 8.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [2.75, 13.5, 3, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 2.75, 13.75, 3], "texture": "#0"}, + "south": {"uv": [3, 13.5, 3.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 3, 13.75, 3.25], "texture": "#0"}, + "up": {"uv": [3.5, 13.75, 3.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 3.25, 13.5, 3.5], "texture": "#0"} + } + }, + { + "from": [7.2, 10.2, 1.5], + "to": [7.7, 10.7, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [6.45, 9.45, 1.75]}, + "faces": { + "north": {"uv": [3.5, 13.5, 3.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 3.5, 13.75, 3.75], "texture": "#0"}, + "south": {"uv": [3.75, 13.5, 4, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 3.75, 13.75, 4], "texture": "#0"}, + "up": {"uv": [4.25, 13.75, 4, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 4, 13.5, 4.25], "texture": "#0"} + } + }, + { + "from": [9.9, 6.4, 1.5], + "to": [10.4, 10.9, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [10, 10.25, 10.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.25, 10.25, 10.5, 11.5], "texture": "#0"}, + "south": {"uv": [10.5, 0, 10.75, 1.25], "texture": "#0"}, + "west": {"uv": [10.5, 1.25, 10.75, 2.5], "texture": "#0"}, + "up": {"uv": [4.5, 13.75, 4.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 4.25, 13.5, 4.5], "texture": "#0"} + } + }, + { + "from": [9.4, 8.9, 1.5], + "to": [9.9, 9.4, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [4.5, 13.5, 4.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 4.5, 13.75, 4.75], "texture": "#0"}, + "south": {"uv": [4.75, 13.5, 5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 4.75, 13.75, 5], "texture": "#0"}, + "up": {"uv": [5.25, 13.75, 5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 5, 13.5, 5.25], "texture": "#0"} + } + }, + { + "from": [9.4, 7.9, 1.5], + "to": [9.9, 8.4, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [5.25, 13.5, 5.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 5.25, 13.75, 5.5], "texture": "#0"}, + "south": {"uv": [5.5, 13.5, 5.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 5.5, 13.75, 5.75], "texture": "#0"}, + "up": {"uv": [6, 13.75, 5.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 5.75, 13.5, 6], "texture": "#0"} + } + }, + { + "from": [8.4, 8.4, 1.5], + "to": [9.9, 8.9, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [12.5, 11.75, 13, 12], "texture": "#0"}, + "east": {"uv": [6, 13.5, 6.25, 13.75], "texture": "#0"}, + "south": {"uv": [12.5, 12, 13, 12.25], "texture": "#0"}, + "west": {"uv": [13.5, 6, 13.75, 6.25], "texture": "#0"}, + "up": {"uv": [12.75, 12.75, 12.25, 12.5], "texture": "#0"}, + "down": {"uv": [13.25, 0, 12.75, 0.25], "texture": "#0"} + } + }, + { + "from": [10.4, 8.4, 1.5], + "to": [11.9, 8.9, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [12.75, 0.25, 13.25, 0.5], "texture": "#0"}, + "east": {"uv": [6.25, 13.5, 6.5, 13.75], "texture": "#0"}, + "south": {"uv": [12.75, 0.5, 13.25, 0.75], "texture": "#0"}, + "west": {"uv": [13.5, 6.25, 13.75, 6.5], "texture": "#0"}, + "up": {"uv": [13.25, 1, 12.75, 0.75], "texture": "#0"}, + "down": {"uv": [1.5, 12.75, 1, 13], "texture": "#0"} + } + }, + { + "from": [10.4, 8.9, 1.5], + "to": [10.9, 9.4, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [6.5, 13.5, 6.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 6.5, 13.75, 6.75], "texture": "#0"}, + "south": {"uv": [6.75, 13.5, 7, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 6.75, 13.75, 7], "texture": "#0"}, + "up": {"uv": [7.25, 13.75, 7, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 7, 13.5, 7.25], "texture": "#0"} + } + }, + { + "from": [10.4, 7.9, 1.5], + "to": [10.9, 8.4, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [10.15, 8.65, 1.75]}, + "faces": { + "north": {"uv": [7.25, 13.5, 7.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 7.25, 13.75, 7.5], "texture": "#0"}, + "south": {"uv": [7.5, 13.5, 7.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 7.5, 13.75, 7.75], "texture": "#0"}, + "up": {"uv": [8, 13.75, 7.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 7.75, 13.5, 8], "texture": "#0"} + } + }, + { + "from": [1.425, 6, 8.125], + "to": [1.925, 6.5, 9.125], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [8, 13.5, 8.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 8, 13.75, 8.25], "texture": "#0"}, + "south": {"uv": [8.25, 13.5, 8.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 8.25, 13.75, 8.5], "texture": "#0"}, + "up": {"uv": [8.75, 13.75, 8.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 8.5, 13.5, 8.75], "texture": "#0"} + } + }, + { + "from": [1.425, 4.5, 7.625], + "to": [1.925, 8, 8.125], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [6.25, 11.75, 6.5, 12.75], "texture": "#0"}, + "east": {"uv": [6.5, 11.75, 6.75, 12.75], "texture": "#0"}, + "south": {"uv": [6.75, 11.75, 7, 12.75], "texture": "#0"}, + "west": {"uv": [7, 11.75, 7.25, 12.75], "texture": "#0"}, + "up": {"uv": [9, 13.75, 8.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 8.75, 13.5, 9], "texture": "#0"} + } + }, + { + "from": [1.425, 6, 6.625], + "to": [1.925, 6.5, 7.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [9, 13.5, 9.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 9, 13.75, 9.25], "texture": "#0"}, + "south": {"uv": [9.25, 13.5, 9.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 9.25, 13.75, 9.5], "texture": "#0"}, + "up": {"uv": [9.75, 13.75, 9.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 9.5, 13.5, 9.75], "texture": "#0"} + } + }, + { + "from": [1.425, 9.2, 7.325], + "to": [1.925, 9.7, 9.325], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [9.75, 13.5, 10, 13.75], "texture": "#0"}, + "east": {"uv": [1.5, 12.75, 2, 13], "texture": "#0"}, + "south": {"uv": [13.5, 9.75, 13.75, 10], "texture": "#0"}, + "west": {"uv": [12.75, 2, 13.25, 2.25], "texture": "#0"}, + "up": {"uv": [8.5, 13, 8.25, 12.5], "texture": "#0"}, + "down": {"uv": [9.5, 12.5, 9.25, 13], "texture": "#0"} + } + }, + { + "from": [1.425, 6.7, 9.325], + "to": [1.925, 11.7, 9.825], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [10.5, 2.5, 10.75, 3.75], "texture": "#0"}, + "east": {"uv": [3, 10.5, 3.25, 11.75], "texture": "#0"}, + "south": {"uv": [3.25, 10.5, 3.5, 11.75], "texture": "#0"}, + "west": {"uv": [3.5, 10.5, 3.75, 11.75], "texture": "#0"}, + "up": {"uv": [10.25, 13.75, 10, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 10, 13.5, 10.25], "texture": "#0"} + } + }, + { + "from": [1.425, 9.2, 9.825], + "to": [1.925, 9.7, 11.825], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [10.25, 13.5, 10.5, 13.75], "texture": "#0"}, + "east": {"uv": [12.75, 2.25, 13.25, 2.5], "texture": "#0"}, + "south": {"uv": [13.5, 10.25, 13.75, 10.5], "texture": "#0"}, + "west": {"uv": [3, 12.75, 3.5, 13], "texture": "#0"}, + "up": {"uv": [10.25, 13, 10, 12.5], "texture": "#0"}, + "down": {"uv": [11.5, 12.5, 11.25, 13], "texture": "#0"} + } + }, + { + "from": [1.425, 9.7, 9.825], + "to": [1.925, 10.2, 10.325], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [10.5, 13.5, 10.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 10.5, 13.75, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 13.5, 11, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 10.75, 13.75, 11], "texture": "#0"}, + "up": {"uv": [11.25, 13.75, 11, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 11, 13.5, 11.25], "texture": "#0"} + } + }, + { + "from": [1.425, 9.7, 8.825], + "to": [1.925, 10.2, 9.325], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [11.25, 13.5, 11.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 11.25, 13.75, 11.5], "texture": "#0"}, + "south": {"uv": [11.5, 13.5, 11.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 11.5, 13.75, 11.75], "texture": "#0"}, + "up": {"uv": [12, 13.75, 11.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 11.75, 13.5, 12], "texture": "#0"} + } + }, + { + "from": [1.425, 8.7, 9.825], + "to": [1.925, 9.2, 10.325], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [12, 13.5, 12.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 12, 13.75, 12.25], "texture": "#0"}, + "south": {"uv": [12.25, 13.5, 12.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 12.25, 13.75, 12.5], "texture": "#0"}, + "up": {"uv": [12.75, 13.75, 12.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 12.5, 13.5, 12.75], "texture": "#0"} + } + }, + { + "from": [1.425, 8.7, 8.825], + "to": [1.925, 9.2, 9.325], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [12.75, 13.5, 13, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 12.75, 13.75, 13], "texture": "#0"}, + "south": {"uv": [13, 13.5, 13.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 13, 13.75, 13.25], "texture": "#0"}, + "up": {"uv": [13.5, 13.75, 13.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 13.25, 13.5, 13.5], "texture": "#0"} + } + }, + { + "from": [1.425, 10.2, 10.325], + "to": [1.925, 10.7, 10.825], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.5, 13.5, 13.75, 13.75], "texture": "#0"}, + "east": {"uv": [0, 13.75, 0.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 0, 14, 0.25], "texture": "#0"}, + "west": {"uv": [0.25, 13.75, 0.5, 14], "texture": "#0"}, + "up": {"uv": [14, 0.5, 13.75, 0.25], "texture": "#0"}, + "down": {"uv": [0.75, 13.75, 0.5, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 8.2, 10.325], + "to": [1.925, 8.7, 10.825], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 0.5, 14, 0.75], "texture": "#0"}, + "east": {"uv": [0.75, 13.75, 1, 14], "texture": "#0"}, + "south": {"uv": [13.75, 0.75, 14, 1], "texture": "#0"}, + "west": {"uv": [1, 13.75, 1.25, 14], "texture": "#0"}, + "up": {"uv": [14, 1.25, 13.75, 1], "texture": "#0"}, + "down": {"uv": [1.5, 13.75, 1.25, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 8.2, 8.325], + "to": [1.925, 8.7, 8.825], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 1.25, 14, 1.5], "texture": "#0"}, + "east": {"uv": [1.5, 13.75, 1.75, 14], "texture": "#0"}, + "south": {"uv": [13.75, 1.5, 14, 1.75], "texture": "#0"}, + "west": {"uv": [1.75, 13.75, 2, 14], "texture": "#0"}, + "up": {"uv": [14, 2, 13.75, 1.75], "texture": "#0"}, + "down": {"uv": [2.25, 13.75, 2, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 10.2, 8.325], + "to": [1.925, 10.7, 8.825], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 2, 14, 2.25], "texture": "#0"}, + "east": {"uv": [2.25, 13.75, 2.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 2.25, 14, 2.5], "texture": "#0"}, + "west": {"uv": [2.5, 13.75, 2.75, 14], "texture": "#0"}, + "up": {"uv": [14, 2.75, 13.75, 2.5], "texture": "#0"}, + "down": {"uv": [3, 13.75, 2.75, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 6.4, 5.625], + "to": [1.925, 10.9, 6.125], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [3.75, 10.5, 4, 11.75], "texture": "#0"}, + "east": {"uv": [10.5, 4.5, 10.75, 5.75], "texture": "#0"}, + "south": {"uv": [10.5, 7.25, 10.75, 8.5], "texture": "#0"}, + "west": {"uv": [10.5, 10, 10.75, 11.25], "texture": "#0"}, + "up": {"uv": [14, 3, 13.75, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 13.75, 3, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 8.9, 6.125], + "to": [1.925, 9.4, 6.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 3, 14, 3.25], "texture": "#0"}, + "east": {"uv": [3.25, 13.75, 3.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 3.25, 14, 3.5], "texture": "#0"}, + "west": {"uv": [3.5, 13.75, 3.75, 14], "texture": "#0"}, + "up": {"uv": [14, 3.75, 13.75, 3.5], "texture": "#0"}, + "down": {"uv": [4, 13.75, 3.75, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 7.9, 6.125], + "to": [1.925, 8.4, 6.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 3.75, 14, 4], "texture": "#0"}, + "east": {"uv": [4, 13.75, 4.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 4, 14, 4.25], "texture": "#0"}, + "west": {"uv": [4.25, 13.75, 4.5, 14], "texture": "#0"}, + "up": {"uv": [14, 4.5, 13.75, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13.75, 4.5, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 8.4, 6.125], + "to": [1.925, 8.9, 7.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 4.5, 14, 4.75], "texture": "#0"}, + "east": {"uv": [12.75, 3, 13.25, 3.25], "texture": "#0"}, + "south": {"uv": [4.75, 13.75, 5, 14], "texture": "#0"}, + "west": {"uv": [12.75, 3.25, 13.25, 3.5], "texture": "#0"}, + "up": {"uv": [3.75, 13.25, 3.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 3.5, 12.75, 4], "texture": "#0"} + } + }, + { + "from": [1.425, 8.4, 4.125], + "to": [1.925, 8.9, 5.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 4.75, 14, 5], "texture": "#0"}, + "east": {"uv": [12.75, 4, 13.25, 4.25], "texture": "#0"}, + "south": {"uv": [5, 13.75, 5.25, 14], "texture": "#0"}, + "west": {"uv": [12.75, 4.25, 13.25, 4.5], "texture": "#0"}, + "up": {"uv": [4, 13.25, 3.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 4.5, 12.75, 5], "texture": "#0"} + } + }, + { + "from": [1.425, 8.9, 5.125], + "to": [1.925, 9.4, 5.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 5, 14, 5.25], "texture": "#0"}, + "east": {"uv": [5.25, 13.75, 5.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 5.25, 14, 5.5], "texture": "#0"}, + "west": {"uv": [5.5, 13.75, 5.75, 14], "texture": "#0"}, + "up": {"uv": [14, 5.75, 13.75, 5.5], "texture": "#0"}, + "down": {"uv": [6, 13.75, 5.75, 14], "texture": "#0"} + } + }, + { + "from": [1.425, 7.9, 5.125], + "to": [1.925, 8.4, 5.625], + "rotation": {"angle": 0, "axis": "y", "origin": [1.675, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 5.75, 14, 6], "texture": "#0"}, + "east": {"uv": [6, 13.75, 6.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 6, 14, 6.25], "texture": "#0"}, + "west": {"uv": [6.25, 13.75, 6.5, 14], "texture": "#0"}, + "up": {"uv": [14, 6.5, 13.75, 6.25], "texture": "#0"}, + "down": {"uv": [6.75, 13.75, 6.5, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 6, 8.125], + "to": [14.425, 6.5, 9.125], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 6.5, 14, 6.75], "texture": "#0"}, + "east": {"uv": [6.75, 13.75, 7, 14], "texture": "#0"}, + "south": {"uv": [13.75, 6.75, 14, 7], "texture": "#0"}, + "west": {"uv": [7, 13.75, 7.25, 14], "texture": "#0"}, + "up": {"uv": [14, 7.25, 13.75, 7], "texture": "#0"}, + "down": {"uv": [7.5, 13.75, 7.25, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 4.5, 7.625], + "to": [14.425, 8, 8.125], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [11.75, 7.25, 12, 8.25], "texture": "#0"}, + "east": {"uv": [8.5, 11.75, 8.75, 12.75], "texture": "#0"}, + "south": {"uv": [8.75, 11.75, 9, 12.75], "texture": "#0"}, + "west": {"uv": [9, 11.75, 9.25, 12.75], "texture": "#0"}, + "up": {"uv": [14, 7.5, 13.75, 7.25], "texture": "#0"}, + "down": {"uv": [7.75, 13.75, 7.5, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 6, 6.625], + "to": [14.425, 6.5, 7.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 7.5, 14, 7.75], "texture": "#0"}, + "east": {"uv": [7.75, 13.75, 8, 14], "texture": "#0"}, + "south": {"uv": [13.75, 7.75, 14, 8], "texture": "#0"}, + "west": {"uv": [8, 13.75, 8.25, 14], "texture": "#0"}, + "up": {"uv": [14, 8.25, 13.75, 8], "texture": "#0"}, + "down": {"uv": [8.5, 13.75, 8.25, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 9.2, 7.325], + "to": [14.425, 9.7, 9.325], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 8.25, 14, 8.5], "texture": "#0"}, + "east": {"uv": [5.25, 12.75, 5.75, 13], "texture": "#0"}, + "south": {"uv": [8.5, 13.75, 8.75, 14], "texture": "#0"}, + "west": {"uv": [12.75, 5.5, 13.25, 5.75], "texture": "#0"}, + "up": {"uv": [6, 13.25, 5.75, 12.75], "texture": "#0"}, + "down": {"uv": [6.25, 12.75, 6, 13.25], "texture": "#0"} + } + }, + { + "from": [13.925, 6.7, 9.325], + "to": [14.425, 11.7, 9.825], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [10.75, 0, 11, 1.25], "texture": "#0"}, + "east": {"uv": [10.75, 1.25, 11, 2.5], "texture": "#0"}, + "south": {"uv": [10.75, 2.5, 11, 3.75], "texture": "#0"}, + "west": {"uv": [10.75, 4.5, 11, 5.75], "texture": "#0"}, + "up": {"uv": [14, 8.75, 13.75, 8.5], "texture": "#0"}, + "down": {"uv": [9, 13.75, 8.75, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 9.2, 9.825], + "to": [14.425, 9.7, 11.825], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 8.75, 14, 9], "texture": "#0"}, + "east": {"uv": [6.25, 12.75, 6.75, 13], "texture": "#0"}, + "south": {"uv": [9, 13.75, 9.25, 14], "texture": "#0"}, + "west": {"uv": [6.75, 12.75, 7.25, 13], "texture": "#0"}, + "up": {"uv": [7.5, 13.25, 7.25, 12.75], "texture": "#0"}, + "down": {"uv": [7.75, 12.75, 7.5, 13.25], "texture": "#0"} + } + }, + { + "from": [13.925, 9.7, 9.825], + "to": [14.425, 10.2, 10.325], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 9, 14, 9.25], "texture": "#0"}, + "east": {"uv": [9.25, 13.75, 9.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 9.25, 14, 9.5], "texture": "#0"}, + "west": {"uv": [9.5, 13.75, 9.75, 14], "texture": "#0"}, + "up": {"uv": [14, 9.75, 13.75, 9.5], "texture": "#0"}, + "down": {"uv": [10, 13.75, 9.75, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 9.7, 8.825], + "to": [14.425, 10.2, 9.325], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 9.75, 14, 10], "texture": "#0"}, + "east": {"uv": [10, 13.75, 10.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 10, 14, 10.25], "texture": "#0"}, + "west": {"uv": [10.25, 13.75, 10.5, 14], "texture": "#0"}, + "up": {"uv": [14, 10.5, 13.75, 10.25], "texture": "#0"}, + "down": {"uv": [10.75, 13.75, 10.5, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 8.7, 9.825], + "to": [14.425, 9.2, 10.325], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 10.5, 14, 10.75], "texture": "#0"}, + "east": {"uv": [10.75, 13.75, 11, 14], "texture": "#0"}, + "south": {"uv": [13.75, 10.75, 14, 11], "texture": "#0"}, + "west": {"uv": [11, 13.75, 11.25, 14], "texture": "#0"}, + "up": {"uv": [14, 11.25, 13.75, 11], "texture": "#0"}, + "down": {"uv": [11.5, 13.75, 11.25, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 8.7, 8.825], + "to": [14.425, 9.2, 9.325], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 11.25, 14, 11.5], "texture": "#0"}, + "east": {"uv": [11.5, 13.75, 11.75, 14], "texture": "#0"}, + "south": {"uv": [13.75, 11.5, 14, 11.75], "texture": "#0"}, + "west": {"uv": [11.75, 13.75, 12, 14], "texture": "#0"}, + "up": {"uv": [14, 12, 13.75, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 13.75, 12, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 10.2, 10.325], + "to": [14.425, 10.7, 10.825], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 12, 14, 12.25], "texture": "#0"}, + "east": {"uv": [12.25, 13.75, 12.5, 14], "texture": "#0"}, + "south": {"uv": [13.75, 12.25, 14, 12.5], "texture": "#0"}, + "west": {"uv": [12.5, 13.75, 12.75, 14], "texture": "#0"}, + "up": {"uv": [14, 12.75, 13.75, 12.5], "texture": "#0"}, + "down": {"uv": [13, 13.75, 12.75, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 8.2, 10.325], + "to": [14.425, 8.7, 10.825], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 12.75, 14, 13], "texture": "#0"}, + "east": {"uv": [13, 13.75, 13.25, 14], "texture": "#0"}, + "south": {"uv": [13.75, 13, 14, 13.25], "texture": "#0"}, + "west": {"uv": [13.25, 13.75, 13.5, 14], "texture": "#0"}, + "up": {"uv": [14, 13.5, 13.75, 13.25], "texture": "#0"}, + "down": {"uv": [13.75, 13.75, 13.5, 14], "texture": "#0"} + } + }, + { + "from": [13.925, 8.2, 8.325], + "to": [14.425, 8.7, 8.825], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [13.75, 13.5, 14, 13.75], "texture": "#0"}, + "east": {"uv": [13.75, 13.75, 14, 14], "texture": "#0"}, + "south": {"uv": [0, 14, 0.25, 14.25], "texture": "#0"}, + "west": {"uv": [14, 0, 14.25, 0.25], "texture": "#0"}, + "up": {"uv": [0.5, 14.25, 0.25, 14], "texture": "#0"}, + "down": {"uv": [14.25, 0.25, 14, 0.5], "texture": "#0"} + } + }, + { + "from": [13.925, 10.2, 8.325], + "to": [14.425, 10.7, 8.825], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [0.5, 14, 0.75, 14.25], "texture": "#0"}, + "east": {"uv": [14, 0.5, 14.25, 0.75], "texture": "#0"}, + "south": {"uv": [0.75, 14, 1, 14.25], "texture": "#0"}, + "west": {"uv": [14, 0.75, 14.25, 1], "texture": "#0"}, + "up": {"uv": [1.25, 14.25, 1, 14], "texture": "#0"}, + "down": {"uv": [14.25, 1, 14, 1.25], "texture": "#0"} + } + }, + { + "from": [13.925, 6.4, 5.625], + "to": [14.425, 10.9, 6.125], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [10.75, 7.25, 11, 8.5], "texture": "#0"}, + "east": {"uv": [10.75, 10, 11, 11.25], "texture": "#0"}, + "south": {"uv": [11, 0, 11.25, 1.25], "texture": "#0"}, + "west": {"uv": [11, 1.25, 11.25, 2.5], "texture": "#0"}, + "up": {"uv": [1.5, 14.25, 1.25, 14], "texture": "#0"}, + "down": {"uv": [14.25, 1.25, 14, 1.5], "texture": "#0"} + } + }, + { + "from": [13.925, 8.9, 6.125], + "to": [14.425, 9.4, 6.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [1.5, 14, 1.75, 14.25], "texture": "#0"}, + "east": {"uv": [14, 1.5, 14.25, 1.75], "texture": "#0"}, + "south": {"uv": [1.75, 14, 2, 14.25], "texture": "#0"}, + "west": {"uv": [14, 1.75, 14.25, 2], "texture": "#0"}, + "up": {"uv": [2.25, 14.25, 2, 14], "texture": "#0"}, + "down": {"uv": [14.25, 2, 14, 2.25], "texture": "#0"} + } + }, + { + "from": [13.925, 7.9, 6.125], + "to": [14.425, 8.4, 6.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [2.25, 14, 2.5, 14.25], "texture": "#0"}, + "east": {"uv": [14, 2.25, 14.25, 2.5], "texture": "#0"}, + "south": {"uv": [2.5, 14, 2.75, 14.25], "texture": "#0"}, + "west": {"uv": [14, 2.5, 14.25, 2.75], "texture": "#0"}, + "up": {"uv": [3, 14.25, 2.75, 14], "texture": "#0"}, + "down": {"uv": [14.25, 2.75, 14, 3], "texture": "#0"} + } + }, + { + "from": [13.925, 8.4, 6.125], + "to": [14.425, 8.9, 7.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [3, 14, 3.25, 14.25], "texture": "#0"}, + "east": {"uv": [7.75, 12.75, 8.25, 13], "texture": "#0"}, + "south": {"uv": [14, 3, 14.25, 3.25], "texture": "#0"}, + "west": {"uv": [12.75, 8.25, 13.25, 8.5], "texture": "#0"}, + "up": {"uv": [8.75, 13.25, 8.5, 12.75], "texture": "#0"}, + "down": {"uv": [9, 12.75, 8.75, 13.25], "texture": "#0"} + } + }, + { + "from": [13.925, 8.4, 4.125], + "to": [14.425, 8.9, 5.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [3.25, 14, 3.5, 14.25], "texture": "#0"}, + "east": {"uv": [9.5, 12.75, 10, 13], "texture": "#0"}, + "south": {"uv": [14, 3.25, 14.25, 3.5], "texture": "#0"}, + "west": {"uv": [12.75, 10, 13.25, 10.25], "texture": "#0"}, + "up": {"uv": [9.25, 13.25, 9, 12.75], "texture": "#0"}, + "down": {"uv": [10.5, 12.75, 10.25, 13.25], "texture": "#0"} + } + }, + { + "from": [13.925, 8.9, 5.125], + "to": [14.425, 9.4, 5.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [3.5, 14, 3.75, 14.25], "texture": "#0"}, + "east": {"uv": [14, 3.5, 14.25, 3.75], "texture": "#0"}, + "south": {"uv": [3.75, 14, 4, 14.25], "texture": "#0"}, + "west": {"uv": [14, 3.75, 14.25, 4], "texture": "#0"}, + "up": {"uv": [4.25, 14.25, 4, 14], "texture": "#0"}, + "down": {"uv": [14.25, 4, 14, 4.25], "texture": "#0"} + } + }, + { + "from": [13.925, 7.9, 5.125], + "to": [14.425, 8.4, 5.625], + "rotation": {"angle": 0, "axis": "y", "origin": [14.175, 8.35, 7.85]}, + "faces": { + "north": {"uv": [4.25, 14, 4.5, 14.25], "texture": "#0"}, + "east": {"uv": [14, 4.25, 14.25, 4.5], "texture": "#0"}, + "south": {"uv": [4.5, 14, 4.75, 14.25], "texture": "#0"}, + "west": {"uv": [14, 4.5, 14.25, 4.75], "texture": "#0"}, + "up": {"uv": [5, 14.25, 4.75, 14], "texture": "#0"}, + "down": {"uv": [14.25, 4.75, 14, 5], "texture": "#0"} + } + }, + { + "from": [6.9, 14, 9.7], + "to": [7.9, 14.5, 10.2], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [5, 14, 5.25, 14.25], "texture": "#0"}, + "east": {"uv": [14, 5, 14.25, 5.25], "texture": "#0"}, + "south": {"uv": [5.25, 14, 5.5, 14.25], "texture": "#0"}, + "west": {"uv": [14, 5.25, 14.25, 5.5], "texture": "#0"}, + "up": {"uv": [5.75, 14.25, 5.5, 14], "texture": "#0"}, + "down": {"uv": [14.25, 5.5, 14, 5.75], "texture": "#0"} + } + }, + { + "from": [7.9, 14, 8.2], + "to": [8.4, 14.5, 11.7], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [5.75, 14, 6, 14.25], "texture": "#0"}, + "east": {"uv": [9.25, 11.75, 10.25, 12], "texture": "#0"}, + "south": {"uv": [14, 5.75, 14.25, 6], "texture": "#0"}, + "west": {"uv": [11.75, 10, 12.75, 10.25], "texture": "#0"}, + "up": {"uv": [10.5, 12.75, 10.25, 11.75], "texture": "#0"}, + "down": {"uv": [12, 10.25, 11.75, 11.25], "texture": "#0"} + } + }, + { + "from": [8.4, 14, 9.7], + "to": [9.4, 14.5, 10.2], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [6, 14, 6.25, 14.25], "texture": "#0"}, + "east": {"uv": [14, 6, 14.25, 6.25], "texture": "#0"}, + "south": {"uv": [6.25, 14, 6.5, 14.25], "texture": "#0"}, + "west": {"uv": [14, 6.25, 14.25, 6.5], "texture": "#0"}, + "up": {"uv": [6.75, 14.25, 6.5, 14], "texture": "#0"}, + "down": {"uv": [14.25, 6.5, 14, 6.75], "texture": "#0"} + } + }, + { + "from": [6.7, 14, 6.5], + "to": [8.7, 14.5, 7], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [10.5, 12.75, 11, 13], "texture": "#0"}, + "east": {"uv": [6.75, 14, 7, 14.25], "texture": "#0"}, + "south": {"uv": [12.75, 10.75, 13.25, 11], "texture": "#0"}, + "west": {"uv": [14, 6.75, 14.25, 7], "texture": "#0"}, + "up": {"uv": [13.25, 11.25, 12.75, 11], "texture": "#0"}, + "down": {"uv": [13.25, 11.25, 12.75, 11.5], "texture": "#0"} + } + }, + { + "from": [6.2, 14, 4.5], + "to": [6.7, 14.5, 9.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [7, 14, 7.25, 14.25], "texture": "#0"}, + "east": {"uv": [10.25, 5.75, 11.5, 6], "texture": "#0"}, + "south": {"uv": [14, 7, 14.25, 7.25], "texture": "#0"}, + "west": {"uv": [10.25, 8.5, 11.5, 8.75], "texture": "#0"}, + "up": {"uv": [11.25, 3.75, 11, 2.5], "texture": "#0"}, + "down": {"uv": [11.25, 4.5, 11, 5.75], "texture": "#0"} + } + }, + { + "from": [4.2, 14, 6.5], + "to": [6.2, 14.5, 7], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [11.5, 12.75, 12, 13], "texture": "#0"}, + "east": {"uv": [7.25, 14, 7.5, 14.25], "texture": "#0"}, + "south": {"uv": [12, 12.75, 12.5, 13], "texture": "#0"}, + "west": {"uv": [14, 7.25, 14.25, 7.5], "texture": "#0"}, + "up": {"uv": [13.25, 12.5, 12.75, 12.25], "texture": "#0"}, + "down": {"uv": [13, 12.75, 12.5, 13], "texture": "#0"} + } + }, + { + "from": [5.7, 14, 6], + "to": [6.2, 14.5, 6.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [7.5, 14, 7.75, 14.25], "texture": "#0"}, + "east": {"uv": [14, 7.5, 14.25, 7.75], "texture": "#0"}, + "south": {"uv": [7.75, 14, 8, 14.25], "texture": "#0"}, + "west": {"uv": [14, 7.75, 14.25, 8], "texture": "#0"}, + "up": {"uv": [8.25, 14.25, 8, 14], "texture": "#0"}, + "down": {"uv": [14.25, 8, 14, 8.25], "texture": "#0"} + } + }, + { + "from": [6.7, 14, 6], + "to": [7.2, 14.5, 6.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [8.25, 14, 8.5, 14.25], "texture": "#0"}, + "east": {"uv": [14, 8.25, 14.25, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 14, 8.75, 14.25], "texture": "#0"}, + "west": {"uv": [14, 8.5, 14.25, 8.75], "texture": "#0"}, + "up": {"uv": [9, 14.25, 8.75, 14], "texture": "#0"}, + "down": {"uv": [14.25, 8.75, 14, 9], "texture": "#0"} + } + }, + { + "from": [5.7, 14, 7], + "to": [6.2, 14.5, 7.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [9, 14, 9.25, 14.25], "texture": "#0"}, + "east": {"uv": [14, 9, 14.25, 9.25], "texture": "#0"}, + "south": {"uv": [9.25, 14, 9.5, 14.25], "texture": "#0"}, + "west": {"uv": [14, 9.25, 14.25, 9.5], "texture": "#0"}, + "up": {"uv": [9.75, 14.25, 9.5, 14], "texture": "#0"}, + "down": {"uv": [14.25, 9.5, 14, 9.75], "texture": "#0"} + } + }, + { + "from": [6.7, 14, 7], + "to": [7.2, 14.5, 7.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [9.75, 14, 10, 14.25], "texture": "#0"}, + "east": {"uv": [14, 9.75, 14.25, 10], "texture": "#0"}, + "south": {"uv": [10, 14, 10.25, 14.25], "texture": "#0"}, + "west": {"uv": [14, 10, 14.25, 10.25], "texture": "#0"}, + "up": {"uv": [10.5, 14.25, 10.25, 14], "texture": "#0"}, + "down": {"uv": [14.25, 10.25, 14, 10.5], "texture": "#0"} + } + }, + { + "from": [5.2, 14, 5.5], + "to": [5.7, 14.5, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [10.5, 14, 10.75, 14.25], "texture": "#0"}, + "east": {"uv": [14, 10.5, 14.25, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 14, 11, 14.25], "texture": "#0"}, + "west": {"uv": [14, 10.75, 14.25, 11], "texture": "#0"}, + "up": {"uv": [11.25, 14.25, 11, 14], "texture": "#0"}, + "down": {"uv": [14.25, 11, 14, 11.25], "texture": "#0"} + } + }, + { + "from": [5.2, 14, 7.5], + "to": [5.7, 14.5, 8], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [11.25, 14, 11.5, 14.25], "texture": "#0"}, + "east": {"uv": [14, 11.25, 14.25, 11.5], "texture": "#0"}, + "south": {"uv": [11.5, 14, 11.75, 14.25], "texture": "#0"}, + "west": {"uv": [14, 11.5, 14.25, 11.75], "texture": "#0"}, + "up": {"uv": [12, 14.25, 11.75, 14], "texture": "#0"}, + "down": {"uv": [14.25, 11.75, 14, 12], "texture": "#0"} + } + }, + { + "from": [7.2, 14, 7.5], + "to": [7.7, 14.5, 8], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [12, 14, 12.25, 14.25], "texture": "#0"}, + "east": {"uv": [14, 12, 14.25, 12.25], "texture": "#0"}, + "south": {"uv": [12.25, 14, 12.5, 14.25], "texture": "#0"}, + "west": {"uv": [14, 12.25, 14.25, 12.5], "texture": "#0"}, + "up": {"uv": [12.75, 14.25, 12.5, 14], "texture": "#0"}, + "down": {"uv": [14.25, 12.5, 14, 12.75], "texture": "#0"} + } + }, + { + "from": [7.2, 14, 5.5], + "to": [7.7, 14.5, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [12.75, 14, 13, 14.25], "texture": "#0"}, + "east": {"uv": [14, 12.75, 14.25, 13], "texture": "#0"}, + "south": {"uv": [13, 14, 13.25, 14.25], "texture": "#0"}, + "west": {"uv": [14, 13, 14.25, 13.25], "texture": "#0"}, + "up": {"uv": [13.5, 14.25, 13.25, 14], "texture": "#0"}, + "down": {"uv": [14.25, 13.25, 14, 13.5], "texture": "#0"} + } + }, + { + "from": [9.9, 14, 5.3], + "to": [10.4, 14.5, 9.8], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [13.5, 14, 13.75, 14.25], "texture": "#0"}, + "east": {"uv": [10.25, 8.75, 11.5, 9], "texture": "#0"}, + "south": {"uv": [14, 13.5, 14.25, 13.75], "texture": "#0"}, + "west": {"uv": [10.25, 9, 11.5, 9.25], "texture": "#0"}, + "up": {"uv": [11.25, 8.5, 11, 7.25], "texture": "#0"}, + "down": {"uv": [11.25, 10, 11, 11.25], "texture": "#0"} + } + }, + { + "from": [9.4, 14, 6.8], + "to": [9.9, 14.5, 7.3], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [13.75, 14, 14, 14.25], "texture": "#0"}, + "east": {"uv": [14, 13.75, 14.25, 14], "texture": "#0"}, + "south": {"uv": [14, 14, 14.25, 14.25], "texture": "#0"}, + "west": {"uv": [0, 14.25, 0.25, 14.5], "texture": "#0"}, + "up": {"uv": [14.5, 0.25, 14.25, 0], "texture": "#0"}, + "down": {"uv": [0.5, 14.25, 0.25, 14.5], "texture": "#0"} + } + }, + { + "from": [9.4, 14, 7.8], + "to": [9.9, 14.5, 8.3], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [14.25, 0.25, 14.5, 0.5], "texture": "#0"}, + "east": {"uv": [0.5, 14.25, 0.75, 14.5], "texture": "#0"}, + "south": {"uv": [14.25, 0.5, 14.5, 0.75], "texture": "#0"}, + "west": {"uv": [0.75, 14.25, 1, 14.5], "texture": "#0"}, + "up": {"uv": [14.5, 1, 14.25, 0.75], "texture": "#0"}, + "down": {"uv": [1.25, 14.25, 1, 14.5], "texture": "#0"} + } + }, + { + "from": [8.4, 14, 7.3], + "to": [9.9, 14.5, 7.8], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [12.75, 12.5, 13.25, 12.75], "texture": "#0"}, + "east": {"uv": [14.25, 1, 14.5, 1.25], "texture": "#0"}, + "south": {"uv": [0, 13, 0.5, 13.25], "texture": "#0"}, + "west": {"uv": [1.25, 14.25, 1.5, 14.5], "texture": "#0"}, + "up": {"uv": [1, 13.25, 0.5, 13], "texture": "#0"}, + "down": {"uv": [1.5, 13, 1, 13.25], "texture": "#0"} + } + }, + { + "from": [10.4, 14, 7.3], + "to": [11.9, 14.5, 7.8], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [13, 1, 13.5, 1.25], "texture": "#0"}, + "east": {"uv": [14.25, 1.25, 14.5, 1.5], "texture": "#0"}, + "south": {"uv": [13, 1.25, 13.5, 1.5], "texture": "#0"}, + "west": {"uv": [1.5, 14.25, 1.75, 14.5], "texture": "#0"}, + "up": {"uv": [2, 13.25, 1.5, 13], "texture": "#0"}, + "down": {"uv": [13.5, 1.5, 13, 1.75], "texture": "#0"} + } + }, + { + "from": [10.4, 14, 6.8], + "to": [10.9, 14.5, 7.3], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [14.25, 1.5, 14.5, 1.75], "texture": "#0"}, + "east": {"uv": [1.75, 14.25, 2, 14.5], "texture": "#0"}, + "south": {"uv": [14.25, 1.75, 14.5, 2], "texture": "#0"}, + "west": {"uv": [2, 14.25, 2.25, 14.5], "texture": "#0"}, + "up": {"uv": [14.5, 2.25, 14.25, 2], "texture": "#0"}, + "down": {"uv": [2.5, 14.25, 2.25, 14.5], "texture": "#0"} + } + }, + { + "from": [10.4, 14, 7.8], + "to": [10.9, 14.5, 8.3], + "rotation": {"angle": 0, "axis": "x", "origin": [8.175, 14.25, 7.85]}, + "faces": { + "north": {"uv": [14.25, 2.25, 14.5, 2.5], "texture": "#0"}, + "east": {"uv": [2.5, 14.25, 2.75, 14.5], "texture": "#0"}, + "south": {"uv": [14.25, 2.5, 14.5, 2.75], "texture": "#0"}, + "west": {"uv": [2.75, 14.25, 3, 14.5], "texture": "#0"}, + "up": {"uv": [14.5, 3, 14.25, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 14.25, 3, 14.5], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [ + { + "name": "side1", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [41, 42, 43] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [55, 56, 57, 58, 59, 60, 61] + } + ] + }, + { + "name": "side2", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [62, 63, 64] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [76, 77, 78, 79, 80, 81, 82] + } + ] + }, + { + "name": "side3", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [83, 84, 85] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [97, 98, 99, 100, 101, 102, 103] + } + ] + }, + { + "name": "side4", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [104, 105, 106] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [118, 119, 120, 121, 122, 123, 124] + } + ] + }, + { + "name": "top", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [ + { + "name": "group", + "origin": [7.5, 8.5, 14], + "color": 0, + "children": [125, 126, 127] + }, + { + "name": "group", + "origin": [6, 8, 1], + "color": 0, + "children": [128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138] + }, + { + "name": "group", + "origin": [6, 3.5, 14], + "color": 0, + "children": [139, 140, 141, 142, 143, 144, 145] + } + ] + } + ] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_pump_fluids.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_pump_fluids.json new file mode 100644 index 00000000..4940b064 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_pump_fluids.json @@ -0,0 +1,992 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_pump_fluids", + "particle": "utilitiesinexcess:upgrade_pump_fluids" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [3.5, 12, 3.75, 12.5], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [3.75, 12, 4, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 4, 12, 3.75], "texture": "#0"}, + "down": {"uv": [4.5, 12, 4, 12.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.5, 12, 4.75, 12.5], "texture": "#0"}, + "east": {"uv": [4.75, 12, 5, 12.5], "texture": "#0"}, + "south": {"uv": [12, 6, 12.25, 6.5], "texture": "#0"}, + "west": {"uv": [12, 6.5, 12.25, 7], "texture": "#0"}, + "up": {"uv": [7.5, 13, 7.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.25, 12.75, 7.5], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.75, 12, 8, 12.5], "texture": "#0"}, + "east": {"uv": [8, 12, 8.25, 12.5], "texture": "#0"}, + "south": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "west": {"uv": [8.5, 12, 8.75, 12.5], "texture": "#0"}, + "up": {"uv": [7.75, 13, 7.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.5, 12.75, 7.75], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8.75, 12, 9, 12.5], "texture": "#0"}, + "east": {"uv": [12, 8.75, 12.25, 9.25], "texture": "#0"}, + "south": {"uv": [9, 12, 9.25, 12.5], "texture": "#0"}, + "west": {"uv": [12, 9.25, 12.25, 9.75], "texture": "#0"}, + "up": {"uv": [13, 8, 12.75, 7.75], "texture": "#0"}, + "down": {"uv": [13, 8, 12.75, 8.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 11, 12.25, 11.5], "texture": "#0"}, + "east": {"uv": [10.75, 3.5, 11.25, 4], "texture": "#0"}, + "south": {"uv": [12, 11.5, 12.25, 12], "texture": "#0"}, + "west": {"uv": [5, 10.75, 5.5, 11.25], "texture": "#0"}, + "up": {"uv": [12, 12.5, 11.75, 12], "texture": "#0"}, + "down": {"uv": [12.25, 12, 12, 12.5], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 0, 12.5, 0.5], "texture": "#0"}, + "east": {"uv": [12.25, 0.5, 12.5, 1], "texture": "#0"}, + "south": {"uv": [12.25, 1, 12.5, 1.5], "texture": "#0"}, + "west": {"uv": [1.5, 12.25, 1.75, 12.75], "texture": "#0"}, + "up": {"uv": [13, 8.5, 12.75, 8.25], "texture": "#0"}, + "down": {"uv": [13, 8.5, 12.75, 8.75], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 1.5, 12.5, 2], "texture": "#0"}, + "east": {"uv": [1.75, 12.25, 2, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 2, 12.5, 2.5], "texture": "#0"}, + "west": {"uv": [12.25, 2.5, 12.5, 3], "texture": "#0"}, + "up": {"uv": [13, 9, 12.75, 8.75], "texture": "#0"}, + "down": {"uv": [13, 9, 12.75, 9.25], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 3, 12.5, 3.5], "texture": "#0"}, + "east": {"uv": [4, 12.25, 4.25, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 4, 12.5, 4.5], "texture": "#0"}, + "west": {"uv": [4.25, 12.25, 4.5, 12.75], "texture": "#0"}, + "up": {"uv": [9.5, 13, 9.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 9.25, 12.75, 9.5], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.5, 10.75, 6, 11.25], "texture": "#0"}, + "east": {"uv": [12.25, 4.5, 12.5, 5], "texture": "#0"}, + "south": {"uv": [6, 10.75, 6.5, 11.25], "texture": "#0"}, + "west": {"uv": [5, 12.25, 5.25, 12.75], "texture": "#0"}, + "up": {"uv": [12.5, 10, 12, 9.75], "texture": "#0"}, + "down": {"uv": [12.75, 3.5, 12.25, 3.75], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 5, 12.5, 5.5], "texture": "#0"}, + "east": {"uv": [5.25, 12.25, 5.5, 12.75], "texture": "#0"}, + "south": {"uv": [5.5, 12.25, 5.75, 12.75], "texture": "#0"}, + "west": {"uv": [12.25, 5.5, 12.5, 6], "texture": "#0"}, + "up": {"uv": [9.75, 13, 9.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 9.5, 12.75, 9.75], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "east": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 6, 12.5, 6.5], "texture": "#0"}, + "west": {"uv": [6.25, 12.25, 6.5, 12.75], "texture": "#0"}, + "up": {"uv": [10, 13, 9.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 9.75, 12.75, 10], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 12.25, 6.75, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 6.5, 12.5, 7], "texture": "#0"}, + "south": {"uv": [6.75, 12.25, 7, 12.75], "texture": "#0"}, + "west": {"uv": [7, 12.25, 7.25, 12.75], "texture": "#0"}, + "up": {"uv": [10.25, 13, 10, 12.75], "texture": "#0"}, + "down": {"uv": [13, 10, 12.75, 10.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 7, 12.5, 7.5], "texture": "#0"}, + "east": {"uv": [6.5, 10.75, 7, 11.25], "texture": "#0"}, + "south": {"uv": [7.25, 12.25, 7.5, 12.75], "texture": "#0"}, + "west": {"uv": [7, 10.75, 7.5, 11.25], "texture": "#0"}, + "up": {"uv": [7.75, 12.75, 7.5, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 7.5, 12.25, 8], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 8, 12.5, 8.5], "texture": "#0"}, + "east": {"uv": [12.25, 8.5, 12.5, 9], "texture": "#0"}, + "south": {"uv": [12.25, 9, 12.5, 9.5], "texture": "#0"}, + "west": {"uv": [9.25, 12.25, 9.5, 12.75], "texture": "#0"}, + "up": {"uv": [10.5, 13, 10.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 10.25, 12.75, 10.5], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9.5, 12.25, 9.75, 12.75], "texture": "#0"}, + "east": {"uv": [9.75, 12.25, 10, 12.75], "texture": "#0"}, + "south": {"uv": [10, 12.25, 10.25, 12.75], "texture": "#0"}, + "west": {"uv": [12.25, 10, 12.5, 10.5], "texture": "#0"}, + "up": {"uv": [10.75, 13, 10.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 10.5, 12.75, 10.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 12.25, 10.5, 12.75], "texture": "#0"}, + "east": {"uv": [10.5, 12.25, 10.75, 12.75], "texture": "#0"}, + "south": {"uv": [12.25, 10.5, 12.5, 11], "texture": "#0"}, + "west": {"uv": [10.75, 12.25, 11, 12.75], "texture": "#0"}, + "up": {"uv": [11, 13, 10.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 10.75, 12.75, 11], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 10.75, 8, 11.25], "texture": "#0"}, + "east": {"uv": [11, 12.25, 11.25, 12.75], "texture": "#0"}, + "south": {"uv": [10.75, 10.25, 11.25, 10.75], "texture": "#0"}, + "west": {"uv": [12.25, 11, 12.5, 11.5], "texture": "#0"}, + "up": {"uv": [12.75, 9.75, 12.25, 9.5], "texture": "#0"}, + "down": {"uv": [11.75, 12.25, 11.25, 12.5], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 11.5, 12.75, 11.75], "texture": "#0"}, + "east": {"uv": [11, 12.75, 11.25, 13], "texture": "#0"}, + "south": {"uv": [12.25, 11.75, 12.75, 12], "texture": "#0"}, + "west": {"uv": [12.75, 11, 13, 11.25], "texture": "#0"}, + "up": {"uv": [12.75, 12.25, 12.25, 12], "texture": "#0"}, + "down": {"uv": [12.75, 12.25, 12.25, 12.5], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 12.5, 0.25, 13], "texture": "#0"}, + "east": {"uv": [10.75, 10.75, 11.25, 11.25], "texture": "#0"}, + "south": {"uv": [12.5, 0, 12.75, 0.5], "texture": "#0"}, + "west": {"uv": [11, 0, 11.5, 0.5], "texture": "#0"}, + "up": {"uv": [0.5, 13, 0.25, 12.5], "texture": "#0"}, + "down": {"uv": [0.75, 12.5, 0.5, 13], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 11.5, 13, 11.75], "texture": "#0"}, + "east": {"uv": [12.5, 0.5, 13, 0.75], "texture": "#0"}, + "south": {"uv": [12.75, 11.75, 13, 12], "texture": "#0"}, + "west": {"uv": [0.75, 12.5, 1.25, 12.75], "texture": "#0"}, + "up": {"uv": [12.75, 1.25, 12.5, 0.75], "texture": "#0"}, + "down": {"uv": [1.5, 12.5, 1.25, 13], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11, 0.5, 11.5, 1], "texture": "#0"}, + "east": {"uv": [12.5, 1.25, 12.75, 1.75], "texture": "#0"}, + "south": {"uv": [11, 1, 11.5, 1.5], "texture": "#0"}, + "west": {"uv": [12.5, 1.75, 12.75, 2.25], "texture": "#0"}, + "up": {"uv": [13, 2.5, 12.5, 2.25], "texture": "#0"}, + "down": {"uv": [13, 2.5, 12.5, 2.75], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.75, 12.5, 3.25, 12.75], "texture": "#0"}, + "east": {"uv": [12.75, 12, 13, 12.25], "texture": "#0"}, + "south": {"uv": [12.5, 2.75, 13, 3], "texture": "#0"}, + "west": {"uv": [12.75, 12.25, 13, 12.5], "texture": "#0"}, + "up": {"uv": [13, 3.25, 12.5, 3], "texture": "#0"}, + "down": {"uv": [3.75, 12.5, 3.25, 12.75], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3.75, 12.5, 4, 13], "texture": "#0"}, + "east": {"uv": [11, 1.5, 11.5, 2], "texture": "#0"}, + "south": {"uv": [12.5, 3.75, 12.75, 4.25], "texture": "#0"}, + "west": {"uv": [11, 2.25, 11.5, 2.75], "texture": "#0"}, + "up": {"uv": [12.75, 4.75, 12.5, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 12.5, 4.5, 13], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 12.5, 13, 12.75], "texture": "#0"}, + "east": {"uv": [12.5, 3.25, 13, 3.5], "texture": "#0"}, + "south": {"uv": [12.75, 12.75, 13, 13], "texture": "#0"}, + "west": {"uv": [12.5, 4.75, 13, 5], "texture": "#0"}, + "up": {"uv": [5, 13, 4.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 5, 12.5, 5.5], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 13, 0.25, 13.25], "texture": "#0"}, + "east": {"uv": [11, 2.75, 12, 3], "texture": "#0"}, + "south": {"uv": [13, 0, 13.25, 0.25], "texture": "#0"}, + "west": {"uv": [11, 3, 12, 3.25], "texture": "#0"}, + "up": {"uv": [11.25, 5, 11, 4], "texture": "#0"}, + "down": {"uv": [11.25, 5, 11, 6], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 5.5, 12.75, 6], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [12.5, 6, 12.75, 6.5], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [11.25, 7, 11, 6], "texture": "#0"}, + "down": {"uv": [11.5, 3.25, 11.25, 4.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "east": {"uv": [0.25, 13, 0.5, 13.25], "texture": "#0"}, + "south": {"uv": [11.25, 4.5, 12.25, 4.75], "texture": "#0"}, + "west": {"uv": [13, 0.25, 13.25, 0.5], "texture": "#0"}, + "up": {"uv": [12.25, 5, 11.25, 4.75], "texture": "#0"}, + "down": {"uv": [6, 11.25, 5, 11.5], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 4, 11, 4.5], "texture": "#0"}, + "east": {"uv": [12.5, 6.5, 12.75, 7], "texture": "#0"}, + "south": {"uv": [10, 4.5, 11, 5], "texture": "#0"}, + "west": {"uv": [12.5, 7, 12.75, 7.5], "texture": "#0"}, + "up": {"uv": [12.25, 5.25, 11.25, 5], "texture": "#0"}, + "down": {"uv": [12.25, 5.25, 11.25, 5.5], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.5, 13, 0.75, 13.25], "texture": "#0"}, + "east": {"uv": [11.25, 5.5, 12.25, 5.75], "texture": "#0"}, + "south": {"uv": [13, 0.5, 13.25, 0.75], "texture": "#0"}, + "west": {"uv": [11.25, 5.75, 12.25, 6], "texture": "#0"}, + "up": {"uv": [6.25, 12.25, 6, 11.25], "texture": "#0"}, + "down": {"uv": [11.5, 6, 11.25, 7], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 7.5, 12.75, 8], "texture": "#0"}, + "east": {"uv": [10, 5, 11, 5.5], "texture": "#0"}, + "south": {"uv": [7.75, 12.5, 8, 13], "texture": "#0"}, + "west": {"uv": [10, 5.5, 11, 6], "texture": "#0"}, + "up": {"uv": [6.5, 12.25, 6.25, 11.25], "texture": "#0"}, + "down": {"uv": [6.75, 11.25, 6.5, 12.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.75, 11.25, 7.75, 11.5], "texture": "#0"}, + "east": {"uv": [13, 0.75, 13.25, 1], "texture": "#0"}, + "south": {"uv": [11.25, 10, 12.25, 10.25], "texture": "#0"}, + "west": {"uv": [13, 1, 13.25, 1.25], "texture": "#0"}, + "up": {"uv": [12.25, 10.5, 11.25, 10.25], "texture": "#0"}, + "down": {"uv": [12.25, 10.5, 11.25, 10.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6, 11, 6.5], "texture": "#0"}, + "east": {"uv": [8, 12.5, 8.25, 13], "texture": "#0"}, + "south": {"uv": [10, 6.5, 11, 7], "texture": "#0"}, + "west": {"uv": [12.5, 8, 12.75, 8.5], "texture": "#0"}, + "up": {"uv": [11.75, 11.5, 10.75, 11.25], "texture": "#0"}, + "down": {"uv": [12.25, 10.75, 11.25, 11], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 3.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [3.25, 9.5, 3.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [10.25, 9, 10, 7], "texture": "#0"}, + "down": {"uv": [8.25, 10, 8, 12], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3.5, 9.5, 3.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [3.75, 9.5, 4, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [8.5, 12, 8.25, 10], "texture": "#0"}, + "down": {"uv": [8.75, 10, 8.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [8.75, 10, 9, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [9, 10, 9.25, 12], "texture": "#0"}, + "up": {"uv": [12, 9.25, 10, 9], "texture": "#0"}, + "down": {"uv": [11.25, 10, 9.25, 10.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.25, 12, 9.5], "texture": "#0"}, + "east": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "south": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "west": {"uv": [10.25, 7, 12.25, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9.25, 10.25, 9.5, 12.25], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 9.75, 12.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [10, 12.25, 9.75, 10.25], "texture": "#0"}, + "down": {"uv": [10.25, 10.25, 10, 12.25], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [10.25, 10.25, 10.5, 12.25], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [10.5, 0, 10.75, 2], "texture": "#0"}, + "up": {"uv": [12.25, 7.5, 10.25, 7.25], "texture": "#0"}, + "down": {"uv": [12.25, 7.5, 10.25, 7.75], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 7.75, 12.25, 8], "texture": "#0"}, + "east": {"uv": [10.25, 8, 12.25, 8.25], "texture": "#0"}, + "south": {"uv": [10.25, 8.25, 12.25, 8.5], "texture": "#0"}, + "west": {"uv": [10.25, 8.5, 12.25, 8.75], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 2, 10.75, 4], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [10.5, 10.25, 10.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [11, 2, 10.75, 0], "texture": "#0"}, + "down": {"uv": [2.25, 10.75, 2, 12.75], "texture": "#0"} + } + }, + { + "from": [1.5, 9.75, 7.75], + "to": [2, 10.75, 8.25], + "rotation": {"angle": 0, "axis": "x", "origin": [1.5, 6.5, 8.25]}, + "faces": { + "north": {"uv": [1.25, 13, 1.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 1.25, 13.25, 1.5], "texture": "#0"}, + "south": {"uv": [1.5, 13, 1.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 1.5, 13.25, 1.75], "texture": "#0"}, + "up": {"uv": [2, 13.25, 1.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 1.75, 13, 2], "texture": "#0"} + } + }, + { + "from": [1.5, 5.25, 7.25], + "to": [2, 9.75, 8.75], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 6.5, 7.75]}, + "faces": { + "north": {"uv": [2.25, 10.75, 2.5, 12], "texture": "#0"}, + "east": {"uv": [4, 9.5, 4.5, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 2.25, 11, 3.5], "texture": "#0"}, + "west": {"uv": [4.5, 9.5, 5, 10.75], "texture": "#0"}, + "up": {"uv": [8.5, 13, 8.25, 12.5], "texture": "#0"}, + "down": {"uv": [8.75, 12.5, 8.5, 13], "texture": "#0"} + } + }, + { + "from": [1.5, 5.75, 6.75], + "to": [2, 8.75, 7.25], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 6.5, 6.25]}, + "faces": { + "north": {"uv": [1.75, 11.5, 2, 12.25], "texture": "#0"}, + "east": {"uv": [11.5, 3.25, 11.75, 4], "texture": "#0"}, + "south": {"uv": [5, 11.5, 5.25, 12.25], "texture": "#0"}, + "west": {"uv": [5.25, 11.5, 5.5, 12.25], "texture": "#0"}, + "up": {"uv": [13.25, 2.25, 13, 2], "texture": "#0"}, + "down": {"uv": [13.25, 2.25, 13, 2.5], "texture": "#0"} + } + }, + { + "from": [1.5, 6.25, 6.25], + "to": [2, 7.75, 6.75], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 6.5, 5.75]}, + "faces": { + "north": {"uv": [12.5, 8.5, 12.75, 9], "texture": "#0"}, + "east": {"uv": [8.75, 12.5, 9, 13], "texture": "#0"}, + "south": {"uv": [9, 12.5, 9.25, 13], "texture": "#0"}, + "west": {"uv": [12.5, 9, 12.75, 9.5], "texture": "#0"}, + "up": {"uv": [13.25, 2.75, 13, 2.5], "texture": "#0"}, + "down": {"uv": [13.25, 2.75, 13, 3], "texture": "#0"} + } + }, + { + "from": [1.5, 6.25, 9.25], + "to": [2, 7.75, 9.75], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 6.5, 8.75]}, + "faces": { + "north": {"uv": [12.5, 9.75, 12.75, 10.25], "texture": "#0"}, + "east": {"uv": [12.5, 10.25, 12.75, 10.75], "texture": "#0"}, + "south": {"uv": [12.5, 10.75, 12.75, 11.25], "texture": "#0"}, + "west": {"uv": [11.25, 12.5, 11.5, 13], "texture": "#0"}, + "up": {"uv": [13.25, 3.25, 13, 3], "texture": "#0"}, + "down": {"uv": [13.25, 3.25, 13, 3.5], "texture": "#0"} + } + }, + { + "from": [1.5, 5.75, 8.75], + "to": [2, 8.75, 9.25], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 6.5, 8.25]}, + "faces": { + "north": {"uv": [5.5, 11.5, 5.75, 12.25], "texture": "#0"}, + "east": {"uv": [5.75, 11.5, 6, 12.25], "texture": "#0"}, + "south": {"uv": [11.5, 6, 11.75, 6.75], "texture": "#0"}, + "west": {"uv": [6.75, 11.5, 7, 12.25], "texture": "#0"}, + "up": {"uv": [4, 13.25, 3.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 4, 13, 4.25], "texture": "#0"} + } + }, + { + "from": [14, 9.75, 7.75], + "to": [14.5, 10.75, 8.25], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 6.5, 8.25]}, + "faces": { + "north": {"uv": [13, 4.25, 13.25, 4.5], "texture": "#0"}, + "east": {"uv": [4.5, 13, 4.75, 13.25], "texture": "#0"}, + "south": {"uv": [13, 4.5, 13.25, 4.75], "texture": "#0"}, + "west": {"uv": [4.75, 13, 5, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 5, 13, 4.75], "texture": "#0"}, + "down": {"uv": [13.25, 5, 13, 5.25], "texture": "#0"} + } + }, + { + "from": [14, 5.25, 7.25], + "to": [14.5, 9.75, 8.75], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 6.5, 7.75]}, + "faces": { + "north": {"uv": [2.5, 10.75, 2.75, 12], "texture": "#0"}, + "east": {"uv": [5, 9.5, 5.5, 10.75], "texture": "#0"}, + "south": {"uv": [2.75, 10.75, 3, 12], "texture": "#0"}, + "west": {"uv": [5.5, 9.5, 6, 10.75], "texture": "#0"}, + "up": {"uv": [11.75, 13, 11.5, 12.5], "texture": "#0"}, + "down": {"uv": [12, 12.5, 11.75, 13], "texture": "#0"} + } + }, + { + "from": [14, 5.75, 6.75], + "to": [14.5, 8.75, 7.25], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 6.5, 6.25]}, + "faces": { + "north": {"uv": [7, 11.5, 7.25, 12.25], "texture": "#0"}, + "east": {"uv": [7.25, 11.5, 7.5, 12.25], "texture": "#0"}, + "south": {"uv": [7.5, 11.5, 7.75, 12.25], "texture": "#0"}, + "west": {"uv": [10.75, 11.5, 11, 12.25], "texture": "#0"}, + "up": {"uv": [13.25, 5.5, 13, 5.25], "texture": "#0"}, + "down": {"uv": [13.25, 5.5, 13, 5.75], "texture": "#0"} + } + }, + { + "from": [14, 6.25, 6.25], + "to": [14.5, 7.75, 6.75], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 6.5, 5.75]}, + "faces": { + "north": {"uv": [12, 12.5, 12.25, 13], "texture": "#0"}, + "east": {"uv": [12.25, 12.5, 12.5, 13], "texture": "#0"}, + "south": {"uv": [12.5, 12.5, 12.75, 13], "texture": "#0"}, + "west": {"uv": [12.75, 0, 13, 0.5], "texture": "#0"}, + "up": {"uv": [6, 13.25, 5.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 5.75, 13, 6], "texture": "#0"} + } + }, + { + "from": [14, 6.25, 9.25], + "to": [14.5, 7.75, 9.75], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 6.5, 8.75]}, + "faces": { + "north": {"uv": [0.75, 12.75, 1, 13.25], "texture": "#0"}, + "east": {"uv": [12.75, 0.75, 13, 1.25], "texture": "#0"}, + "south": {"uv": [1, 12.75, 1.25, 13.25], "texture": "#0"}, + "west": {"uv": [12.75, 1.25, 13, 1.75], "texture": "#0"}, + "up": {"uv": [6.25, 13.25, 6, 13], "texture": "#0"}, + "down": {"uv": [13.25, 6, 13, 6.25], "texture": "#0"} + } + }, + { + "from": [14, 5.75, 8.75], + "to": [14.5, 8.75, 9.25], + "rotation": {"angle": 0, "axis": "y", "origin": [14, 6.5, 8.25]}, + "faces": { + "north": {"uv": [11, 11.5, 11.25, 12.25], "texture": "#0"}, + "east": {"uv": [11.25, 11.5, 11.5, 12.25], "texture": "#0"}, + "south": {"uv": [11.5, 11.5, 11.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 11.75, 0.25, 12.5], "texture": "#0"}, + "up": {"uv": [6.5, 13.25, 6.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 6.25, 13, 6.5], "texture": "#0"} + } + }, + { + "from": [7.75, 9.75, 14], + "to": [8.25, 10.75, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 14.25]}, + "faces": { + "north": {"uv": [6.5, 13, 6.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 6.5, 13.25, 6.75], "texture": "#0"}, + "south": {"uv": [6.75, 13, 7, 13.25], "texture": "#0"}, + "west": {"uv": [13, 6.75, 13.25, 7], "texture": "#0"}, + "up": {"uv": [7.25, 13.25, 7, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7, 13, 7.25], "texture": "#0"} + } + }, + { + "from": [7.25, 5.25, 14], + "to": [8.75, 9.75, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 14.25]}, + "faces": { + "north": {"uv": [6, 9.5, 6.5, 10.75], "texture": "#0"}, + "east": {"uv": [4, 10.75, 4.25, 12], "texture": "#0"}, + "south": {"uv": [6.5, 9.5, 7, 10.75], "texture": "#0"}, + "west": {"uv": [4.25, 10.75, 4.5, 12], "texture": "#0"}, + "up": {"uv": [13, 11.5, 12.5, 11.25], "texture": "#0"}, + "down": {"uv": [2, 12.75, 1.5, 13], "texture": "#0"} + } + }, + { + "from": [6.75, 5.75, 14], + "to": [7.25, 8.75, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 14.25]}, + "faces": { + "north": {"uv": [11.75, 0, 12, 0.75], "texture": "#0"}, + "east": {"uv": [0.25, 11.75, 0.5, 12.5], "texture": "#0"}, + "south": {"uv": [0.5, 11.75, 0.75, 12.5], "texture": "#0"}, + "west": {"uv": [0.75, 11.75, 1, 12.5], "texture": "#0"}, + "up": {"uv": [7.5, 13.25, 7.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7.25, 13, 7.5], "texture": "#0"} + } + }, + { + "from": [6.25, 6.25, 14], + "to": [6.75, 7.75, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 14.25]}, + "faces": { + "north": {"uv": [12.75, 1.75, 13, 2.25], "texture": "#0"}, + "east": {"uv": [2, 12.75, 2.25, 13.25], "texture": "#0"}, + "south": {"uv": [2.25, 12.75, 2.5, 13.25], "texture": "#0"}, + "west": {"uv": [2.5, 12.75, 2.75, 13.25], "texture": "#0"}, + "up": {"uv": [7.75, 13.25, 7.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7.5, 13, 7.75], "texture": "#0"} + } + }, + { + "from": [9.25, 6.25, 14], + "to": [9.75, 7.75, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 14.25]}, + "faces": { + "north": {"uv": [2.75, 12.75, 3, 13.25], "texture": "#0"}, + "east": {"uv": [3, 12.75, 3.25, 13.25], "texture": "#0"}, + "south": {"uv": [3.25, 12.75, 3.5, 13.25], "texture": "#0"}, + "west": {"uv": [3.5, 12.75, 3.75, 13.25], "texture": "#0"}, + "up": {"uv": [8, 13.25, 7.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7.75, 13, 8], "texture": "#0"} + } + }, + { + "from": [8.75, 5.75, 14], + "to": [9.25, 8.75, 14.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 14.25]}, + "faces": { + "north": {"uv": [1, 11.75, 1.25, 12.5], "texture": "#0"}, + "east": {"uv": [11.75, 1, 12, 1.75], "texture": "#0"}, + "south": {"uv": [1.25, 11.75, 1.5, 12.5], "texture": "#0"}, + "west": {"uv": [11.75, 3.25, 12, 4], "texture": "#0"}, + "up": {"uv": [8.25, 13.25, 8, 13], "texture": "#0"}, + "down": {"uv": [13.25, 8, 13, 8.25], "texture": "#0"} + } + }, + { + "from": [7.75, 9.75, 1.5], + "to": [8.25, 10.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 1.75]}, + "faces": { + "north": {"uv": [8.25, 13, 8.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 8.25, 13.25, 8.5], "texture": "#0"}, + "south": {"uv": [8.5, 13, 8.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 8.5, 13.25, 8.75], "texture": "#0"}, + "up": {"uv": [9, 13.25, 8.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 8.75, 13, 9], "texture": "#0"} + } + }, + { + "from": [7.25, 5.25, 1.5], + "to": [8.75, 9.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 1.75]}, + "faces": { + "north": {"uv": [7, 9.5, 7.5, 10.75], "texture": "#0"}, + "east": {"uv": [4.5, 10.75, 4.75, 12], "texture": "#0"}, + "south": {"uv": [7.5, 9.5, 8, 10.75], "texture": "#0"}, + "west": {"uv": [4.75, 10.75, 5, 12], "texture": "#0"}, + "up": {"uv": [13.25, 3.75, 12.75, 3.5], "texture": "#0"}, + "down": {"uv": [13.25, 3.75, 12.75, 4], "texture": "#0"} + } + }, + { + "from": [8.75, 5.75, 1.5], + "to": [9.25, 8.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 1.75]}, + "faces": { + "north": {"uv": [11.75, 6, 12, 6.75], "texture": "#0"}, + "east": {"uv": [11.75, 11.25, 12, 12], "texture": "#0"}, + "south": {"uv": [12, 0, 12.25, 0.75], "texture": "#0"}, + "west": {"uv": [12, 1, 12.25, 1.75], "texture": "#0"}, + "up": {"uv": [9.25, 13.25, 9, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9, 13, 9.25], "texture": "#0"} + } + }, + { + "from": [9.25, 6.25, 1.5], + "to": [9.75, 7.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 1.75]}, + "faces": { + "north": {"uv": [4, 12.75, 4.25, 13.25], "texture": "#0"}, + "east": {"uv": [12.75, 4, 13, 4.5], "texture": "#0"}, + "south": {"uv": [4.25, 12.75, 4.5, 13.25], "texture": "#0"}, + "west": {"uv": [5, 12.75, 5.25, 13.25], "texture": "#0"}, + "up": {"uv": [9.5, 13.25, 9.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.25, 13, 9.5], "texture": "#0"} + } + }, + { + "from": [6.25, 6.25, 1.5], + "to": [6.75, 7.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 1.75]}, + "faces": { + "north": {"uv": [12.75, 5, 13, 5.5], "texture": "#0"}, + "east": {"uv": [5.25, 12.75, 5.5, 13.25], "texture": "#0"}, + "south": {"uv": [5.5, 12.75, 5.75, 13.25], "texture": "#0"}, + "west": {"uv": [12.75, 5.5, 13, 6], "texture": "#0"}, + "up": {"uv": [9.75, 13.25, 9.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.5, 13, 9.75], "texture": "#0"} + } + }, + { + "from": [6.75, 5.75, 1.5], + "to": [7.25, 8.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8.625, 1.75]}, + "faces": { + "north": {"uv": [12, 1.75, 12.25, 2.5], "texture": "#0"}, + "east": {"uv": [2.25, 12, 2.5, 12.75], "texture": "#0"}, + "south": {"uv": [2.5, 12, 2.75, 12.75], "texture": "#0"}, + "west": {"uv": [12, 2.5, 12.25, 3.25], "texture": "#0"}, + "up": {"uv": [10, 13.25, 9.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.75, 13, 10], "texture": "#0"} + } + }, + { + "from": [7.75, 13.975, 5.125], + "to": [8.25, 14.475, 6.125], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.225, 7.25]}, + "faces": { + "north": {"uv": [11, 3.25, 11.25, 3.5], "texture": "#0"}, + "east": {"uv": [12, 4, 12.25, 4.25], "texture": "#0"}, + "south": {"uv": [12.75, 4.5, 13, 4.75], "texture": "#0"}, + "west": {"uv": [5.75, 12.75, 6, 13], "texture": "#0"}, + "up": {"uv": [6.25, 13, 6, 12.75], "texture": "#0"}, + "down": {"uv": [13, 6, 12.75, 6.25], "texture": "#0"} + } + }, + { + "from": [7.25, 13.975, 6.125], + "to": [8.75, 14.475, 10.625], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.225, 7.25]}, + "faces": { + "north": {"uv": [11.5, 1.75, 12, 2], "texture": "#0"}, + "east": {"uv": [10.25, 8.75, 11.5, 9], "texture": "#0"}, + "south": {"uv": [11.5, 2.25, 12, 2.5], "texture": "#0"}, + "west": {"uv": [10.75, 2, 12, 2.25], "texture": "#0"}, + "up": {"uv": [2.5, 10.75, 2, 9.5], "texture": "#0"}, + "down": {"uv": [3, 9.5, 2.5, 10.75], "texture": "#0"} + } + }, + { + "from": [8.75, 13.975, 7.125], + "to": [9.25, 14.475, 10.125], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.225, 7.25]}, + "faces": { + "north": {"uv": [6.25, 12.75, 6.5, 13], "texture": "#0"}, + "east": {"uv": [11.25, 11, 12, 11.25], "texture": "#0"}, + "south": {"uv": [12.75, 6.25, 13, 6.5], "texture": "#0"}, + "west": {"uv": [0, 11.5, 0.75, 11.75], "texture": "#0"}, + "up": {"uv": [8, 12, 7.75, 11.25], "texture": "#0"}, + "down": {"uv": [11.75, 0, 11.5, 0.75], "texture": "#0"} + } + }, + { + "from": [9.25, 13.975, 8.125], + "to": [9.75, 14.475, 9.625], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.225, 7.25]}, + "faces": { + "north": {"uv": [6.5, 12.75, 6.75, 13], "texture": "#0"}, + "east": {"uv": [11.5, 2.5, 12, 2.75], "texture": "#0"}, + "south": {"uv": [12.75, 6.5, 13, 6.75], "texture": "#0"}, + "west": {"uv": [11.5, 4, 12, 4.25], "texture": "#0"}, + "up": {"uv": [3, 12.5, 2.75, 12], "texture": "#0"}, + "down": {"uv": [3.25, 12, 3, 12.5], "texture": "#0"} + } + }, + { + "from": [6.25, 13.975, 8.125], + "to": [6.75, 14.475, 9.625], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.225, 7.25]}, + "faces": { + "north": {"uv": [6.75, 12.75, 7, 13], "texture": "#0"}, + "east": {"uv": [11.5, 6.75, 12, 7], "texture": "#0"}, + "south": {"uv": [12.75, 6.75, 13, 7], "texture": "#0"}, + "west": {"uv": [11.5, 8.75, 12, 9], "texture": "#0"}, + "up": {"uv": [3.5, 12.5, 3.25, 12], "texture": "#0"}, + "down": {"uv": [12.25, 3.25, 12, 3.75], "texture": "#0"} + } + }, + { + "from": [6.75, 13.975, 7.125], + "to": [7.25, 14.475, 10.125], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.225, 7.25]}, + "faces": { + "north": {"uv": [7, 12.75, 7.25, 13], "texture": "#0"}, + "east": {"uv": [0.75, 11.5, 1.5, 11.75], "texture": "#0"}, + "south": {"uv": [12.75, 7, 13, 7.25], "texture": "#0"}, + "west": {"uv": [11.5, 0.75, 12.25, 1], "texture": "#0"}, + "up": {"uv": [11.75, 1.75, 11.5, 1], "texture": "#0"}, + "down": {"uv": [1.75, 11.5, 1.5, 12.25], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [ + { + "name": "side1", + "origin": [1.5, 6.5, 8.25], + "color": 0, + "children": [41, 42, 43, 44, 45, 46] + }, + { + "name": "side2", + "origin": [1.5, 6.5, 8.25], + "color": 0, + "children": [47, 48, 49, 50, 51, 52] + }, + { + "name": "side3", + "origin": [1.5, 6.5, 8.25], + "color": 0, + "children": [53, 54, 55, 56, 57, 58] + }, + { + "name": "side4", + "origin": [1.5, 6.5, 8.25], + "color": 0, + "children": [59, 60, 61, 62, 63, 64] + }, + { + "name": "top", + "origin": [1.5, 6.5, 8.25], + "color": 0, + "children": [65, 66, 67, 68, 69, 70] + } + ] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_silk_touch.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_silk_touch.json new file mode 100644 index 00000000..25e0f790 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_silk_touch.json @@ -0,0 +1,1122 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_silk_touch", + "particle": "utilitiesinexcess:upgrade_silk_touch" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [10, 10.75, 10.25, 11.25], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [10.25, 10.75, 10.5, 11.25], "texture": "#0"}, + "up": {"uv": [11.25, 6, 10.75, 5.75], "texture": "#0"}, + "down": {"uv": [11, 10.75, 10.5, 11], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11, 0, 11.25, 0.5], "texture": "#0"}, + "east": {"uv": [11, 2.25, 11.25, 2.75], "texture": "#0"}, + "south": {"uv": [11, 2.75, 11.25, 3.25], "texture": "#0"}, + "west": {"uv": [10.5, 11, 10.75, 11.5], "texture": "#0"}, + "up": {"uv": [11.75, 1, 11.5, 0.75], "texture": "#0"}, + "down": {"uv": [1.75, 12.5, 1.5, 12.75], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 11, 11, 11.5], "texture": "#0"}, + "east": {"uv": [11, 10.75, 11.25, 11.25], "texture": "#0"}, + "south": {"uv": [11.25, 0, 11.5, 0.5], "texture": "#0"}, + "west": {"uv": [11.25, 2.25, 11.5, 2.75], "texture": "#0"}, + "up": {"uv": [12.75, 6.75, 12.5, 6.5], "texture": "#0"}, + "down": {"uv": [12.75, 6.75, 12.5, 7], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 2.75, 11.5, 3.25], "texture": "#0"}, + "east": {"uv": [11.25, 5.25, 11.5, 5.75], "texture": "#0"}, + "south": {"uv": [6.25, 11.25, 6.5, 11.75], "texture": "#0"}, + "west": {"uv": [6.5, 11.25, 6.75, 11.75], "texture": "#0"}, + "up": {"uv": [7.25, 12.75, 7, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7, 12.5, 7.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.75, 11.25, 7, 11.75], "texture": "#0"}, + "east": {"uv": [10.25, 4.5, 10.75, 5], "texture": "#0"}, + "south": {"uv": [7, 11.25, 7.25, 11.75], "texture": "#0"}, + "west": {"uv": [10.25, 5, 10.75, 5.5], "texture": "#0"}, + "up": {"uv": [11.5, 7.75, 11.25, 7.25], "texture": "#0"}, + "down": {"uv": [11.5, 7.75, 11.25, 8.25], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 8.25, 11.5, 8.75], "texture": "#0"}, + "east": {"uv": [8.5, 11.25, 8.75, 11.75], "texture": "#0"}, + "south": {"uv": [8.75, 11.25, 9, 11.75], "texture": "#0"}, + "west": {"uv": [11.25, 8.75, 11.5, 9.25], "texture": "#0"}, + "up": {"uv": [7.5, 12.75, 7.25, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7.25, 12.5, 7.5], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9, 11.25, 9.25, 11.75], "texture": "#0"}, + "east": {"uv": [9.25, 11.25, 9.5, 11.75], "texture": "#0"}, + "south": {"uv": [9.5, 11.25, 9.75, 11.75], "texture": "#0"}, + "west": {"uv": [9.75, 11.25, 10, 11.75], "texture": "#0"}, + "up": {"uv": [7.75, 12.75, 7.5, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7.5, 12.5, 7.75], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 11.25, 10.25, 11.75], "texture": "#0"}, + "east": {"uv": [10.25, 11.25, 10.5, 11.75], "texture": "#0"}, + "south": {"uv": [11.25, 10.75, 11.5, 11.25], "texture": "#0"}, + "west": {"uv": [11, 11.25, 11.25, 11.75], "texture": "#0"}, + "up": {"uv": [8, 12.75, 7.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 7.75, 12.5, 8], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 5.5, 10.75, 6], "texture": "#0"}, + "east": {"uv": [11.25, 11.25, 11.5, 11.75], "texture": "#0"}, + "south": {"uv": [6.25, 10.25, 6.75, 10.75], "texture": "#0"}, + "west": {"uv": [0, 11.5, 0.25, 12], "texture": "#0"}, + "up": {"uv": [11.75, 6, 11.25, 5.75], "texture": "#0"}, + "down": {"uv": [12, 0, 11.5, 0.25], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.25, 11.5, 0.5, 12], "texture": "#0"}, + "east": {"uv": [11.5, 0.25, 11.75, 0.75], "texture": "#0"}, + "south": {"uv": [0.5, 11.5, 0.75, 12], "texture": "#0"}, + "west": {"uv": [0.75, 11.5, 1, 12], "texture": "#0"}, + "up": {"uv": [12.75, 8.25, 12.5, 8], "texture": "#0"}, + "down": {"uv": [8.5, 12.5, 8.25, 12.75], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1, 11.5, 1.25, 12], "texture": "#0"}, + "east": {"uv": [1.25, 11.5, 1.5, 12], "texture": "#0"}, + "south": {"uv": [1.5, 11.5, 1.75, 12], "texture": "#0"}, + "west": {"uv": [1.75, 11.5, 2, 12], "texture": "#0"}, + "up": {"uv": [12.75, 8.5, 12.5, 8.25], "texture": "#0"}, + "down": {"uv": [12.75, 8.5, 12.5, 8.75], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 2.25, 11.75, 2.75], "texture": "#0"}, + "east": {"uv": [11.5, 2.75, 11.75, 3.25], "texture": "#0"}, + "south": {"uv": [3, 11.5, 3.25, 12], "texture": "#0"}, + "west": {"uv": [3.25, 11.5, 3.5, 12], "texture": "#0"}, + "up": {"uv": [9, 12.75, 8.75, 12.5], "texture": "#0"}, + "down": {"uv": [12.75, 8.75, 12.5, 9], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3.5, 11.5, 3.75, 12], "texture": "#0"}, + "east": {"uv": [6.75, 10.25, 7.25, 10.75], "texture": "#0"}, + "south": {"uv": [3.75, 11.5, 4, 12], "texture": "#0"}, + "west": {"uv": [10.25, 7.25, 10.75, 7.75], "texture": "#0"}, + "up": {"uv": [11.75, 5.75, 11.5, 5.25], "texture": "#0"}, + "down": {"uv": [11.75, 7.25, 11.5, 7.75], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 7.75, 11.75, 8.25], "texture": "#0"}, + "east": {"uv": [11.5, 8.25, 11.75, 8.75], "texture": "#0"}, + "south": {"uv": [11.5, 8.75, 11.75, 9.25], "texture": "#0"}, + "west": {"uv": [11.5, 10, 11.75, 10.5], "texture": "#0"}, + "up": {"uv": [12.75, 9.25, 12.5, 9], "texture": "#0"}, + "down": {"uv": [12.75, 9.25, 12.5, 9.5], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 11.5, 10.75, 12], "texture": "#0"}, + "east": {"uv": [11.5, 10.5, 11.75, 11], "texture": "#0"}, + "south": {"uv": [10.75, 11.5, 11, 12], "texture": "#0"}, + "west": {"uv": [11.5, 11, 11.75, 11.5], "texture": "#0"}, + "up": {"uv": [12.75, 9.75, 12.5, 9.5], "texture": "#0"}, + "down": {"uv": [12.75, 9.75, 12.5, 10], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.5, 11.5, 11.75, 12], "texture": "#0"}, + "east": {"uv": [11.75, 0.25, 12, 0.75], "texture": "#0"}, + "south": {"uv": [11.75, 0.75, 12, 1.25], "texture": "#0"}, + "west": {"uv": [11.75, 1.25, 12, 1.75], "texture": "#0"}, + "up": {"uv": [10.25, 12.75, 10, 12.5], "texture": "#0"}, + "down": {"uv": [10.5, 12.5, 10.25, 12.75], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 7.75, 10.75, 8.25], "texture": "#0"}, + "east": {"uv": [11.75, 1.75, 12, 2.25], "texture": "#0"}, + "south": {"uv": [10.25, 8.25, 10.75, 8.75], "texture": "#0"}, + "west": {"uv": [11.75, 2.25, 12, 2.75], "texture": "#0"}, + "up": {"uv": [12.25, 3, 11.75, 2.75], "texture": "#0"}, + "down": {"uv": [12.25, 3, 11.75, 3.25], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 3.25, 12.25, 3.5], "texture": "#0"}, + "east": {"uv": [12.5, 10.25, 12.75, 10.5], "texture": "#0"}, + "south": {"uv": [11.75, 3.5, 12.25, 3.75], "texture": "#0"}, + "west": {"uv": [10.5, 12.5, 10.75, 12.75], "texture": "#0"}, + "up": {"uv": [12.25, 4, 11.75, 3.75], "texture": "#0"}, + "down": {"uv": [12.25, 4.5, 11.75, 4.75], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 4.75, 12, 5.25], "texture": "#0"}, + "east": {"uv": [8.5, 10.25, 9, 10.75], "texture": "#0"}, + "south": {"uv": [11.75, 5.25, 12, 5.75], "texture": "#0"}, + "west": {"uv": [10.25, 8.75, 10.75, 9.25], "texture": "#0"}, + "up": {"uv": [6.5, 12.25, 6.25, 11.75], "texture": "#0"}, + "down": {"uv": [6.75, 11.75, 6.5, 12.25], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 10.5, 12.75, 10.75], "texture": "#0"}, + "east": {"uv": [11.75, 5.75, 12.25, 6], "texture": "#0"}, + "south": {"uv": [12.5, 10.75, 12.75, 11], "texture": "#0"}, + "west": {"uv": [6.75, 11.75, 7.25, 12], "texture": "#0"}, + "up": {"uv": [12, 7.75, 11.75, 7.25], "texture": "#0"}, + "down": {"uv": [12, 7.75, 11.75, 8.25], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9, 10.25, 9.5, 10.75], "texture": "#0"}, + "east": {"uv": [11.75, 8.25, 12, 8.75], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 10, 10.75], "texture": "#0"}, + "west": {"uv": [8.5, 11.75, 8.75, 12.25], "texture": "#0"}, + "up": {"uv": [9.25, 12, 8.75, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 8.75, 11.75, 9], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 9, 12.25, 9.25], "texture": "#0"}, + "east": {"uv": [11, 12.5, 11.25, 12.75], "texture": "#0"}, + "south": {"uv": [9.25, 11.75, 9.75, 12], "texture": "#0"}, + "west": {"uv": [12.5, 11, 12.75, 11.25], "texture": "#0"}, + "up": {"uv": [10.25, 12, 9.75, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 10, 11.75, 10.25], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 11.75, 10.5, 12.25], "texture": "#0"}, + "east": {"uv": [10, 10.25, 10.5, 10.75], "texture": "#0"}, + "south": {"uv": [11.75, 10.25, 12, 10.75], "texture": "#0"}, + "west": {"uv": [10.5, 0, 11, 0.5], "texture": "#0"}, + "up": {"uv": [12, 11.25, 11.75, 10.75], "texture": "#0"}, + "down": {"uv": [11.25, 11.75, 11, 12.25], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11.25, 12.5, 11.5, 12.75], "texture": "#0"}, + "east": {"uv": [11.75, 11.25, 12.25, 11.5], "texture": "#0"}, + "south": {"uv": [11.5, 12.5, 11.75, 12.75], "texture": "#0"}, + "west": {"uv": [11.75, 11.5, 12.25, 11.75], "texture": "#0"}, + "up": {"uv": [11.5, 12.25, 11.25, 11.75], "texture": "#0"}, + "down": {"uv": [12, 11.75, 11.75, 12.25], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 11.75, 12.75, 12], "texture": "#0"}, + "east": {"uv": [10.5, 0.5, 11.5, 0.75], "texture": "#0"}, + "south": {"uv": [12.5, 12, 12.75, 12.25], "texture": "#0"}, + "west": {"uv": [10.5, 0.75, 11.5, 1], "texture": "#0"}, + "up": {"uv": [10.75, 2, 10.5, 1], "texture": "#0"}, + "down": {"uv": [10.75, 2, 10.5, 3], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 12, 0.25, 12.5], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [12, 0, 12.25, 0.5], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [3.25, 11.5, 3, 10.5], "texture": "#0"}, + "down": {"uv": [10.75, 3, 10.5, 4], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 10, 11.5, 10.25], "texture": "#0"}, + "east": {"uv": [12.25, 12.5, 12.5, 12.75], "texture": "#0"}, + "south": {"uv": [10.5, 10.25, 11.5, 10.5], "texture": "#0"}, + "west": {"uv": [12.5, 12.5, 12.75, 12.75], "texture": "#0"}, + "up": {"uv": [11.5, 10.75, 10.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.75, 1, 10.75, 1.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [0.25, 12, 0.5, 12.5], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [0.5, 12, 0.75, 12.5], "texture": "#0"}, + "up": {"uv": [11.75, 1.5, 10.75, 1.25], "texture": "#0"}, + "down": {"uv": [11.75, 1.5, 10.75, 1.75], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 0, 13, 0.25], "texture": "#0"}, + "east": {"uv": [10.75, 1.75, 11.75, 2], "texture": "#0"}, + "south": {"uv": [12.75, 0.25, 13, 0.5], "texture": "#0"}, + "west": {"uv": [10.75, 2, 11.75, 2.25], "texture": "#0"}, + "up": {"uv": [3.5, 11.5, 3.25, 10.5], "texture": "#0"}, + "down": {"uv": [3.75, 10.5, 3.5, 11.5], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 0.5, 12.25, 1], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [0.75, 12, 1, 12.5], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [4, 11.5, 3.75, 10.5], "texture": "#0"}, + "down": {"uv": [11, 2.25, 10.75, 3.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 3.25, 11.75, 3.5], "texture": "#0"}, + "east": {"uv": [0.5, 12.75, 0.75, 13], "texture": "#0"}, + "south": {"uv": [10.75, 3.5, 11.75, 3.75], "texture": "#0"}, + "west": {"uv": [12.75, 0.5, 13, 0.75], "texture": "#0"}, + "up": {"uv": [11.75, 4, 10.75, 3.75], "texture": "#0"}, + "down": {"uv": [11.75, 4.5, 10.75, 4.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [1, 12, 1.25, 12.5], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [12, 1, 12.25, 1.5], "texture": "#0"}, + "up": {"uv": [11.75, 5, 10.75, 4.75], "texture": "#0"}, + "down": {"uv": [11.75, 5, 10.75, 5.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [5.25, 9.75, 1], + "to": [7.25, 10.75, 2], + "rotation": {"angle": 0, "axis": "z", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [1.25, 12, 1.75, 12.25], "texture": "#0"}, + "east": {"uv": [0.75, 12.75, 1, 13], "texture": "#0"}, + "south": {"uv": [12, 1.5, 12.5, 1.75], "texture": "#0"}, + "west": {"uv": [12.75, 0.75, 13, 1], "texture": "#0"}, + "up": {"uv": [2.25, 12.25, 1.75, 12], "texture": "#0"}, + "down": {"uv": [12.5, 1.75, 12, 2], "texture": "#0"} + } + }, + { + "from": [5.25, 8.75, 1], + "to": [6.25, 9.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [1, 12.75, 1.25, 13], "texture": "#0"}, + "east": {"uv": [12.75, 1, 13, 1.25], "texture": "#0"}, + "south": {"uv": [1.25, 12.75, 1.5, 13], "texture": "#0"}, + "west": {"uv": [12.75, 1.25, 13, 1.5], "texture": "#0"}, + "up": {"uv": [1.75, 13, 1.5, 12.75], "texture": "#0"}, + "down": {"uv": [2, 12.75, 1.75, 13], "texture": "#0"} + } + }, + { + "from": [6.25, 7.75, 1], + "to": [7.25, 8.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [2, 12.75, 2.25, 13], "texture": "#0"}, + "east": {"uv": [2.25, 12.75, 2.5, 13], "texture": "#0"}, + "south": {"uv": [2.5, 12.75, 2.75, 13], "texture": "#0"}, + "west": {"uv": [2.75, 12.75, 3, 13], "texture": "#0"}, + "up": {"uv": [3.25, 13, 3, 12.75], "texture": "#0"}, + "down": {"uv": [3.5, 12.75, 3.25, 13], "texture": "#0"} + } + }, + { + "from": [6.25, 8.75, 1], + "to": [8.25, 9.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [12, 2, 12.5, 2.25], "texture": "#0"}, + "east": {"uv": [12.75, 3.25, 13, 3.5], "texture": "#0"}, + "south": {"uv": [2.25, 12, 2.75, 12.25], "texture": "#0"}, + "west": {"uv": [3.5, 12.75, 3.75, 13], "texture": "#0"}, + "up": {"uv": [12.5, 2.5, 12, 2.25], "texture": "#0"}, + "down": {"uv": [12.5, 2.5, 12, 2.75], "texture": "#0"} + } + }, + { + "from": [7.25, 6.75, 1], + "to": [9.25, 8.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [10.75, 5.25, 11.25, 5.75], "texture": "#0"}, + "east": {"uv": [2.75, 12, 3, 12.5], "texture": "#0"}, + "south": {"uv": [6.25, 10.75, 6.75, 11.25], "texture": "#0"}, + "west": {"uv": [3, 12, 3.25, 12.5], "texture": "#0"}, + "up": {"uv": [3.75, 12.25, 3.25, 12], "texture": "#0"}, + "down": {"uv": [4.25, 12, 3.75, 12.25], "texture": "#0"} + } + }, + { + "from": [9.53789, 8.3307, 1], + "to": [11.53789, 8.6307, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [4.25, 12, 4.75, 12.25], "texture": "#0"}, + "east": {"uv": [3.75, 12.75, 4, 13], "texture": "#0"}, + "south": {"uv": [4.75, 12, 5.25, 12.25], "texture": "#0"}, + "west": {"uv": [4, 12.75, 4.25, 13], "texture": "#0"}, + "up": {"uv": [12.5, 5, 12, 4.75], "texture": "#0"}, + "down": {"uv": [12.5, 5, 12, 5.25], "texture": "#0"} + } + }, + { + "from": [9.67932, 7.96764, 1], + "to": [11.53732, 8.33264, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [12, 5.25, 12.5, 5.5], "texture": "#0"}, + "east": {"uv": [12.75, 4, 13, 4.25], "texture": "#0"}, + "south": {"uv": [12, 5.5, 12.5, 5.75], "texture": "#0"}, + "west": {"uv": [4.25, 12.75, 4.5, 13], "texture": "#0"}, + "up": {"uv": [12.5, 6.25, 12, 6], "texture": "#0"}, + "down": {"uv": [12.5, 6.25, 12, 6.5], "texture": "#0"} + } + }, + { + "from": [9.53789, 7.67359, 1], + "to": [11.53789, 7.97359, 2], + "rotation": {"angle": -45, "axis": "z", "origin": [7.87374, 8.14142, 1.5]}, + "faces": { + "north": {"uv": [12, 6.5, 12.5, 6.75], "texture": "#0"}, + "east": {"uv": [4.5, 12.75, 4.75, 13], "texture": "#0"}, + "south": {"uv": [6.75, 12, 7.25, 12.25], "texture": "#0"}, + "west": {"uv": [4.75, 12.75, 5, 13], "texture": "#0"}, + "up": {"uv": [12.5, 7, 12, 6.75], "texture": "#0"}, + "down": {"uv": [12.5, 7, 12, 7.25], "texture": "#0"} + } + }, + { + "from": [5.25, 9.75, 14], + "to": [7.25, 10.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [7.25, 12, 7.75, 12.25], "texture": "#0"}, + "east": {"uv": [5, 12.75, 5.25, 13], "texture": "#0"}, + "south": {"uv": [12, 7.25, 12.5, 7.5], "texture": "#0"}, + "west": {"uv": [5.25, 12.75, 5.5, 13], "texture": "#0"}, + "up": {"uv": [12.5, 7.75, 12, 7.5], "texture": "#0"}, + "down": {"uv": [8.25, 12, 7.75, 12.25], "texture": "#0"} + } + }, + { + "from": [5.25, 8.75, 14], + "to": [6.25, 9.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [5.5, 12.75, 5.75, 13], "texture": "#0"}, + "east": {"uv": [5.75, 12.75, 6, 13], "texture": "#0"}, + "south": {"uv": [12.75, 5.75, 13, 6], "texture": "#0"}, + "west": {"uv": [6, 12.75, 6.25, 13], "texture": "#0"}, + "up": {"uv": [6.5, 13, 6.25, 12.75], "texture": "#0"}, + "down": {"uv": [6.75, 12.75, 6.5, 13], "texture": "#0"} + } + }, + { + "from": [6.25, 7.75, 14], + "to": [7.25, 8.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [12.75, 6.5, 13, 6.75], "texture": "#0"}, + "east": {"uv": [6.75, 12.75, 7, 13], "texture": "#0"}, + "south": {"uv": [12.75, 6.75, 13, 7], "texture": "#0"}, + "west": {"uv": [7, 12.75, 7.25, 13], "texture": "#0"}, + "up": {"uv": [13, 7.25, 12.75, 7], "texture": "#0"}, + "down": {"uv": [7.5, 12.75, 7.25, 13], "texture": "#0"} + } + }, + { + "from": [6.25, 8.75, 14], + "to": [8.25, 9.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [12, 7.75, 12.5, 8], "texture": "#0"}, + "east": {"uv": [12.75, 7.25, 13, 7.5], "texture": "#0"}, + "south": {"uv": [12, 8, 12.5, 8.25], "texture": "#0"}, + "west": {"uv": [7.5, 12.75, 7.75, 13], "texture": "#0"}, + "up": {"uv": [12.5, 8.5, 12, 8.25], "texture": "#0"}, + "down": {"uv": [12.5, 8.5, 12, 8.75], "texture": "#0"} + } + }, + { + "from": [7.25, 6.75, 14], + "to": [9.25, 8.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [6.75, 10.75, 7.25, 11.25], "texture": "#0"}, + "east": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "south": {"uv": [10.75, 7.25, 11.25, 7.75], "texture": "#0"}, + "west": {"uv": [8.75, 12, 9, 12.5], "texture": "#0"}, + "up": {"uv": [9.5, 12.25, 9, 12], "texture": "#0"}, + "down": {"uv": [12.5, 9.25, 12, 9.5], "texture": "#0"} + } + }, + { + "from": [9.53789, 8.3307, 14], + "to": [11.53789, 8.6307, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [9.5, 12, 10, 12.25], "texture": "#0"}, + "east": {"uv": [12.75, 7.5, 13, 7.75], "texture": "#0"}, + "south": {"uv": [12, 9.5, 12.5, 9.75], "texture": "#0"}, + "west": {"uv": [7.75, 12.75, 8, 13], "texture": "#0"}, + "up": {"uv": [12.5, 10, 12, 9.75], "texture": "#0"}, + "down": {"uv": [12.5, 10.25, 12, 10.5], "texture": "#0"} + } + }, + { + "from": [9.67932, 7.96764, 14], + "to": [11.53732, 8.33264, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [10.5, 12, 11, 12.25], "texture": "#0"}, + "east": {"uv": [12.75, 7.75, 13, 8], "texture": "#0"}, + "south": {"uv": [12, 10.5, 12.5, 10.75], "texture": "#0"}, + "west": {"uv": [8, 12.75, 8.25, 13], "texture": "#0"}, + "up": {"uv": [12.5, 11, 12, 10.75], "texture": "#0"}, + "down": {"uv": [12.5, 11, 12, 11.25], "texture": "#0"} + } + }, + { + "from": [9.53789, 7.67359, 14], + "to": [11.53789, 7.97359, 15], + "rotation": {"angle": -45, "axis": "z", "origin": [7.87374, 8.14142, 14.5]}, + "faces": { + "north": {"uv": [12, 11.75, 12.5, 12], "texture": "#0"}, + "east": {"uv": [12.75, 8, 13, 8.25], "texture": "#0"}, + "south": {"uv": [12, 12, 12.5, 12.25], "texture": "#0"}, + "west": {"uv": [8.25, 12.75, 8.5, 13], "texture": "#0"}, + "up": {"uv": [12.75, 0.25, 12.25, 0], "texture": "#0"}, + "down": {"uv": [12.75, 0.25, 12.25, 0.5], "texture": "#0"} + } + }, + { + "from": [13.97374, 9.75, 8.62374], + "to": [14.97374, 10.75, 10.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 8.25, 13, 8.5], "texture": "#0"}, + "east": {"uv": [12.25, 0.5, 12.75, 0.75], "texture": "#0"}, + "south": {"uv": [8.5, 12.75, 8.75, 13], "texture": "#0"}, + "west": {"uv": [12.25, 0.75, 12.75, 1], "texture": "#0"}, + "up": {"uv": [10.25, 12.5, 10, 12], "texture": "#0"}, + "down": {"uv": [11.75, 12, 11.5, 12.5], "texture": "#0"} + } + }, + { + "from": [13.97374, 8.75, 9.62374], + "to": [14.97374, 9.75, 10.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 8.5, 13, 8.75], "texture": "#0"}, + "east": {"uv": [8.75, 12.75, 9, 13], "texture": "#0"}, + "south": {"uv": [12.75, 8.75, 13, 9], "texture": "#0"}, + "west": {"uv": [9, 12.75, 9.25, 13], "texture": "#0"}, + "up": {"uv": [13, 9.25, 12.75, 9], "texture": "#0"}, + "down": {"uv": [9.5, 12.75, 9.25, 13], "texture": "#0"} + } + }, + { + "from": [13.97374, 7.75, 8.62374], + "to": [14.97374, 8.75, 9.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 9.25, 13, 9.5], "texture": "#0"}, + "east": {"uv": [9.5, 12.75, 9.75, 13], "texture": "#0"}, + "south": {"uv": [12.75, 9.5, 13, 9.75], "texture": "#0"}, + "west": {"uv": [9.75, 12.75, 10, 13], "texture": "#0"}, + "up": {"uv": [13, 10, 12.75, 9.75], "texture": "#0"}, + "down": {"uv": [10.25, 12.75, 10, 13], "texture": "#0"} + } + }, + { + "from": [13.97374, 8.75, 7.62374], + "to": [14.97374, 9.75, 9.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 10, 13, 10.25], "texture": "#0"}, + "east": {"uv": [12.25, 1, 12.75, 1.25], "texture": "#0"}, + "south": {"uv": [10.25, 12.75, 10.5, 13], "texture": "#0"}, + "west": {"uv": [1.25, 12.25, 1.75, 12.5], "texture": "#0"}, + "up": {"uv": [2, 12.75, 1.75, 12.25], "texture": "#0"}, + "down": {"uv": [2.25, 12.25, 2, 12.75], "texture": "#0"} + } + }, + { + "from": [13.97374, 6.75, 6.62374], + "to": [14.97374, 8.75, 8.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [2.25, 12.25, 2.5, 12.75], "texture": "#0"}, + "east": {"uv": [10.75, 7.75, 11.25, 8.25], "texture": "#0"}, + "south": {"uv": [2.5, 12.25, 2.75, 12.75], "texture": "#0"}, + "west": {"uv": [10.75, 8.25, 11.25, 8.75], "texture": "#0"}, + "up": {"uv": [12.5, 3.25, 12.25, 2.75], "texture": "#0"}, + "down": {"uv": [3.5, 12.25, 3.25, 12.75], "texture": "#0"} + } + }, + { + "from": [13.97374, 8.3307, 4.33585], + "to": [14.97374, 8.6307, 6.33585], + "rotation": {"angle": -45, "axis": "x", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 10.25, 13, 10.5], "texture": "#0"}, + "east": {"uv": [12.25, 1.25, 12.75, 1.5], "texture": "#0"}, + "south": {"uv": [10.5, 12.75, 10.75, 13], "texture": "#0"}, + "west": {"uv": [12.25, 3.25, 12.75, 3.5], "texture": "#0"}, + "up": {"uv": [3.75, 12.75, 3.5, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 3.5, 12.25, 4], "texture": "#0"} + } + }, + { + "from": [13.97374, 7.96764, 4.33643], + "to": [14.97374, 8.33264, 6.19443], + "rotation": {"angle": -45, "axis": "x", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 10.5, 13, 10.75], "texture": "#0"}, + "east": {"uv": [3.75, 12.25, 4.25, 12.5], "texture": "#0"}, + "south": {"uv": [10.75, 12.75, 11, 13], "texture": "#0"}, + "west": {"uv": [12.25, 4, 12.75, 4.25], "texture": "#0"}, + "up": {"uv": [4.5, 12.75, 4.25, 12.25], "texture": "#0"}, + "down": {"uv": [12.5, 4.25, 12.25, 4.75], "texture": "#0"} + } + }, + { + "from": [13.97374, 7.67359, 4.33585], + "to": [14.97374, 7.97359, 6.33585], + "rotation": {"angle": -45, "axis": "x", "origin": [14.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 10.75, 13, 11], "texture": "#0"}, + "east": {"uv": [4.5, 12.25, 5, 12.5], "texture": "#0"}, + "south": {"uv": [11, 12.75, 11.25, 13], "texture": "#0"}, + "west": {"uv": [5, 12.25, 5.5, 12.5], "texture": "#0"}, + "up": {"uv": [5.75, 12.75, 5.5, 12.25], "texture": "#0"}, + "down": {"uv": [6, 12.25, 5.75, 12.75], "texture": "#0"} + } + }, + { + "from": [0.97374, 9.75, 8.62374], + "to": [1.97374, 10.75, 10.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 11, 13, 11.25], "texture": "#0"}, + "east": {"uv": [12.25, 5.75, 12.75, 6], "texture": "#0"}, + "south": {"uv": [11.25, 12.75, 11.5, 13], "texture": "#0"}, + "west": {"uv": [6, 12.25, 6.5, 12.5], "texture": "#0"}, + "up": {"uv": [6.75, 12.75, 6.5, 12.25], "texture": "#0"}, + "down": {"uv": [7, 12.25, 6.75, 12.75], "texture": "#0"} + } + }, + { + "from": [0.97374, 8.75, 9.62374], + "to": [1.97374, 9.75, 10.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 11.25, 13, 11.5], "texture": "#0"}, + "east": {"uv": [11.5, 12.75, 11.75, 13], "texture": "#0"}, + "south": {"uv": [12.75, 11.5, 13, 11.75], "texture": "#0"}, + "west": {"uv": [11.75, 12.75, 12, 13], "texture": "#0"}, + "up": {"uv": [13, 12, 12.75, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 12.75, 12, 13], "texture": "#0"} + } + }, + { + "from": [0.97374, 7.75, 8.62374], + "to": [1.97374, 8.75, 9.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.75, 12, 13, 12.25], "texture": "#0"}, + "east": {"uv": [12.25, 12.75, 12.5, 13], "texture": "#0"}, + "south": {"uv": [12.75, 12.25, 13, 12.5], "texture": "#0"}, + "west": {"uv": [12.5, 12.75, 12.75, 13], "texture": "#0"}, + "up": {"uv": [13, 12.75, 12.75, 12.5], "texture": "#0"}, + "down": {"uv": [13, 12.75, 12.75, 13], "texture": "#0"} + } + }, + { + "from": [0.97374, 8.75, 7.62374], + "to": [1.97374, 9.75, 9.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [0, 13, 0.25, 13.25], "texture": "#0"}, + "east": {"uv": [7, 12.25, 7.5, 12.5], "texture": "#0"}, + "south": {"uv": [13, 0, 13.25, 0.25], "texture": "#0"}, + "west": {"uv": [7.5, 12.25, 8, 12.5], "texture": "#0"}, + "up": {"uv": [8.25, 12.75, 8, 12.25], "texture": "#0"}, + "down": {"uv": [8.75, 12.25, 8.5, 12.75], "texture": "#0"} + } + }, + { + "from": [0.97374, 6.75, 6.62374], + "to": [1.97374, 8.75, 8.62374], + "rotation": {"angle": 0, "axis": "y", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [12.25, 8.75, 12.5, 9.25], "texture": "#0"}, + "east": {"uv": [8.5, 10.75, 9, 11.25], "texture": "#0"}, + "south": {"uv": [9, 12.25, 9.25, 12.75], "texture": "#0"}, + "west": {"uv": [10.75, 8.75, 11.25, 9.25], "texture": "#0"}, + "up": {"uv": [9.5, 12.75, 9.25, 12.25], "texture": "#0"}, + "down": {"uv": [9.75, 12.25, 9.5, 12.75], "texture": "#0"} + } + }, + { + "from": [0.97374, 8.3307, 4.33585], + "to": [1.97374, 8.6307, 6.33585], + "rotation": {"angle": -45, "axis": "x", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [0.25, 13, 0.5, 13.25], "texture": "#0"}, + "east": {"uv": [12.25, 10, 12.75, 10.25], "texture": "#0"}, + "south": {"uv": [13, 0.25, 13.25, 0.5], "texture": "#0"}, + "west": {"uv": [10.25, 12.25, 10.75, 12.5], "texture": "#0"}, + "up": {"uv": [10, 12.75, 9.75, 12.25], "texture": "#0"}, + "down": {"uv": [11, 12.25, 10.75, 12.75], "texture": "#0"} + } + }, + { + "from": [0.97374, 7.96764, 4.33643], + "to": [1.97374, 8.33264, 6.19443], + "rotation": {"angle": -45, "axis": "x", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [0.5, 13, 0.75, 13.25], "texture": "#0"}, + "east": {"uv": [11, 12.25, 11.5, 12.5], "texture": "#0"}, + "south": {"uv": [13, 0.5, 13.25, 0.75], "texture": "#0"}, + "west": {"uv": [12.25, 11.25, 12.75, 11.5], "texture": "#0"}, + "up": {"uv": [12, 12.75, 11.75, 12.25], "texture": "#0"}, + "down": {"uv": [12.25, 12.25, 12, 12.75], "texture": "#0"} + } + }, + { + "from": [0.97374, 7.67359, 4.33585], + "to": [1.97374, 7.97359, 6.33585], + "rotation": {"angle": -45, "axis": "x", "origin": [1.47374, 8.14142, 8]}, + "faces": { + "north": {"uv": [0.75, 13, 1, 13.25], "texture": "#0"}, + "east": {"uv": [12.25, 11.5, 12.75, 11.75], "texture": "#0"}, + "south": {"uv": [13, 0.75, 13.25, 1], "texture": "#0"}, + "west": {"uv": [12.25, 12.25, 12.75, 12.5], "texture": "#0"}, + "up": {"uv": [0.25, 13, 0, 12.5], "texture": "#0"}, + "down": {"uv": [0.5, 12.5, 0.25, 13], "texture": "#0"} + } + }, + { + "from": [5.25, 13.89142, 5.39142], + "to": [7.25, 14.89142, 6.39142], + "rotation": {"angle": 0, "axis": "x", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [0.5, 12.5, 1, 12.75], "texture": "#0"}, + "east": {"uv": [1, 13, 1.25, 13.25], "texture": "#0"}, + "south": {"uv": [1, 12.5, 1.5, 12.75], "texture": "#0"}, + "west": {"uv": [13, 1, 13.25, 1.25], "texture": "#0"}, + "up": {"uv": [13, 1.75, 12.5, 1.5], "texture": "#0"}, + "down": {"uv": [13, 1.75, 12.5, 2], "texture": "#0"} + } + }, + { + "from": [5.25, 13.89142, 6.39142], + "to": [6.25, 14.89142, 7.39142], + "rotation": {"angle": 0, "axis": "x", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [1.25, 13, 1.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 1.25, 13.25, 1.5], "texture": "#0"}, + "south": {"uv": [1.5, 13, 1.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 1.5, 13.25, 1.75], "texture": "#0"}, + "up": {"uv": [2, 13.25, 1.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 1.75, 13, 2], "texture": "#0"} + } + }, + { + "from": [6.25, 13.89142, 7.39142], + "to": [7.25, 14.89142, 8.39142], + "rotation": {"angle": 0, "axis": "x", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [2, 13, 2.25, 13.25], "texture": "#0"}, + "east": {"uv": [13, 2, 13.25, 2.25], "texture": "#0"}, + "south": {"uv": [2.25, 13, 2.5, 13.25], "texture": "#0"}, + "west": {"uv": [13, 2.25, 13.25, 2.5], "texture": "#0"}, + "up": {"uv": [2.75, 13.25, 2.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 2.5, 13, 2.75], "texture": "#0"} + } + }, + { + "from": [6.25, 13.89142, 6.39142], + "to": [8.25, 14.89142, 7.39142], + "rotation": {"angle": 0, "axis": "x", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [12.5, 2, 13, 2.25], "texture": "#0"}, + "east": {"uv": [2.75, 13, 3, 13.25], "texture": "#0"}, + "south": {"uv": [12.5, 2.25, 13, 2.5], "texture": "#0"}, + "west": {"uv": [13, 2.75, 13.25, 3], "texture": "#0"}, + "up": {"uv": [13, 2.75, 12.5, 2.5], "texture": "#0"}, + "down": {"uv": [3.25, 12.5, 2.75, 12.75], "texture": "#0"} + } + }, + { + "from": [7.25, 13.89142, 7.39142], + "to": [9.25, 14.89142, 9.39142], + "rotation": {"angle": 0, "axis": "x", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [12.5, 2.75, 13, 3], "texture": "#0"}, + "east": {"uv": [12.5, 3, 13, 3.25], "texture": "#0"}, + "south": {"uv": [12.5, 3.5, 13, 3.75], "texture": "#0"}, + "west": {"uv": [3.75, 12.5, 4.25, 12.75], "texture": "#0"}, + "up": {"uv": [9.5, 11.25, 9, 10.75], "texture": "#0"}, + "down": {"uv": [10, 10.75, 9.5, 11.25], "texture": "#0"} + } + }, + { + "from": [9.53789, 13.89142, 7.51072], + "to": [11.53789, 14.89142, 7.81072], + "rotation": {"angle": -45, "axis": "y", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [12.5, 3.75, 13, 4], "texture": "#0"}, + "east": {"uv": [3, 13, 3.25, 13.25], "texture": "#0"}, + "south": {"uv": [12.5, 4.25, 13, 4.5], "texture": "#0"}, + "west": {"uv": [13, 3, 13.25, 3.25], "texture": "#0"}, + "up": {"uv": [5, 12.75, 4.5, 12.5], "texture": "#0"}, + "down": {"uv": [13, 4.5, 12.5, 4.75], "texture": "#0"} + } + }, + { + "from": [9.67932, 13.89142, 7.80878], + "to": [11.53732, 14.89142, 8.17378], + "rotation": {"angle": -45, "axis": "y", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [12.5, 4.75, 13, 5], "texture": "#0"}, + "east": {"uv": [3.25, 13, 3.5, 13.25], "texture": "#0"}, + "south": {"uv": [5, 12.5, 5.5, 12.75], "texture": "#0"}, + "west": {"uv": [13, 3.25, 13.25, 3.5], "texture": "#0"}, + "up": {"uv": [13, 5.25, 12.5, 5], "texture": "#0"}, + "down": {"uv": [13, 5.25, 12.5, 5.5], "texture": "#0"} + } + }, + { + "from": [9.53789, 13.89142, 8.16783], + "to": [11.53789, 14.89142, 8.46783], + "rotation": {"angle": -45, "axis": "y", "origin": [7.87374, 14.39142, 8]}, + "faces": { + "north": {"uv": [12.5, 5.5, 13, 5.75], "texture": "#0"}, + "east": {"uv": [3.5, 13, 3.75, 13.25], "texture": "#0"}, + "south": {"uv": [6, 12.5, 6.5, 12.75], "texture": "#0"}, + "west": {"uv": [13, 3.5, 13.25, 3.75], "texture": "#0"}, + "up": {"uv": [13, 6.25, 12.5, 6], "texture": "#0"}, + "down": {"uv": [13, 6.25, 12.5, 6.5], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [ + { + "name": "side1", + "origin": [11.5, 12.1, 1.5], + "color": 0, + "children": [41, 42, 43, 44, 45, 46, 47, 48] + }, + { + "name": "side2", + "origin": [11.5, 12.1, 1.5], + "color": 0, + "children": [49, 50, 51, 52, 53, 54, 55, 56] + }, + { + "name": "side3", + "origin": [11.5, 12.1, 1.5], + "color": 0, + "children": [57, 58, 59, 60, 61, 62, 63, 64] + }, + { + "name": "side4", + "origin": [11.5, 12.1, 1.5], + "color": 0, + "children": [65, 66, 67, 68, 69, 70, 71, 72] + }, + { + "name": "top", + "origin": [11.5, 12.1, 1.5], + "color": 0, + "children": [73, 74, 75, 76, 77, 78, 79, 80] + } + ] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json index 4b111cb3..ccba740c 100644 --- a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_speed_1.json @@ -3,8 +3,8 @@ "credit": "Made with Blockbench", "texture_size": [64, 64], "textures": { - "0": "utilitiesinexcess:blocks/upgrade_speed_1", - "particle": "utilitiesinexcess:blocks/upgrade_speed_1" + "0": "utilitiesinexcess:upgrade_speed_1", + "particle": "utilitiesinexcess:upgrade_speed_1" }, "elements": [ { @@ -698,4 +698,4 @@ "children": [41, 42, 43, 44, 45, 46, 47, 48, 49, 50] } ] -} \ No newline at end of file +} diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_world_hole.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_world_hole.json new file mode 100644 index 00000000..14c5b236 --- /dev/null +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/upgrade_world_hole.json @@ -0,0 +1,1382 @@ +{ + "format_version": "1.9.0", + "credit": "Made with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "utilitiesinexcess:upgrade_world_hole", + "particle": "utilitiesinexcess:upgrade_world_hole" + }, + "elements": [ + { + "from": [2, 7, 3], + "to": [4, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.5, 4, 8, 4.5], "texture": "#0"}, + "east": {"uv": [12, 7.5, 12.25, 8], "texture": "#0"}, + "south": {"uv": [7.5, 4.5, 8, 5], "texture": "#0"}, + "west": {"uv": [8, 12, 8.25, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 8.25, 12, 8], "texture": "#0"}, + "down": {"uv": [12.5, 9.25, 12, 9.5], "texture": "#0"} + } + }, + { + "from": [3, 7, 2], + "to": [4, 9, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8.25, 12, 8.5, 12.5], "texture": "#0"}, + "east": {"uv": [12, 9.5, 12.25, 10], "texture": "#0"}, + "south": {"uv": [10.25, 12, 10.5, 12.5], "texture": "#0"}, + "west": {"uv": [10.5, 12, 10.75, 12.5], "texture": "#0"}, + "up": {"uv": [12.5, 9.25, 12.25, 9], "texture": "#0"}, + "down": {"uv": [6.75, 12.75, 6.5, 13], "texture": "#0"} + } + }, + { + "from": [3, 11, 3], + "to": [4, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 12, 11, 12.5], "texture": "#0"}, + "east": {"uv": [11, 12, 11.25, 12.5], "texture": "#0"}, + "south": {"uv": [11.75, 12, 12, 12.5], "texture": "#0"}, + "west": {"uv": [12, 12, 12.25, 12.5], "texture": "#0"}, + "up": {"uv": [7, 13, 6.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 6.75, 12.75, 7], "texture": "#0"} + } + }, + { + "from": [3, 3, 3], + "to": [4, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 1, 12.5, 1.5], "texture": "#0"}, + "east": {"uv": [12.25, 1.5, 12.5, 2], "texture": "#0"}, + "south": {"uv": [2, 12.25, 2.25, 12.75], "texture": "#0"}, + "west": {"uv": [2.25, 12.25, 2.5, 12.75], "texture": "#0"}, + "up": {"uv": [7.25, 13, 7, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7, 12.75, 7.25], "texture": "#0"} + } + }, + { + "from": [3, 7, 12], + "to": [4, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 12.25, 2.75, 12.75], "texture": "#0"}, + "east": {"uv": [10.25, 4.5, 10.75, 5], "texture": "#0"}, + "south": {"uv": [4, 12.25, 4.25, 12.75], "texture": "#0"}, + "west": {"uv": [10.25, 5, 10.75, 5.5], "texture": "#0"}, + "up": {"uv": [12.5, 4.5, 12.25, 4], "texture": "#0"}, + "down": {"uv": [4.5, 12.25, 4.25, 12.75], "texture": "#0"} + } + }, + { + "from": [2, 7, 12], + "to": [3, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4.5, 12.25, 4.75, 12.75], "texture": "#0"}, + "east": {"uv": [5.25, 12.25, 5.5, 12.75], "texture": "#0"}, + "south": {"uv": [5.5, 12.25, 5.75, 12.75], "texture": "#0"}, + "west": {"uv": [5.75, 12.25, 6, 12.75], "texture": "#0"}, + "up": {"uv": [7.5, 13, 7.25, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.25, 12.75, 7.5], "texture": "#0"} + } + }, + { + "from": [3, 11, 12], + "to": [4, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6, 12.25, 6.25, 12.75], "texture": "#0"}, + "east": {"uv": [12.25, 6, 12.5, 6.5], "texture": "#0"}, + "south": {"uv": [6.25, 12.25, 6.5, 12.75], "texture": "#0"}, + "west": {"uv": [6.5, 12.25, 6.75, 12.75], "texture": "#0"}, + "up": {"uv": [7.75, 13, 7.5, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.5, 12.75, 7.75], "texture": "#0"} + } + }, + { + "from": [3, 3, 12], + "to": [4, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.75, 12.25, 7, 12.75], "texture": "#0"}, + "east": {"uv": [7, 12.25, 7.25, 12.75], "texture": "#0"}, + "south": {"uv": [7.25, 12.25, 7.5, 12.75], "texture": "#0"}, + "west": {"uv": [7.5, 12.25, 7.75, 12.75], "texture": "#0"}, + "up": {"uv": [8, 13, 7.75, 12.75], "texture": "#0"}, + "down": {"uv": [13, 7.75, 12.75, 8], "texture": "#0"} + } + }, + { + "from": [12, 7, 12], + "to": [14, 9, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 5.5, 10.75, 6], "texture": "#0"}, + "east": {"uv": [12.25, 7.5, 12.5, 8], "texture": "#0"}, + "south": {"uv": [6.25, 10.25, 6.75, 10.75], "texture": "#0"}, + "west": {"uv": [7.75, 12.25, 8, 12.75], "texture": "#0"}, + "up": {"uv": [12.75, 6.75, 12.25, 6.5], "texture": "#0"}, + "down": {"uv": [12.75, 8.25, 12.25, 8.5], "texture": "#0"} + } + }, + { + "from": [12, 7, 13], + "to": [13, 9, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.25, 8.5, 12.5, 9], "texture": "#0"}, + "east": {"uv": [12.25, 9.5, 12.5, 10], "texture": "#0"}, + "south": {"uv": [12.25, 12, 12.5, 12.5], "texture": "#0"}, + "west": {"uv": [0, 12.5, 0.25, 13], "texture": "#0"}, + "up": {"uv": [13, 8.5, 12.75, 8.25], "texture": "#0"}, + "down": {"uv": [8.75, 12.75, 8.5, 13], "texture": "#0"} + } + }, + { + "from": [12, 11, 12], + "to": [13, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0.25, 12.5, 0.5, 13], "texture": "#0"}, + "east": {"uv": [0.5, 12.5, 0.75, 13], "texture": "#0"}, + "south": {"uv": [12.5, 0.5, 12.75, 1], "texture": "#0"}, + "west": {"uv": [0.75, 12.5, 1, 13], "texture": "#0"}, + "up": {"uv": [13, 8.75, 12.75, 8.5], "texture": "#0"}, + "down": {"uv": [9, 12.75, 8.75, 13], "texture": "#0"} + } + }, + { + "from": [12, 3, 12], + "to": [13, 5, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [1, 12.5, 1.25, 13], "texture": "#0"}, + "east": {"uv": [12.5, 1, 12.75, 1.5], "texture": "#0"}, + "south": {"uv": [1.25, 12.5, 1.5, 13], "texture": "#0"}, + "west": {"uv": [1.5, 12.5, 1.75, 13], "texture": "#0"}, + "up": {"uv": [13, 9, 12.75, 8.75], "texture": "#0"}, + "down": {"uv": [9.25, 12.75, 9, 13], "texture": "#0"} + } + }, + { + "from": [12, 7, 2], + "to": [13, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 1.5, 12.75, 2], "texture": "#0"}, + "east": {"uv": [6.75, 10.25, 7.25, 10.75], "texture": "#0"}, + "south": {"uv": [1.75, 12.5, 2, 13], "texture": "#0"}, + "west": {"uv": [10.25, 7.25, 10.75, 7.75], "texture": "#0"}, + "up": {"uv": [3.25, 13, 3, 12.5], "texture": "#0"}, + "down": {"uv": [3.5, 12.5, 3.25, 13], "texture": "#0"} + } + }, + { + "from": [13, 7, 3], + "to": [14, 9, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3.5, 12.5, 3.75, 13], "texture": "#0"}, + "east": {"uv": [3.75, 12.5, 4, 13], "texture": "#0"}, + "south": {"uv": [12.5, 4, 12.75, 4.5], "texture": "#0"}, + "west": {"uv": [12.5, 6, 12.75, 6.5], "texture": "#0"}, + "up": {"uv": [13, 9.25, 12.75, 9], "texture": "#0"}, + "down": {"uv": [9.5, 12.75, 9.25, 13], "texture": "#0"} + } + }, + { + "from": [12, 11, 3], + "to": [13, 13, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 7.5, 12.75, 8], "texture": "#0"}, + "east": {"uv": [8, 12.5, 8.25, 13], "texture": "#0"}, + "south": {"uv": [8.25, 12.5, 8.5, 13], "texture": "#0"}, + "west": {"uv": [12.5, 8.5, 12.75, 9], "texture": "#0"}, + "up": {"uv": [13, 9.5, 12.75, 9.25], "texture": "#0"}, + "down": {"uv": [9.75, 12.75, 9.5, 13], "texture": "#0"} + } + }, + { + "from": [12, 3, 3], + "to": [13, 5, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.5, 9, 12.75, 9.5], "texture": "#0"}, + "east": {"uv": [12.5, 9.5, 12.75, 10], "texture": "#0"}, + "south": {"uv": [12.5, 10, 12.75, 10.5], "texture": "#0"}, + "west": {"uv": [10.25, 12.5, 10.5, 13], "texture": "#0"}, + "up": {"uv": [13, 9.75, 12.75, 9.5], "texture": "#0"}, + "down": {"uv": [10, 12.75, 9.75, 13], "texture": "#0"} + } + }, + { + "from": [7, 12, 2], + "to": [9, 14, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.25, 7.75, 10.75, 8.25], "texture": "#0"}, + "east": {"uv": [10.5, 12.5, 10.75, 13], "texture": "#0"}, + "south": {"uv": [10.25, 8.25, 10.75, 8.75], "texture": "#0"}, + "west": {"uv": [10.75, 12.5, 11, 13], "texture": "#0"}, + "up": {"uv": [13, 8.25, 12.5, 8], "texture": "#0"}, + "down": {"uv": [12, 12.5, 11.5, 12.75], "texture": "#0"} + } + }, + { + "from": [7, 13, 3], + "to": [9, 14, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12, 12.5, 12.5, 12.75], "texture": "#0"}, + "east": {"uv": [12.75, 9.75, 13, 10], "texture": "#0"}, + "south": {"uv": [12.5, 12, 13, 12.25], "texture": "#0"}, + "west": {"uv": [10, 12.75, 10.25, 13], "texture": "#0"}, + "up": {"uv": [13, 12.5, 12.5, 12.25], "texture": "#0"}, + "down": {"uv": [13, 12.5, 12.5, 12.75], "texture": "#0"} + } + }, + { + "from": [13, 12, 7], + "to": [14, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [11, 12.5, 11.25, 13], "texture": "#0"}, + "east": {"uv": [8.5, 10.25, 9, 10.75], "texture": "#0"}, + "south": {"uv": [12.75, 0, 13, 0.5], "texture": "#0"}, + "west": {"uv": [10.25, 8.75, 10.75, 9.25], "texture": "#0"}, + "up": {"uv": [13, 1, 12.75, 0.5], "texture": "#0"}, + "down": {"uv": [13, 1, 12.75, 1.5], "texture": "#0"} + } + }, + { + "from": [12, 13, 7], + "to": [13, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 10, 13, 10.25], "texture": "#0"}, + "east": {"uv": [12.75, 1.5, 13.25, 1.75], "texture": "#0"}, + "south": {"uv": [12.75, 10.25, 13, 10.5], "texture": "#0"}, + "west": {"uv": [12.75, 1.75, 13.25, 2], "texture": "#0"}, + "up": {"uv": [2.25, 13.25, 2, 12.75], "texture": "#0"}, + "down": {"uv": [13, 2, 12.75, 2.5], "texture": "#0"} + } + }, + { + "from": [7, 12, 13], + "to": [9, 14, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [9, 10.25, 9.5, 10.75], "texture": "#0"}, + "east": {"uv": [2.25, 12.75, 2.5, 13.25], "texture": "#0"}, + "south": {"uv": [9.5, 10.25, 10, 10.75], "texture": "#0"}, + "west": {"uv": [2.5, 12.75, 2.75, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 2.75, 12.75, 2.5], "texture": "#0"}, + "down": {"uv": [13.25, 2.75, 12.75, 3], "texture": "#0"} + } + }, + { + "from": [7, 13, 12], + "to": [9, 14, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 3, 13.25, 3.25], "texture": "#0"}, + "east": {"uv": [12.75, 10.5, 13, 10.75], "texture": "#0"}, + "south": {"uv": [12.75, 3.25, 13.25, 3.5], "texture": "#0"}, + "west": {"uv": [12.75, 10.75, 13, 11], "texture": "#0"}, + "up": {"uv": [13.25, 3.75, 12.75, 3.5], "texture": "#0"}, + "down": {"uv": [13.25, 3.75, 12.75, 4], "texture": "#0"} + } + }, + { + "from": [2, 12, 7], + "to": [3, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.75, 12.75, 3, 13.25], "texture": "#0"}, + "east": {"uv": [10, 10.25, 10.5, 10.75], "texture": "#0"}, + "south": {"uv": [4, 12.75, 4.25, 13.25], "texture": "#0"}, + "west": {"uv": [10.5, 0, 11, 0.5], "texture": "#0"}, + "up": {"uv": [13, 4.5, 12.75, 4], "texture": "#0"}, + "down": {"uv": [4.5, 12.75, 4.25, 13.25], "texture": "#0"} + } + }, + { + "from": [3, 13, 7], + "to": [4, 14, 9], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 11, 13, 11.25], "texture": "#0"}, + "east": {"uv": [4.5, 12.75, 5, 13], "texture": "#0"}, + "south": {"uv": [11.25, 12.75, 11.5, 13], "texture": "#0"}, + "west": {"uv": [12.75, 4.5, 13.25, 4.75], "texture": "#0"}, + "up": {"uv": [13, 5.25, 12.75, 4.75], "texture": "#0"}, + "down": {"uv": [5.25, 12.75, 5, 13.25], "texture": "#0"} + } + }, + { + "from": [3, 2, 6], + "to": [4, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 11.25, 13, 11.5], "texture": "#0"}, + "east": {"uv": [10.5, 0.5, 11.5, 0.75], "texture": "#0"}, + "south": {"uv": [11.5, 12.75, 11.75, 13], "texture": "#0"}, + "west": {"uv": [10.5, 0.75, 11.5, 1], "texture": "#0"}, + "up": {"uv": [10.75, 2, 10.5, 1], "texture": "#0"}, + "down": {"uv": [10.75, 2, 10.5, 3], "texture": "#0"} + } + }, + { + "from": [2, 2, 6], + "to": [3, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 12.75, 5.5, 13.25], "texture": "#0"}, + "east": {"uv": [6.5, 4, 7.5, 4.5], "texture": "#0"}, + "south": {"uv": [12.75, 5.25, 13, 5.75], "texture": "#0"}, + "west": {"uv": [6.5, 4.5, 7.5, 5], "texture": "#0"}, + "up": {"uv": [3.25, 11.5, 3, 10.5], "texture": "#0"}, + "down": {"uv": [10.75, 3, 10.5, 4], "texture": "#0"} + } + }, + { + "from": [6, 2, 12], + "to": [10, 3, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.5, 10, 11.5, 10.25], "texture": "#0"}, + "east": {"uv": [12.75, 11.5, 13, 11.75], "texture": "#0"}, + "south": {"uv": [10.5, 10.25, 11.5, 10.5], "texture": "#0"}, + "west": {"uv": [11.75, 12.75, 12, 13], "texture": "#0"}, + "up": {"uv": [11.5, 10.75, 10.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.75, 1, 10.75, 1.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 13], + "to": [10, 4, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 9.5, 4, 10], "texture": "#0"}, + "east": {"uv": [5.5, 12.75, 5.75, 13.25], "texture": "#0"}, + "south": {"uv": [4, 9.5, 5, 10], "texture": "#0"}, + "west": {"uv": [5.75, 12.75, 6, 13.25], "texture": "#0"}, + "up": {"uv": [11.75, 1.5, 10.75, 1.25], "texture": "#0"}, + "down": {"uv": [11.75, 1.5, 10.75, 1.75], "texture": "#0"} + } + }, + { + "from": [12, 2, 6], + "to": [13, 3, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 11.75, 13, 12], "texture": "#0"}, + "east": {"uv": [10.75, 1.75, 11.75, 2], "texture": "#0"}, + "south": {"uv": [12, 12.75, 12.25, 13], "texture": "#0"}, + "west": {"uv": [10.75, 2, 11.75, 2.25], "texture": "#0"}, + "up": {"uv": [3.5, 11.5, 3.25, 10.5], "texture": "#0"}, + "down": {"uv": [3.75, 10.5, 3.5, 11.5], "texture": "#0"} + } + }, + { + "from": [13, 2, 6], + "to": [14, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [12.75, 5.75, 13, 6.25], "texture": "#0"}, + "east": {"uv": [5, 9.5, 6, 10], "texture": "#0"}, + "south": {"uv": [6, 12.75, 6.25, 13.25], "texture": "#0"}, + "west": {"uv": [6, 9.5, 7, 10], "texture": "#0"}, + "up": {"uv": [4, 11.5, 3.75, 10.5], "texture": "#0"}, + "down": {"uv": [11, 2.25, 10.75, 3.25], "texture": "#0"} + } + }, + { + "from": [6, 2, 3], + "to": [10, 3, 4], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10.75, 3.25, 11.75, 3.5], "texture": "#0"}, + "east": {"uv": [12.25, 12.75, 12.5, 13], "texture": "#0"}, + "south": {"uv": [10.75, 3.5, 11.75, 3.75], "texture": "#0"}, + "west": {"uv": [12.5, 12.75, 12.75, 13], "texture": "#0"}, + "up": {"uv": [11.75, 4, 10.75, 3.75], "texture": "#0"}, + "down": {"uv": [11.75, 4.5, 10.75, 4.75], "texture": "#0"} + } + }, + { + "from": [6, 2, 2], + "to": [10, 4, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 9.5, 8, 10], "texture": "#0"}, + "east": {"uv": [6.25, 12.75, 6.5, 13.25], "texture": "#0"}, + "south": {"uv": [3, 10, 4, 10.5], "texture": "#0"}, + "west": {"uv": [12.75, 6.25, 13, 6.75], "texture": "#0"}, + "up": {"uv": [11.75, 5, 10.75, 4.75], "texture": "#0"}, + "down": {"uv": [11.75, 5, 10.75, 5.25], "texture": "#0"} + } + }, + { + "from": [12, 3, 4], + "to": [13, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2, 9.5, 2.25, 12], "texture": "#0"}, + "east": {"uv": [2.5, 0, 4.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.25, 9.5, 2.5, 12], "texture": "#0"}, + "west": {"uv": [2.5, 2.5, 4.5, 5], "texture": "#0"}, + "up": {"uv": [4.25, 12, 4, 10], "texture": "#0"}, + "down": {"uv": [10.25, 4, 10, 6], "texture": "#0"} + } + }, + { + "from": [3, 3, 4], + "to": [4, 13, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [2.5, 9.5, 2.75, 12], "texture": "#0"}, + "east": {"uv": [4.5, 0, 6.5, 2.5], "texture": "#0"}, + "south": {"uv": [2.75, 9.5, 3, 12], "texture": "#0"}, + "west": {"uv": [4.5, 2.5, 6.5, 5], "texture": "#0"}, + "up": {"uv": [4.5, 12, 4.25, 10], "texture": "#0"}, + "down": {"uv": [4.75, 10, 4.5, 12], "texture": "#0"} + } + }, + { + "name": "base", + "from": [4, 3, 3], + "to": [12, 13, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 5, 2, 7.5], "texture": "#0"}, + "east": {"uv": [0, 0, 2.5, 2.5], "texture": "#0"}, + "south": {"uv": [2, 5, 4, 7.5], "texture": "#0"}, + "west": {"uv": [0, 2.5, 2.5, 5], "texture": "#0"}, + "up": {"uv": [6, 7.5, 4, 5], "texture": "#0"}, + "down": {"uv": [8, 5, 6, 7.5], "texture": "#0"} + } + }, + { + "from": [4, 4, 13], + "to": [12, 12, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [6.5, 0, 8.5, 2], "texture": "#0"}, + "east": {"uv": [4.75, 10, 5, 12], "texture": "#0"}, + "south": {"uv": [6.5, 2, 8.5, 4], "texture": "#0"}, + "west": {"uv": [5, 10, 5.25, 12], "texture": "#0"}, + "up": {"uv": [7.25, 10.25, 5.25, 10], "texture": "#0"}, + "down": {"uv": [12, 6, 10, 6.25], "texture": "#0"} + } + }, + { + "from": [4, 13, 4], + "to": [12, 14, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 6.25, 12, 6.5], "texture": "#0"}, + "east": {"uv": [10, 6.5, 12, 6.75], "texture": "#0"}, + "south": {"uv": [10, 6.75, 12, 7], "texture": "#0"}, + "west": {"uv": [10, 7, 12, 7.25], "texture": "#0"}, + "up": {"uv": [2, 9.5, 0, 7.5], "texture": "#0"}, + "down": {"uv": [4, 7.5, 2, 9.5], "texture": "#0"} + } + }, + { + "from": [13, 4, 4], + "to": [14, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7.25, 10, 7.5, 12], "texture": "#0"}, + "east": {"uv": [4, 7.5, 6, 9.5], "texture": "#0"}, + "south": {"uv": [10, 7.25, 10.25, 9.25], "texture": "#0"}, + "west": {"uv": [6, 7.5, 8, 9.5], "texture": "#0"}, + "up": {"uv": [7.75, 12, 7.5, 10], "texture": "#0"}, + "down": {"uv": [8, 10, 7.75, 12], "texture": "#0"} + } + }, + { + "from": [4, 4, 2], + "to": [12, 12, 3], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [8, 4, 10, 6], "texture": "#0"}, + "east": {"uv": [8, 10, 8.25, 12], "texture": "#0"}, + "south": {"uv": [8, 6, 10, 8], "texture": "#0"}, + "west": {"uv": [8.25, 10, 8.5, 12], "texture": "#0"}, + "up": {"uv": [10.5, 10.25, 8.5, 10], "texture": "#0"}, + "down": {"uv": [12, 9.25, 10, 9.5], "texture": "#0"} + } + }, + { + "from": [4, 2, 4], + "to": [12, 3, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 9.5, 12, 9.75], "texture": "#0"}, + "east": {"uv": [10, 9.75, 12, 10], "texture": "#0"}, + "south": {"uv": [10.25, 4, 12.25, 4.25], "texture": "#0"}, + "west": {"uv": [10.25, 4.25, 12.25, 4.5], "texture": "#0"}, + "up": {"uv": [10, 10, 8, 8], "texture": "#0"}, + "down": {"uv": [10.5, 0, 8.5, 2], "texture": "#0"} + } + }, + { + "from": [2, 4, 4], + "to": [3, 12, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [5.25, 10.25, 5.5, 12.25], "texture": "#0"}, + "east": {"uv": [8.5, 2, 10.5, 4], "texture": "#0"}, + "south": {"uv": [5.5, 10.25, 5.75, 12.25], "texture": "#0"}, + "west": {"uv": [0, 9.5, 2, 11.5], "texture": "#0"}, + "up": {"uv": [6, 12.25, 5.75, 10.25], "texture": "#0"}, + "down": {"uv": [6.25, 10.25, 6, 12.25], "texture": "#0"} + } + }, + { + "from": [6, 10.5, 1], + "to": [10, 11.5, 2], + "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [10.75, 5.25, 11.75, 5.5], "texture": "#0"}, + "east": {"uv": [12.75, 12.75, 13, 13], "texture": "#0"}, + "south": {"uv": [10.75, 5.5, 11.75, 5.75], "texture": "#0"}, + "west": {"uv": [0, 13, 0.25, 13.25], "texture": "#0"}, + "up": {"uv": [11.75, 6, 10.75, 5.75], "texture": "#0"}, + "down": {"uv": [7.25, 10.75, 6.25, 11], "texture": "#0"} + } + }, + { + "from": [6, 4.5, 1], + "to": [10, 5.5, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [10.75, 7.25, 11.75, 7.5], "texture": "#0"}, + "east": {"uv": [13, 0, 13.25, 0.25], "texture": "#0"}, + "south": {"uv": [10.75, 7.5, 11.75, 7.75], "texture": "#0"}, + "west": {"uv": [0.25, 13, 0.5, 13.25], "texture": "#0"}, + "up": {"uv": [11.75, 8, 10.75, 7.75], "texture": "#0"}, + "down": {"uv": [11.75, 8, 10.75, 8.25], "texture": "#0"} + } + }, + { + "from": [10.5, 6, 1], + "to": [11.5, 10, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [10.75, 8.25, 11, 9.25], "texture": "#0"}, + "east": {"uv": [8.5, 10.75, 8.75, 11.75], "texture": "#0"}, + "south": {"uv": [8.75, 10.75, 9, 11.75], "texture": "#0"}, + "west": {"uv": [9, 10.75, 9.25, 11.75], "texture": "#0"}, + "up": {"uv": [13.25, 0.5, 13, 0.25], "texture": "#0"}, + "down": {"uv": [0.75, 13, 0.5, 13.25], "texture": "#0"} + } + }, + { + "from": [10, 5, 1], + "to": [11, 6, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [13, 0.5, 13.25, 0.75], "texture": "#0"}, + "east": {"uv": [0.75, 13, 1, 13.25], "texture": "#0"}, + "south": {"uv": [13, 0.75, 13.25, 1], "texture": "#0"}, + "west": {"uv": [1, 13, 1.25, 13.25], "texture": "#0"}, + "up": {"uv": [13.25, 1.25, 13, 1], "texture": "#0"}, + "down": {"uv": [1.5, 13, 1.25, 13.25], "texture": "#0"} + } + }, + { + "from": [5, 5, 1], + "to": [6, 6, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [13, 1.25, 13.25, 1.5], "texture": "#0"}, + "east": {"uv": [1.5, 13, 1.75, 13.25], "texture": "#0"}, + "south": {"uv": [1.75, 13, 2, 13.25], "texture": "#0"}, + "west": {"uv": [13, 2, 13.25, 2.25], "texture": "#0"}, + "up": {"uv": [13.25, 2.5, 13, 2.25], "texture": "#0"}, + "down": {"uv": [3.25, 13, 3, 13.25], "texture": "#0"} + } + }, + { + "from": [5, 10, 1], + "to": [6, 11, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [3.25, 13, 3.5, 13.25], "texture": "#0"}, + "east": {"uv": [3.5, 13, 3.75, 13.25], "texture": "#0"}, + "south": {"uv": [3.75, 13, 4, 13.25], "texture": "#0"}, + "west": {"uv": [13, 4, 13.25, 4.25], "texture": "#0"}, + "up": {"uv": [13.25, 4.5, 13, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13, 4.5, 13.25], "texture": "#0"} + } + }, + { + "from": [10, 10, 1], + "to": [11, 11, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [4.75, 13, 5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 4.75, 13.25, 5], "texture": "#0"}, + "south": {"uv": [13, 5, 13.25, 5.25], "texture": "#0"}, + "west": {"uv": [13, 5.25, 13.25, 5.5], "texture": "#0"}, + "up": {"uv": [13.25, 5.75, 13, 5.5], "texture": "#0"}, + "down": {"uv": [13.25, 5.75, 13, 6], "texture": "#0"} + } + }, + { + "from": [10, 5, 1], + "to": [11, 6, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [13, 6, 13.25, 6.25], "texture": "#0"}, + "east": {"uv": [13, 6.25, 13.25, 6.5], "texture": "#0"}, + "south": {"uv": [6.5, 13, 6.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 6.5, 13.25, 6.75], "texture": "#0"}, + "up": {"uv": [7, 13.25, 6.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 6.75, 13, 7], "texture": "#0"} + } + }, + { + "from": [4.5, 6, 1], + "to": [5.5, 10, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [9.25, 10.75, 9.5, 11.75], "texture": "#0"}, + "east": {"uv": [9.5, 10.75, 9.75, 11.75], "texture": "#0"}, + "south": {"uv": [9.75, 10.75, 10, 11.75], "texture": "#0"}, + "west": {"uv": [10, 10.75, 10.25, 11.75], "texture": "#0"}, + "up": {"uv": [7.25, 13.25, 7, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7, 13, 7.25], "texture": "#0"} + } + }, + { + "from": [5.5, 8, 1], + "to": [6.5, 8.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [7.25, 13, 7.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 7.25, 13.25, 7.5], "texture": "#0"}, + "south": {"uv": [7.5, 13, 7.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 7.5, 13.25, 7.75], "texture": "#0"}, + "up": {"uv": [8, 13.25, 7.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 7.75, 13, 8], "texture": "#0"} + } + }, + { + "from": [9.5, 8, 1], + "to": [10.5, 8.75, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [8, 13, 8.25, 13.25], "texture": "#0"}, + "east": {"uv": [13, 8, 13.25, 8.25], "texture": "#0"}, + "south": {"uv": [8.25, 13, 8.5, 13.25], "texture": "#0"}, + "west": {"uv": [13, 8.25, 13.25, 8.5], "texture": "#0"}, + "up": {"uv": [8.75, 13.25, 8.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 8.5, 13, 8.75], "texture": "#0"} + } + }, + { + "from": [6.5, 7.25, 1], + "to": [9.5, 8, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1.5]}, + "faces": { + "north": {"uv": [12, 0, 12.75, 0.25], "texture": "#0"}, + "east": {"uv": [8.75, 13, 9, 13.25], "texture": "#0"}, + "south": {"uv": [12, 0.25, 12.75, 0.5], "texture": "#0"}, + "west": {"uv": [13, 8.75, 13.25, 9], "texture": "#0"}, + "up": {"uv": [2.75, 12.25, 2, 12], "texture": "#0"}, + "down": {"uv": [12.75, 2.25, 12, 2.5], "texture": "#0"} + } + }, + { + "from": [6, 10.5, 14], + "to": [10, 11.5, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [10.25, 10.75, 11.25, 11], "texture": "#0"}, + "east": {"uv": [9, 13, 9.25, 13.25], "texture": "#0"}, + "south": {"uv": [11, 0, 12, 0.25], "texture": "#0"}, + "west": {"uv": [13, 9, 13.25, 9.25], "texture": "#0"}, + "up": {"uv": [12, 0.5, 11, 0.25], "texture": "#0"}, + "down": {"uv": [12, 2.25, 11, 2.5], "texture": "#0"} + } + }, + { + "from": [6, 4.5, 14], + "to": [10, 5.5, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [11, 2.5, 12, 2.75], "texture": "#0"}, + "east": {"uv": [9.25, 13, 9.5, 13.25], "texture": "#0"}, + "south": {"uv": [11, 2.75, 12, 3], "texture": "#0"}, + "west": {"uv": [13, 9.25, 13.25, 9.5], "texture": "#0"}, + "up": {"uv": [12, 3.25, 11, 3], "texture": "#0"}, + "down": {"uv": [7.25, 11, 6.25, 11.25], "texture": "#0"} + } + }, + { + "from": [10.5, 6, 14], + "to": [11.5, 10, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [11, 8.25, 11.25, 9.25], "texture": "#0"}, + "east": {"uv": [10.25, 11, 10.5, 12], "texture": "#0"}, + "south": {"uv": [10.5, 11, 10.75, 12], "texture": "#0"}, + "west": {"uv": [10.75, 11, 11, 12], "texture": "#0"}, + "up": {"uv": [9.75, 13.25, 9.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 9.5, 13, 9.75], "texture": "#0"} + } + }, + { + "from": [10, 5, 14], + "to": [11, 6, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [9.75, 13, 10, 13.25], "texture": "#0"}, + "east": {"uv": [13, 9.75, 13.25, 10], "texture": "#0"}, + "south": {"uv": [10, 13, 10.25, 13.25], "texture": "#0"}, + "west": {"uv": [13, 10, 13.25, 10.25], "texture": "#0"}, + "up": {"uv": [10.5, 13.25, 10.25, 13], "texture": "#0"}, + "down": {"uv": [13.25, 10.25, 13, 10.5], "texture": "#0"} + } + }, + { + "from": [5, 5, 14], + "to": [6, 6, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [10.5, 13, 10.75, 13.25], "texture": "#0"}, + "east": {"uv": [13, 10.5, 13.25, 10.75], "texture": "#0"}, + "south": {"uv": [10.75, 13, 11, 13.25], "texture": "#0"}, + "west": {"uv": [13, 10.75, 13.25, 11], "texture": "#0"}, + "up": {"uv": [11.25, 13.25, 11, 13], "texture": "#0"}, + "down": {"uv": [13.25, 11, 13, 11.25], "texture": "#0"} + } + }, + { + "from": [5, 10, 14], + "to": [6, 11, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [11.25, 13, 11.5, 13.25], "texture": "#0"}, + "east": {"uv": [13, 11.25, 13.25, 11.5], "texture": "#0"}, + "south": {"uv": [11.5, 13, 11.75, 13.25], "texture": "#0"}, + "west": {"uv": [13, 11.5, 13.25, 11.75], "texture": "#0"}, + "up": {"uv": [12, 13.25, 11.75, 13], "texture": "#0"}, + "down": {"uv": [13.25, 11.75, 13, 12], "texture": "#0"} + } + }, + { + "from": [10, 10, 14], + "to": [11, 11, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [12, 13, 12.25, 13.25], "texture": "#0"}, + "east": {"uv": [13, 12, 13.25, 12.25], "texture": "#0"}, + "south": {"uv": [12.25, 13, 12.5, 13.25], "texture": "#0"}, + "west": {"uv": [13, 12.25, 13.25, 12.5], "texture": "#0"}, + "up": {"uv": [12.75, 13.25, 12.5, 13], "texture": "#0"}, + "down": {"uv": [13.25, 12.5, 13, 12.75], "texture": "#0"} + } + }, + { + "from": [10, 5, 14], + "to": [11, 6, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [12.75, 13, 13, 13.25], "texture": "#0"}, + "east": {"uv": [13, 12.75, 13.25, 13], "texture": "#0"}, + "south": {"uv": [13, 13, 13.25, 13.25], "texture": "#0"}, + "west": {"uv": [0, 13.25, 0.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 0.25, 13.25, 0], "texture": "#0"}, + "down": {"uv": [0.5, 13.25, 0.25, 13.5], "texture": "#0"} + } + }, + { + "from": [4.5, 6, 14], + "to": [5.5, 10, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [11, 11, 11.25, 12], "texture": "#0"}, + "east": {"uv": [6.25, 11.25, 6.5, 12.25], "texture": "#0"}, + "south": {"uv": [6.5, 11.25, 6.75, 12.25], "texture": "#0"}, + "west": {"uv": [6.75, 11.25, 7, 12.25], "texture": "#0"}, + "up": {"uv": [13.5, 0.5, 13.25, 0.25], "texture": "#0"}, + "down": {"uv": [0.75, 13.25, 0.5, 13.5], "texture": "#0"} + } + }, + { + "from": [5.5, 8, 14], + "to": [6.5, 8.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [13.25, 0.5, 13.5, 0.75], "texture": "#0"}, + "east": {"uv": [0.75, 13.25, 1, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 0.75, 13.5, 1], "texture": "#0"}, + "west": {"uv": [1, 13.25, 1.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 1.25, 13.25, 1], "texture": "#0"}, + "down": {"uv": [1.5, 13.25, 1.25, 13.5], "texture": "#0"} + } + }, + { + "from": [9.5, 8, 14], + "to": [10.5, 8.75, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [13.25, 1.25, 13.5, 1.5], "texture": "#0"}, + "east": {"uv": [1.5, 13.25, 1.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 1.5, 13.5, 1.75], "texture": "#0"}, + "west": {"uv": [1.75, 13.25, 2, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 2, 13.25, 1.75], "texture": "#0"}, + "down": {"uv": [2.25, 13.25, 2, 13.5], "texture": "#0"} + } + }, + { + "from": [6.5, 7.25, 14], + "to": [9.5, 8, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 14.5]}, + "faces": { + "north": {"uv": [12, 2.5, 12.75, 2.75], "texture": "#0"}, + "east": {"uv": [13.25, 2, 13.5, 2.25], "texture": "#0"}, + "south": {"uv": [12, 2.75, 12.75, 3], "texture": "#0"}, + "west": {"uv": [2.25, 13.25, 2.5, 13.5], "texture": "#0"}, + "up": {"uv": [12.75, 3.25, 12, 3], "texture": "#0"}, + "down": {"uv": [4.75, 12, 4, 12.25], "texture": "#0"} + } + }, + { + "from": [14, 10.5, 6], + "to": [15, 11.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 2.25, 13.5, 2.5], "texture": "#0"}, + "east": {"uv": [11.25, 8.25, 12.25, 8.5], "texture": "#0"}, + "south": {"uv": [2.5, 13.25, 2.75, 13.5], "texture": "#0"}, + "west": {"uv": [11.25, 8.5, 12.25, 8.75], "texture": "#0"}, + "up": {"uv": [7.25, 12.25, 7, 11.25], "texture": "#0"}, + "down": {"uv": [11.5, 10.75, 11.25, 11.75], "texture": "#0"} + } + }, + { + "from": [14, 4.5, 6], + "to": [15, 5.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 2.5, 13.5, 2.75], "texture": "#0"}, + "east": {"uv": [11.25, 8.75, 12.25, 9], "texture": "#0"}, + "south": {"uv": [2.75, 13.25, 3, 13.5], "texture": "#0"}, + "west": {"uv": [11.25, 9, 12.25, 9.25], "texture": "#0"}, + "up": {"uv": [0.25, 12.5, 0, 11.5], "texture": "#0"}, + "down": {"uv": [0.5, 11.5, 0.25, 12.5], "texture": "#0"} + } + }, + { + "from": [14, 6, 4.5], + "to": [15, 10, 5.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [0.5, 11.5, 0.75, 12.5], "texture": "#0"}, + "east": {"uv": [0.75, 11.5, 1, 12.5], "texture": "#0"}, + "south": {"uv": [1, 11.5, 1.25, 12.5], "texture": "#0"}, + "west": {"uv": [1.25, 11.5, 1.5, 12.5], "texture": "#0"}, + "up": {"uv": [13.5, 3, 13.25, 2.75], "texture": "#0"}, + "down": {"uv": [3.25, 13.25, 3, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 5, 5], + "to": [15, 6, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 3, 13.5, 3.25], "texture": "#0"}, + "east": {"uv": [3.25, 13.25, 3.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 3.25, 13.5, 3.5], "texture": "#0"}, + "west": {"uv": [3.5, 13.25, 3.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 3.75, 13.25, 3.5], "texture": "#0"}, + "down": {"uv": [4, 13.25, 3.75, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 5, 10], + "to": [15, 6, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 3.75, 13.5, 4], "texture": "#0"}, + "east": {"uv": [4, 13.25, 4.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 4, 13.5, 4.25], "texture": "#0"}, + "west": {"uv": [4.25, 13.25, 4.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 4.5, 13.25, 4.25], "texture": "#0"}, + "down": {"uv": [4.75, 13.25, 4.5, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 10, 10], + "to": [15, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 4.5, 13.5, 4.75], "texture": "#0"}, + "east": {"uv": [4.75, 13.25, 5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 4.75, 13.5, 5], "texture": "#0"}, + "west": {"uv": [5, 13.25, 5.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 5.25, 13.25, 5], "texture": "#0"}, + "down": {"uv": [5.5, 13.25, 5.25, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 10, 5], + "to": [15, 11, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 5.25, 13.5, 5.5], "texture": "#0"}, + "east": {"uv": [5.5, 13.25, 5.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 5.5, 13.5, 5.75], "texture": "#0"}, + "west": {"uv": [5.75, 13.25, 6, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 6, 13.25, 5.75], "texture": "#0"}, + "down": {"uv": [6.25, 13.25, 6, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 5, 5], + "to": [15, 6, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 6, 13.5, 6.25], "texture": "#0"}, + "east": {"uv": [6.25, 13.25, 6.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 6.25, 13.5, 6.5], "texture": "#0"}, + "west": {"uv": [6.5, 13.25, 6.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 6.75, 13.25, 6.5], "texture": "#0"}, + "down": {"uv": [7, 13.25, 6.75, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 6, 10.5], + "to": [15, 10, 11.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [1.5, 11.5, 1.75, 12.5], "texture": "#0"}, + "east": {"uv": [1.75, 11.5, 2, 12.5], "texture": "#0"}, + "south": {"uv": [3, 11.5, 3.25, 12.5], "texture": "#0"}, + "west": {"uv": [3.25, 11.5, 3.5, 12.5], "texture": "#0"}, + "up": {"uv": [13.5, 7, 13.25, 6.75], "texture": "#0"}, + "down": {"uv": [7.25, 13.25, 7, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 8, 9.5], + "to": [15, 8.75, 10.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 7, 13.5, 7.25], "texture": "#0"}, + "east": {"uv": [7.25, 13.25, 7.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 7.25, 13.5, 7.5], "texture": "#0"}, + "west": {"uv": [7.5, 13.25, 7.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 7.75, 13.25, 7.5], "texture": "#0"}, + "down": {"uv": [8, 13.25, 7.75, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 8, 5.5], + "to": [15, 8.75, 6.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 7.75, 13.5, 8], "texture": "#0"}, + "east": {"uv": [8, 13.25, 8.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 8, 13.5, 8.25], "texture": "#0"}, + "west": {"uv": [8.25, 13.25, 8.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 8.5, 13.25, 8.25], "texture": "#0"}, + "down": {"uv": [8.75, 13.25, 8.5, 13.5], "texture": "#0"} + } + }, + { + "from": [14, 7.25, 6.5], + "to": [15, 8, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [14.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 8.5, 13.5, 8.75], "texture": "#0"}, + "east": {"uv": [12, 4.5, 12.75, 4.75], "texture": "#0"}, + "south": {"uv": [8.75, 13.25, 9, 13.5], "texture": "#0"}, + "west": {"uv": [12, 4.75, 12.75, 5], "texture": "#0"}, + "up": {"uv": [3, 12.75, 2.75, 12], "texture": "#0"}, + "down": {"uv": [5, 12, 4.75, 12.75], "texture": "#0"} + } + }, + { + "from": [1, 10.5, 6], + "to": [2, 11.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 8.75, 13.5, 9], "texture": "#0"}, + "east": {"uv": [11.5, 0.5, 12.5, 0.75], "texture": "#0"}, + "south": {"uv": [9, 13.25, 9.25, 13.5], "texture": "#0"}, + "west": {"uv": [11.5, 0.75, 12.5, 1], "texture": "#0"}, + "up": {"uv": [3.75, 12.5, 3.5, 11.5], "texture": "#0"}, + "down": {"uv": [4, 11.5, 3.75, 12.5], "texture": "#0"} + } + }, + { + "from": [1, 4.5, 6], + "to": [2, 5.5, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 9, 13.5, 9.25], "texture": "#0"}, + "east": {"uv": [11.5, 10, 12.5, 10.25], "texture": "#0"}, + "south": {"uv": [9.25, 13.25, 9.5, 13.5], "texture": "#0"}, + "west": {"uv": [11.5, 10.25, 12.5, 10.5], "texture": "#0"}, + "up": {"uv": [11.75, 11.5, 11.5, 10.5], "texture": "#0"}, + "down": {"uv": [11.75, 11.5, 11.5, 12.5], "texture": "#0"} + } + }, + { + "from": [1, 6, 4.5], + "to": [2, 10, 5.5], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [11.75, 1, 12, 2], "texture": "#0"}, + "east": {"uv": [11.75, 4.5, 12, 5.5], "texture": "#0"}, + "south": {"uv": [11.75, 7.25, 12, 8.25], "texture": "#0"}, + "west": {"uv": [8.5, 11.75, 8.75, 12.75], "texture": "#0"}, + "up": {"uv": [13.5, 9.5, 13.25, 9.25], "texture": "#0"}, + "down": {"uv": [9.75, 13.25, 9.5, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 5, 5], + "to": [2, 6, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 9.5, 13.5, 9.75], "texture": "#0"}, + "east": {"uv": [9.75, 13.25, 10, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 9.75, 13.5, 10], "texture": "#0"}, + "west": {"uv": [10, 13.25, 10.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 10.25, 13.25, 10], "texture": "#0"}, + "down": {"uv": [10.5, 13.25, 10.25, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 5, 10], + "to": [2, 6, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 10.25, 13.5, 10.5], "texture": "#0"}, + "east": {"uv": [10.5, 13.25, 10.75, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 10.5, 13.5, 10.75], "texture": "#0"}, + "west": {"uv": [10.75, 13.25, 11, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 11, 13.25, 10.75], "texture": "#0"}, + "down": {"uv": [11.25, 13.25, 11, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 10, 10], + "to": [2, 11, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 11, 13.5, 11.25], "texture": "#0"}, + "east": {"uv": [11.25, 13.25, 11.5, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 11.25, 13.5, 11.5], "texture": "#0"}, + "west": {"uv": [11.5, 13.25, 11.75, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 11.75, 13.25, 11.5], "texture": "#0"}, + "down": {"uv": [12, 13.25, 11.75, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 10, 5], + "to": [2, 11, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 11.75, 13.5, 12], "texture": "#0"}, + "east": {"uv": [12, 13.25, 12.25, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 12, 13.5, 12.25], "texture": "#0"}, + "west": {"uv": [12.25, 13.25, 12.5, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 12.5, 13.25, 12.25], "texture": "#0"}, + "down": {"uv": [12.75, 13.25, 12.5, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 5, 5], + "to": [2, 6, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [13.25, 12.5, 13.5, 12.75], "texture": "#0"}, + "east": {"uv": [12.75, 13.25, 13, 13.5], "texture": "#0"}, + "south": {"uv": [13.25, 12.75, 13.5, 13], "texture": "#0"}, + "west": {"uv": [13, 13.25, 13.25, 13.5], "texture": "#0"}, + "up": {"uv": [13.5, 13.25, 13.25, 13], "texture": "#0"}, + "down": {"uv": [13.5, 13.25, 13.25, 13.5], "texture": "#0"} + } + }, + { + "from": [1, 6, 10.5], + "to": [2, 10, 11.5], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [8.75, 11.75, 9, 12.75], "texture": "#0"}, + "east": {"uv": [9, 11.75, 9.25, 12.75], "texture": "#0"}, + "south": {"uv": [9.25, 11.75, 9.5, 12.75], "texture": "#0"}, + "west": {"uv": [9.5, 11.75, 9.75, 12.75], "texture": "#0"}, + "up": {"uv": [0.25, 13.75, 0, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0, 13.5, 0.25], "texture": "#0"} + } + }, + { + "from": [1, 8, 9.5], + "to": [2, 8.75, 10.5], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [0.25, 13.5, 0.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 0.25, 13.75, 0.5], "texture": "#0"}, + "south": {"uv": [0.5, 13.5, 0.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 0.5, 13.75, 0.75], "texture": "#0"}, + "up": {"uv": [1, 13.75, 0.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 0.75, 13.5, 1], "texture": "#0"} + } + }, + { + "from": [1, 8, 5.5], + "to": [2, 8.75, 6.5], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [1, 13.5, 1.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 1, 13.75, 1.25], "texture": "#0"}, + "south": {"uv": [1.25, 13.5, 1.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 1.25, 13.75, 1.5], "texture": "#0"}, + "up": {"uv": [1.75, 13.75, 1.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 1.5, 13.5, 1.75], "texture": "#0"} + } + }, + { + "from": [1, 7.25, 6.5], + "to": [2, 8, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [1.5, 8, 8]}, + "faces": { + "north": {"uv": [1.75, 13.5, 2, 13.75], "texture": "#0"}, + "east": {"uv": [12, 5, 12.75, 5.25], "texture": "#0"}, + "south": {"uv": [13.5, 1.75, 13.75, 2], "texture": "#0"}, + "west": {"uv": [12, 5.25, 12.75, 5.5], "texture": "#0"}, + "up": {"uv": [5.25, 12.75, 5, 12], "texture": "#0"}, + "down": {"uv": [12.25, 6, 12, 6.75], "texture": "#0"} + } + }, + { + "from": [6, 14, 4.5], + "to": [10, 15, 5.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [11.75, 2, 12.75, 2.25], "texture": "#0"}, + "east": {"uv": [2, 13.5, 2.25, 13.75], "texture": "#0"}, + "south": {"uv": [11.75, 3.25, 12.75, 3.5], "texture": "#0"}, + "west": {"uv": [13.5, 2, 13.75, 2.25], "texture": "#0"}, + "up": {"uv": [12.75, 3.75, 11.75, 3.5], "texture": "#0"}, + "down": {"uv": [12.75, 3.75, 11.75, 4], "texture": "#0"} + } + }, + { + "from": [6, 14, 10.5], + "to": [10, 15, 11.5], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [11.75, 5.5, 12.75, 5.75], "texture": "#0"}, + "east": {"uv": [2.25, 13.5, 2.5, 13.75], "texture": "#0"}, + "south": {"uv": [11.75, 5.75, 12.75, 6], "texture": "#0"}, + "west": {"uv": [13.5, 2.25, 13.75, 2.5], "texture": "#0"}, + "up": {"uv": [12.75, 10.75, 11.75, 10.5], "texture": "#0"}, + "down": {"uv": [12.75, 10.75, 11.75, 11], "texture": "#0"} + } + }, + { + "from": [4.5, 14, 6], + "to": [5.5, 15, 10], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [2.5, 13.5, 2.75, 13.75], "texture": "#0"}, + "east": {"uv": [11.75, 11, 12.75, 11.25], "texture": "#0"}, + "south": {"uv": [13.5, 2.5, 13.75, 2.75], "texture": "#0"}, + "west": {"uv": [11.75, 11.25, 12.75, 11.5], "texture": "#0"}, + "up": {"uv": [10, 12.75, 9.75, 11.75], "texture": "#0"}, + "down": {"uv": [10.25, 11.75, 10, 12.75], "texture": "#0"} + } + }, + { + "from": [5, 14, 10], + "to": [6, 15, 11], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [2.75, 13.5, 3, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 2.75, 13.75, 3], "texture": "#0"}, + "south": {"uv": [3, 13.5, 3.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 3, 13.75, 3.25], "texture": "#0"}, + "up": {"uv": [3.5, 13.75, 3.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 3.25, 13.5, 3.5], "texture": "#0"} + } + }, + { + "from": [10, 14, 10], + "to": [11, 15, 11], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [3.5, 13.5, 3.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 3.5, 13.75, 3.75], "texture": "#0"}, + "south": {"uv": [3.75, 13.5, 4, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 3.75, 13.75, 4], "texture": "#0"}, + "up": {"uv": [4.25, 13.75, 4, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 4, 13.5, 4.25], "texture": "#0"} + } + }, + { + "from": [10, 14, 5], + "to": [11, 15, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [4.25, 13.5, 4.5, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 4.25, 13.75, 4.5], "texture": "#0"}, + "south": {"uv": [4.5, 13.5, 4.75, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 4.5, 13.75, 4.75], "texture": "#0"}, + "up": {"uv": [5, 13.75, 4.75, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 4.75, 13.5, 5], "texture": "#0"} + } + }, + { + "from": [5, 14, 5], + "to": [6, 15, 6], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [5, 13.5, 5.25, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 5, 13.75, 5.25], "texture": "#0"}, + "south": {"uv": [5.25, 13.5, 5.5, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 5.25, 13.75, 5.5], "texture": "#0"}, + "up": {"uv": [5.75, 13.75, 5.5, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 5.5, 13.5, 5.75], "texture": "#0"} + } + }, + { + "from": [5, 14, 10], + "to": [6, 15, 11], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [5.75, 13.5, 6, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 5.75, 13.75, 6], "texture": "#0"}, + "south": {"uv": [6, 13.5, 6.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 6, 13.75, 6.25], "texture": "#0"}, + "up": {"uv": [6.5, 13.75, 6.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 6.25, 13.5, 6.5], "texture": "#0"} + } + }, + { + "from": [10.5, 14, 6], + "to": [11.5, 15, 10], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [6.5, 13.5, 6.75, 13.75], "texture": "#0"}, + "east": {"uv": [11.75, 11.5, 12.75, 11.75], "texture": "#0"}, + "south": {"uv": [13.5, 6.5, 13.75, 6.75], "texture": "#0"}, + "west": {"uv": [11.75, 11.75, 12.75, 12], "texture": "#0"}, + "up": {"uv": [11.5, 12.75, 11.25, 11.75], "texture": "#0"}, + "down": {"uv": [12.25, 1, 12, 2], "texture": "#0"} + } + }, + { + "from": [9.5, 14, 7.25], + "to": [10.5, 15, 8], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [6.75, 13.5, 7, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 6.75, 13.75, 7], "texture": "#0"}, + "south": {"uv": [7, 13.5, 7.25, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 7, 13.75, 7.25], "texture": "#0"}, + "up": {"uv": [7.5, 13.75, 7.25, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 7.25, 13.5, 7.5], "texture": "#0"} + } + }, + { + "from": [5.5, 14, 7.25], + "to": [6.5, 15, 8], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [7.5, 13.5, 7.75, 13.75], "texture": "#0"}, + "east": {"uv": [13.5, 7.5, 13.75, 7.75], "texture": "#0"}, + "south": {"uv": [7.75, 13.5, 8, 13.75], "texture": "#0"}, + "west": {"uv": [13.5, 7.75, 13.75, 8], "texture": "#0"}, + "up": {"uv": [8.25, 13.75, 8, 13.5], "texture": "#0"}, + "down": {"uv": [13.75, 8, 13.5, 8.25], "texture": "#0"} + } + }, + { + "from": [6.5, 14, 8], + "to": [9.5, 15, 8.75], + "rotation": {"angle": 0, "axis": "x", "origin": [8, 14.5, 8]}, + "faces": { + "north": {"uv": [12, 6.75, 12.75, 7], "texture": "#0"}, + "east": {"uv": [8.25, 13.5, 8.5, 13.75], "texture": "#0"}, + "south": {"uv": [12, 7, 12.75, 7.25], "texture": "#0"}, + "west": {"uv": [13.5, 8.25, 13.75, 8.5], "texture": "#0"}, + "up": {"uv": [8, 12.25, 7.25, 12], "texture": "#0"}, + "down": {"uv": [12.75, 7.25, 12, 7.5], "texture": "#0"} + } + } + ], + "groups": [ + { + "name": "ornaments", + "origin": [3, 8, 3], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "center", + "origin": [8, 8, 8], + "color": 0, + "children": [32, 33, 34] + }, + { + "name": "plates", + "origin": [8, 8, 8], + "color": 0, + "children": [35, 36, 37, 38, 39, 40] + }, + { + "name": "symbol", + "origin": [6, 8, 1], + "color": 0, + "children": [ + { + "name": "side1", + "origin": [8, 8, 1.5], + "color": 0, + "children": [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52] + }, + { + "name": "side2", + "origin": [8, 8, 1.5], + "color": 0, + "children": [53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64] + }, + { + "name": "side3", + "origin": [8, 8, 1.5], + "color": 0, + "children": [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76] + }, + { + "name": "side4", + "origin": [8, 8, 1.5], + "color": 0, + "children": [77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88] + }, + { + "name": "top", + "origin": [8, 8, 1.5], + "color": 0, + "children": [89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] + } + ] + } + ] +} diff --git a/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_1.png b/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_1.png new file mode 100644 index 0000000000000000000000000000000000000000..4ddac83c0740e206e018fcb33fe1fe8306ad55b9 GIT binary patch literal 1811 zcmV+u2kiKXP)*fc-}XOh4Y{4Uk@Rgpep@3OAdJH1uH3JDSP|b(C=5m^ zCcwz!!}|a_eAqblT+3P!1__D@Sj8Dobse(P8=p~B#6|$RiZjMCIr{i?hmvzG|FZXL zhnDB=i@nuQ&y9@$fIF){wH=on*Kj*|BDc_jY*9GDrH53caEO%vObvK1RDPN6)9GeT z9v=$tzpybwmHLkq)(H+&0PO8M!RGDXcd&7o0#NyILglALVFI5}SPV=6`?C)N+c9!M zD6H*L+2I0=#8RC|d+xoXM^vbGVFDbdZLnQuYri)0t?&8f@o#c#;}P)u@B^vwZQ=Z; z-F6lC9U-qr;{gdXyz~uA)N?=x-~^M8m2a58IW4u~hg)BUVu66vfQi{J0xxPD{Y637 zg>CN_;2RVns?);6Or(2b6$Kbl#nT9baCUpGa{_RJ*TX+5UIKu(FZl!tBw8g>B*6>Qs7lWD_Ag0g`;37Ou$wr&||(I3RM*bL6jDU)dWnwkyujp4EAON z=l`&G9ng%-<5o!n4zQe`if#g7_37|rgF-N< z1%MFAqU!33xaMGZ!(g{yEk%ek&)=pK_ZWgGKX1m53EJue43xzHHmk8YwGU@l5u=|P z7bZ~b!UWC}HXj3<<~ajXv-P>*V^oF{@(E9*% zFbHmhAmsywIv3~$eiI)$DrqMS3lo4jeauf^taR8$gHab?fDaU^UHAZ-7q(_`PQ34g zsPSG0N?a@iggrPuOq60D`9BgMogzmz0iIOo1f6U9K@yJun3Y^F>eNfXCqy=Zvobkb zHP==vv}u?hU59hTq{!r2&OHYwScqFi$d*f>1a3kJL`4SI}Jb>)N2f~UP!7K0!`?uEPmEFZmZ^=rBbBAuCH>K~N{ z%s&sFM=d!eCrFsLIu0A&T73}Akf!JJey}A&m=Z6pZ@a(S6%RPCIXy;@2HKJ#l8Yf! z$06J-v?2l`J3%vl=Qs|#1P#TDaNz=Yk#4^qBMxs3M}~X0eD2Zl!0Dix2lT#6Mm$5- z2AyNM&fvUx0A6Lczg zQ}+)hW0a=RXfKowEfvOS)g0D__`N*my+&{2&T*{AF|P{Y(ProqE^@LZA{CA;6GX=m zBK5crE$NC04$9m23vi&&kkV1~viK+p4hSDmExGE7$aAa%b_Rz9$7bUY{Qyh_juP(% zj=(+uqYuX!l?e{S1a$j1Z8+#!3LZ!Y+oFh(HSe+nV00{;;jthA=y>g0? zX)RwzO}-9CB#w`Qp9wxck4h1uNS)-uMkF#}php9_E*L9CDd%#Fs2Jr3=?fO1N*INp zscUpxHvzl=lNEFj4hp8ie{|5PDg*5t|2g9W@xkvxQ>3s-#d#)9et;QZpn8@L_cD*9Cg5dK#%c|gzJBi(9k`tWm=)f^zC7O|K)BVqoPGQY`MyK|DZLN-Z z+^c8vwYGbUsq3LvijLqZ4Vwp&t8fxmQu;4Xy`>WAhq+GdB5Fxv+uo`b&d6@KUm+}H*e>?`R4oc`@R_q zU)87f_tq!$`RvuPmiE${a(rB>Wh+OQ;xtG(Vnb0q*`_~Cc)3ok2j zckQjdUhgZ5Yx-pvk+pGs4*OqJ$9V|kB$>e8 zi$0MNb53VR6j#l)`A0L-ql5ud8Jg@rc_sk0P6AYYNQ`#Wua%{?d0(n8D#y(?0m_^R=Jr|041FyNzG<(Z?f_0LS=XM;auFCV(&q-{3eodLlNZH%xkqUs`J`JJK2r3T`56WAtW0s${ggl)-YYljq+1HT?fcEQx zYFbSB9qI%aj3DKWBFU`8ImJDhNaPu~mWyP_S0~VUKo+hyQ)Gt2>Aod-prXXFi2`YS zWAwUsL52rFBQVH8l>*-yITu|mOEHQBixHcyzBH8B2Q7fmIPJUdu;YC@xKlLrnAIUv zgeoCK$d+yrd>q~_Pq`pLNkdP72H5XWvLbgi$`3U6=W`yK_W>2Ig_?lR2E82q^N$kS zU;Xx%K8Du%Izf{RZRv>6mI>qmjVLlHn&C}b_K#Y?a-<=+Z~6R2xCzKHfUizKwr9h3 zs&Ixh#o~HzqbOQCPEjBYJ|6>{zx3#J%XP9rmdEI8z!@Nn>RgUYAj`+tiB2+%c4)FB z#wo)GAREk$31lHj{cY${m?BEGo5ln%cvggJCm|ii07C%gr|(w|5CcCk3nM-ntgAx0 z0%VjOn$Qe`;)ND~&kK9AxG3JIp_6!zsxLwtlO`}eI6U|UvOfTV01Lb!>T|UXCTMq( z1a}?I1yn5y)qxKr8=G=axGZyCnh8v3js6>A5B2*G4p%Ur+onmqr4Y6 z0E(g_SG;}aRs$I0Sh@PAWwv48Lt{SR2=Lx2F_su!?Gd zccUBtCJ?kVhT2*d7NZRZSbXHs#`1RD0i{)c3AX~}uJpR_B|~t8m!wIBan5yt;H9yn zTGLBImInHgAwY8gWhR(?s6KK49(`!r2e=`Jahwa5RlPb}YP4ge*2bNvWJO9lo$wnQ zCX`t-tZD;HeF;PX2!Vnqv||GD9>A1Tt70S=rmbm3SP&kzW7h5aT`g!OPcw~30RauS zVFGP0j>0M71$aCvKugm20JuLi1%qdW2*dq*D_k^RQ2{g9J)O%YtQ>NiSJ48YU{yvem`vXEj zhz;HR<@EuWF|NOxlr&CD8NR?6!3vR_g!G9QMYU*D4FvJ}Ab=2Vjg-xE2cqX%8zn<< zAPs;zyJWVSB4OoDV+u9s5c19hfcLYriB1i0fc_WP1Q&I$59%2$cB?36jl6FmP^? z3V}8O-t5v=8)S9xw5-)r7|lI;(Z94PmBuy}{bH)m-KlYXoz4og=njtG%`$h~gF_fA zc$R9T@P@75*zW=!fYP|B2l&zT&G3)=KBuOzDB`yB-1q-NK+FeFMPUxKIlLs!Im>uq49aKQv_Z_CJ9wPK zthne2mF6{?1Fk_+@Gd-O8~VX-^%9u6247>pXGjJ~bnX8B!+x3seKTQiP0N~zw0>a! z?8Eo_gS=Da#zn6Nbm8CTl2p__hsVcE@H=Lo=8~r3p?%qH)T*>Tf1dxzJD_%P=pE(_ zxy%L+6K4XDQ;stM$SKE}0OXY8KL7v#|Nm|JCYb;L00v1!K~w_(@E+&E+u&rr00000 LNkvXXu0mjfswc&_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_3.png b/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_fortune_3.png new file mode 100644 index 0000000000000000000000000000000000000000..344570d68aa297f81c022720b8ab106e311c87b1 GIT binary patch literal 2244 zcmV;#2s`(QP)|DkvzOu)mr_%uI3+`a6EIER?|b)?o=$i7PEo8#PJSTY-TQaklQXM*1srUPOL94{U`IgSLDu_WjQxArezPG?=`h4TpiTw-SH3gn#u3W;k{AW znkmTF6Q2%5jhn%rFHe1x^lo|d87kmxDWibJs10RJKRt+4a+gQ04E8n0DUlP2y z5da~q1zLz4tE&W{wvR)cwoe1SoC&s>rEPuK8i(Y+xgb%Y9 zPUtmfCSHBe5P>9Jkf}P9JyPVQBS7=Ro?~^Z>z1K0VaWI452(~e>LP&a2MO)FMu6~2g!2Qrn0}CL$gvpbCJe;;00@JaK+LMI z7#;BNTnEe8Zpj0a_m`j4>eN zR@+PcS&SR}&~Qm%XRs~;Xj9ovDFSKjw69$^?0p(QDI#htPFg=82^redq`{KlQ%o&M z3nGZxvIS`yE;lh(IKRuWtg_Tpm?402i%$f6GU)Z;KmRzg{?V_0>b2cm-|K)2#RXw1 z44lvtR^Uy7e!s<-3>!;PFfI+U!BfWT2UkP(p$x$ZN$3dz`0@m5;)D~o3a}|9zGPQ^ z%iOM*^cPkh<`3Cmh{*k3umW-L=@_i+@Zw*QJBh>Srpf8q~ zj>rSJAP2J@&jWxnH%*4PFFZjX2ZlkpUK8sBfaNPDF}fD`1a3wsRWQ`+K=gMgqxA^` zSY_h8Jgt|xuJGKL+`5Xxi}uQ3h?c?C{+TpZVCq3xl29>01_tK|t#A%c z{cs1q=qsFoqWs1oZ7|fZCad*~5Ko9CA#rc8TRFZY4)g^>Te*VS5N)L1=mFYP*%4TQ zaQsfn;{)(A$g8WN!za10;!sYo-{`Xv(Z>Z(et=Uo?l-vsH!Cc`zV8%0&UI`4N)Uym zx8w+6f*_Q#Vn7)9^041=Nmw{CtjDUm?eRPn(Z@}ZQwhQs&_xQeL_QH{dvB03j*m-| z(?1G;E65oH_Y1iFlI*49F{36q}f|0unJ1b$E6z z%dojaDhtp6=W%meelNcn9v@GeTMw|P{n`?NmgDMXlQ9a+%pet|hn7<8*<-@4X0Jq8 z0@qT&+|4?5ARxz}2dMjhK~#uEtT@ED;~bJoZ3XZFa4SJork7lRqETKRJ`BnSyRx8s zRNxW@J`i@Dla2!HJ$}CbGjPY`k0(UZSb-xsB17*Dlk~c>F~B0+b<$~A01%6d11?0q z5M~eW0XL{MFh9))7<~a^_>KX#5P(NfXi@+?<3(ZH3y>AbqG`?@;^X1qV1*(3VK30# z3379moM0H}uVP@nfle6x0pIp`3jv^D@B_O?u{7P1_o@hVSlv2xM@Wo(i6-E zIe^Xmi}ti-epxfDarcP$uoca-yjccArf&8E_6@9UhFykfDhBnJLgPn`qZ=W{p+W9n z+Z`0NJQLC1y{{CY55&7YK7fCla}OW+?zz%Yb?)v=yW^lQR@?gW{{Ct2fuC~=u}5VG zUi|jMD~bSZC`EwJ;d>&kKHQeiU{i1c|Ia5)|4#1t?(Er{@dWty+O=0<%2AG88FTCJ zmwpcuxR%$MK#3@{Z$w00030|Gy4sL;wH)21!IgR09C(&E|;3 SEcZMB00006Rg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_pump_fluids.png b/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_pump_fluids.png new file mode 100644 index 0000000000000000000000000000000000000000..8b6aaaa96b8d6bf261fef646ff4e4218a58b5bd6 GIT binary patch literal 1682 zcmV;D25tF?P)Y1UK6AAkNX?Mzpwyxo+4SNHy=Vx>-xZZ6Tn{*Ur__H>*IGlx|C z^ub%?NCCgTyzzeOMK50cMQPW$j(-x2#g6~hIhMzX8$s;&!K)6r0wMTNpqx>gyrVws z)uVHrTkAAM6GZbw&yNp??vpduD5tyFZTR1ftm;hk6X-uu8JFl9^Bi6SYRF2Lx)Oj&!nFH)9XE?X`9X;FpiCmEa;5co= zcAahf`otHp=T8rQkw;q|fh32$K0ZtK)d5a%mX2t1^&$CC@U@xD%urupKhQ!b6mNfX zPfa|N28`YODD|S_{og6=I@j@jkmTi?Kl~$)m1R#2pqvq>vG4=4Cr9%0+#>mFK%QVv z|9a&EDGa$NnpAlJDBb(h=XUwZ17P>a`%NGaOdRPje1H!F)d2SPNw`KM9iH&*w}1Aw ze=slsnAw|I9|*_}{%1B2p^hF#z5uGvYXO*ruL%r=53tnE&EY@$Fn0XKuYbs6KWcqCrckBbhh$2HhZ)m%R7rXJi`O)$CWL?z84@EOhC%?g zI)O<)7jTy@0Gp3lpo>vrZu*F-!UVAR;eLR;4=qup>7UpRg$CGsj4KdIW!X;%L+=CP z@&FwaNc8Hk^beclOm3^fNeiQ)E{bLWZ=Cyqpe*vdvS^BsHc{jOhybWMWr_keFKk`K zh4DU#tnr>v*5{RnB|x0B(+M(WfnX?{piKky=4TuW;Sm}jYl9wPFc(Ep z+op%k{JJQR+>t9m_?&mhom>bYt{1@ZULS)7RP*KoT;RA40N<@hJ*UvG;5v>Qee zXvAv9viKl+dTFF-pe+Klr&SoWX-X28?gOA%oWSVqu4p`Fy~d4DfQAU7#FJ3dL^;fP zk02OIC&*?7)n>uynqVeRSRlbvO5{)Onxnu>cW+CL@Y^28SOtJD!y7qwe%;sb?kpUW zoS(j-0i2h*>f{RFqeR{9FK9@Ga#|AeMXw5@7I5~oA-Bw6^W-z~!>cpoLgc{0rD=On z*((|vvZptBeywVBx??6pyeB|@4~N409{Xg;&;UKHM3=RgVT}zB4X+FnkTG0a?qkKq z=b^GLD?uIPEc*Yi3A>kO33JL=bCCkT#Bsyc#(1>ttdgU0pi2JHMT`q*gJ$Or761?k^gh1P*q6I#yC2(>P_L1l2 zh)4iXwLmL8>Z#~j$`|6nco70Yl{cDQ6Vb2ZdouWpogEm%8*GOY*7^F@$6dL-toU%a z3tQnu%7)j%;r~c3Gr@jxB>)<6t^`0M&XoXY#JLgxjW{DOfd2ph0RR8lX12Zn000I_ cL_t&o0JO6fP4}OA5C8xG07*qoM6N<$f=k#YTL1t6 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_silk_touch.png b/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_silk_touch.png new file mode 100644 index 0000000000000000000000000000000000000000..945e42232359aa8c695207f1df8d9a00549c9a4a GIT binary patch literal 1790 zcmVI6~~5aJ4BU!lq|LY0g#hcT$JEG$(j;zPwD z40a(Cbr(=+RI3RNToNL34w7*Z;Rue{`_0Z<&(7`6?ChP+x12s$-R#cn&%Aj*-@F+W zzHh9)qb|TZm^aq{S1lXsx2}C6e&@%3q@S+drQ-cTab8{ipK5P!i}H5kvZx#S>fr#T zL;-)kyYi{A(Ti9Akf~kT`Y*lV*!tfaC*pVPiWggd_^Q!z?aj((LE+?(Dyti!2w}LQ zKsmw8%?q-$NmDcyUN6<}`NG%~<>up4D$E{{3vwfX9lvub_NI$8dGQgIV(MIw8v&rh ztm*=Od2-p;hy8U$J0OfvazSnc0Nhx-6*+G{sYcGB2|^fmC^d2=fQSb8nD8S5Y<}1G zp@;-~(-1U`PRRHYmlMv_aRfL{jV#Ys4mImcnAe;)hp=$y_d^TZfWz1M#S=yi- z$psit_n~4ra;@?0rjrze060$DU~6ZqzcBVq?Df;bU&XJDM_hmTT4(`m9fWA-@w%_} z$D$4q0ciS8(8&ba2_a6Hr87b37Zw9HSb4eObidGm;j3R58=chuBvZSz^_{@3&-_Z` z_k^nNc}2P*uY(2ypv=!4o|mgApo2#r;ZfK=uuo3Nl>p%IO7);DCRfFvJn?*;CWs=0 zC@pd&fTT!LtPYBa5coiodYB2aw@-pCitx0z6&qqw1XS-_N<9?@x+Z|B;b@%K^8;SO zeaI7tY3?%49>unqxaN<#HYZ4no-hGho}l@}tk&QMYyv~%hOP-f=NpCDz@AxkP2iCo zS^7J%4QGg4Aq0>%PTSgbGTz5QpA91R&w0mTS)lTOTu=a|G{MHV_QB!dsId131;PR9ErxW;=GNPDvoc-adb#fZZJR%I>_RCsOlDY>E$hL!}_ zIQE+r^Go$iZY$i@@^pM)qHeiBMzA{ECX=&eb8Wdon}#tFY77SsapeAnq`_rtl(6BW z`vBWqg45keU>;HPv%+hX!33xWVADYEWK%AD&ikV-!lS=NP?M|>2$C#|?N_ z#PxD`iiuJd6DUkPHr@y8{Ahv%NhT`X zjWsy<{lLbGR%cZ zPYF0eq{Ax|ycZ#H``|LU;!K<%Z&+l`&inV+JiJEhrb*`UykNqD=6iN8MrVc1{Za8; z_y|M;efLOOI14}>aXn3ame3jF+Rdk8A%ulnvg|}nLRZ6}m*OLNau!a)R)VxRr^GSR z-~ekh77Ye_jw(n_;1H}3yTR%SFJrB$e~9Y{VKb5oK(p{{ z2#o91j8)*|%A(9`1?s4G%n5>I-~ixY^at}46!wP$9|<8sK(KY(%NzjqiH;#;LSSfv zwrGPo4y71PfXxaW666rc&{C>#++ia$sN+Nsys1_3ZO z9Jp8YANo1B!$>px<7sUY8`QrA8mj(t1UpGvb}0@6KOg+@j%h4ETxD@XGW!C-K+3#r z9ixXAvjDivzie7;GAdO@hjh;kA+d?6J3@K)hmNGS&ngd`_6Q9WYyU?6X9ypy3PGi{ zdfNFHZ_(*xy@=d*pk3rXC)h5|1weg{a{*AF<6HpL=QtMt^*Q=p0Pg_+0RR7e2%k&< g000I_L_t&o0KxinWWh`gxBvhE07*qoM6N<$g2g;a-T(jq literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_world_hole.png b/src/main/resources/assets/utilitiesinexcess/textures/blocks/upgrade_world_hole.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee6d4de0f6ccdb87f789032e5df44cd4f1f8056 GIT binary patch literal 1877 zcmV-b2demqP)tRk=37%F&L>9cnhvgk{PP{Mz`>ty z9(-c#^y1ZjWa^i+{fk_0Wcy$B6S?hwkc(_Td{uAk@0?DC+AwGp7(DQ1;ZveAS>aGd z@cpB^{$4F~eu?_!cKhT|3a47XCy%$DpV3ETM`i@DwSH#qQ1CXa5pJc6k{~kz*d1}D zU90GJ=cMnzw%%K!zNeyn$}^ES3$@NN0bqRU$Ix-L-rT2SO#q0}0HFSLNk*CaLP;Ez zq(5j&6JT#22i4n0L3(xtxxH{Z`-z>ANvg~&i!ex5T$=#&aHV5-q2JhEezHniJ{MpV z^PU#4AjbAzOTMslzeW$&?^6;;0dSnQ!Pd{#ez*Ut$n$51f5@$kM__;~ zVn9>c&IEL)q}K;>G1r0XS7zQ4&ERM`)HXEZZ6TPUo@Kdx_`E`n(3DJ>fUQn&zG)#f zBf&0s0G+m>3Fv&oDR}m*R&?UPwSc}SFGJ@8YFv|bfvwGoJSyfNr!#@1aoX0emGM3b zdN#Nu2CfPgvkw})eUiciAT1MY9Ny%KfjCD+K56N8$qF9i*uHizp#edq2<5P#GL*^$ zY&K}+@Lzrw+5Y0sf8{nwtuG0hX>ci|0Zw6A`ipA*US|;@)>2&?C-&xLM#xyBS!o7q zJsFDvwmJdBRuP6nmF4xR$?TNGY(B=NuEv^yKAtjpvy5mUO(7r9cqRy!Y*l6%t zA@Cut?-NiOPvrw_Uf7z&`SCsuY2&>Ll(tw1I%$Igx^fh8qL) zN^UcbC@G>M2Im9FF5wW_2c!yvmt}IcYObwTXwxvB`O(wxWFZCCfy!}rJwr^;mP@FE z)bk3|19&-{pvfXxN}UW*8^ET4torcy0Efn5{OPSV(qv?dYyumD6?#T?WUeX<=(UBS zAWI-YyA+tfchl3>uS0-L2%r%P{lrI2fdO@m5KCj`k8#522o2aBIhKxcl#VGaytTng z#&KIU_cE0tFAV!@g8o|InBGY^M>S_aXs=K*IN;AFRK<_4WN{WTb)6S=Db{%rhV*=gs;bp zgR4VLam`Z1H7YpHSG8Hy1%sps+KFg@Fi7+ZCBcssy^}; zHeyQQP~&rnA!xc z+S_|Mx;FqgyXwWu10gg~;(SmKNNNIIcp=12NEQ^6zsBZc9mvkW9imk5I)P{7{nQ^c zMJ7X*3{yq2obeif7e^SwK;X@JAWdI}QLsjcKI#t<1pTWiOu#At#B00=Z*^cMSQC(z z#>I2}o;(nCawe$uuf~7YR&4Qu;9vr_wFVVd0kF!)NtI)~N?G|OaUIU913;+ad)jDt z8enqpOKSo$7XaU9` zU)%uukZe>00Lp&q$+4M%>rMX`#HIqkoVeD<*T5e@qxgqoJO{Szeg0aM`ZWBVo9Pl`Lg?*08vOH13%4&p6 P00000NkvXXu0mjf0z!y6 literal 0 HcmV?d00001 From 06e181d4b361a1ab5477e717531d1a3287d68345 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Fri, 19 Dec 2025 10:34:20 +0100 Subject: [PATCH 16/23] Block replace logic --- .../EnderQuarryUpgradeManager.java | 4 ++++ .../tileentities/TileEntityEnderQuarry.java | 6 +++--- .../config/blocks/EnderQuarryConfig.java | 21 +++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java index 8187331c..d5531800 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java @@ -63,6 +63,10 @@ public void clear() { activeUpgrades.clear(); } + public double getTotalCostMultiplier() { + return activeUpgrades.values().stream().mapToDouble(EnderQuarryUpgrade::getCost).sum(); + } + public enum EnderQuarryUpgrade { // Boolean upgrades (presence only) WORLD_HOLE(1.2, "upgrade_world_hole"), diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 3c0b2c94..b04b5029 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -60,7 +60,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver public static final int BASE_STEPS_PER_TICK = 400; public static final int ITEM_BUFFER_CAPACITY = 256; - public static final Block REPLACE_BLOCK = Blocks.dirt; + public static final Block REPLACE_BLOCK = EnderQuarryConfig.enderQuarryReplaceBlock.block; public boolean isCreativeBoosted = false; private int storedItems; public ForgeDirection facing; @@ -547,7 +547,7 @@ private boolean[] tryHarvestCurrentBlock() { } private boolean tryConsumeEnergy(float hardness, boolean simulate) { - float costMultiplier = 1.0f; + double costMultiplier = upgradeManager.getTotalCostMultiplier(); int cost = (int) ((hardness == 0 ? 100 : EnderQuarryConfig.enderQuarryBaseRFCost) * costMultiplier); if (energyStorage.extractEnergy(cost, true) >= cost) { if (!simulate) { @@ -852,7 +852,7 @@ public void scanSidesForTEs() { * Does not check for anything, should be called after tryHarvestCurrentBlock() returns true. */ private void removeCurrentBlock() { - worldObj.setBlock(dx, dy, dz, Blocks.air); + worldObj.setBlock(dx, dy, dz, upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.WORLD_HOLE) ? Blocks.air : REPLACE_BLOCK); } // TileEntity & LoadableTE diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java index 3d865068..50c25167 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java @@ -2,6 +2,8 @@ import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.gtnewhorizon.gtnhlib.config.Config; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; @Config(modid = UtilitiesInExcess.MODID, category = "blocks.ender_quarry") @Config.Comment("Ender Quarry Configuration") @@ -28,4 +30,23 @@ public class EnderQuarryConfig { @Config.DefaultInt(1_000) @Config.RangeInt(min = 100, max = 1_024_000) public static int enderQuarryBaseRFCost; + + @Config.DefaultEnum("COBBLE") + @Config.Comment("Block type to replace mined blocks with if the world hole upgrade isn't present.") + public static EnderQuarryReplaceBlock enderQuarryReplaceBlock; + + + public enum EnderQuarryReplaceBlock { + COBBLE(Blocks.cobblestone), + DIRT(Blocks.dirt), + GLASS(Blocks.glass), + SNOW(Blocks.snow), + STONE(Blocks.stone); + + public final Block block; + + EnderQuarryReplaceBlock(Block block) { + this.block = block; + } + } } From 85dff1411ec092b7cb170b4c97ddc0852e7d7f98 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Sun, 21 Dec 2025 01:08:20 +0100 Subject: [PATCH 17/23] Move stack hash Strategy to utils, commit early to debug Entity mixin failing --- .../tileentities/TileEntityEnderQuarry.java | 24 ++----------------- .../utilitiesinexcess/utils/UIEUtils.java | 23 ++++++++++++++++++ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index b04b5029..caa72be4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -8,12 +8,12 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.fouristhenumber.utilitiesinexcess.utils.UIEUtils; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; @@ -52,7 +52,6 @@ import cofh.api.energy.EnergyStorage; import cofh.api.energy.IEnergyReceiver; -import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; @@ -84,7 +83,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver .collect(Collectors.toList()); protected final Object2IntMap<@NotNull ItemStack> itemStorage = new Object2IntOpenCustomHashMap<>( ITEM_BUFFER_CAPACITY, - ItemStackHashStrategy.instance); + UIEUtils.ItemStackHashStrategy.instance); public TileEntityEnderQuarry() { resetState(); @@ -1216,23 +1215,4 @@ public String toString() { return String.format("[(%d, %d), (%d, %d)]", this.low.x, this.low.y, this.high.x, this.high.y); } } - - protected static class ItemStackHashStrategy implements Hash.Strategy { - - public static final ItemStackHashStrategy instance = new ItemStackHashStrategy(); - - @Override - public int hashCode(ItemStack itemStack) { - if (itemStack == null || itemStack.getItem() == null) return 0; - return Objects.hash(itemStack.getItem(), itemStack.getItemDamage(), itemStack.getTagCompound()); - } - - @Override - public boolean equals(ItemStack a, ItemStack b) { - if (a == null && b == null) return true; - if (a == null || b == null) return false; - return a.getItem() == b.getItem() && a.getItemDamage() == b.getItemDamage() - && Objects.equals(a.getTagCompound(), b.getTagCompound()); - } - } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java index aeea2464..b397c6f4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java @@ -1,6 +1,10 @@ package com.fouristhenumber.utilitiesinexcess.utils; +import it.unimi.dsi.fastutil.Hash; +import net.minecraft.item.ItemStack; + import java.text.DecimalFormat; +import java.util.Objects; import java.util.Random; public class UIEUtils { @@ -24,4 +28,23 @@ public static String formatNumber(double number) { public static String formatNumber(float number) { return COMMA_FORMAT.format(number); } + + public static class ItemStackHashStrategy implements Hash.Strategy { + + public static final ItemStackHashStrategy instance = new ItemStackHashStrategy(); + + @Override + public int hashCode(ItemStack itemStack) { + if (itemStack == null || itemStack.getItem() == null) return 0; + return Objects.hash(itemStack.getItem(), itemStack.getItemDamage(), itemStack.getTagCompound()); + } + + @Override + public boolean equals(ItemStack a, ItemStack b) { + if (a == null && b == null) return true; + if (a == null || b == null) return false; + return a.getItem() == b.getItem() && a.getItemDamage() == b.getItemDamage() + && Objects.equals(a.getTagCompound(), b.getTagCompound()); + } + } } From 1b0e5c590b5c834a8fe86fc0e8e5119eec477b90 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Mon, 22 Dec 2025 04:10:12 +0100 Subject: [PATCH 18/23] Drop items on quarry break, set upgrade collision boxes, register upgrade sub-blocks, make upgrades & more configurable, fix vanilla fluid harvests & initial side validation --- .../blocks/ender_quarry/BlockEnderQuarry.java | 44 +++++++++++ .../ender_quarry/BlockEnderQuarryUpgrade.java | 28 ++++++- .../EnderQuarryUpgradeManager.java | 26 ++++--- .../tileentities/TileEntityEnderMarker.java | 2 + .../tileentities/TileEntityEnderQuarry.java | 75 +++++++++++++++---- .../config/blocks/EnderQuarryConfig.java | 72 ++++++++++++++---- .../utilitiesinexcess/utils/UIEUtils.java | 7 +- 7 files changed, 209 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java index 5b6da2d3..1eb94d90 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java @@ -6,8 +6,10 @@ import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.world.World; @@ -71,6 +73,48 @@ public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer return true; } + @Override + public void breakBlock(World worldIn, int x, int y, int z, Block blockBroken, int meta) { + TileEntity te = worldIn.getTileEntity(x, y, z); + if (te instanceof TileEntityEnderQuarry quarry) { + dropContent(quarry, worldIn); + } + super.breakBlock(worldIn, x, y, z, blockBroken, meta); + } + + public void dropContent(TileEntityEnderQuarry quarry, World world) { + for (ItemStack item : quarry.retrieveAllItems()) { + float dx = world.rand.nextFloat() * 0.8F + 0.1F; + float dy = world.rand.nextFloat() * 0.8F + 0.1F; + float dz = world.rand.nextFloat() * 0.8F + 0.1F; + float f3 = 0.05F; + + while (item.stackSize > 0) { + int stackSize = Math.min(item.stackSize, 64); + item.stackSize -= stackSize; + + EntityItem entityitem = new EntityItem( + world, + (float) quarry.xCoord + dx, + (float) quarry.yCoord + dy, + (float) quarry.zCoord + dz, + new ItemStack(item.getItem(), stackSize, item.getItemDamage())); + + entityitem.motionX = (float) world.rand.nextGaussian() * f3; + entityitem.motionY = (float) world.rand.nextGaussian() * f3 + 0.2F; + entityitem.motionZ = (float) world.rand.nextGaussian() * f3; + + if (item.hasTagCompound()) { + entityitem.getEntityItem() + .setTagCompound( + (NBTTagCompound) item.getTagCompound() + .copy()); + } + world.spawnEntityInWorld(entityitem); + } + } + } + @Override public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neighbor) { super.onNeighborBlockChange(worldIn, x, y, z, neighbor); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java index e964a0c5..accc208e 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java @@ -2,15 +2,23 @@ import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.IIcon; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.world.World; + +import java.util.List; + public class BlockEnderQuarryUpgrade extends Block { @SideOnly(Side.CLIENT) @@ -20,9 +28,24 @@ public BlockEnderQuarryUpgrade() { super(Material.iron); setHardness(1f); setBlockName("utilitiesinexcess:ender_quarry_upgrade"); + setBlockBounds(0.5F / 16F, 1.5F / 16F, 0.5F / 16F, 15.5F / 16F, 15F / 16F, 15.5F / 16F); setLightOpacity(0); } + @Override + public void addCollisionBoxesToList(World worldIn, int x, int y, int z, AxisAlignedBB mask, + List list, Entity collider) { + this.setBlockBounds(0.5F / 16F, 1.5F / 16F, 0.5F / 16F, 15.5F / 16F, 15F / 16F, 15.5F / 16F); + super.addCollisionBoxesToList(worldIn, x, y, z, mask, list, collider); + } + + @SideOnly(Side.CLIENT) + public void getSubBlocks(Item itemIn, CreativeTabs tab, List list) { + for (int i = 0; i < EnderQuarryUpgradeManager.EnderQuarryUpgrade.VALUES.length; ++i) { + list.add(new ItemStack(itemIn, 1, i)); + } + } + @Override public boolean isOpaqueCube() { return false; @@ -48,6 +71,7 @@ public int damageDropped(int meta) { } public static class ItemEnderQuarryUpgrade extends ItemBlock { + public ItemEnderQuarryUpgrade(Block block) { super(block); setMaxDamage(0); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java index d5531800..d0f5dfdf 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java @@ -2,6 +2,7 @@ import java.util.HashMap; +import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import org.jetbrains.annotations.Nullable; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; @@ -64,24 +65,27 @@ public void clear() { } public double getTotalCostMultiplier() { - return activeUpgrades.values().stream().mapToDouble(EnderQuarryUpgrade::getCost).sum(); + return activeUpgrades.values() + .stream() + .mapToDouble(EnderQuarryUpgrade::getCost) + .sum(); } public enum EnderQuarryUpgrade { + // Boolean upgrades (presence only) - WORLD_HOLE(1.2, "upgrade_world_hole"), - SILK_TOUCH(8, "upgrade_silk_touch"), - PUMP_FLUIDS(3, "upgrade_pump_fluids"), + WORLD_HOLE(EnderQuarryConfig.enderQuarryWorldHoleEnergyMultiplier, "upgrade_world_hole"), + SILK_TOUCH(EnderQuarryConfig.enderQuarrySilkTouchEnergyMultiplier, "upgrade_silk_touch"), + PUMP_FLUIDS(EnderQuarryConfig.enderQuarryFluidPumpEnergyMultiplier, "upgrade_pump_fluids"), // Tiered upgrades with hardcoded values - SPEED_1(TieredEnderQuarryUpgrade.SPEED, 1, 16, 2.0, "upgrade_speed_1"), - SPEED_2(TieredEnderQuarryUpgrade.SPEED, 2, 32, 4.0, "upgrade_speed_2"), - SPEED_3(TieredEnderQuarryUpgrade.SPEED, 3, 80, 7.0, "upgrade_speed_3"), - - FORTUNE_1(TieredEnderQuarryUpgrade.FORTUNE, 1, 12, 1, "upgrade_fortune_1"), - FORTUNE_2(TieredEnderQuarryUpgrade.FORTUNE, 2, 40, 2, "upgrade_fortune_2"), - FORTUNE_3(TieredEnderQuarryUpgrade.FORTUNE, 3, 100, 3, "upgrade_fortune_3"); + SPEED_1(TieredEnderQuarryUpgrade.SPEED, 1, EnderQuarryConfig.enderQuarrySpeed1EnergyMultiplier, EnderQuarryConfig.enderQuarrySpeed1Multiplier, "upgrade_speed_1"), + SPEED_2(TieredEnderQuarryUpgrade.SPEED, 2, EnderQuarryConfig.enderQuarrySpeed2EnergyMultiplier, EnderQuarryConfig.enderQuarrySpeed2Multiplier, "upgrade_speed_2"), + SPEED_3(TieredEnderQuarryUpgrade.SPEED, 3, EnderQuarryConfig.enderQuarrySpeed3EnergyMultiplier, EnderQuarryConfig.enderQuarrySpeed3Multiplier, "upgrade_speed_3"), + FORTUNE_1(TieredEnderQuarryUpgrade.FORTUNE, 1, EnderQuarryConfig.enderQuarryFortune1EnergyMultiplier, 1, "upgrade_fortune_1"), + FORTUNE_2(TieredEnderQuarryUpgrade.FORTUNE, 2, EnderQuarryConfig.enderQuarryFortune2EnergyMultiplier, 2, "upgrade_fortune_2"), + FORTUNE_3(TieredEnderQuarryUpgrade.FORTUNE, 3, EnderQuarryConfig.enderQuarryFortune3EnergyMultiplier, 3, "upgrade_fortune_3"); public static final EnderQuarryUpgrade[] VALUES = values(); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index af46f63a..51c6a493 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -472,6 +472,7 @@ public void readFromNBT(NBTTagCompound compound) { registeredMarkers.computeIfAbsent(dim, k -> new ConcurrentHashMap<>()) .put(new BlockPos(xCoord, yCoord, zCoord), this); this.operationMode = MarkerOperationMode.values()[compound.getInteger("mode")]; + this.cuboidSize = compound.getInteger("cuboidSize"); } @Override @@ -481,6 +482,7 @@ public void writeToNBT(NBTTagCompound compound) { compound.setInteger("meta", this.activeDirections); compound.setInteger("dim", this.worldObj.provider.dimensionId); compound.setInteger("mode", this.operationMode.ordinal()); + compound.setInteger("cuboidSize", this.cuboidSize); } public enum MarkerOperationMode { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index caa72be4..6d8f1de8 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -13,7 +13,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.fouristhenumber.utilitiesinexcess.utils.UIEUtils; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; @@ -49,6 +48,7 @@ import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; +import com.fouristhenumber.utilitiesinexcess.utils.UIEUtils; import cofh.api.energy.EnergyStorage; import cofh.api.energy.IEnergyReceiver; @@ -57,9 +57,9 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver, IFluidHandler { - public static final int BASE_STEPS_PER_TICK = 400; - public static final int ITEM_BUFFER_CAPACITY = 256; - public static final Block REPLACE_BLOCK = EnderQuarryConfig.enderQuarryReplaceBlock.block; + public static final int BASE_STEPS_PER_TICK = EnderQuarryConfig.enderQuarryBaseSpeed; + public static final int ITEM_BUFFER_CAPACITY = 1024; + public static Block REPLACE_BLOCK = Blocks.dirt; public boolean isCreativeBoosted = false; private int storedItems; public ForgeDirection facing; @@ -72,6 +72,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int chunkX; private int chunkZ; private int brokenBlocksTotal; + private boolean sidesValidated = false; private final HashMap sidedItemAcceptors = new HashMap<>(); private final HashMap sidedFluidAcceptors = new HashMap<>(); private final EnderQuarryUpgradeManager upgradeManager = new EnderQuarryUpgradeManager(); @@ -86,6 +87,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver UIEUtils.ItemStackHashStrategy.instance); public TileEntityEnderQuarry() { + REPLACE_BLOCK = EnderQuarryReplaceBlock.valueOf(EnderQuarryConfig.enderQuarryReplaceBlock).block; resetState(); storedItems = 0; } @@ -558,13 +560,18 @@ private boolean tryConsumeEnergy(float hardness, boolean simulate) { return false; } + /** + * Harvests the block at the current position, and tries to store the resulting items / fluids to internal storage + * @return If we could store all resulting items / fluids + */ private boolean harvestAndStoreBlock(Block block) { try { + int meta = worldObj.getBlockMetadata(dx, dy, dz); if (upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.PUMP_FLUIDS)) { FluidStack fluid = null; - if (block == Blocks.water) { + if ((block == Blocks.water || block == Blocks.flowing_water) && meta == 0) { fluid = new FluidStack(FluidRegistry.WATER, 1000); - } else if (block == Blocks.lava) { + } else if ((block == Blocks.lava || block == Blocks.flowing_water) && meta == 0) { fluid = new FluidStack(FluidRegistry.LAVA, 1000); } else if (block instanceof IFluidBlock fluidBlock) { fluid = fluidBlock.drain(worldObj, dx, dy, dz, false); @@ -575,8 +582,8 @@ private boolean harvestAndStoreBlock(Block block) { } EntityPlayer fakePlayer = FakePlayerFactory.getMinecraft((WorldServer) worldObj); - @Nullable List drops = null; - int meta = worldObj.getBlockMetadata(dx, dy, dz); + @Nullable + List drops = null; // Try to silk touch if we have the upgrade if (upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.SILK_TOUCH)) { if (block.canSilkHarvest(worldObj, fakePlayer, dx, dy, dz, meta)) { @@ -788,6 +795,24 @@ private boolean ejectStoredToAdjacent() { return null; } + /** + * Retrieve all stored items, i.e. for the quarry is broke, and we need to drop its contents + * @return A list of all stored items as ItemStacks with correct stack sizes + */ + public List retrieveAllItems() { + List items = new ArrayList<>(); + for (Object2IntMap.Entry entry : itemStorage.object2IntEntrySet()) { + if (entry.getIntValue() > 0) { + ItemStack item = entry.getKey().copy(); + item.stackSize = entry.getIntValue(); + items.add(item); + } + } + itemStorage.clear(); + storedItems = 0; + return items; + } + public void scanSidesForTEs() { sidedFluidAcceptors.clear(); sidedItemAcceptors.clear(); @@ -851,7 +876,11 @@ public void scanSidesForTEs() { * Does not check for anything, should be called after tryHarvestCurrentBlock() returns true. */ private void removeCurrentBlock() { - worldObj.setBlock(dx, dy, dz, upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.WORLD_HOLE) ? Blocks.air : REPLACE_BLOCK); + worldObj.setBlock( + dx, + dy, + dz, + upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.WORLD_HOLE) ? Blocks.air : REPLACE_BLOCK); } // TileEntity & LoadableTE @@ -866,9 +895,9 @@ public void validate() { * preferably we would call scanSidesForTEs() here, however * the contained getTileEntity() seems to always re-load the chunk at this stage * which then re-validates back to this function, eventually leading to a stackoverflow - * Instead we add a null inventory, so that it gets rechecked later - **/ - sidedItemAcceptors.put(ForgeDirection.UP, null); + * Instead we set the side check state to false, so that the next updateEntity() call does the scan + */ + sidesValidated = false; } } @@ -878,6 +907,10 @@ public void updateEntity() { if (worldObj.isRemote) return; if (facing == ForgeDirection.UNKNOWN) return; if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty()) return; + if (!sidesValidated) { + scanSidesForTEs(); + sidesValidated = true; + } int brokenBlocksTick = 0; if (state != QuarryWorkState.RUNNING && state != QuarryWorkState.FINISHED && state != QuarryWorkState.STOPPED) { @@ -893,8 +926,9 @@ public void updateEntity() { } } + int stepsPerTick = (int) (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1) * upgradeManager.getValue(EnderQuarryUpgradeManager.TieredEnderQuarryUpgrade.SPEED, 1.0)); if (state == QuarryWorkState.RUNNING) { - while (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) && stepPos()) { + while (brokenBlocksTick < (stepsPerTick) && stepPos()) { // TODO: Remove after this has been tested by others if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { UtilitiesInExcess.LOG.warn( @@ -1215,4 +1249,19 @@ public String toString() { return String.format("[(%d, %d), (%d, %d)]", this.low.x, this.low.y, this.high.x, this.high.y); } } + + private enum EnderQuarryReplaceBlock { + + COBBLE(Blocks.cobblestone), + DIRT(Blocks.dirt), + GLASS(Blocks.glass), + SNOW(Blocks.snow), + STONE(Blocks.stone); + + public final Block block; + + EnderQuarryReplaceBlock(Block block) { + this.block = block; + } + } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java index 50c25167..f2f135b2 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java @@ -2,13 +2,12 @@ import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.gtnewhorizon.gtnhlib.config.Config; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; @Config(modid = UtilitiesInExcess.MODID, category = "blocks.ender_quarry") @Config.Comment("Ender Quarry Configuration") @Config.LangKey("utilitiesinexcess.config.block.ender_quarry") public class EnderQuarryConfig { + // TODO: Balance everything in here @Config.DefaultBoolean(true) public static boolean enableEnderQuarry; @@ -31,22 +30,63 @@ public class EnderQuarryConfig { @Config.RangeInt(min = 100, max = 1_024_000) public static int enderQuarryBaseRFCost; - @Config.DefaultEnum("COBBLE") - @Config.Comment("Block type to replace mined blocks with if the world hole upgrade isn't present.") - public static EnderQuarryReplaceBlock enderQuarryReplaceBlock; + @Config.DefaultStringList({"COBBLE", "DIRT", "GLASS", "SNOW", "STONE"}) + @Config.DefaultString("COBBLE") + @Config.Comment("Block type to replace mined blocks with if the world hole upgrade isn't present.") + public static String enderQuarryReplaceBlock; + @Config.DefaultInt(400) + @Config.Comment("The amount of blocks the quarry tries to mine per tick, without speed upgrades.") + public static int enderQuarryBaseSpeed; - public enum EnderQuarryReplaceBlock { - COBBLE(Blocks.cobblestone), - DIRT(Blocks.dirt), - GLASS(Blocks.glass), - SNOW(Blocks.snow), - STONE(Blocks.stone); - public final Block block; + @Config.DefaultDouble(2D) + @Config.Comment("The multiplier applied to the base speed mine speed.") + public static double enderQuarrySpeed1Multiplier; - EnderQuarryReplaceBlock(Block block) { - this.block = block; - } - } + @Config.DefaultDouble(8D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySpeed1EnergyMultiplier; + + @Config.DefaultDouble(4D) + @Config.Comment("The multiplier applied to the base speed mine speed.") + public static double enderQuarrySpeed2Multiplier; + + @Config.DefaultDouble(16D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySpeed2EnergyMultiplier; + + @Config.DefaultDouble(7D) + @Config.Comment("The multiplier applied to the base speed mine speed.") + public static double enderQuarrySpeed3Multiplier; + + @Config.DefaultDouble(32D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySpeed3EnergyMultiplier; + + + @Config.DefaultDouble(12D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarryFortune1EnergyMultiplier; + + @Config.DefaultDouble(40D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarryFortune2EnergyMultiplier; + + @Config.DefaultDouble(100D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarryFortune3EnergyMultiplier; + + + @Config.DefaultDouble(1.2D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarryWorldHoleEnergyMultiplier; + + @Config.DefaultDouble(8D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySilkTouchEnergyMultiplier; + + @Config.DefaultDouble(3D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarryFluidPumpEnergyMultiplier; } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java index b397c6f4..5e65117a 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/utils/UIEUtils.java @@ -1,12 +1,13 @@ package com.fouristhenumber.utilitiesinexcess.utils; -import it.unimi.dsi.fastutil.Hash; -import net.minecraft.item.ItemStack; - import java.text.DecimalFormat; import java.util.Objects; import java.util.Random; +import net.minecraft.item.ItemStack; + +import it.unimi.dsi.fastutil.Hash; + public class UIEUtils { public static final Random uieRandom = new Random(); From 7a9c490dc331fd4c0da303a4f8195ee352fafa1f Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Mon, 22 Dec 2025 22:40:20 +0100 Subject: [PATCH 19/23] Send message to owner when done --- .../blocks/ender_quarry/BlockEnderQuarry.java | 2 +- .../tileentities/TileEntityEnderQuarry.java | 41 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java index 1eb94d90..7661e6d7 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java @@ -46,7 +46,7 @@ public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase TileEntity te = worldIn.getTileEntity(x, y, z); if (te instanceof TileEntityEnderQuarry quarry) { quarry.facing = getFacing(direction); - quarry.resetState(); + quarry.ownerUUID = placer.getUniqueID(); } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 6d8f1de8..3cdf694e 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -10,18 +10,21 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.world.ChunkCoordIntPair; @@ -73,6 +76,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int chunkZ; private int brokenBlocksTotal; private boolean sidesValidated = false; + public @Nullable UUID ownerUUID = null; private final HashMap sidedItemAcceptors = new HashMap<>(); private final HashMap sidedFluidAcceptors = new HashMap<>(); private final EnderQuarryUpgradeManager upgradeManager = new EnderQuarryUpgradeManager(); @@ -92,7 +96,7 @@ public TileEntityEnderQuarry() { storedItems = 0; } - public void resetState() { + private void resetState() { state = QuarryWorkState.STOPPED; brokenBlocksTotal = 0; } @@ -100,18 +104,18 @@ public void resetState() { public String getState() { return switch (state) { case RUNNING -> String.format( - "Quarry is currently mining at %d %d %d, has already mined %d blocks", + "Quarry is currently mining at %d %d %d, has already mined %d blocks.", dx, dy, dz, brokenBlocksTotal); - case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids"; - case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items"; - case STOPPED_WAITING_FOR_ENERGY -> "Quarry is missing energy"; - case THROTTLED_BY_ENERGY -> "Quarry is running, but not at full speed because of missing energy"; + case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids."; + case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items."; + case STOPPED_WAITING_FOR_ENERGY -> "Quarry is missing energy."; + case THROTTLED_BY_ENERGY -> "Quarry is running, but not at full speed because of missing energy."; case STOPPED -> "Quarry is stopped."; case FINISHED -> String.format( - "Quarry has finished after mining %d blocks%s", + "Quarry has finished after mining %d blocks%s.", brokenBlocksTotal, String.format(storedItems > 0 ? ", still holding %d items" : "", storedItems)); }; @@ -906,7 +910,21 @@ public void validate() { public void updateEntity() { if (worldObj.isRemote) return; if (facing == ForgeDirection.UNKNOWN) return; - if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty()) return; + if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty() && ownerUUID == null) { + if (selfIsLoaded) unloadSelf(); + return; + }; + if (state == QuarryWorkState.FINISHED && ownerUUID != null && worldObj.getTotalWorldTime() % 100 == 0) { + // Check if owner is online + MinecraftServer server = MinecraftServer.getServer(); + if (server != null) { + EntityPlayerMP owner = server.getConfigurationManager().playerEntityList.stream().filter((EntityPlayerMP player) -> player.getUniqueID().equals(ownerUUID)).findFirst().orElse(null); + if (owner != null) { + owner.addChatMessage(new ChatComponentText(String.format("Your Ender Quarry at (%d %d %d) in DIM %d has finished.", xCoord, yCoord, zCoord, worldObj.provider.dimensionId))); + ownerUUID = null; + } + } + } if (!sidesValidated) { scanSidesForTEs(); sidesValidated = true; @@ -959,7 +977,6 @@ public void updateEntity() { && state == QuarryWorkState.RUNNING) { if (nextWorkAreas.isEmpty()) { state = QuarryWorkState.FINISHED; - unloadSelf(); } else { setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); } @@ -977,7 +994,7 @@ public void updateEntity() { this.brokenBlocksTotal += brokenBlocksTick; } - if (worldObj.getTotalWorldTime() % 4 == 0) { + if (worldObj.getTotalWorldTime() % 4 == 0 && (storedItems > 0 || !fluidStorageIsEmpty())) { // Move internally stored stuff to adjacent blocks if (!ejectStoredToAdjacent()) scanSidesForTEs(); } @@ -1008,6 +1025,8 @@ public void readFromNBT(NBTTagCompound nbt) { } } brokenBlocksTotal = nbt.getInteger("blocks"); + ownerUUID = nbt.getString("owner").isEmpty() ? null : UUID.fromString(nbt.getString("owner")); + energyStorage.readFromNBT(nbt); NBTTagList tanksNBT = nbt.getTagList("tanks", Constants.NBT.TAG_COMPOUND); @@ -1051,6 +1070,8 @@ public void writeToNBT(NBTTagCompound nbt) { nbt.setTag("nextAreas", areasNBT); } nbt.setInteger("blocks", brokenBlocksTotal); + nbt.setString("owner", ownerUUID != null ? ownerUUID.toString() : ""); + energyStorage.writeToNBT(nbt); NBTTagList tanksNBT = new NBTTagList(); From ca477dd36ed3e0a4f7e93d98e71060d42a2f15ca Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Fri, 9 Jan 2026 03:43:25 +0100 Subject: [PATCH 20/23] Add waila support, fix some more bugs with arbitrary areas, start on providing messages via localization --- dependencies.gradle | 2 +- .../utilitiesinexcess/CommonProxy.java | 4 + .../utilitiesinexcess/UtilitiesInExcess.java | 2 +- .../tileentities/TileEntityEnderQuarry.java | 180 ++++++++++++------ .../utilitiesinexcess/compat/Mods.java | 1 + .../compat/waila/WailaHandler.java | 62 ++++++ .../assets/utilitiesinexcess/lang/en_US.lang | 10 + .../models/blocks/ender_quarry.json | 82 +------- 8 files changed, 204 insertions(+), 139 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java diff --git a/dependencies.gradle b/dependencies.gradle index 71cad413..cc1becba 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -58,6 +58,6 @@ dependencies { runtimeOnlyNonPublishable('curse.maven:chicken-chunks-229316:2233250') runtimeOnlyNonPublishable("com.github.GTNewHorizons:WAILAPlugins:0.7.2:dev") { transitive = false } - runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.9.15:dev") { transitive = false } + api("com.github.GTNewHorizons:waila:1.9.15:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.5:dev") { transitive = false } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java index 2d8fcedf..9d971ffb 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java @@ -8,6 +8,7 @@ import com.fouristhenumber.utilitiesinexcess.utils.SoundVolumeChecks; import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLInterModComms; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.event.FMLServerStartingEvent; @@ -35,6 +36,9 @@ public void preInit(FMLPreInitializationEvent event) { public void init(FMLInitializationEvent event) { soundVolumeChecks = new SoundVolumeChecks(); ModTileEntities.init(); + if (Mods.Waila.isLoaded()) { + FMLInterModComms.sendMessage("Waila", "register", "com.fouristhenumber.utilitiesinexcess.compat.waila.WailaHandler.callbackRegister"); + } } public void postInit(FMLPostInitializationEvent event) {} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java index 11d6b47f..ba864f27 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/UtilitiesInExcess.java @@ -38,7 +38,7 @@ version = Tags.VERSION, name = "UtilitiesInExcess", acceptedMinecraftVersions = "[1.7.10]", - dependencies = "required-after:gtnhlib@[0.6.31,)") + dependencies = "required-after:gtnhlib@[0.6.31,);after:Waila;") public class UtilitiesInExcess { public static final String MODID = "utilitiesinexcess"; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 3cdf694e..afc6acf3 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -75,6 +75,8 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int chunkX; private int chunkZ; private int brokenBlocksTotal; + private int estimatedTotalBlocks; + private int startedAt; private boolean sidesValidated = false; public @Nullable UUID ownerUUID = null; private final HashMap sidedItemAcceptors = new HashMap<>(); @@ -172,29 +174,27 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { } else { // DEBUG: Clear work area of debug blocks - /* - * Vector2i low = new Vector2i(Integer.MAX_VALUE); - * Vector2i high = new Vector2i(Integer.MIN_VALUE); - * for (Vector2i point : scanReturn) { - * if (point.x < low.x) { - * low.x = point.x; - * } - * if (point.y < low.y) { - * low.y = point.y; - * } - * if (point.x > high.x) { - * high.x = point.x; - * } - * if (point.y > high.y) { - * high.y = point.y; - * } - * } - * for (int x = low.x; x <= high.x; x++) { - * for (int z = low.y; z <= high.y; z++) { - * worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); - * } - * } - */ +// Vector2i low = new Vector2i(Integer.MAX_VALUE); +// Vector2i high = new Vector2i(Integer.MIN_VALUE); +// for (Vector2i point : scanReturn) { +// if (point.x < low.x) { +// low.x = point.x; +// } +// if (point.y < low.y) { +// low.y = point.y; +// } +// if (point.x > high.x) { +// high.x = point.x; +// } +// if (point.y > high.y) { +// high.y = point.y; +// } +// } +// for (int x = low.x; x <= high.x; x++) { +// for (int z = low.y; z <= high.y; z++) { +// worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); +// } +// } nextWorkAreas = computeRectanglesFromRectilinearPointPolygon(scanReturn); setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); @@ -220,9 +220,9 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { private List computeRectanglesFromRectilinearPointPolygon(List points) { RectilinearEdgePoly poly = new RectilinearEdgePoly(points); List subAreas = new ArrayList<>(); - // DEBUG: int color = 0; List activeSpans = new ArrayList<>(); + HashMap> shrunkSpansByY = new HashMap<>(); // Sweep through y coordinates from bottom to top, skip last since we handle active spans after the loop for (int i = 0; i < poly.yCoords.size() - 1; i++) { @@ -257,6 +257,34 @@ private List computeRectanglesFromRectilinearPointPolygon(List boolean intersectsWithTopBoundary = poly.intersectsWithHorizontalBoundary(y, lowX, highX) && (active.y != y); + // Check for overlaps with previously shrunk spans which would be excluded, + // And create smaller single-line areas for them + // Store bottom and top spans that were shrunk inwards due to boundary intersections + if (intersectsWithBottomBoundary) { + for (RectilinearEdgePoly.Span span : shrunkSpansByY.getOrDefault(active.y, Collections.emptyList())) { + // Check if the two intersect horizontally + if (span.pointIsInSpan(lowX)) { + subAreas.add(new Area2d(lowX, active.y, span.getMaxX(), active.y)); + } else if (span.pointIsInSpan(highX)) { + subAreas.add(new Area2d(span.getMinX(), active.y, highX, active.y)); + } + } + shrunkSpansByY.computeIfAbsent(active.y, k -> new ArrayList<>()) + .add(new RectilinearEdgePoly.Span(lowX, highX, active.y)); + } + if (intersectsWithTopBoundary) { + for (RectilinearEdgePoly.Span span : shrunkSpansByY.getOrDefault(y, Collections.emptyList())) { + // Check if the two intersect horizontally + if (span.pointIsInSpan(lowX)) { + subAreas.add(new Area2d(lowX, y, span.getMaxX(), y)); + } else if (span.pointIsInSpan(highX)) { + subAreas.add(new Area2d(span.getMinX(), y, highX, y)); + } + } + shrunkSpansByY.computeIfAbsent(y, k -> new ArrayList<>()) + .add(new RectilinearEdgePoly.Span(lowX, highX, y)); + } + Area2d subArea = new Area2d( active.x1, active.y, @@ -264,9 +292,11 @@ private List computeRectanglesFromRectilinearPointPolygon(List y, new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, intersectsWithTopBoundary ? 1 : 0)); - // The base area should have a width and height greater than zero, but that might change after - // applying shrinkage - if (subArea.height > 0 && subArea.width > 0) subAreas.add(subArea); + // The base area should have a width and height on init greater than zero, but that might change after + // applying shrinkage. + // We keep any area with width >= 0 and height >= 0 to allow for weird cases with single line areas. + if (subArea.height >= 0 && subArea.width >= 0) + subAreas.add(subArea); } } @@ -283,10 +313,22 @@ private List computeRectanglesFromRectilinearPointPolygon(List // Output remaining active spans that can't be extended anymore int lastY = poly.yCoords.get(poly.yCoords.size() - 1); for (RectilinearEdgePoly.Span span : activeSpans) { - boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary( - span.y, - Math.min(span.x1, span.x2) + 1, - Math.max(span.x1, span.x2) - 1) && (span.y != lastY); + int lowX = Math.min(span.x1, span.x2) + 1; + int highX = Math.max(span.x1, span.x2) - 1; + boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary(span.y, lowX, highX) && (span.y != lastY); + + // Same overlap check as before, just for bottom side now + if (intersectsWithBottomBoundary) { + for (RectilinearEdgePoly.Span shrunkSpan : shrunkSpansByY.getOrDefault(span.y, Collections.emptyList())) { + // Check if the two intersect horizontally + if (shrunkSpan.pointIsInSpan(lowX)) { + subAreas.add(new Area2d(lowX, span.y, shrunkSpan.getMaxX(), span.y)); + } else if (shrunkSpan.pointIsInSpan(highX)) { + subAreas.add(new Area2d(shrunkSpan.getMinX(), span.y, highX, span.y)); + } + } + } + // Always shrink top side on last rectangle subAreas.add( new Area2d( @@ -298,25 +340,26 @@ private List computeRectanglesFromRectilinearPointPolygon(List } // DEBUG: Draw sub areas on floor - /* - * for (Area2d subArea : subAreas) { - * for (int x = subArea.low.x; x <= subArea.high.x; x++) { - * for (int z = subArea.low.y; z <= subArea.high.y; z++) { - * if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == - * Blocks.gold_block) { - * worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); - * //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); - * } else { - * worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); - * } - * } - * } - * color++; - * if (color == 16) { - * color = 0; - * } - * } - */ +// int color = 0; +// for (Area2d subArea : subAreas) { +// for (int x = subArea.low.x; x <= subArea.high.x; x++) { +// for (int z = subArea.low.y; z <= subArea.high.y; z++) { +// if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == +// Blocks.gold_block) { +// worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); +// //worldObj.setBlock(x, this.yCoord + 3 + color, z, Blocks.stained_glass, color, 2); +// +// //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); +// } else { +// worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); +// } +// } +// } +// color++; +// if (color == 16) { +// color = 0; +// } +// } return subAreas; } @@ -462,6 +505,21 @@ static class Span { boolean matches(Span other) { return this.x1 == other.x1 && this.x2 == other.x2; } + + int getMinX() { + return Math.min(x1, x2); + } + + int getMaxX() { + return Math.max(x1, x2); + } + + /** + * Check if an x value is within the span, assuming y is the same + */ + boolean pointIsInSpan(int x) { + return x >= getMinX() && x <= getMaxX(); + } } } @@ -1169,13 +1227,21 @@ public FluidTankInfo[] getTankInfo(ForgeDirection from) { } public enum QuarryWorkState { - STOPPED, - STOPPED_WAITING_FOR_FLUID_SPACE, - STOPPED_WAITING_FOR_ITEM_SPACE, - STOPPED_WAITING_FOR_ENERGY, - THROTTLED_BY_ENERGY, - FINISHED, - RUNNING + STOPPED("uie.quarry.state.1"), + STOPPED_WAITING_FOR_FLUID_SPACE("uie.quarry.state.2"), + STOPPED_WAITING_FOR_ITEM_SPACE("uie.quarry.state.3"), + STOPPED_WAITING_FOR_ENERGY("uie.quarry.state.4"), + THROTTLED_BY_ENERGY("uie.quarry.state.5"), + FINISHED("uie.quarry.state.6"), + RUNNING("uie.quarry.state.7"); + + public static final QuarryWorkState[] VALUES = values(); + + public final String localKey; + + QuarryWorkState(String localKey) { + this.localKey = localKey; + } } public static class Area2d { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/Mods.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/Mods.java index d5d73f19..5ed769c6 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/Mods.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/Mods.java @@ -12,6 +12,7 @@ public enum Mods { Thaumcraft("Thaumcraft"), NEI("NotEnoughItems"), CraftTweaker("MineTweaker3"), + Waila("Waila"), ; // spotless:on diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java new file mode 100644 index 00000000..75e0075f --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java @@ -0,0 +1,62 @@ +package com.fouristhenumber.utilitiesinexcess.compat.waila; + + +import com.fouristhenumber.utilitiesinexcess.CommonProxy; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; +import mcp.mobius.waila.api.IWailaDataProvider; +import mcp.mobius.waila.api.IWailaRegistrar; +import mcp.mobius.waila.cbcore.LangUtil; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import java.util.List; + + +/** + Registered in {@link CommonProxy} via IMC. + */ +public class WailaHandler implements IWailaDataProvider { + + public static void callbackRegister(IWailaRegistrar registrar) { + WailaHandler instance = new WailaHandler(); + registrar.registerBodyProvider(instance, TileEntityEnderQuarry.class); + registrar.registerNBTProvider(instance, TileEntityEnderQuarry.class); + } + + @Override + public ItemStack getWailaStack(IWailaDataAccessor accessor, IWailaConfigHandler config) { + return null; + } + + @Override + public List getWailaHead(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { + return currenttip; + } + + @Override + public List getWailaBody(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { + if (accessor.getTileEntity() instanceof TileEntityEnderQuarry) { + TileEntityEnderQuarry.QuarryWorkState state = TileEntityEnderQuarry.QuarryWorkState.VALUES[accessor.getNBTData().getInteger("state")]; + currenttip.add(String.format(LangUtil.instance.translate("uie.quarry.waila.state"), LangUtil.instance.translate(state.localKey))); + } + return currenttip; + } + + @Override + public List getWailaTail(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { + return currenttip; + } + + @Override + public NBTTagCompound getNBTData(EntityPlayerMP player, TileEntity te, NBTTagCompound tag, World world, int x, int y, int z) { + if (te != null) { + te.writeToNBT(tag); + } + return tag; + } +} diff --git a/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang b/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang index b8a22740..8a591550 100644 --- a/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang +++ b/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang @@ -281,3 +281,13 @@ nei.infopage.uie.temporal_gate.2=No monsters will spawn to harass you, and there nei.infopage.uie.temporal_gate.3=Right-click the portal in any other dimension to enter. Right-click the portal in the End of Time to return to where you came from, or shift-right-click it to return to the Overworld's spawn point. nei.infopage.uie.temporal_gate.4=You will appear at the most recent portal placed in the End of Time. If it no longer exists, you will appear at the bedrock below where you originally entered. If even that is gone, the platform will reappear, replacing anything in its way. nei.infopage.uie.temporal_gate.5=§4§oWas that red star in the sky always there? + +uie.quarry.state.1=Stopped +uie.quarry.state.2=Waiting for Fluid Space +uie.quarry.state.3=Waiting for Item Space +uie.quarry.state.4=Waiting for Energy +uie.quarry.state.5=Throttled by Energy +uie.quarry.state.6=Finished +uie.quarry.state.7=Running + +uie.quarry.waila.state=Status: %s diff --git a/src/main/resources/assets/utilitiesinexcess/models/blocks/ender_quarry.json b/src/main/resources/assets/utilitiesinexcess/models/blocks/ender_quarry.json index 3ea408cb..3dd99364 100644 --- a/src/main/resources/assets/utilitiesinexcess/models/blocks/ender_quarry.json +++ b/src/main/resources/assets/utilitiesinexcess/models/blocks/ender_quarry.json @@ -722,84 +722,6 @@ "down": {"uv": [3.75, 12.5, 3.5, 13], "texture": "#0"} } }, - { - "from": [12, 5, 1], - "to": [13, 11, 2], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.25, 1]}, - "faces": { - "north": {"uv": [10.5, 2, 10.75, 3.5], "texture": "#0"}, - "east": {"uv": [5.5, 10.5, 5.75, 12], "texture": "#0"}, - "south": {"uv": [5.75, 10.5, 6, 12], "texture": "#0"}, - "west": {"uv": [10.5, 9.5, 10.75, 11], "texture": "#0"}, - "up": {"uv": [5, 13.25, 4.75, 13], "texture": "#0"}, - "down": {"uv": [13.25, 4.75, 13, 5], "texture": "#0"} - } - }, - { - "from": [10, 6, 1], - "to": [11, 10, 2], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.25, 1]}, - "faces": { - "north": {"uv": [10.5, 11, 10.75, 12], "texture": "#0"}, - "east": {"uv": [11.5, 8, 11.75, 9], "texture": "#0"}, - "south": {"uv": [8.5, 11.5, 8.75, 12.5], "texture": "#0"}, - "west": {"uv": [8.75, 11.5, 9, 12.5], "texture": "#0"}, - "up": {"uv": [5.25, 13.25, 5, 13], "texture": "#0"}, - "down": {"uv": [13.25, 5, 13, 5.25], "texture": "#0"} - } - }, - { - "from": [5, 12, 1], - "to": [11, 13, 2], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.25, 1]}, - "faces": { - "north": {"uv": [9.5, 8.5, 11, 8.75], "texture": "#0"}, - "east": {"uv": [5.25, 13, 5.5, 13.25], "texture": "#0"}, - "south": {"uv": [9.5, 8.75, 11, 9], "texture": "#0"}, - "west": {"uv": [13, 5.25, 13.25, 5.5], "texture": "#0"}, - "up": {"uv": [11.5, 6.75, 10, 6.5], "texture": "#0"}, - "down": {"uv": [11.5, 6.75, 10, 7], "texture": "#0"} - } - }, - { - "from": [6, 10, 1], - "to": [10, 11, 2], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.25, 1]}, - "faces": { - "north": {"uv": [9, 11.5, 10, 11.75], "texture": "#0"}, - "east": {"uv": [5.5, 13, 5.75, 13.25], "texture": "#0"}, - "south": {"uv": [11.5, 9, 12.5, 9.25], "texture": "#0"}, - "west": {"uv": [13, 5.5, 13.25, 5.75], "texture": "#0"}, - "up": {"uv": [12.5, 9.5, 11.5, 9.25], "texture": "#0"}, - "down": {"uv": [12.5, 9.5, 11.5, 9.75], "texture": "#0"} - } - }, - { - "from": [3, 5, 1], - "to": [4, 11, 2], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.25, 1]}, - "faces": { - "north": {"uv": [1.5, 10.75, 1.75, 12.25], "texture": "#0"}, - "east": {"uv": [1.75, 10.75, 2, 12.25], "texture": "#0"}, - "south": {"uv": [10.75, 2, 11, 3.5], "texture": "#0"}, - "west": {"uv": [3, 10.75, 3.25, 12.25], "texture": "#0"}, - "up": {"uv": [6, 13.25, 5.75, 13], "texture": "#0"}, - "down": {"uv": [13.25, 5.75, 13, 6], "texture": "#0"} - } - }, - { - "from": [5, 6, 1], - "to": [6, 10, 2], - "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.25, 1]}, - "faces": { - "north": {"uv": [11.5, 9.75, 11.75, 10.75], "texture": "#0"}, - "east": {"uv": [11.5, 10.75, 11.75, 11.75], "texture": "#0"}, - "south": {"uv": [11, 11.5, 11.25, 12.5], "texture": "#0"}, - "west": {"uv": [11.25, 11.5, 11.5, 12.5], "texture": "#0"}, - "up": {"uv": [6.25, 13.25, 6, 13], "texture": "#0"}, - "down": {"uv": [13.25, 6, 13, 6.25], "texture": "#0"} - } - }, { "from": [7, 6, 1], "to": [9, 9, 2], @@ -980,7 +902,7 @@ "name": "group", "origin": [8, 8, 8], "color": 0, - "children": [47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72] + "children": [47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66] } ] -} +} \ No newline at end of file From 96f88bd9c5655855b51ac26ed9b5309c920b1b57 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:48:42 +0100 Subject: [PATCH 21/23] Use GTNHLib ItemSink for storing items, improve performance in some hot-paths, fix overflowing item size in nbt, apply spotless --- dependencies.gradle | 5 + .../utilitiesinexcess/CommonProxy.java | 5 +- .../ender_quarry/BlockEnderQuarryUpgrade.java | 8 +- .../EnderQuarryUpgradeManager.java | 54 +++- .../tileentities/TileEntityEnderQuarry.java | 241 ++++++++++-------- .../compat/waila/WailaHandler.java | 41 +-- .../config/blocks/EnderQuarryConfig.java | 55 ++-- 7 files changed, 243 insertions(+), 166 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index cc1becba..dc8f482c 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -56,6 +56,11 @@ dependencies { // For debugging chunkloading runtimeOnlyNonPublishable('curse.maven:chicken-chunks-229316:2233250') + // Testing performance with ender quarry inserts + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Avaritiaddons:1.9.3-GTNH:dev') { transitive = true } + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Avaritia:1.81-pre:dev') { transitive = false } + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-789-GTNH:dev') { transitive = true } + runtimeOnlyNonPublishable rfg.deobf(files("libs/spark-1.10.pre-forge-1.7.10.jar")) runtimeOnlyNonPublishable("com.github.GTNewHorizons:WAILAPlugins:0.7.2:dev") { transitive = false } api("com.github.GTNewHorizons:waila:1.9.15:dev") { transitive = false } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java index 9d971ffb..5c25e69c 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/CommonProxy.java @@ -37,7 +37,10 @@ public void init(FMLInitializationEvent event) { soundVolumeChecks = new SoundVolumeChecks(); ModTileEntities.init(); if (Mods.Waila.isLoaded()) { - FMLInterModComms.sendMessage("Waila", "register", "com.fouristhenumber.utilitiesinexcess.compat.waila.WailaHandler.callbackRegister"); + FMLInterModComms.sendMessage( + "Waila", + "register", + "com.fouristhenumber.utilitiesinexcess.compat.waila.WailaHandler.callbackRegister"); } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java index accc208e..ec0cb259 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarryUpgrade.java @@ -2,6 +2,8 @@ import static com.gtnewhorizon.gtnhlib.client.model.ModelISBRH.JSON_ISBRH_ID; +import java.util.List; + import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; @@ -12,12 +14,10 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.IIcon; +import net.minecraft.world.World; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.world.World; - -import java.util.List; public class BlockEnderQuarryUpgrade extends Block { @@ -34,7 +34,7 @@ public BlockEnderQuarryUpgrade() { @Override public void addCollisionBoxesToList(World worldIn, int x, int y, int z, AxisAlignedBB mask, - List list, Entity collider) { + List list, Entity collider) { this.setBlockBounds(0.5F / 16F, 1.5F / 16F, 0.5F / 16F, 15.5F / 16F, 15F / 16F, 15.5F / 16F); super.addCollisionBoxesToList(worldIn, x, y, z, mask, list, collider); } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java index d0f5dfdf..c0fdff61 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java @@ -1,15 +1,17 @@ package com.fouristhenumber.utilitiesinexcess.common.blocks.ender_quarry; import java.util.HashMap; +import java.util.HashSet; -import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import org.jetbrains.annotations.Nullable; import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; +import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; public class EnderQuarryUpgradeManager { private final HashMap activeUpgrades = new HashMap<>(); + public double totalCostMultiplier = 1.0; // Add an upgrade - keeps highest tier public void addUpgrade(EnderQuarryUpgrade upgrade) { @@ -24,6 +26,7 @@ public void addUpgrade(EnderQuarryUpgrade upgrade) { activeUpgrades.put(baseName, upgrade); } } + totalCostMultiplier = getTotalCostMultiplier(); } // Check if a specific upgrade is active @@ -58,17 +61,38 @@ public void remove(EnderQuarryUpgrade upgrade) { } else { activeUpgrades.remove(upgrade.getTierGroup()); } + totalCostMultiplier = getTotalCostMultiplier(); } public void clear() { activeUpgrades.clear(); + totalCostMultiplier = 1.0; } + /** + * Calculate the total cost multiplier from all active upgrades, mainly used internally. + * Use totalCostMultiplier field for fewer recalculations instead. + */ public double getTotalCostMultiplier() { - return activeUpgrades.values() - .stream() - .mapToDouble(EnderQuarryUpgrade::getCost) - .sum(); + double total = 1.0; + HashSet countedTieredUpgrades = new HashSet<>(); + for (EnderQuarryUpgrade upgrade : EnderQuarryUpgrade.VALUES) { + if (this.has(upgrade)) { + if (upgrade.isBoolean()) { + total += upgrade.cost; + } else { + // Tiered upgrade - only count the highest tier once + TieredEnderQuarryUpgrade tierGroup = upgrade.tierGroup; + if (tierGroup == null || countedTieredUpgrades.contains(tierGroup)) { + continue; + } + countedTieredUpgrades.add(tierGroup); + + total += this.getActive(tierGroup).cost; + } + } + } + return total; } public enum EnderQuarryUpgrade { @@ -79,13 +103,19 @@ public enum EnderQuarryUpgrade { PUMP_FLUIDS(EnderQuarryConfig.enderQuarryFluidPumpEnergyMultiplier, "upgrade_pump_fluids"), // Tiered upgrades with hardcoded values - SPEED_1(TieredEnderQuarryUpgrade.SPEED, 1, EnderQuarryConfig.enderQuarrySpeed1EnergyMultiplier, EnderQuarryConfig.enderQuarrySpeed1Multiplier, "upgrade_speed_1"), - SPEED_2(TieredEnderQuarryUpgrade.SPEED, 2, EnderQuarryConfig.enderQuarrySpeed2EnergyMultiplier, EnderQuarryConfig.enderQuarrySpeed2Multiplier, "upgrade_speed_2"), - SPEED_3(TieredEnderQuarryUpgrade.SPEED, 3, EnderQuarryConfig.enderQuarrySpeed3EnergyMultiplier, EnderQuarryConfig.enderQuarrySpeed3Multiplier, "upgrade_speed_3"), - - FORTUNE_1(TieredEnderQuarryUpgrade.FORTUNE, 1, EnderQuarryConfig.enderQuarryFortune1EnergyMultiplier, 1, "upgrade_fortune_1"), - FORTUNE_2(TieredEnderQuarryUpgrade.FORTUNE, 2, EnderQuarryConfig.enderQuarryFortune2EnergyMultiplier, 2, "upgrade_fortune_2"), - FORTUNE_3(TieredEnderQuarryUpgrade.FORTUNE, 3, EnderQuarryConfig.enderQuarryFortune3EnergyMultiplier, 3, "upgrade_fortune_3"); + SPEED_1(TieredEnderQuarryUpgrade.SPEED, 1, EnderQuarryConfig.enderQuarrySpeed1EnergyMultiplier, + EnderQuarryConfig.enderQuarrySpeed1Multiplier, "upgrade_speed_1"), + SPEED_2(TieredEnderQuarryUpgrade.SPEED, 2, EnderQuarryConfig.enderQuarrySpeed2EnergyMultiplier, + EnderQuarryConfig.enderQuarrySpeed2Multiplier, "upgrade_speed_2"), + SPEED_3(TieredEnderQuarryUpgrade.SPEED, 3, EnderQuarryConfig.enderQuarrySpeed3EnergyMultiplier, + EnderQuarryConfig.enderQuarrySpeed3Multiplier, "upgrade_speed_3"), + + FORTUNE_1(TieredEnderQuarryUpgrade.FORTUNE, 1, EnderQuarryConfig.enderQuarryFortune1EnergyMultiplier, 1, + "upgrade_fortune_1"), + FORTUNE_2(TieredEnderQuarryUpgrade.FORTUNE, 2, EnderQuarryConfig.enderQuarryFortune2EnergyMultiplier, 2, + "upgrade_fortune_2"), + FORTUNE_3(TieredEnderQuarryUpgrade.FORTUNE, 3, EnderQuarryConfig.enderQuarryFortune3EnergyMultiplier, 3, + "upgrade_fortune_3"); public static final EnderQuarryUpgrade[] VALUES = values(); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index afc6acf3..31a3ff26 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -52,6 +53,11 @@ import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; import com.fouristhenumber.utilitiesinexcess.utils.UIEUtils; +import com.gtnewhorizon.gtnhlib.capability.item.ItemSink; +import com.gtnewhorizon.gtnhlib.item.ImmutableItemStack; +import com.gtnewhorizon.gtnhlib.item.InsertionItemStack; +import com.gtnewhorizon.gtnhlib.item.InventoryIterator; +import com.gtnewhorizon.gtnhlib.util.ItemUtil; import cofh.api.energy.EnergyStorage; import cofh.api.energy.IEnergyReceiver; @@ -79,7 +85,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int startedAt; private boolean sidesValidated = false; public @Nullable UUID ownerUUID = null; - private final HashMap sidedItemAcceptors = new HashMap<>(); + private final HashMap sidedItemAcceptors = new LinkedHashMap<>(); private final HashMap sidedFluidAcceptors = new HashMap<>(); private final EnderQuarryUpgradeManager upgradeManager = new EnderQuarryUpgradeManager(); @@ -174,27 +180,27 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { } else { // DEBUG: Clear work area of debug blocks -// Vector2i low = new Vector2i(Integer.MAX_VALUE); -// Vector2i high = new Vector2i(Integer.MIN_VALUE); -// for (Vector2i point : scanReturn) { -// if (point.x < low.x) { -// low.x = point.x; -// } -// if (point.y < low.y) { -// low.y = point.y; -// } -// if (point.x > high.x) { -// high.x = point.x; -// } -// if (point.y > high.y) { -// high.y = point.y; -// } -// } -// for (int x = low.x; x <= high.x; x++) { -// for (int z = low.y; z <= high.y; z++) { -// worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); -// } -// } + // Vector2i low = new Vector2i(Integer.MAX_VALUE); + // Vector2i high = new Vector2i(Integer.MIN_VALUE); + // for (Vector2i point : scanReturn) { + // if (point.x < low.x) { + // low.x = point.x; + // } + // if (point.y < low.y) { + // low.y = point.y; + // } + // if (point.x > high.x) { + // high.x = point.x; + // } + // if (point.y > high.y) { + // high.y = point.y; + // } + // } + // for (int x = low.x; x <= high.x; x++) { + // for (int z = low.y; z <= high.y; z++) { + // worldObj.setBlock(x, this.yCoord - 1, z, Blocks.grass); + // } + // } nextWorkAreas = computeRectanglesFromRectilinearPointPolygon(scanReturn); setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); @@ -261,7 +267,8 @@ private List computeRectanglesFromRectilinearPointPolygon(List // And create smaller single-line areas for them // Store bottom and top spans that were shrunk inwards due to boundary intersections if (intersectsWithBottomBoundary) { - for (RectilinearEdgePoly.Span span : shrunkSpansByY.getOrDefault(active.y, Collections.emptyList())) { + for (RectilinearEdgePoly.Span span : shrunkSpansByY + .getOrDefault(active.y, Collections.emptyList())) { // Check if the two intersect horizontally if (span.pointIsInSpan(lowX)) { subAreas.add(new Area2d(lowX, active.y, span.getMaxX(), active.y)); @@ -292,11 +299,11 @@ private List computeRectanglesFromRectilinearPointPolygon(List y, new Vector4i(1, intersectsWithBottomBoundary ? 1 : 0, 1, intersectsWithTopBoundary ? 1 : 0)); - // The base area should have a width and height on init greater than zero, but that might change after + // The base area should have a width and height on init greater than zero, but that might change + // after // applying shrinkage. // We keep any area with width >= 0 and height >= 0 to allow for weird cases with single line areas. - if (subArea.height >= 0 && subArea.width >= 0) - subAreas.add(subArea); + if (subArea.height >= 0 && subArea.width >= 0) subAreas.add(subArea); } } @@ -315,11 +322,13 @@ private List computeRectanglesFromRectilinearPointPolygon(List for (RectilinearEdgePoly.Span span : activeSpans) { int lowX = Math.min(span.x1, span.x2) + 1; int highX = Math.max(span.x1, span.x2) - 1; - boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary(span.y, lowX, highX) && (span.y != lastY); + boolean intersectsWithBottomBoundary = poly.intersectsWithHorizontalBoundary(span.y, lowX, highX) + && (span.y != lastY); // Same overlap check as before, just for bottom side now if (intersectsWithBottomBoundary) { - for (RectilinearEdgePoly.Span shrunkSpan : shrunkSpansByY.getOrDefault(span.y, Collections.emptyList())) { + for (RectilinearEdgePoly.Span shrunkSpan : shrunkSpansByY + .getOrDefault(span.y, Collections.emptyList())) { // Check if the two intersect horizontally if (shrunkSpan.pointIsInSpan(lowX)) { subAreas.add(new Area2d(lowX, span.y, shrunkSpan.getMaxX(), span.y)); @@ -340,26 +349,26 @@ private List computeRectanglesFromRectilinearPointPolygon(List } // DEBUG: Draw sub areas on floor -// int color = 0; -// for (Area2d subArea : subAreas) { -// for (int x = subArea.low.x; x <= subArea.high.x; x++) { -// for (int z = subArea.low.y; z <= subArea.high.y; z++) { -// if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == -// Blocks.gold_block) { -// worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); -// //worldObj.setBlock(x, this.yCoord + 3 + color, z, Blocks.stained_glass, color, 2); -// -// //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); -// } else { -// worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); -// } -// } -// } -// color++; -// if (color == 16) { -// color = 0; -// } -// } + // int color = 0; + // for (Area2d subArea : subAreas) { + // for (int x = subArea.low.x; x <= subArea.high.x; x++) { + // for (int z = subArea.low.y; z <= subArea.high.y; z++) { + // if (worldObj.getBlock(x, this.yCoord - 1, z) == Blocks.wool || worldObj.getBlock(x, this.yCoord - 1, z) == + // Blocks.gold_block) { + // worldObj.setBlock(x, this.yCoord - 1, z, Blocks.gold_block); + // //worldObj.setBlock(x, this.yCoord + 3 + color, z, Blocks.stained_glass, color, 2); + // + // //throw new RuntimeException("Overlapping sub-areas detected at " + x + ", " + z); + // } else { + // worldObj.setBlock(x, this.yCoord - 1, z, Blocks.wool, color, 2); + // } + // } + // } + // color++; + // if (color == 16) { + // color = 0; + // } + // } return subAreas; } @@ -597,9 +606,10 @@ private boolean stepPos() { private boolean[] tryHarvestCurrentBlock() { Block block = worldObj.getBlock(dx, dy, dz); if (block.getMaterial() == Material.air || worldObj.isAirBlock(dx, dy, dz) + || block == REPLACE_BLOCK || block.getBlockHardness(worldObj, dx, dy, dz) < 0 || block.getBlockHardness(worldObj, dx, dy, dz) > 100) { - return new boolean[] { tryConsumeEnergy(0.0f, false), true }; + return new boolean[] { tryConsumeEnergy(0f, false), true }; } ; float hardness = block.getBlockHardness(worldObj, dx, dy, dz); @@ -610,8 +620,10 @@ private boolean[] tryHarvestCurrentBlock() { } private boolean tryConsumeEnergy(float hardness, boolean simulate) { - double costMultiplier = upgradeManager.getTotalCostMultiplier(); - int cost = (int) ((hardness == 0 ? 100 : EnderQuarryConfig.enderQuarryBaseRFCost) * costMultiplier); + // Get base cost multiplier from upgrades, add a multiplier based on hardness (that roughly scales 0 - 10 to 1.0 + // - 1.66) + double costMultiplier = upgradeManager.totalCostMultiplier + (1.0 + 0.8 * (hardness / (hardness + 2))); + int cost = (int) (EnderQuarryConfig.enderQuarryBaseRFCost * costMultiplier); if (energyStorage.extractEnergy(cost, true) >= cost) { if (!simulate) { energyStorage.extractEnergy(cost, false); @@ -624,6 +636,7 @@ private boolean tryConsumeEnergy(float hardness, boolean simulate) { /** * Harvests the block at the current position, and tries to store the resulting items / fluids to internal storage + * * @return If we could store all resulting items / fluids */ private boolean harvestAndStoreBlock(Block block) { @@ -715,9 +728,10 @@ private boolean tryStoreFluid(FluidStack fluid) { * @return If we could store all the provided items */ private boolean tryStoreItems(List items) { - int toStore = items.stream() - .mapToInt((item) -> item != null ? item.stackSize : 0) - .sum(); + int toStore = 0; + for (ItemStack item : items) { + toStore += item != null ? item.stackSize : 0; + } if (storedItems + toStore <= ITEM_BUFFER_CAPACITY) { for (ItemStack item : items) { storeItemToStorage(item); @@ -738,9 +752,12 @@ private void storeItemToStorage(ItemStack item) { } private boolean fluidStorageIsEmpty() { - return fluidStorage.stream() - .mapToInt(FluidTank::getFluidAmount) - .sum() == 0; + for (FluidTank tank : fluidStorage) { + if (tank.getFluidAmount() > 0) { + return false; + } + } + return true; } /** @@ -750,54 +767,50 @@ private boolean fluidStorageIsEmpty() { */ private boolean ejectStoredToAdjacent() { if (storedItems > 0) { - for (IInventory inventory : sidedItemAcceptors.values()) { - if (inventory != null) { - for (int i = 0; i < inventory.getSizeInventory(); i++) { - ItemStack slotItem = inventory.getStackInSlot(i); - if (slotItem != null) { - // There is already an item in this slot, see if we can merge with it - int canBeAddedToStack = Math - .min(slotItem.getMaxStackSize(), inventory.getInventoryStackLimit()) - - slotItem.stackSize; - if (canBeAddedToStack > 0) { - ItemStack key = slotItem.copy(); - key.stackSize = 0; - - // Do we also have this item type in our storage? - int storedAmount = itemStorage.getOrDefault(key, -1); - if (storedAmount > 0) { - int toBeAdded = Math.min(storedAmount, canBeAddedToStack); - - ItemStack modifiedStack = key.copy(); - modifiedStack.stackSize = slotItem.stackSize + toBeAdded; - if (inventory.isItemValidForSlot(i, modifiedStack)) { - inventory.setInventorySlotContents(i, modifiedStack); - - itemStorage.put(key, storedAmount - toBeAdded); - storedItems -= toBeAdded; - } + for (Map.Entry inventory : sidedItemAcceptors.entrySet()) { + if (inventory.getValue() != null) { + ItemSink sink = ItemUtil.getItemSink(inventory.getValue(), inventory.getKey()); + InventoryIterator iter = sink.sinkIterator(); + if (iter == null) { + continue; + } + + while (iter.hasNext() && storedItems > 0) { + ImmutableItemStack slotContent = iter.next(); + if (slotContent != null) { + // There is already an item in this slot, see if we have more of the same type to add + ItemStack slotItem = slotContent.toStack(); + ItemStack key = slotItem.copy(); + key.stackSize = 0; + + int storedAmount = itemStorage.getOrDefault(key, -1); + if (storedAmount > 0) { + // modifiedStack.stackSize = slotItem.stackSize + toBeAdded; Stack size is handled by + // InsertionItemStack + int addedAmount = storedAmount + - iter.insert(new InsertionItemStack(key.copy(), storedAmount), false); + if (addedAmount > 0) { + itemStorage.put(key, storedAmount - addedAmount); + storedItems -= addedAmount; } } } else { // Empty slot, don't mind us Object2IntMap.Entry entry = peekItem(); if (entry != null) { - int canBeAddedToSlot = Math - .min(entry.getIntValue(), inventory.getInventoryStackLimit()); - if (canBeAddedToSlot > 0) { - ItemStack item = entry.getKey() - .copy(); - item.stackSize = canBeAddedToSlot; - if (inventory.isItemValidForSlot(i, item)) { - inventory.setInventorySlotContents(i, item); - - itemStorage.put(entry.getKey(), entry.getIntValue() - canBeAddedToSlot); - storedItems -= canBeAddedToSlot; - } + ItemStack item = entry.getKey() + .copy(); + int storedAmount = entry.getIntValue(); + // item.stackSize = canBeAddedToSlot; Stack size is handled by InsertionItemStack + int addedAmount = storedAmount + - iter.insert(new InsertionItemStack(item, storedAmount), false); + if (addedAmount > 0) { + itemStorage.put(entry.getKey(), storedAmount - addedAmount); + storedItems -= addedAmount; } } else { // Not sure why, but it seems our inventory is empty even though we thought it wasn't - // Lets re-count + // Lets re-count instead of breaking things storedItems = itemStorage.values() .intStream() .sum(); @@ -858,14 +871,16 @@ private boolean ejectStoredToAdjacent() { } /** - * Retrieve all stored items, i.e. for the quarry is broke, and we need to drop its contents + * Retrieve all stored items, i.e. for the quarry is broken, and we need to drop its contents + * * @return A list of all stored items as ItemStacks with correct stack sizes */ public List retrieveAllItems() { List items = new ArrayList<>(); for (Object2IntMap.Entry entry : itemStorage.object2IntEntrySet()) { if (entry.getIntValue() > 0) { - ItemStack item = entry.getKey().copy(); + ItemStack item = entry.getKey() + .copy(); item.stackSize = entry.getIntValue(); items.add(item); } @@ -971,14 +986,26 @@ public void updateEntity() { if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty() && ownerUUID == null) { if (selfIsLoaded) unloadSelf(); return; - }; + } ; if (state == QuarryWorkState.FINISHED && ownerUUID != null && worldObj.getTotalWorldTime() % 100 == 0) { // Check if owner is online MinecraftServer server = MinecraftServer.getServer(); if (server != null) { - EntityPlayerMP owner = server.getConfigurationManager().playerEntityList.stream().filter((EntityPlayerMP player) -> player.getUniqueID().equals(ownerUUID)).findFirst().orElse(null); + EntityPlayerMP owner = server.getConfigurationManager().playerEntityList.stream() + .filter( + (EntityPlayerMP player) -> player.getUniqueID() + .equals(ownerUUID)) + .findFirst() + .orElse(null); if (owner != null) { - owner.addChatMessage(new ChatComponentText(String.format("Your Ender Quarry at (%d %d %d) in DIM %d has finished.", xCoord, yCoord, zCoord, worldObj.provider.dimensionId))); + owner.addChatMessage( + new ChatComponentText( + String.format( + "Your Ender Quarry at (%d %d %d) in DIM %d has finished.", + xCoord, + yCoord, + zCoord, + worldObj.provider.dimensionId))); ownerUUID = null; } } @@ -1002,7 +1029,8 @@ public void updateEntity() { } } - int stepsPerTick = (int) (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1) * upgradeManager.getValue(EnderQuarryUpgradeManager.TieredEnderQuarryUpgrade.SPEED, 1.0)); + int stepsPerTick = (int) (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1) + * upgradeManager.getValue(EnderQuarryUpgradeManager.TieredEnderQuarryUpgrade.SPEED, 1.0)); if (state == QuarryWorkState.RUNNING) { while (brokenBlocksTick < (stepsPerTick) && stepPos()) { // TODO: Remove after this has been tested by others @@ -1083,7 +1111,8 @@ public void readFromNBT(NBTTagCompound nbt) { } } brokenBlocksTotal = nbt.getInteger("blocks"); - ownerUUID = nbt.getString("owner").isEmpty() ? null : UUID.fromString(nbt.getString("owner")); + ownerUUID = nbt.getString("owner") + .isEmpty() ? null : UUID.fromString(nbt.getString("owner")); energyStorage.readFromNBT(nbt); @@ -1103,6 +1132,7 @@ public void readFromNBT(NBTTagCompound nbt) { NBTTagCompound tag = itemsNBT.getCompoundTagAt(i); ItemStack item = ItemStack.loadItemStackFromNBT(tag); if (item != null) { + item.stackSize = tag.getShort("Count"); storeItemToStorage(item); } } @@ -1148,8 +1178,10 @@ public void writeToNBT(NBTTagCompound nbt) { NBTTagCompound tag = new NBTTagCompound(); ItemStack item = entry.getKey() .copy(); - item.stackSize = entry.getIntValue(); item.writeToNBT(tag); + // Default itemstack uses byte for count, but we can have more than 255 of an item + tag.setShort("Count", (short) entry.getIntValue()); + itemsNBT.appendTag(tag); } } @@ -1227,6 +1259,7 @@ public FluidTankInfo[] getTankInfo(ForgeDirection from) { } public enum QuarryWorkState { + STOPPED("uie.quarry.state.1"), STOPPED_WAITING_FOR_FLUID_SPACE("uie.quarry.state.2"), STOPPED_WAITING_FOR_ITEM_SPACE("uie.quarry.state.3"), diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java index 75e0075f..ac31887d 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java @@ -1,25 +1,25 @@ package com.fouristhenumber.utilitiesinexcess.compat.waila; +import java.util.List; -import com.fouristhenumber.utilitiesinexcess.CommonProxy; -import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; -import mcp.mobius.waila.api.IWailaConfigHandler; -import mcp.mobius.waila.api.IWailaDataAccessor; -import mcp.mobius.waila.api.IWailaDataProvider; -import mcp.mobius.waila.api.IWailaRegistrar; -import mcp.mobius.waila.cbcore.LangUtil; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; -import java.util.List; +import com.fouristhenumber.utilitiesinexcess.CommonProxy; +import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityEnderQuarry; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; +import mcp.mobius.waila.api.IWailaDataProvider; +import mcp.mobius.waila.api.IWailaRegistrar; +import mcp.mobius.waila.cbcore.LangUtil; /** - Registered in {@link CommonProxy} via IMC. - */ + * Registered in {@link CommonProxy} via IMC. + */ public class WailaHandler implements IWailaDataProvider { public static void callbackRegister(IWailaRegistrar registrar) { @@ -34,26 +34,35 @@ public ItemStack getWailaStack(IWailaDataAccessor accessor, IWailaConfigHandler } @Override - public List getWailaHead(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { + public List getWailaHead(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { return currenttip; } @Override - public List getWailaBody(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { + public List getWailaBody(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { if (accessor.getTileEntity() instanceof TileEntityEnderQuarry) { - TileEntityEnderQuarry.QuarryWorkState state = TileEntityEnderQuarry.QuarryWorkState.VALUES[accessor.getNBTData().getInteger("state")]; - currenttip.add(String.format(LangUtil.instance.translate("uie.quarry.waila.state"), LangUtil.instance.translate(state.localKey))); + TileEntityEnderQuarry.QuarryWorkState state = TileEntityEnderQuarry.QuarryWorkState.VALUES[accessor + .getNBTData() + .getInteger("state")]; + currenttip.add( + String.format( + LangUtil.instance.translate("uie.quarry.waila.state"), + LangUtil.instance.translate(state.localKey))); } return currenttip; } @Override - public List getWailaTail(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { + public List getWailaTail(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { return currenttip; } @Override - public NBTTagCompound getNBTData(EntityPlayerMP player, TileEntity te, NBTTagCompound tag, World world, int x, int y, int z) { + public NBTTagCompound getNBTData(EntityPlayerMP player, TileEntity te, NBTTagCompound tag, World world, int x, + int y, int z) { if (te != null) { te.writeToNBT(tag); } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java index f2f135b2..ae77c24e 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java @@ -26,44 +26,42 @@ public class EnderQuarryConfig { public static int enderQuarryFluidTankStorage; @Config.Comment("Base factor of RF that is used per operation. Is influenced by upgrades & block hardness.") - @Config.DefaultInt(1_000) + @Config.DefaultInt(100) @Config.RangeInt(min = 100, max = 1_024_000) public static int enderQuarryBaseRFCost; - @Config.DefaultStringList({"COBBLE", "DIRT", "GLASS", "SNOW", "STONE"}) - @Config.DefaultString("COBBLE") - @Config.Comment("Block type to replace mined blocks with if the world hole upgrade isn't present.") - public static String enderQuarryReplaceBlock; + @Config.DefaultStringList({ "COBBLE", "DIRT", "GLASS", "SNOW", "STONE" }) + @Config.DefaultString("COBBLE") + @Config.Comment("Block type to replace mined blocks with if the world hole upgrade isn't present.") + public static String enderQuarryReplaceBlock; - @Config.DefaultInt(400) - @Config.Comment("The amount of blocks the quarry tries to mine per tick, without speed upgrades.") - public static int enderQuarryBaseSpeed; + @Config.DefaultInt(400) + @Config.Comment("The amount of blocks the quarry tries to mine per tick, without speed upgrades.") + public static int enderQuarryBaseSpeed; + @Config.DefaultDouble(2D) + @Config.Comment("The multiplier applied to the base speed mine speed.") + public static double enderQuarrySpeed1Multiplier; - @Config.DefaultDouble(2D) - @Config.Comment("The multiplier applied to the base speed mine speed.") - public static double enderQuarrySpeed1Multiplier; - - @Config.DefaultDouble(8D) - @Config.Comment("The energy multiplier applied when the upgrade is active.") - public static double enderQuarrySpeed1EnergyMultiplier; - - @Config.DefaultDouble(4D) - @Config.Comment("The multiplier applied to the base speed mine speed.") - public static double enderQuarrySpeed2Multiplier; + @Config.DefaultDouble(8D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySpeed1EnergyMultiplier; - @Config.DefaultDouble(16D) - @Config.Comment("The energy multiplier applied when the upgrade is active.") - public static double enderQuarrySpeed2EnergyMultiplier; + @Config.DefaultDouble(4D) + @Config.Comment("The multiplier applied to the base speed mine speed.") + public static double enderQuarrySpeed2Multiplier; - @Config.DefaultDouble(7D) - @Config.Comment("The multiplier applied to the base speed mine speed.") - public static double enderQuarrySpeed3Multiplier; + @Config.DefaultDouble(16D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySpeed2EnergyMultiplier; - @Config.DefaultDouble(32D) - @Config.Comment("The energy multiplier applied when the upgrade is active.") - public static double enderQuarrySpeed3EnergyMultiplier; + @Config.DefaultDouble(7D) + @Config.Comment("The multiplier applied to the base speed mine speed.") + public static double enderQuarrySpeed3Multiplier; + @Config.DefaultDouble(32D) + @Config.Comment("The energy multiplier applied when the upgrade is active.") + public static double enderQuarrySpeed3EnergyMultiplier; @Config.DefaultDouble(12D) @Config.Comment("The energy multiplier applied when the upgrade is active.") @@ -77,7 +75,6 @@ public class EnderQuarryConfig { @Config.Comment("The energy multiplier applied when the upgrade is active.") public static double enderQuarryFortune3EnergyMultiplier; - @Config.DefaultDouble(1.2D) @Config.Comment("The energy multiplier applied when the upgrade is active.") public static double enderQuarryWorldHoleEnergyMultiplier; From 5026580ce2965244eae05f5f0ab7a1c5f80fb49f Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:53:30 +0100 Subject: [PATCH 22/23] Add configurable bottom and top bounds by markers, more localizations, add etl in chat and waila with progress bars, fix skipping first block --- .../utilitiesinexcess/ClientProxy.java | 11 + .../EnderQuarryUpgradeManager.java | 6 +- .../tileentities/TileEntityEnderMarker.java | 6 +- .../tileentities/TileEntityEnderQuarry.java | 249 +++++++++++++----- .../compat/waila/TTRenderUIETimeLeftBar.java | 68 +++++ .../compat/waila/WailaHandler.java | 25 +- .../config/blocks/EnderQuarryConfig.java | 4 + .../assets/utilitiesinexcess/lang/en_US.lang | 8 +- 8 files changed, 300 insertions(+), 77 deletions(-) create mode 100644 src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/TTRenderUIETimeLeftBar.java diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/ClientProxy.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/ClientProxy.java index 0f1aedb7..e90357ba 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/ClientProxy.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/ClientProxy.java @@ -4,12 +4,15 @@ import com.fouristhenumber.utilitiesinexcess.common.renderers.InvertedIngotRenderer; import com.fouristhenumber.utilitiesinexcess.common.tileentities.TileEntityPortalUnderWorld; +import com.fouristhenumber.utilitiesinexcess.compat.Mods; +import com.fouristhenumber.utilitiesinexcess.compat.waila.TTRenderUIETimeLeftBar; import com.fouristhenumber.utilitiesinexcess.render.ISBRHUnderworldPortal; import com.fouristhenumber.utilitiesinexcess.render.TESRUnderworldPortal; import cpw.mods.fml.client.registry.ClientRegistry; import cpw.mods.fml.client.registry.RenderingRegistry; import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; public class ClientProxy extends CommonProxy { @@ -24,4 +27,12 @@ public void init(FMLInitializationEvent event) { RenderingRegistry.registerBlockHandler(ISBRHUnderworldPortal.INSTANCE); } } + + @Override + public void postInit(FMLPostInitializationEvent event) { + super.postInit(event); + if (Mods.Waila.isLoaded()) { + TTRenderUIETimeLeftBar.register(); + } + } } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java index c0fdff61..bfd42a01 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/EnderQuarryUpgradeManager.java @@ -11,7 +11,7 @@ public class EnderQuarryUpgradeManager { private final HashMap activeUpgrades = new HashMap<>(); - public double totalCostMultiplier = 1.0; + public double totalCostMultiplier = 0.0; // Add an upgrade - keeps highest tier public void addUpgrade(EnderQuarryUpgrade upgrade) { @@ -66,7 +66,7 @@ public void remove(EnderQuarryUpgrade upgrade) { public void clear() { activeUpgrades.clear(); - totalCostMultiplier = 1.0; + totalCostMultiplier = 0.0; } /** @@ -74,7 +74,7 @@ public void clear() { * Use totalCostMultiplier field for fewer recalculations instead. */ public double getTotalCostMultiplier() { - double total = 1.0; + double total = 0.0; HashSet countedTieredUpgrades = new HashSet<>(); for (EnderQuarryUpgrade upgrade : EnderQuarryUpgrade.VALUES) { if (this.has(upgrade)) { diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java index 51c6a493..c126c352 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderMarker.java @@ -17,6 +17,7 @@ import net.minecraft.network.play.server.S2APacketParticles; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; +import net.minecraft.util.StatCollector; import net.minecraftforge.common.util.ForgeDirection; import org.jetbrains.annotations.NotNull; @@ -24,7 +25,6 @@ import org.joml.Vector2i; import com.fouristhenumber.utilitiesinexcess.ModBlocks; -import com.fouristhenumber.utilitiesinexcess.UtilitiesInExcess; import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.IFacingTE; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; import com.fouristhenumber.utilitiesinexcess.utils.Tuple; @@ -164,7 +164,6 @@ public List boundaryForArbitraryLoop(EntityPlayer player) { } while (!stack.isEmpty()); if (markerChain != null) { - UtilitiesInExcess.chat("Completed marker chain with " + markerChain.size() + " entries."); ArrayList pointChain = new ArrayList<>(markerChain.size()); markerChain.forEach((e) -> pointChain.add(new Vector2i(e.xCoord, e.zCoord))); return pointChain; @@ -173,10 +172,11 @@ public List boundaryForArbitraryLoop(EntityPlayer player) { player.addChatComponentMessage( new ChatComponentText( String.format( - "Failed to complete marker chain, with last marker at (%d %d %d).", + StatCollector.translateToLocal("uie.quarry.scanmessage.5"), lastVisited.current.xCoord, lastVisited.current.yCoord, lastVisited.current.zCoord))); + // Spawn particles to show where the chain broke for (Object obj : worldObj.playerEntities) { EntityPlayerMP playerMP = (EntityPlayerMP) obj; diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index 31a3ff26..c48ea2da 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -1,5 +1,6 @@ package com.fouristhenumber.utilitiesinexcess.common.tileentities; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -28,6 +29,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; +import net.minecraft.util.StatCollector; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.Constants; @@ -52,6 +54,7 @@ import com.fouristhenumber.utilitiesinexcess.common.tileentities.utils.LoadableTE; import com.fouristhenumber.utilitiesinexcess.config.blocks.EnderQuarryConfig; import com.fouristhenumber.utilitiesinexcess.utils.DirectionUtil; +import com.fouristhenumber.utilitiesinexcess.utils.Tuple; import com.fouristhenumber.utilitiesinexcess.utils.UIEUtils; import com.gtnewhorizon.gtnhlib.capability.item.ItemSink; import com.gtnewhorizon.gtnhlib.item.ImmutableItemStack; @@ -67,7 +70,8 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver, IFluidHandler { public static final int BASE_STEPS_PER_TICK = EnderQuarryConfig.enderQuarryBaseSpeed; - public static final int ITEM_BUFFER_CAPACITY = 1024; + public static final int ITEM_BUFFER_CAPACITY = BASE_STEPS_PER_TICK * 5; // Emptied every 4 ticks + some margin for + // more than one item per block public static Block REPLACE_BLOCK = Blocks.dirt; public boolean isCreativeBoosted = false; private int storedItems; @@ -78,13 +82,16 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private int dx; private int dy; private int dz; + private int lowerYBound; + private int upperYBound; private int chunkX; private int chunkZ; - private int brokenBlocksTotal; - private int estimatedTotalBlocks; - private int startedAt; + private long brokenBlocksTotal; + private long estimatedTotalBlocks; + private int estimatedSecondsLeft = -1; private boolean sidesValidated = false; public @Nullable UUID ownerUUID = null; + private final ArrayDeque brokenBlocksSlidingWindow = new ArrayDeque<>(); private final HashMap sidedItemAcceptors = new LinkedHashMap<>(); private final HashMap sidedFluidAcceptors = new HashMap<>(); private final EnderQuarryUpgradeManager upgradeManager = new EnderQuarryUpgradeManager(); @@ -100,23 +107,23 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver public TileEntityEnderQuarry() { REPLACE_BLOCK = EnderQuarryReplaceBlock.valueOf(EnderQuarryConfig.enderQuarryReplaceBlock).block; - resetState(); + resetQuarry(); storedItems = 0; } - private void resetState() { - state = QuarryWorkState.STOPPED; - brokenBlocksTotal = 0; - } - public String getState() { return switch (state) { case RUNNING -> String.format( - "Quarry is currently mining at %d %d %d, has already mined %d blocks.", + "Quarry is currently mining at %d %d %d, has already mined/scanned %d blocks.%s", dx, dy, dz, - brokenBlocksTotal); + brokenBlocksTotal, + estimatedSecondsLeft > 0 ? String.format( + " Estimated time remaining: %02d:%02d:%02d.", + estimatedSecondsLeft / 3600, + (estimatedSecondsLeft % 3600) / 60, + (estimatedSecondsLeft % 60)) : ""); case STOPPED_WAITING_FOR_FLUID_SPACE -> "Quarry is full on fluids."; case STOPPED_WAITING_FOR_ITEM_SPACE -> "Quarry is full on items."; case STOPPED_WAITING_FOR_ENERGY -> "Quarry is missing energy."; @@ -129,19 +136,68 @@ public String getState() { }; } - public Area2d getWorkArea() { - return this.workArea; + private void resetQuarry() { + state = QuarryWorkState.STOPPED; + brokenBlocksTotal = 0; + workArea = null; + nextWorkAreas.clear(); } public void setWorkArea(Area2d area) { workArea = area; dx = area.low.x; - dy = yCoord + 5; + dy = upperYBound; dz = area.low.y; chunkX = dx >> 4; chunkZ = dz >> 4; } + /** + * Starts the quarry if it is currently stopped and has a valid work area. + * Attempts to harvest the first block, because updateEntity steps before harvesting. + */ + public void startQuarry() { + if (state == QuarryWorkState.STOPPED && workArea != null) { + state = QuarryWorkState.RUNNING; + Tuple harvestResult = tryHarvestCurrentBlock(); + boolean wasAbleToHarvest = harvestResult.first; + boolean blockWasSkipped = harvestResult.second; + if (wasAbleToHarvest) { + if (!blockWasSkipped) { + removeCurrentBlock(); + } + brokenBlocksTotal++; + } + } + } + + /** + * Tries to find a lower Y bound than the default of bedrock level (1), by scanning downwards from the quarry, + * looking for the first marker. (Returns block above the marker, since we don't want to mine the marker itself.) + */ + public int findLowerYBound() { + for (int i = this.yCoord - 2; i > 1; i--) { + if (worldObj.getTileEntity(xCoord, i, zCoord) instanceof TileEntityEnderMarker) { + return i + 1; + } + } + return 1; + } + + /** + * Tries to find a higher Y bound than the default of 5 blocks above the quarry (also configurable), by scanning + * upwards from the quarry, + * looking for the first marker. (Returns block below the marker, since we don't want to mine the marker itself.) + */ + public int findUpperYBound() { + for (int i = this.yCoord + 2; i < 255; i++) { + if (worldObj.getTileEntity(xCoord, i, zCoord) instanceof TileEntityEnderMarker) { + return i - 1; + } + } + return Math.min(this.yCoord + EnderQuarryConfig.enderQuarryDefaultTopPadding, 255); + } + public void scanForWorkAreaFromMarkers(EntityPlayer player) { boolean foundMarkers = false; for (ForgeDirection dir : DirectionUtil.HORIZONTAL_DIRECTIONS) { @@ -150,35 +206,31 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { @Nullable List scanReturn = marker.checkForBoundary(dir, player); if (scanReturn != null) { + lowerYBound = findLowerYBound(); + upperYBound = findUpperYBound(); + long estBlocks = 0; nextWorkAreas.clear(); + // Do we have a simple rectangle as defined by two points if (scanReturn.size() == 2) { - Vector2i firstCorner = scanReturn.get(0); - Vector2i secondCorner = scanReturn.get(1); - - // Pad work area by one (inwards), so that we don't mine the markers - Vector2i low = new Vector2i( - Math.min(firstCorner.x, secondCorner.x) + 1, - Math.min(firstCorner.y, secondCorner.y) + 1); - Vector2i high = new Vector2i( - Math.max(firstCorner.x, secondCorner.x) - 1, - Math.max(firstCorner.y, secondCorner.y) - 1); - setWorkArea(new Area2d(low, high)); - state = QuarryWorkState.RUNNING; - - int estBlocks = (worldObj.getHeightValue(dx, dy) + 5) * workArea.height * workArea.width; + setWorkArea(new Area2d(scanReturn.get(0), scanReturn.get(1), true)); + startQuarry(); + // Estimate total blocks to be scanned, based upon work area size and vertical bounds. (Add one + // to each dimension to make it inclusive) + estBlocks = (long) (upperYBound - lowerYBound + 1) * (workArea.height + 1) + * (workArea.width + 1); player.addChatComponentMessage( new ChatComponentText( String.format( - "Found ender marker fence boundary, setting up work area from (%d %d) to (%d %d). Should roughly contain %d blocks.", + StatCollector.translateToLocal("uie.quarry.scanmessage.1"), workArea.low.x, workArea.low.y, workArea.high.x, workArea.high.y, estBlocks))); + // Or do we have a more complex rectilinear polygon as defined by many points } else { - // DEBUG: Clear work area of debug blocks // Vector2i low = new Vector2i(Integer.MAX_VALUE); // Vector2i high = new Vector2i(Integer.MIN_VALUE); @@ -203,15 +255,31 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { // } nextWorkAreas = computeRectanglesFromRectilinearPointPolygon(scanReturn); + for (Area2d nextWorkArea : nextWorkAreas) { + // Estimate blocks in this area to be scanned, based upon work area size and vertical + // bounds. (Add one to each dimension to make it inclusive) + estBlocks += (long) (upperYBound - lowerYBound + 1) * (nextWorkArea.height + 1) + * (nextWorkArea.width + 1); + } + + player.addChatComponentMessage( + new ChatComponentText( + String.format( + StatCollector.translateToLocal("uie.quarry.scanmessage.2"), + scanReturn.size(), + estBlocks))); + setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); - state = QuarryWorkState.RUNNING; + startQuarry(); } + + estimatedTotalBlocks = estBlocks; return; } else { player.addChatComponentMessage( new ChatComponentText( String.format( - "Ender marker at (%d %d %d) failed to set up a fence boundary.", + StatCollector.translateToLocal("uie.quarry.scanmessage.3"), marker.xCoord, marker.yCoord, marker.zCoord))); @@ -219,8 +287,8 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { } } } - if (!foundMarkers) - player.addChatComponentMessage(new ChatComponentText("Found no ender markers around quarry.")); + if (!foundMarkers) player + .addChatComponentMessage(new ChatComponentText(StatCollector.translateToLocal("uie.quarry.scanmessage.4"))); } private List computeRectanglesFromRectilinearPointPolygon(List points) { @@ -536,7 +604,7 @@ boolean pointIsInSpan(int x) { * Are the current dx & dy & dz in work area bounds */ private boolean isInBounds() { - return dy > 0 && this.workArea.isInBounds(dx, dz); + return dy >= lowerYBound && dy <= upperYBound && this.workArea.isInBounds(dx, dz); } /** @@ -546,9 +614,9 @@ private boolean isInBounds() { */ private boolean stepPos() { dy--; - if (dy <= 0) { + if (dy < lowerYBound) { // stack is done, move back up - dy = this.yCoord + 5; + dy = upperYBound; boolean resetX = false; if (dx + 1 >> 4 == chunkX && dx + 1 <= workArea.high.x) { @@ -603,20 +671,21 @@ private boolean stepPos() { * * @return A boolean tuple of: <Could we work & harvest the block, Did we skip this block> */ - private boolean[] tryHarvestCurrentBlock() { + private Tuple tryHarvestCurrentBlock() { Block block = worldObj.getBlock(dx, dy, dz); if (block.getMaterial() == Material.air || worldObj.isAirBlock(dx, dy, dz) - || block == REPLACE_BLOCK + || block == (upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.WORLD_HOLE) ? Blocks.air + : REPLACE_BLOCK) || block.getBlockHardness(worldObj, dx, dy, dz) < 0 || block.getBlockHardness(worldObj, dx, dy, dz) > 100) { - return new boolean[] { tryConsumeEnergy(0f, false), true }; + return new Tuple<>(tryConsumeEnergy(0f, false), true); } ; float hardness = block.getBlockHardness(worldObj, dx, dy, dz); if (tryConsumeEnergy(hardness, true)) { - return new boolean[] { (harvestAndStoreBlock(block) && tryConsumeEnergy(hardness, false)), false }; + return new Tuple<>((harvestAndStoreBlock(block) && tryConsumeEnergy(hardness, false)), false); } - return new boolean[] { false, false }; + return new Tuple<>(false, false); } private boolean tryConsumeEnergy(float hardness, boolean simulate) { @@ -978,6 +1047,40 @@ public void validate() { } } + /** + * Update the time left data with a new data point of blocks broken this tick & recalculate the estimate every + * second + */ + private void updateTimeLeftEstimate(int newDataPoint) { + brokenBlocksSlidingWindow.add(newDataPoint); + if (brokenBlocksSlidingWindow.size() > 300) brokenBlocksSlidingWindow.removeFirst(); + + if (worldObj.getTotalWorldTime() % 20 == 0 && estimatedTotalBlocks > brokenBlocksTotal) { + double averageBlocksPerTick = getAverageBlocksPerTick(); + if (averageBlocksPerTick > 0) { + long blocksLeft = estimatedTotalBlocks - brokenBlocksTotal; + estimatedSecondsLeft = (int) (blocksLeft / averageBlocksPerTick / 20); + } else { + estimatedSecondsLeft = -1; + } + } + } + + private double getAverageBlocksPerTick() { + int queuePos = 1; + int currentWeight = 1; + int weightedSum = 0; + int weightTotal = 0; + for (int blocksBrokenInTick : brokenBlocksSlidingWindow) { + weightedSum += blocksBrokenInTick * currentWeight; + weightTotal += currentWeight; + // Increase weight every 20 ticks, to reduce the impact of short-lived spikes that only happened once + if (queuePos % 20 == 0) currentWeight++; + queuePos++; + } + return (double) weightedSum / weightTotal; + } + // TileEntity @Override public void updateEntity() { @@ -986,7 +1089,8 @@ public void updateEntity() { if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty() && ownerUUID == null) { if (selfIsLoaded) unloadSelf(); return; - } ; + } + // Try to notify owner if the quarry has finished if (state == QuarryWorkState.FINISHED && ownerUUID != null && worldObj.getTotalWorldTime() % 100 == 0) { // Check if owner is online MinecraftServer server = MinecraftServer.getServer(); @@ -1015,24 +1119,26 @@ public void updateEntity() { sidesValidated = true; } - int brokenBlocksTick = 0; + // This may mean any mix of: Blocks broken & Blocks skipped + int blocksVisitedThisTick = 0; + // Check if we can harvest the current block if we are stopped by something, but have already started working if (state != QuarryWorkState.RUNNING && state != QuarryWorkState.FINISHED && state != QuarryWorkState.STOPPED) { - boolean[] harvestResult = tryHarvestCurrentBlock(); - boolean wasAbleToHarvest = harvestResult[0]; - boolean blockWasSkipped = harvestResult[1]; + Tuple harvestResult = tryHarvestCurrentBlock(); + boolean wasAbleToHarvest = harvestResult.first; + boolean blockWasSkipped = harvestResult.second; if (wasAbleToHarvest) { state = QuarryWorkState.RUNNING; if (!blockWasSkipped) { removeCurrentBlock(); - brokenBlocksTick++; } + blocksVisitedThisTick++; } } - int stepsPerTick = (int) (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1) - * upgradeManager.getValue(EnderQuarryUpgradeManager.TieredEnderQuarryUpgrade.SPEED, 1.0)); if (state == QuarryWorkState.RUNNING) { - while (brokenBlocksTick < (stepsPerTick) && stepPos()) { + int stepsPerTick = (int) (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1) + * upgradeManager.getValue(EnderQuarryUpgradeManager.TieredEnderQuarryUpgrade.SPEED, 1.0)); + while (blocksVisitedThisTick < stepsPerTick && stepPos()) { // TODO: Remove after this has been tested by others if (!isInBounds() || this.chunkX > 1000 || this.chunkZ > 1000) { UtilitiesInExcess.LOG.warn( @@ -1046,28 +1152,27 @@ public void updateEntity() { String.format("Tried to quarry outside of work area at %d %d %d", dx, dy, dz)); } - boolean[] harvestResult = tryHarvestCurrentBlock(); - boolean wasAbleToHarvest = harvestResult[0]; - boolean blockWasSkipped = harvestResult[1]; + Tuple harvestResult = tryHarvestCurrentBlock(); + boolean wasAbleToHarvest = harvestResult.first; + boolean blockWasSkipped = harvestResult.second; if (wasAbleToHarvest) { if (!blockWasSkipped) { removeCurrentBlock(); - brokenBlocksTick++; } + blocksVisitedThisTick++; } else { // Check if something has stopped us (out of space / energy) if (state != QuarryWorkState.RUNNING) break; } } - if (brokenBlocksTick < (BASE_STEPS_PER_TICK * (isCreativeBoosted ? 8 : 1)) - && state == QuarryWorkState.RUNNING) { + if (blocksVisitedThisTick < stepsPerTick && state == QuarryWorkState.RUNNING) { if (nextWorkAreas.isEmpty()) { state = QuarryWorkState.FINISHED; } else { setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); } } - if (brokenBlocksTick > 0) { + if (blocksVisitedThisTick > 0) { markDirty(); if (state == QuarryWorkState.STOPPED_WAITING_FOR_ENERGY) { // We were still able to mine some blocks this tick, so we don't consider this fully stopped @@ -1077,11 +1182,12 @@ public void updateEntity() { } } - this.brokenBlocksTotal += brokenBlocksTick; + updateTimeLeftEstimate(blocksVisitedThisTick); + this.brokenBlocksTotal += blocksVisitedThisTick; } + // Move internally stored stuff to adjacent blocks if (worldObj.getTotalWorldTime() % 4 == 0 && (storedItems > 0 || !fluidStorageIsEmpty())) { - // Move internally stored stuff to adjacent blocks if (!ejectStoredToAdjacent()) scanSidesForTEs(); } } @@ -1099,8 +1205,13 @@ public void readFromNBT(NBTTagCompound nbt) { dz = nbt.getInteger("dz"); chunkX = dx >> 4; chunkZ = dz >> 4; + // Y bounds + lowerYBound = nbt.getInteger("lowerYBound"); + upperYBound = nbt.getInteger("upperYBound"); + // Fix for cases where upperYBound was not present yet + if (lowerYBound == upperYBound) upperYBound = findUpperYBound(); - // Possible next work areas + // Possible next work areas for complex markers NBTTagList possibleNextAreas = nbt.getTagList("nextAreas", Constants.NBT.TAG_COMPOUND); if (possibleNextAreas.tagCount() > 0) { nextWorkAreas.clear(); @@ -1110,7 +1221,9 @@ public void readFromNBT(NBTTagCompound nbt) { } } } - brokenBlocksTotal = nbt.getInteger("blocks"); + brokenBlocksTotal = nbt.getLong("brokenBlocks"); + estimatedTotalBlocks = nbt.getLong("estBlocks"); + estimatedSecondsLeft = nbt.getInteger("estSecondsLeft"); ownerUUID = nbt.getString("owner") .isEmpty() ? null : UUID.fromString(nbt.getString("owner")); @@ -1144,11 +1257,15 @@ public void writeToNBT(NBTTagCompound nbt) { nbt.setInteger("facing", facing.ordinal()); nbt.setInteger("state", state.ordinal()); if (state != QuarryWorkState.FINISHED && workArea != null) { + // Work area workArea.writeNBTTag(nbt); nbt.setInteger("dx", dx); nbt.setInteger("dy", dy); nbt.setInteger("dz", dz); - + // Y bounds + nbt.setInteger("lowerYBound", lowerYBound); + nbt.setInteger("upperYBound", upperYBound); + // Possible next work areas for complex markers NBTTagList areasNBT = new NBTTagList(); for (Area2d nextArea : nextWorkAreas) { NBTTagCompound tag = new NBTTagCompound(); @@ -1157,7 +1274,9 @@ public void writeToNBT(NBTTagCompound nbt) { } nbt.setTag("nextAreas", areasNBT); } - nbt.setInteger("blocks", brokenBlocksTotal); + nbt.setLong("brokenBlocks", brokenBlocksTotal); + nbt.setLong("estBlocks", estimatedTotalBlocks); + nbt.setInteger("estSecondsLeft", estimatedSecondsLeft); nbt.setString("owner", ownerUUID != null ? ownerUUID.toString() : ""); energyStorage.writeToNBT(nbt); diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/TTRenderUIETimeLeftBar.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/TTRenderUIETimeLeftBar.java new file mode 100644 index 00000000..54a57d37 --- /dev/null +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/TTRenderUIETimeLeftBar.java @@ -0,0 +1,68 @@ +package com.fouristhenumber.utilitiesinexcess.compat.waila; + +import java.awt.Dimension; + +import net.minecraft.client.gui.Gui; + +import mcp.mobius.waila.api.IWailaCommonAccessor; +import mcp.mobius.waila.api.IWailaVariableWidthTooltipRenderer; +import mcp.mobius.waila.api.impl.ModuleRegistrar; +import mcp.mobius.waila.overlay.DisplayUtil; +import mcp.mobius.waila.overlay.OverlayConfig; + +// Based on Waila's new progress bar renderer by super +public class TTRenderUIETimeLeftBar implements IWailaVariableWidthTooltipRenderer { + + int maxStringW; + + public static void register() { + ModuleRegistrar.instance() + .registerTooltipRenderer("waila.uie.progress", new TTRenderUIETimeLeftBar()); // Registration code would go + // here if needed + } + + @Override + public Dimension getSize(String[] params, IWailaCommonAccessor accessor) { + return new Dimension(DisplayUtil.getDisplayWidth(params[2]), 12); + } + + @Override + public void draw(String[] params, IWailaCommonAccessor accessor) { + drawThickBeveledBox(0, 0, maxStringW, 12, 1, 0xFF505050, 0xFF505050, -1); + long progresstime = Long.parseLong(params[0]); + long maxProgresstime = Long.parseLong(params[1]); + String text = params[2]; + if (progresstime > maxProgresstime) maxProgresstime = progresstime; + int progress = (int) ((maxStringW - 1) * ((double) progresstime / maxProgresstime)); + for (int xx = 1; xx < progress; xx++) { + int color = (xx & 1) == 0 ? 0xFF176087 : 0xFF1D84B5; + drawVerticalLine(xx, 1, 12 - 1, color); + } + DisplayUtil.drawString(text, 2, 2, OverlayConfig.fontcolor, true); + } + + public static void drawThickBeveledBox(int x1, int y1, int x2, int y2, int thickness, int topleftcolor, + int botrightcolor, int fillcolor) { + if (fillcolor != -1) { + Gui.drawRect(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillcolor); + } + Gui.drawRect(x1, y1, x2 - 1, y1 + thickness, topleftcolor); + Gui.drawRect(x1, y1, x1 + thickness, y2 - 1, topleftcolor); + Gui.drawRect(x2 - thickness, y1, x2, y2 - 1, botrightcolor); + Gui.drawRect(x1, y2 - thickness, x2, y2, botrightcolor); + } + + public static void drawVerticalLine(int x1, int y1, int y2, int color) { + Gui.drawRect(x1, y1, x1 + 1, y2, color); + } + + @Override + public void setMaxLineWidth(int width) { + maxStringW = width + 2; + } + + @Override + public int getMaxLineWidth() { + return maxStringW; + } +} diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java index ac31887d..ae10ef42 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/compat/waila/WailaHandler.java @@ -15,6 +15,7 @@ import mcp.mobius.waila.api.IWailaDataAccessor; import mcp.mobius.waila.api.IWailaDataProvider; import mcp.mobius.waila.api.IWailaRegistrar; +import mcp.mobius.waila.api.SpecialChars; import mcp.mobius.waila.cbcore.LangUtil; /** @@ -43,13 +44,27 @@ public List getWailaHead(ItemStack itemStack, List currenttip, I public List getWailaBody(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config) { if (accessor.getTileEntity() instanceof TileEntityEnderQuarry) { - TileEntityEnderQuarry.QuarryWorkState state = TileEntityEnderQuarry.QuarryWorkState.VALUES[accessor - .getNBTData() + NBTTagCompound nbt = accessor.getNBTData(); + int estimatedSecondsLeft = nbt.getInteger("estSecondsLeft"); + if (estimatedSecondsLeft > 0) currenttip.add( + SpecialChars.getRenderString( + "waila.uie.progress", + String.valueOf(nbt.getLong("brokenBlocks")), + String.valueOf(nbt.getLong("estBlocks")), + // Can't pass the time format string via the localization system since it seems to remove the + // leading zero formatting + LangUtil.instance.translate( + "uie.quarry.waila.timeleft", + String.format( + "%02d:%02d:%02d", + estimatedSecondsLeft / 3600, + (estimatedSecondsLeft % 3600) / 60, + (estimatedSecondsLeft % 60))))); + + TileEntityEnderQuarry.QuarryWorkState state = TileEntityEnderQuarry.QuarryWorkState.VALUES[nbt .getInteger("state")]; currenttip.add( - String.format( - LangUtil.instance.translate("uie.quarry.waila.state"), - LangUtil.instance.translate(state.localKey))); + LangUtil.instance.translate("uie.quarry.waila.state", LangUtil.instance.translate(state.localKey))); } return currenttip; } diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java index ae77c24e..bf847b47 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/config/blocks/EnderQuarryConfig.java @@ -12,6 +12,10 @@ public class EnderQuarryConfig { @Config.DefaultBoolean(true) public static boolean enableEnderQuarry; + @Config.Comment("Default amount of blocks the quarry will mine upwards, added to its own y position.") + @Config.DefaultInt(5) + public static int enderQuarryDefaultTopPadding; + @Config.Comment("Energy (RF) capacity of the machine.") @Config.DefaultInt(10_000_000) public static int enderQuarryEnergyStorage; diff --git a/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang b/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang index 8a591550..f3338622 100644 --- a/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang +++ b/src/main/resources/assets/utilitiesinexcess/lang/en_US.lang @@ -289,5 +289,11 @@ uie.quarry.state.4=Waiting for Energy uie.quarry.state.5=Throttled by Energy uie.quarry.state.6=Finished uie.quarry.state.7=Running - uie.quarry.waila.state=Status: %s +uie.quarry.waila.timeleft=Est. Time Left: %s +uie.quarry.scanmessage.1=Found ender marker fence boundary, setting up work area from (%d %d) to (%d %d). Should require %d steps. +uie.quarry.scanmessage.2=Found ender marker fence boundary from %d markers, setting up complex work area. Should require %d steps. +uie.quarry.scanmessage.3=Ender marker at (%d %d %d) failed to set up a fence boundary. +uie.quarry.scanmessage.4=Found no ender markers around quarry. +uie.quarry.scanmessage.5=Failed to complete marker chain, with last marker at (%d %d %d). + From 48558c1107e07f652509ef3f18c6f87c0b626ac4 Mon Sep 17 00:00:00 2001 From: Mika Hensel <41554385+MalTeeez@users.noreply.github.com> Date: Mon, 19 Jan 2026 04:35:50 +0100 Subject: [PATCH 23/23] Add redstone functionality, remove unused facing prop, fix nbt loading issues --- .../blocks/ender_quarry/BlockEnderQuarry.java | 32 ++-- .../tileentities/TileEntityEnderQuarry.java | 143 ++++++++++++------ 2 files changed, 110 insertions(+), 65 deletions(-) diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java index 7661e6d7..eda876c9 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/blocks/ender_quarry/BlockEnderQuarry.java @@ -39,14 +39,9 @@ public int getRenderType() { public void onBlockPlacedBy(World worldIn, int x, int y, int z, EntityLivingBase placer, ItemStack itemIn) { super.onBlockPlacedBy(worldIn, x, y, z, placer, itemIn); - int direction = (int) (((placer.rotationYaw + 45f) / 90f + 4f) % 4f); - - worldIn.setBlockMetadataWithNotify(x, y, z, direction, 2); - - TileEntity te = worldIn.getTileEntity(x, y, z); - if (te instanceof TileEntityEnderQuarry quarry) { - quarry.facing = getFacing(direction); + if (worldIn.getTileEntity(x, y, z) instanceof TileEntityEnderQuarry quarry) { quarry.ownerUUID = placer.getUniqueID(); + quarry.updateRedstoneState(); } } @@ -82,7 +77,7 @@ public void breakBlock(World worldIn, int x, int y, int z, Block blockBroken, in super.breakBlock(worldIn, x, y, z, blockBroken, meta); } - public void dropContent(TileEntityEnderQuarry quarry, World world) { + private void dropContent(TileEntityEnderQuarry quarry, World world) { for (ItemStack item : quarry.retrieveAllItems()) { float dx = world.rand.nextFloat() * 0.8F + 0.1F; float dy = world.rand.nextFloat() * 0.8F + 0.1F; @@ -121,17 +116,22 @@ public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neig TileEntity te = worldIn.getTileEntity(x, y, z); if (!worldIn.isRemote && te instanceof TileEntityEnderQuarry quarry) { quarry.scanSidesForTEs(); + quarry.updateRedstoneState(); } } - public static ForgeDirection getFacing(int meta) { - return switch (meta) { - case 0 -> ForgeDirection.SOUTH; - case 1 -> ForgeDirection.WEST; - case 2 -> ForgeDirection.NORTH; - case 3 -> ForgeDirection.EAST; - default -> ForgeDirection.UNKNOWN; - }; + @Override + public boolean hasComparatorInputOverride() { + return true; + } + + @Override + public int getComparatorInputOverride(World worldIn, int x, int y, int z, int side) { + if (worldIn.isRemote) return 0; + if (worldIn.getTileEntity(x, y, z) instanceof TileEntityEnderQuarry quarry) { + return quarry.getComparatorOutput(); + } + return 0; } @Override diff --git a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java index c48ea2da..a7d949f4 100644 --- a/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java +++ b/src/main/java/com/fouristhenumber/utilitiesinexcess/common/tileentities/TileEntityEnderQuarry.java @@ -75,7 +75,6 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver public static Block REPLACE_BLOCK = Blocks.dirt; public boolean isCreativeBoosted = false; private int storedItems; - public ForgeDirection facing; private Area2d workArea; private List nextWorkAreas = new LinkedList<>(); public QuarryWorkState state; @@ -90,6 +89,7 @@ public class TileEntityEnderQuarry extends LoadableTE implements IEnergyReceiver private long estimatedTotalBlocks; private int estimatedSecondsLeft = -1; private boolean sidesValidated = false; + private boolean redstonePowered = false; public @Nullable UUID ownerUUID = null; private final ArrayDeque brokenBlocksSlidingWindow = new ArrayDeque<>(); private final HashMap sidedItemAcceptors = new LinkedHashMap<>(); @@ -136,9 +136,16 @@ public String getState() { }; } + /** + * Resets the quarry to a state that we can start from if provided with a work area. + * Does not clear stored items, fluids, or energy. + */ private void resetQuarry() { state = QuarryWorkState.STOPPED; brokenBlocksTotal = 0; + estimatedTotalBlocks = 0; + estimatedSecondsLeft = -1; + if (areWeLoadingThisChunk(chunkX, chunkZ)) unloadChunkShifted(chunkX, chunkZ); workArea = null; nextWorkAreas.clear(); } @@ -171,11 +178,23 @@ public void startQuarry() { } } + /** + * Calculates the comparator output signal strength based on quarry progress. + * Will be 0 when stopped or no work done, 1-14 for progress, and 15 when finished. + */ + public int getComparatorOutput() { + if (state == QuarryWorkState.STOPPED || estimatedTotalBlocks == 0 || brokenBlocksTotal == 0) { + return 0; + } + double progress = (double) brokenBlocksTotal / (double) Math.max(estimatedTotalBlocks, brokenBlocksTotal); + return (int) Math.floor(progress * 14.0 - 0.05) + (state == QuarryWorkState.FINISHED ? 1 : 0); + } + /** * Tries to find a lower Y bound than the default of bedrock level (1), by scanning downwards from the quarry, * looking for the first marker. (Returns block above the marker, since we don't want to mine the marker itself.) */ - public int findLowerYBound() { + private int findLowerYBound() { for (int i = this.yCoord - 2; i > 1; i--) { if (worldObj.getTileEntity(xCoord, i, zCoord) instanceof TileEntityEnderMarker) { return i + 1; @@ -189,7 +208,7 @@ public int findLowerYBound() { * upwards from the quarry, * looking for the first marker. (Returns block below the marker, since we don't want to mine the marker itself.) */ - public int findUpperYBound() { + private int findUpperYBound() { for (int i = this.yCoord + 2; i < 255; i++) { if (worldObj.getTileEntity(xCoord, i, zCoord) instanceof TileEntityEnderMarker) { return i - 1; @@ -198,7 +217,7 @@ public int findUpperYBound() { return Math.min(this.yCoord + EnderQuarryConfig.enderQuarryDefaultTopPadding, 255); } - public void scanForWorkAreaFromMarkers(EntityPlayer player) { + public void scanForWorkAreaFromMarkers(@Nullable EntityPlayer player) { boolean foundMarkers = false; for (ForgeDirection dir : DirectionUtil.HORIZONTAL_DIRECTIONS) { TileEntity te = worldObj.getTileEntity(xCoord + dir.offsetX, yCoord, zCoord + dir.offsetZ); @@ -219,15 +238,16 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { // to each dimension to make it inclusive) estBlocks = (long) (upperYBound - lowerYBound + 1) * (workArea.height + 1) * (workArea.width + 1); - player.addChatComponentMessage( - new ChatComponentText( - String.format( - StatCollector.translateToLocal("uie.quarry.scanmessage.1"), - workArea.low.x, - workArea.low.y, - workArea.high.x, - workArea.high.y, - estBlocks))); + if (player != null) + player.addChatComponentMessage( + new ChatComponentText( + String.format( + StatCollector.translateToLocal("uie.quarry.scanmessage.1"), + workArea.low.x, + workArea.low.y, + workArea.high.x, + workArea.high.y, + estBlocks))); // Or do we have a more complex rectilinear polygon as defined by many points } else { @@ -262,12 +282,13 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { * (nextWorkArea.width + 1); } - player.addChatComponentMessage( - new ChatComponentText( - String.format( - StatCollector.translateToLocal("uie.quarry.scanmessage.2"), - scanReturn.size(), - estBlocks))); + if (player != null) + player.addChatComponentMessage( + new ChatComponentText( + String.format( + StatCollector.translateToLocal("uie.quarry.scanmessage.2"), + scanReturn.size(), + estBlocks))); setWorkArea(nextWorkAreas.remove(nextWorkAreas.size() - 1)); startQuarry(); @@ -276,18 +297,19 @@ public void scanForWorkAreaFromMarkers(EntityPlayer player) { estimatedTotalBlocks = estBlocks; return; } else { - player.addChatComponentMessage( - new ChatComponentText( - String.format( - StatCollector.translateToLocal("uie.quarry.scanmessage.3"), - marker.xCoord, - marker.yCoord, - marker.zCoord))); + if (player != null) + player.addChatComponentMessage( + new ChatComponentText( + String.format( + StatCollector.translateToLocal("uie.quarry.scanmessage.3"), + marker.xCoord, + marker.yCoord, + marker.zCoord))); foundMarkers = true; } } } - if (!foundMarkers) player + if (!foundMarkers && player != null) player .addChatComponentMessage(new ChatComponentText(StatCollector.translateToLocal("uie.quarry.scanmessage.4"))); } @@ -959,6 +981,27 @@ public List retrieveAllItems() { return items; } + /** + * Checks if the redstone powered state has changed and acts accordingly. + * For a rising edge that means restarting the quarry. + * Should be called when the redstone state might have changed, so on neighbor block change. + */ + public void updateRedstoneState() { + boolean powered = worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord); + if (powered != redstonePowered) { + redstonePowered = powered; + // Was not powered before, is powered now + if (redstonePowered) { + resetQuarry(); + scanForWorkAreaFromMarkers(null); + startQuarry(); + } + } + } + + /** + * Scans all 6 sides for TileEntities that for containers that can accept items or fluids and ender quarry upgrades. + */ public void scanSidesForTEs() { sidedFluidAcceptors.clear(); sidedItemAcceptors.clear(); @@ -1029,30 +1072,13 @@ private void removeCurrentBlock() { upgradeManager.has(EnderQuarryUpgradeManager.EnderQuarryUpgrade.WORLD_HOLE) ? Blocks.air : REPLACE_BLOCK); } - // TileEntity & LoadableTE - @Override - public void validate() { - super.validate(); - if (state != QuarryWorkState.FINISHED) { - if (workArea != null) { - loadChunkShifted(chunkX, chunkZ); - } - /* - * preferably we would call scanSidesForTEs() here, however - * the contained getTileEntity() seems to always re-load the chunk at this stage - * which then re-validates back to this function, eventually leading to a stackoverflow - * Instead we set the side check state to false, so that the next updateEntity() call does the scan - */ - sidesValidated = false; - } - } - /** * Update the time left data with a new data point of blocks broken this tick & recalculate the estimate every * second */ private void updateTimeLeftEstimate(int newDataPoint) { brokenBlocksSlidingWindow.add(newDataPoint); + // 15-second sliding window if (brokenBlocksSlidingWindow.size() > 300) brokenBlocksSlidingWindow.removeFirst(); if (worldObj.getTotalWorldTime() % 20 == 0 && estimatedTotalBlocks > brokenBlocksTotal) { @@ -1081,11 +1107,28 @@ private double getAverageBlocksPerTick() { return (double) weightedSum / weightTotal; } + // TileEntity & LoadableTE + @Override + public void validate() { + super.validate(); + if (state != QuarryWorkState.FINISHED) { + if (workArea != null) { + loadChunkShifted(chunkX, chunkZ); + } + /* + * preferably we would call scanSidesForTEs() here, however + * the contained getTileEntity() seems to always re-load the chunk at this stage + * which then re-validates back to this function, eventually leading to a stackoverflow + * Instead we set the side check state to false, so that the next updateEntity() call does the scan + */ + sidesValidated = false; + } + } + // TileEntity @Override public void updateEntity() { if (worldObj.isRemote) return; - if (facing == ForgeDirection.UNKNOWN) return; if (state == QuarryWorkState.FINISHED && storedItems == 0 && fluidStorageIsEmpty() && ownerUUID == null) { if (selfIsLoaded) unloadSelf(); return; @@ -1195,7 +1238,6 @@ public void updateEntity() { @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - facing = ForgeDirection.getOrientation(nbt.getInteger("facing")); state = QuarryWorkState.values()[nbt.getInteger("state")]; if (state != QuarryWorkState.FINISHED) { // Current work area @@ -1209,7 +1251,7 @@ public void readFromNBT(NBTTagCompound nbt) { lowerYBound = nbt.getInteger("lowerYBound"); upperYBound = nbt.getInteger("upperYBound"); // Fix for cases where upperYBound was not present yet - if (lowerYBound == upperYBound) upperYBound = findUpperYBound(); + if (lowerYBound == upperYBound) upperYBound = this.yCoord + EnderQuarryConfig.enderQuarryDefaultTopPadding; // Possible next work areas for complex markers NBTTagList possibleNextAreas = nbt.getTagList("nextAreas", Constants.NBT.TAG_COMPOUND); @@ -1221,8 +1263,10 @@ public void readFromNBT(NBTTagCompound nbt) { } } } + redstonePowered = nbt.getBoolean("redstonePowered"); brokenBlocksTotal = nbt.getLong("brokenBlocks"); estimatedTotalBlocks = nbt.getLong("estBlocks"); + // Not strictly necessary, but required for waila tooltip estimatedSecondsLeft = nbt.getInteger("estSecondsLeft"); ownerUUID = nbt.getString("owner") .isEmpty() ? null : UUID.fromString(nbt.getString("owner")); @@ -1254,7 +1298,6 @@ public void readFromNBT(NBTTagCompound nbt) { @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - nbt.setInteger("facing", facing.ordinal()); nbt.setInteger("state", state.ordinal()); if (state != QuarryWorkState.FINISHED && workArea != null) { // Work area @@ -1274,8 +1317,10 @@ public void writeToNBT(NBTTagCompound nbt) { } nbt.setTag("nextAreas", areasNBT); } + nbt.setBoolean("redstonePowered", redstonePowered); nbt.setLong("brokenBlocks", brokenBlocksTotal); nbt.setLong("estBlocks", estimatedTotalBlocks); + // Not strictly necessary, but required for waila tooltip nbt.setInteger("estSecondsLeft", estimatedSecondsLeft); nbt.setString("owner", ownerUUID != null ? ownerUUID.toString() : "");