/** * Copyright (c) Lambda Innovation, 2013-2015 * 本作品版权由Lambda Innovation所有。 * http://www.li-dev.cn/ * * This project is open-source, and it is distributed under * the terms of GNU General Public License. You can modify * and distribute freely as long as you follow the license. * 本项目是一个开源项目,且遵循GNU通用公共授权协议。 * 在遵照该协议的情况下,您可以自由传播和修改。 * http://www.gnu.org/licenses/gpl.html */ package cn.liutils.template.block; import static net.minecraftforge.common.util.ForgeDirection.EAST; import static net.minecraftforge.common.util.ForgeDirection.NORTH; import static net.minecraftforge.common.util.ForgeDirection.SOUTH; import static net.minecraftforge.common.util.ForgeDirection.WEST; import java.util.ArrayList; import java.util.List; import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import cn.liutils.core.LIUtils; import cn.liutils.template.client.render.block.RenderEmptyBlock; import cn.liutils.util.generic.VecUtils; import cn.liutils.util.mc.WorldUtils; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; /** * @author WeathFolD */ public abstract class BlockMulti extends BlockContainer { List<SubBlockPos> subList = new ArrayList(); List<SubBlockPos>[] buffer; private AxisAlignedBB[] renderBB = new AxisAlignedBB[8]; @SideOnly(Side.CLIENT) double[][] rotCenters; boolean init = false; public static class SubBlockPos { public final int dx, dy, dz; public SubBlockPos(int _dx, int _dy, int _dz) { dx = _dx; dy = _dy; dz = _dz; } } /** * notice that you must call finishInit() in your own subclass ctor. */ public BlockMulti(Material p_i45386_1_) { super(p_i45386_1_); addSubBlock(0, 0, 0); } public void addSubBlock(int dx, int dy, int dz) { if(init) { throw new RuntimeException("Trying to add a sub block after block init finished"); } subList.add(new SubBlockPos(dx, dy, dz)); } /** * Accept a int[][3] array, add all the array element as a single subPos inside the list. */ public void addSubBlock(int[][] data) { for(int[] s : data) { addSubBlock(s[0], s[1], s[2]); } } /** * Get the render bounding box of this BlockMulti at the given block position (as origin block) * Usually used on TileEntity rendering. */ public AxisAlignedBB getRenderBB(int x, int y, int z, ForgeDirection dir) { // Lazy init if(renderBB[dir.ordinal()] == null) { Vec3[] vecs = new Vec3[subList.size() * 2]; for(int i = 0; i < subList.size(); ++i) { SubBlockPos rot = rotate(subList.get(i), dir); vecs[i * 2] = VecUtils.vec(rot.dx, rot.dy, rot.dz); vecs[i * 2 + 1] = VecUtils.vec(rot.dx + 1, rot.dy + 1, rot.dz + 1); } renderBB[dir.ordinal()] = WorldUtils.ofPoints(vecs); } AxisAlignedBB box = renderBB[dir.ordinal()]; return AxisAlignedBB.getBoundingBox(box.minX + x, box.minY + y, box.minZ + z, box.maxX + x, box.maxY + y, box.maxZ + z); } /** * You MUST call this via your ctor, after init all the blocks. */ public void finishInit() { //Pre-init rotated position offset list. buffer = new ArrayList[6]; for(int i = 2; i <= 5; ++i) { ForgeDirection dir = ForgeDirection.values()[i]; buffer[i] = new ArrayList(); for(SubBlockPos s : subList) { buffer[i].add(rotate(s, dir)); } } if(FMLCommonHandler.instance().getSide().isClient()) { double[] arr = getRotCenter(); rotCenters = new double[][] { {}, {}, {arr[0], arr[1], arr[2]}, {-arr[0], arr[1], -arr[2]}, {arr[2], arr[1], -arr[0]}, {-arr[2], arr[1], arr[0]} }; } //Finished, set the flag and encapsulate the instance. init = true; } //Rotation API //Some lookup tables private static final ForgeDirection[] rotMap = { NORTH, EAST, SOUTH, WEST }; //-Z, +X, +Z, -X private static final double[] drMap = { 0, 0, 180, 0, -90, 90 }; private static final double[][] offsetMap = { {0, 0}, //placeholder {0, 0}, //placeholder {0, 0}, {1, 1}, {0, 1}, {1, 0} }; public double[] getPivotOffset(InfoBlockMulti info) { return getPivotOffset(info.dir); } public ForgeDirection getRotation(int l) { return rotMap[l]; } /** * Get the whole structure's (minX, minZ) point coord, in [dir = 0] (a.k.a: facing z-) point of view. * @param side * @return */ public double[] getPivotOffset(ForgeDirection dir) { return offsetMap[dir.ordinal()]; } @SideOnly(Side.CLIENT) public abstract double[] getRotCenter(); /** * Build a multiblock at the given coordinate. */ public void setMultiBlock(World world, int x, int y, int z, ForgeDirection dir) { world.setBlockToAir(x, y, z); world.setBlock(x, y, z, this); updateDirInfo(world, x, y, z, dir); } //Placement API @Override public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase placer, ItemStack stack) { if(world.isRemote) return; int l = MathHelper.floor_double(placer.rotationYaw * 4.0F / 360.0F + 0.5D) & 3; ForgeDirection dir = rotMap[l]; updateDirInfo(world, x, y, z, dir); } private void updateDirInfo(World world, int x, int y, int z, ForgeDirection dir) { //Set the origin block. TileEntity te = world.getTileEntity(x, y, z); ((IMultiTile)te).setBlockInfo(new InfoBlockMulti(te, dir, 0)); List<SubBlockPos> rotatedList = buffer[dir.ordinal()]; //Check done in ItemBlockMulti, brutely replace here. for(int i = 1; i < rotatedList.size(); ++i) { SubBlockPos sub = rotatedList.get(i); int nx = x + sub.dx, ny = y + sub.dy, nz = z + sub.dz; world.setBlock(nx, ny, nz, this); te = world.getTileEntity(nx, ny, nz); ((IMultiTile)te).setBlockInfo(new InfoBlockMulti(te, dir, i)); } } @Override public void breakBlock(World world, int x, int y, int z, Block block, int metadata) { if(world.isRemote) return; TileEntity te = world.getTileEntity(x, y, z); if(!(te instanceof IMultiTile)) { LIUtils.log.error("Didn't find correct tile when breaking a BlockMulti."); return; } InfoBlockMulti info = ((IMultiTile)te).getBlockInfo(); int[] origin = getOrigin(te); if(origin == null) return; List<SubBlockPos> rotatedList = buffer[info.dir.ordinal()]; for(SubBlockPos pos : rotatedList) { world.setBlockToAir(origin[0] + pos.dx, origin[1] + pos.dy, origin[2] + pos.dz); } } //A series of getOrigin funcs. public int[] getOrigin(World world, int x, int y, int z) { return getOrigin(world.getTileEntity(x, y, z)); } public int[] getOrigin(TileEntity te) { TileEntity ret = getOriginTile(te); return ret == null ? null : new int[] { ret.xCoord, ret.yCoord, ret.zCoord }; } public TileEntity getOriginTile(World world, int x, int y, int z) { TileEntity now = world.getTileEntity(x, y, z); return getOriginTile(now); } public TileEntity getOriginTile(TileEntity now) { if(!(now instanceof IMultiTile)) { return null; } InfoBlockMulti info = ((IMultiTile)now).getBlockInfo(); if(info == null || !info.isLoaded()) return null; SubBlockPos sbp = buffer[info.dir.ordinal()].get(info.subID); TileEntity ret = validate(now.getWorldObj().getTileEntity(now.xCoord - sbp.dx, now.yCoord - sbp.dy, now.zCoord - sbp.dz)); return ret; } //Internal public static final SubBlockPos rotate(SubBlockPos s, ForgeDirection dir) { switch(dir) { case EAST: return new SubBlockPos(-s.dz, s.dy, s.dx); case WEST: return new SubBlockPos(s.dz, s.dy, -s.dx); case SOUTH: return new SubBlockPos(-s.dx, s.dy, -s.dz); case NORTH: return new SubBlockPos(s.dx, s.dy, s.dz); default: throw new RuntimeException("Invalid rotate direction"); } } double getRotation(InfoBlockMulti info) { return drMap[info.dir.ordinal()]; } private TileEntity validate(TileEntity te) { return te instanceof IMultiTile ? te : null; } @Override public boolean isOpaqueCube() { return false; } @Override public int getRenderType() { return RenderEmptyBlock.id; } @Override public boolean renderAsNormalBlock() { return false; } }