public short[] generate(int i, int k, short[] blocks, byte[] metas) {
    ChunkCoordinates volcanoCoords = getVolcanoNear(worldObj, i, k);

    if (volcanoCoords == null) {
      return blocks;
    }
    int[] heightmap = new int[256];

    for (int x = 0; x < 16; x++) {
      for (int z = 0; z < 16; z++) {
        for (int y = 0; y < 127; y++) {
          int blockID = getBlock(x, y, z, blocks);
          if (blockID == 0 || blockID == TropicraftBlocks.tropicsWaterStationary.blockID) {
            heightmap[x * 16 + z] = y;
            break;
          }
          if (y > 75) {
            heightmap[x * 16 + z] = y;
            break;
          }
        }
      }
    }

    i *= CHUNK_SIZE_X;
    k *= CHUNK_SIZE_Z;
    System.out.println(i + ", " + k + "v");

    // System.out.println(i + ", " + k);
    int volcCenterX = volcanoCoords.posX;
    int volcCenterZ = volcanoCoords.posZ;
    int steepnessMod = volcanoCoords.posY == 1 ? LAND_STEEPNESS_MOD : OCEAN_STEEPNESS_MOD;

    long seed =
        (long) volcCenterX * 341873128712L
            + (long) volcCenterZ * 132897987541L
            + worldObj.getWorldInfo().getSeed()
            + (long) 4291726;
    Random rand = new Random(seed);

    int radiusX = rand.nextInt(MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS;
    int radiusZ = rand.nextInt(MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS;

    NoiseModule volcNoise = new Billowed(seed, 1, 1);
    volcNoise.amplitude = 0.45;

    for (int x = 0; x < CHUNK_SIZE_X; x++) {
      for (int z = 0; z < CHUNK_SIZE_Z; z++) {
        float relativeX = ((x + i) - volcCenterX);
        float relativeZ = ((z + k) - volcCenterZ);

        float distanceSquared =
            ((relativeX / radiusX) * (relativeX / radiusX)
                + (relativeZ / radiusZ) * (relativeZ / radiusZ));

        float perlin =
            (float) volcNoise.getNoise(relativeX * 0.05 + 0.0001, relativeZ * 0.05 + 0.0001) + 1;

        double volcanoHeight = steepnessMod / (distanceSquared) * perlin - steepnessMod - 2;

        int groundHeight = heightmap[x * 16 + z];
        if (distanceSquared < 1) {
          for (int y = CHUNK_SIZE_Y; y > 0; y--) {
            if (volcanoHeight + groundHeight < CALDERA_CUTOFF) {
              if (volcanoHeight + groundHeight <= VOLCANO_TOP) {
                if (y <= volcanoHeight + groundHeight && y >= groundHeight) {
                  placeBlock(x, y, z, TropicraftBlocks.chunkOHead.blockID, blocks);
                }
              } else if (y <= VOLCANO_TOP) {
                placeBlock(x, y, z, TropicraftBlocks.chunkOHead.blockID, blocks);
              }
            } else {
              if (y == VOLCANO_CRUST && rand.nextInt(CRUST_HOLE_CHANCE) != 0) {
                placeBlock(x, y, z, TropicraftBlocks.chunkOHead.blockID, blocks);
              }
              if (y <= LAVA_LEVEL) {
                placeBlock(x, y, z, Block.lavaStill.blockID, blocks);
              }
            }
          }
        }
      }
    }

    return blocks;
  }
  public void generate(int i, int k) {
    int volcCenterX = i;
    int volcCenterZ = k;
    int steepnessMod =
        worldObj.getBiomeGenForCoords(i, k) == BiomeGenTropicraft.tropics
            ? LAND_STEEPNESS_MOD
            : OCEAN_STEEPNESS_MOD;

    long seed =
        (long) volcCenterX * 341873128712L
            + (long) volcCenterZ * 132897987541L
            + worldObj.getWorldInfo().getSeed()
            + (long) 4291726;
    Random rand = new Random(seed);

    int radiusX = rand.nextInt(MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS;
    int radiusZ = rand.nextInt(MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS;

    int[] heightmap = new int[(radiusX * 2) * (radiusZ * 2)];

    // System.out.println(radius);

    for (int x = -radiusX; x < radiusX; x++) {
      for (int z = -radiusZ; z < radiusZ; z++) {
        for (int y = 127; y > 0; y--) {
          int blockID = getBlock(x + i, y, z + k, null);
          if (blockID == Block.sand.blockID
              || blockID == Block.dirt.blockID
              || blockID == Block.grass.blockID) {
            heightmap[(x + radiusX) * (radiusZ * 2) + (z + radiusZ)] = y;
            break;
          }
          if (y < 30) {
            heightmap[(x + radiusX) * (radiusZ * 2) + (z + radiusZ)] = y;
            break;
          }
        }
      }
    }

    NoiseModule volcNoise = new Gradient(seed, 1, 1);
    volcNoise.amplitude = 0.6;
    for (int x = -radiusX; x < radiusX; x++) {
      for (int z = -radiusZ; z < radiusZ; z++) {
        float relativeX = ((x + i) - volcCenterX);
        float relativeZ = ((z + k) - volcCenterZ);

        float distanceSquared =
            ((relativeX / radiusX) * (relativeX / radiusX)
                + (relativeZ / radiusZ) * (relativeZ / radiusZ));

        float perlin =
            (float) volcNoise.getNoise(relativeX * 0.05 + 0.0001, relativeZ * 0.05 + 0.0001) + 1;
        double volcanoHeight = steepnessMod / (distanceSquared) * perlin - steepnessMod - 1;
        int groundHeight = heightmap[(x + radiusX) * (radiusZ * 2) + (z + radiusZ)];
        if (distanceSquared < 1) {
          for (int y = CHUNK_SIZE_Y; y > 0; y--) {
            if (volcanoHeight + groundHeight < CALDERA_CUTOFF) {
              if (volcanoHeight + groundHeight <= VOLCANO_TOP) {
                if (y <= volcanoHeight + groundHeight && y >= groundHeight) {
                  placeBlock(x + i, y, z + k, TropicraftBlocks.chunkOHead.blockID, null);
                }
              } else {
                if (y <= VOLCANO_TOP) {
                  placeBlock(x + i, y, z + k, TropicraftBlocks.chunkOHead.blockID, null);
                }
              }
            } else {
              if (y == VOLCANO_CRUST && rand.nextInt(CRUST_HOLE_CHANCE) != 0) {
                placeBlock(x + i, y, z + k, TropicraftBlocks.chunkOHead.blockID, null);
              }
              if (y <= LAVA_LEVEL) {
                placeBlock(x + i, y, z + k, Block.lavaStill.blockID, null);
              }
            }
          }
        }
      }
    }
    System.out.println("Done");
  }