package slimeknights.tconstruct.smeltery.multiblock; import net.minecraft.block.state.IBlockState; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import java.util.List; import slimeknights.mantle.multiblock.IMasterLogic; import slimeknights.mantle.multiblock.MultiServantLogic; /** * Base class for a rectangular multiblock detection */ public abstract class MultiblockDetection { /** Represents a multiblock structure. Contains all its info. */ public static class MultiblockStructure { public final int xd; // x-width of the structure public final int yd; // y-height of the structure public final int zd; // z-width of the structure public final List<BlockPos> blocks; // all blocks that are part of the structure public final BlockPos minPos; // smallest coordinate in the multiblock public final BlockPos maxPos; // largest coordinate in the multiblock protected final AxisAlignedBB bb; public MultiblockStructure(int xd, int yd, int zd, List<BlockPos> blocks) { this.xd = xd; this.yd = yd; this.zd = zd; this.blocks = blocks; int minx = Integer.MAX_VALUE; int maxx = Integer.MIN_VALUE; int miny = Integer.MAX_VALUE; int maxy = Integer.MIN_VALUE; int minz = Integer.MAX_VALUE; int maxz = Integer.MIN_VALUE; for(BlockPos pos : blocks) { if(pos.getX() < minx) { minx = pos.getX(); } if(pos.getX() > maxx) { maxx = pos.getX(); } if(pos.getY() < miny) { miny = pos.getY(); } if(pos.getY() > maxy) { maxy = pos.getY(); } if(pos.getZ() < minz) { minz = pos.getZ(); } if(pos.getZ() > maxz) { maxz = pos.getZ(); } } bb = new AxisAlignedBB(minx, miny, minz, maxx + 1, maxy + 1, maxz + 1); minPos = new BlockPos(minx, miny, minz); maxPos = new BlockPos(maxx, maxy, maxz); } public AxisAlignedBB getBoundingBox() { return bb; } } /** * Pass in any position inside the multiblock. * It'll detect the bottommost center block if one exists. * This is defined as the block that still is an air block, has a solid block to each side and es equally +-1 distant from each wall. * The passed parameter is a maximum distance where it stops searching. Just pass the max-size of your multiblock. */ public BlockPos detectCenter(World world, BlockPos inside, int limit) { // inside = the block behind the controller "inside the smeltery" // adjust the x-position until the difference between the outer walls is at most 1 // basically this means we center the block inside the smeltery on the x axis. int xd1 = 1, xd2 = 1; // x-difference int zd1 = 1, zd2 = 1; // z-difference for(int i = 1; i < limit; i++) // don't check farther than needed { // expand the range on the x axis as long as one side has not met a wall if(isInnerBlock(world, inside.add(-xd1, 0, 0))) { xd1++; } else if(isInnerBlock(world, inside.add(xd2, 0, 0))) { xd2++; } // if one side hit a wall and the other didn't we might have to re-center our x-position again if(xd1 - xd2 > 1) { // move x and offsets to the -x xd1--; inside = inside.add(-1, 0, 0); xd2++; } // or the right if(xd2 - xd1 > 1) { xd2--; inside = inside.add(1, 0, 0); xd1++; } // also do exactly the same on the z axis if(isInnerBlock(world, inside.add(0, 0, -zd1))) { zd1++; } else if(isInnerBlock(world, inside.add(0, 0, zd2))) { zd2++; } if(zd1 - zd2 > 1) { // move x and offsets to the -x zd1--; inside = inside.add(0, 0, -1); zd2++; } // or the right if(zd2 - zd1 > 1) { zd2--; inside = inside.add(0, 0, 1); zd1++; } } return inside; } protected BlockPos getOuterPos(World world, BlockPos pos, EnumFacing direction, int limit) { for(int i = 0; i < limit && isInnerBlock(world, pos); i++) { pos = pos.offset(direction); } return pos; } public abstract MultiblockStructure detectMultiblock(World world, BlockPos center, int limit); /* Allowed blocks */ public boolean isInnerBlock(World world, BlockPos pos) { return world.isAirBlock(pos); } public abstract boolean isValidBlock(World world, BlockPos pos); public static void assignMultiBlock(World world, BlockPos master, List<BlockPos> servants) { TileEntity masterBlock = world.getTileEntity(master); if(!(masterBlock instanceof IMasterLogic)) { throw new IllegalArgumentException("Master must be of IMasterLogic"); } // assign master to each servant for(BlockPos pos : servants) { TileEntity slave = world.getTileEntity(pos); if(slave instanceof MultiServantLogic && slave.getWorld() != null) { ((MultiServantLogic) slave).overrideMaster(master); IBlockState state = world.getBlockState(pos); world.notifyBlockUpdate(pos, state, state, 3); } } } }