package micdoodle8.mods.galacticraft.api.vector; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.Entity; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.util.MathHelper; import net.minecraft.util.ReportedException; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.gen.ChunkProviderServer; /* BlockVec3 is similar to galacticraft.api.vector.Vector3? * * But for speed it uses integer arithmetic not doubles, for block coordinates * This reduces unnecessary type conversion between integers and doubles and back again. * (Minecraft block coordinates are always integers, only entity coordinates are doubles.) * */ public class BlockVec3 implements Cloneable { public int x; public int y; public int z; public int sideDoneBits = 0; private static Chunk chunkCached; public static int chunkCacheDim = Integer.MAX_VALUE; private static int chunkCacheX = 1876000; // outside the world edge private static int chunkCacheZ = 1876000; // outside the world edge // INVALID_VECTOR is used in cases where a null vector cannot be used public static final BlockVec3 INVALID_VECTOR = new BlockVec3(-1, -1, -1); public BlockVec3() { this(0, 0, 0); } public BlockVec3(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); } public BlockVec3(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } public BlockVec3(Entity par1) { this.x = (int) Math.floor(par1.posX); this.y = (int) Math.floor(par1.posY); this.z = (int) Math.floor(par1.posZ); } public BlockVec3(TileEntity par1) { this.x = par1.getPos().getX(); this.y = par1.getPos().getY(); this.z = par1.getPos().getZ(); } /** * Makes a new copy of this Vector. Prevents variable referencing problems. */ @Override public final BlockVec3 clone() { return new BlockVec3(this.x, this.y, this.z); } public BlockPos toBlockPos() { return new BlockPos(this.x, this.y, this.z); } /** * Get block ID at the BlockVec3 coordinates, with a forced chunk load if * the coordinates are unloaded. * * @param world * @return the block ID, or null if the y-coordinate is less than 0 or * greater than 256 or the x or z is outside the Minecraft worldmap. */ public Block getBlockID(World world) { if (this.y < 0 || this.y >= 256 || this.x < -30000000 || this.z < -30000000 || this.x >= 30000000 || this.z >= 30000000) { return null; } int chunkx = this.x >> 4; int chunkz = this.z >> 4; try { // In a typical inner loop, 80% of the time consecutive calls to // this will be within the same chunk if (BlockVec3.chunkCacheX == chunkx && BlockVec3.chunkCacheZ == chunkz && BlockVec3.chunkCacheDim == world.provider.getDimensionId() && BlockVec3.chunkCached.isLoaded()) { return BlockVec3.chunkCached.getBlock(this.x & 15, this.y, this.z & 15); } else { final Chunk chunk = world.getChunkFromChunkCoords(chunkx, chunkz); BlockVec3.chunkCached = chunk; BlockVec3.chunkCacheDim = world.provider.getDimensionId(); BlockVec3.chunkCacheX = chunkx; BlockVec3.chunkCacheZ = chunkz; return chunk.getBlock(this.x & 15, this.y, this.z & 15); } } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Oxygen Sealer thread: Exception getting block type in world"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Requested block coordinates"); crashreportcategory.addCrashSection("Location", CrashReportCategory.getCoordinateInfo(new BlockPos(this.x, this.y, this.z))); throw new ReportedException(crashreport); } } /** * Get block ID at the BlockVec3 coordinates without forcing a chunk load. * * @param world * @return the block ID, or null if the y-coordinate is less than 0 or * greater than 256 or the x or z is outside the Minecraft worldmap. * Returns Blocks.bedrock if the coordinates being checked are in an * unloaded chunk */ public Block getBlockID_noChunkLoad(World world) { if (this.y < 0 || this.y >= 256 || this.x < -30000000 || this.z < -30000000 || this.x >= 30000000 || this.z >= 30000000) { return null; } int chunkx = this.x >> 4; int chunkz = this.z >> 4; try { if (world.getChunkProvider().chunkExists(chunkx, chunkz)) { // In a typical inner loop, 80% of the time consecutive calls to // this will be within the same chunk if (BlockVec3.chunkCacheX == chunkx && BlockVec3.chunkCacheZ == chunkz && BlockVec3.chunkCacheDim == world.provider.getDimensionId() && BlockVec3.chunkCached.isLoaded()) { return BlockVec3.chunkCached.getBlock(this.x & 15, this.y, this.z & 15); } else { final Chunk chunk = world.getChunkFromChunkCoords(chunkx, chunkz); BlockVec3.chunkCached = chunk; BlockVec3.chunkCacheDim = world.provider.getDimensionId(); BlockVec3.chunkCacheX = chunkx; BlockVec3.chunkCacheZ = chunkz; return chunk.getBlock(this.x & 15, this.y, this.z & 15); } } //Chunk doesn't exist - meaning, it is not loaded return Blocks.bedrock; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Oxygen Sealer thread: Exception getting block type in world"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Requested block coordinates"); crashreportcategory.addCrashSection("Location", CrashReportCategory.getCoordinateInfo(new BlockPos(this.x, this.y, this.z))); throw new ReportedException(crashreport); } } public Block getBlock(IBlockAccess par1iBlockAccess) { return par1iBlockAccess.getBlockState(new BlockPos(this.x, this.y, this.z)).getBlock(); } /** * Get block ID at the BlockVec3 coordinates without forcing a chunk load. * Only call this 'safe' version if x and z coordinates are within the * Minecraft world map (-30m to +30m) * * @param world * @return the block ID, or null if the y-coordinate is less than 0 or * greater than 256. Returns Blocks.bedrock if the coordinates being * checked are in an unloaded chunk */ public Block getBlockIDsafe_noChunkLoad(World world) { if (this.y < 0 || this.y >= 256) { return null; } int chunkx = this.x >> 4; int chunkz = this.z >> 4; try { if (world.getChunkProvider().chunkExists(chunkx, chunkz)) { // In a typical inner loop, 80% of the time consecutive calls to // this will be within the same chunk if (BlockVec3.chunkCacheX == chunkx && BlockVec3.chunkCacheZ == chunkz && BlockVec3.chunkCacheDim == world.provider.getDimensionId() && BlockVec3.chunkCached.isLoaded()) { return BlockVec3.chunkCached.getBlock(this.x & 15, this.y, this.z & 15); } else { final Chunk chunk = world.getChunkFromChunkCoords(chunkx, chunkz); BlockVec3.chunkCached = chunk; BlockVec3.chunkCacheDim = world.provider.getDimensionId(); BlockVec3.chunkCacheX = chunkx; BlockVec3.chunkCacheZ = chunkz; return chunk.getBlock(this.x & 15, this.y, this.z & 15); } } //Chunk doesn't exist - meaning, it is not loaded return Blocks.bedrock; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Oxygen Sealer thread: Exception getting block type in world"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Requested block coordinates"); crashreportcategory.addCrashSection("Location", CrashReportCategory.getCoordinateInfo(new BlockPos(this.x, this.y, this.z))); throw new ReportedException(crashreport); } } public BlockVec3 translate(BlockVec3 par1) { this.x += par1.x; this.y += par1.y; this.z += par1.z; return this; } public BlockVec3 translate(int par1x, int par1y, int par1z) { this.x += par1x; this.y += par1y; this.z += par1z; return this; } public static BlockVec3 add(BlockVec3 par1, BlockVec3 a) { return new BlockVec3(par1.x + a.x, par1.y + a.y, par1.z + a.z); } public BlockVec3 subtract(BlockVec3 par1) { this.x -= par1.x; this.y -= par1.y; this.z -= par1.z; return this; } public BlockVec3 scale(int par1) { this.x *= par1; this.y *= par1; this.z *= par1; return this; } public BlockVec3 modifyPositionFromSide(EnumFacing side, int amount) { switch (side.ordinal()) { case 0: this.y -= amount; break; case 1: this.y += amount; break; case 2: this.z -= amount; break; case 3: this.z += amount; break; case 4: this.x -= amount; break; case 5: this.x += amount; break; } return this; } public BlockVec3 newVecSide(int side) { final BlockVec3 vec = new BlockVec3(this.x, this.y, this.z); vec.sideDoneBits = (1 << (side ^ 1)) + (side << 6); switch (side) { case 0: vec.y--; return vec; case 1: vec.y++; return vec; case 2: vec.z--; return vec; case 3: vec.z++; return vec; case 4: vec.x--; return vec; case 5: vec.x++; return vec; } return vec; } public BlockVec3 modifyPositionFromSide(EnumFacing side) { return this.modifyPositionFromSide(side, 1); } @Override public int hashCode() { // Upgraded hashCode calculation from the one in VecDirPair to something // a bit stronger and faster return ((this.y * 379 + this.x) * 373 + this.z) * 7; } @Override public boolean equals(Object o) { if (o instanceof BlockVec3) { final BlockVec3 vector = (BlockVec3) o; return this.x == vector.x && this.y == vector.y && this.z == vector.z; } return false; } @Override public String toString() { return "[" + this.x + "," + this.y + "," + this.z + "]"; } /** * This will load the chunk. */ public TileEntity getTileEntity(IBlockAccess world) { return world.getTileEntity(new BlockPos(this.x, this.y, this.z)); } /** * No chunk load: returns null if chunk to side is unloaded */ public TileEntity getTileEntityOnSide(World world, EnumFacing side) { int x = this.x; int y = this.y; int z = this.z; switch (side.ordinal()) { case 0: y--; break; case 1: y++; break; case 2: z--; break; case 3: z++; break; case 4: x--; break; case 5: x++; break; default: return null; } final BlockPos pos = new BlockPos(x, y, z); return world.isBlockLoaded(pos, false) ? world.getTileEntity(pos) : null; } /** * No chunk load: returns null if chunk to side is unloaded */ public TileEntity getTileEntityOnSide(World world, int side) { int x = this.x; int y = this.y; int z = this.z; switch (side) { case 0: y--; break; case 1: y++; break; case 2: z--; break; case 3: z++; break; case 4: x--; break; case 5: x++; break; default: return null; } final BlockPos pos = new BlockPos(x, y, z); return world.isBlockLoaded(pos, false) ? world.getTileEntity(pos) : null; } /** * This will load the chunk to the side. */ public boolean blockOnSideHasSolidFace(World world, int side) { int x = this.x; int y = this.y; int z = this.z; switch (side) { case 0: y--; break; case 1: y++; break; case 2: z--; break; case 3: z++; break; case 4: x--; break; case 5: x++; break; default: return false; } final BlockPos pos = new BlockPos(x, y, z); return world.getBlockState(pos).getBlock().isSideSolid(world, pos, EnumFacing.getFront(side ^ 1)); } /** * No chunk load: returns null if chunk is unloaded */ public Block getBlockOnSide(World world, int side) { int x = this.x; int y = this.y; int z = this.z; switch (side) { case 0: y--; break; case 1: y++; break; case 2: z--; break; case 3: z++; break; case 4: x--; break; case 5: x++; break; default: return null; } final BlockPos pos = new BlockPos(x, y, z); return world.isBlockLoaded(pos, false) ? world.getBlockState(pos).getBlock() : null; } public int getBlockMetadata(IBlockAccess world) { final IBlockState state = world.getBlockState(new BlockPos(x, y, z)); return state.getBlock().getMetaFromState(state); } public static BlockVec3 readFromNBT(NBTTagCompound nbtCompound) { final BlockVec3 tempVector = new BlockVec3(); tempVector.x = nbtCompound.getInteger("x"); tempVector.y = nbtCompound.getInteger("y"); tempVector.z = nbtCompound.getInteger("z"); return tempVector; } public int distanceTo(BlockVec3 vector) { int var2 = vector.x - this.x; int var4 = vector.y - this.y; int var6 = vector.z - this.z; return MathHelper.floor_double(Math.sqrt(var2 * var2 + var4 * var4 + var6 * var6)); } public int distanceSquared(BlockVec3 vector) { int var2 = vector.x - this.x; int var4 = vector.y - this.y; int var6 = vector.z - this.z; return var2 * var2 + var4 * var4 + var6 * var6; } public NBTTagCompound writeToNBT(NBTTagCompound par1NBTTagCompound) { par1NBTTagCompound.setInteger("x", this.x); par1NBTTagCompound.setInteger("y", this.y); par1NBTTagCompound.setInteger("z", this.z); return par1NBTTagCompound; } public BlockVec3(NBTTagCompound par1NBTTagCompound) { this.x = par1NBTTagCompound.getInteger("x"); this.y = par1NBTTagCompound.getInteger("y"); this.z = par1NBTTagCompound.getInteger("z"); } public NBTTagCompound writeToNBT(NBTTagCompound par1NBTTagCompound, String prefix) { par1NBTTagCompound.setInteger(prefix + "_x", this.x); par1NBTTagCompound.setInteger(prefix + "_y", this.y); par1NBTTagCompound.setInteger(prefix + "_z", this.z); return par1NBTTagCompound; } public static BlockVec3 readFromNBT(NBTTagCompound par1NBTTagCompound, String prefix) { Integer readX = par1NBTTagCompound.getInteger(prefix + "_x"); if (readX == null) return null; Integer readY = par1NBTTagCompound.getInteger(prefix + "_y"); if (readY == null) return null; Integer readZ = par1NBTTagCompound.getInteger(prefix + "_z"); if (readZ == null) return null; return new BlockVec3(readX, readY, readZ); } public double getMagnitude() { return Math.sqrt(this.getMagnitudeSquared()); } public int getMagnitudeSquared() { return this.x * this.x + this.y * this.y + this.z * this.z; } public void setBlock(World worldObj, IBlockState block) { worldObj.setBlockState(new BlockPos(x, y, z), block, 3); } public boolean blockExists(World world) { return world.isBlockLoaded(new BlockPos(this.x, this.y, this.z), false); } public void setSideDone(int side) { this.sideDoneBits |= 1 << side; } public TileEntity getTileEntityForce(World world) { int chunkx = this.x >> 4; int chunkz = this.z >> 4; if (world.getChunkProvider().chunkExists(chunkx, chunkz)) return world.getTileEntity(this.toBlockPos()); Chunk chunk = ((ChunkProviderServer) world.getChunkProvider()).originalLoadChunk(chunkx, chunkz); return chunk.getTileEntity(new BlockPos(this.x & 15, this.y, this.z & 15), Chunk.EnumCreateEntityType.IMMEDIATE); } }