package gregtech.common.misc; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityClientPlayerMP; import net.minecraft.util.MathHelper; public class GT_ClientPollutionMap { private static final byte RADIUS = 24; private static final byte DISTANCE_RELOAD_MAP = 5; // When player moved x chunks, shift the map to new center. private static final byte SIZE = RADIUS * 2 + 1; // Area to keep stored. private int x0, z0; private int dim; private boolean initialized = false; private static short[][] chunkMatrix; // short because reasons. public GT_ClientPollutionMap() {} public void reset() { initialized = false; } private void initialize(int playerChunkX, int playerChunkZ, int dimension) { initialized = true; chunkMatrix = new short[SIZE][SIZE]; x0 = playerChunkX; z0 = playerChunkZ; dim = dimension; } public void addChunkPollution(int chunkX, int chunkZ, int pollution) { EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; if (player == null || player.worldObj == null) return; int playerXChunk = MathHelper.floor_double(player.posX) >> 4; int playerZChunk = MathHelper.floor_double(player.posZ) >> 4; // posX/Z seems to be always loaded, if (!initialized) { initialize(playerXChunk, playerZChunk, player.dimension); } if (dim != player.dimension) { initialize(playerXChunk, playerZChunk, player.dimension); } if (Math.abs(x0 - playerXChunk) > DISTANCE_RELOAD_MAP || Math.abs(z0 - playerZChunk) > DISTANCE_RELOAD_MAP) shiftCenter(playerXChunk, playerZChunk); int relX = chunkX - x0 + RADIUS; if (relX >= SIZE || relX < 0) // out of bounds return; int relZ = chunkZ - z0 + RADIUS; if (relZ >= SIZE || relZ < 0) // out of bounds return; pollution = pollution / 225; if (pollution > Short.MAX_VALUE) // Sanity chunkMatrix[relX][relZ] = Short.MAX_VALUE; // Max pollution = 7,3mill else if (pollution < 0) chunkMatrix[relX][relZ] = 0; else chunkMatrix[relX][relZ] = (short) (pollution); } // xy interpolation, between 4 chunks as corners, unknown treated as 0. public int getPollution(double fx, double fz) { if (!initialized) return 0; int x = MathHelper.floor_double(fx); int z = MathHelper.floor_double(fz); int xDiff = ((x - 8) >> 4) - x0; int zDiff = ((z - 8) >> 4) - z0; if (xDiff < -RADIUS || zDiff < -RADIUS || xDiff >= RADIUS || zDiff >= RADIUS) return 0; // coordinates in shifted chunk. x = (x - 8) % 16; z = (z - 8) % 16; if (x < 0) x = 16 + x; if (z < 0) z = 16 + z; int xi = 15 - x; int zi = 15 - z; // read pollution in 4 corner chunks int offsetX = RADIUS + xDiff; int offsetZ = RADIUS + zDiff; int c00 = chunkMatrix[offsetX][offsetZ]; int c10 = chunkMatrix[offsetX + 1][offsetZ]; int c01 = chunkMatrix[offsetX][offsetZ + 1]; int c11 = chunkMatrix[offsetX + 1][offsetZ + 1]; // Is divided by 15*15 but is handled when storing chunk data. return c00 * xi * zi + c10 * x * zi + c01 * xi * z + c11 * x * z; } // shift the matrix to fit new center private void shiftCenter(int chunkX, int chunkZ) { int xDiff = chunkX - x0; int zDiff = chunkZ - z0; boolean[] allEmpty = new boolean[SIZE]; // skip check z row if its empty. if (xDiff > 0) for (byte x = 0; x < SIZE; x++) { int xOff = x + xDiff; if (xOff < SIZE) { chunkMatrix[x] = chunkMatrix[xOff].clone(); } else { chunkMatrix[x] = new short[SIZE]; allEmpty[x] = true; } } else if (xDiff < 0) for (byte x = SIZE - 1; x >= 0; x--) { int xOff = x + xDiff; if (xOff > 0) { chunkMatrix[x] = chunkMatrix[xOff].clone(); } else { chunkMatrix[x] = new short[SIZE]; allEmpty[x] = true; } } if (zDiff > 0) for (byte x = 0; x < SIZE; x++) { if (allEmpty[x]) continue; for (int z = 0; z < SIZE; z++) { int zOff = z + zDiff; chunkMatrix[x][z] = (zOff < SIZE) ? chunkMatrix[x][zOff] : 0; } } else if (zDiff < 0) for (byte x = 0; x < SIZE; x++) { if (allEmpty[x]) continue; for (int z = SIZE - 1; z >= 0; z--) { int zOff = z + zDiff; chunkMatrix[x][z] = (zOff > 0) ? chunkMatrix[x][zOff] : 0; } } x0 = chunkX; z0 = chunkZ; } }