package cofh.lib.world;
import cofh.lib.util.WeightedRandomBlock;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import net.minecraft.util.WeightedRandom;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenerator;
public class WorldGenMinableCluster extends WorldGenerator {
public static final List<WeightedRandomBlock> fabricateList(WeightedRandomBlock resource) {
List<WeightedRandomBlock> list = new ArrayList<WeightedRandomBlock>();
list.add(resource);
return list;
}
public static final List<WeightedRandomBlock> fabricateList(Block resource) {
List<WeightedRandomBlock> list = new ArrayList<WeightedRandomBlock>();
list.add(new WeightedRandomBlock(new ItemStack(resource, 1, 0)));
return list;
}
private final List<WeightedRandomBlock> cluster;
private final int genClusterSize;
private final WeightedRandomBlock[] genBlock;
public WorldGenMinableCluster(ItemStack ore, int clusterSize) {
this(new WeightedRandomBlock(ore), clusterSize);
}
public WorldGenMinableCluster(WeightedRandomBlock resource, int clusterSize) {
this(fabricateList(resource), clusterSize);
}
public WorldGenMinableCluster(List<WeightedRandomBlock> resource, int clusterSize) {
this(resource, clusterSize, Blocks.stone);
}
public WorldGenMinableCluster(ItemStack ore, int clusterSize, Block block) {
this(new WeightedRandomBlock(ore, 1), clusterSize, block);
}
public WorldGenMinableCluster(WeightedRandomBlock resource, int clusterSize, Block block) {
this(fabricateList(resource), clusterSize, block);
}
public WorldGenMinableCluster(List<WeightedRandomBlock> resource, int clusterSize, Block block) {
this(resource, clusterSize, fabricateList(block));
}
public WorldGenMinableCluster(List<WeightedRandomBlock> resource, int clusterSize, List<WeightedRandomBlock> block) {
cluster = resource;
genClusterSize = clusterSize > 32 ? 32 : clusterSize;
genBlock = block.toArray(new WeightedRandomBlock[block.size()]);
}
@Override
public boolean generate(World world, Random rand, int x, int y, int z) {
int blocks = genClusterSize;
if (blocks < 4) { // HACK: at 1 and 2 no ores are ever generated. at 3 only 1/3 veins generate
return generateTiny(world, rand, x, y, z);
}
float f = rand.nextFloat() * (float) Math.PI;
// despite naming, these are not exactly min/max. more like direction
float xMin = x + 8 + (MathHelper.sin(f) * blocks) / 8F;
float xMax = x + 8 - (MathHelper.sin(f) * blocks) / 8F;
float zMin = z + 8 + (MathHelper.cos(f) * blocks) / 8F;
float zMax = z + 8 - (MathHelper.cos(f) * blocks) / 8F;
float yMin = (y + rand.nextInt(3)) - 2;
float yMax = (y + rand.nextInt(3)) - 2;
// optimization so this subtraction doesn't occur every time in the loop
xMax -= xMin;
yMax -= yMin;
zMax -= zMin;
boolean r = false;
for (int i = 0; i <= blocks; i++) {
float xCenter = xMin + (xMax * i) / blocks;
float yCenter = yMin + (yMax * i) / blocks;
float zCenter = zMin + (zMax * i) / blocks;
// preserved as nextDouble to ensure the rand gets ticked the same amount
float size = ((float) rand.nextDouble() * blocks) / 16f;
float hMod = ((MathHelper.sin((i * (float) Math.PI) / blocks) + 1f) * size + 1f) * .5f;
float vMod = ((MathHelper.sin((i * (float) Math.PI) / blocks) + 1f) * size + 1f) * .5f;
int xStart = MathHelper.floor_float(xCenter - hMod);
int yStart = MathHelper.floor_float(yCenter - vMod);
int zStart = MathHelper.floor_float(zCenter - hMod);
int xStop = MathHelper.floor_float(xCenter + hMod);
int yStop = MathHelper.floor_float(yCenter + vMod);
int zStop = MathHelper.floor_float(zCenter + hMod);
for (int blockX = xStart; blockX <= xStop; blockX++) {
float xDistSq = ((blockX + .5f) - xCenter) / hMod;
xDistSq *= xDistSq;
if (xDistSq >= 1f) {
continue;
}
for (int blockY = yStart; blockY <= yStop; blockY++) {
float yDistSq = ((blockY + .5f) - yCenter) / vMod;
yDistSq *= yDistSq;
float xyDistSq = yDistSq + xDistSq;
if (xyDistSq >= 1f) {
continue;
}
for (int blockZ = zStart; blockZ <= zStop; blockZ++) {
float zDistSq = ((blockZ + .5f) - zCenter) / hMod;
zDistSq *= zDistSq;
if (zDistSq + xyDistSq >= 1f) {
continue;
}
r |= generateBlock(world, blockX, blockY, blockZ, genBlock, cluster);
}
}
}
}
return r;
}
public boolean generateTiny(World world, Random random, int x, int y, int z) {
boolean r = false;
// not <=; generating up to clusterSize blocks
for (int i = 0; i < genClusterSize; i++) {
int d0 = x + random.nextInt(2);
int d1 = y + random.nextInt(2);
int d2 = z + random.nextInt(2);
r |= generateBlock(world, d0, d1, d2, genBlock, cluster);
}
return r;
}
public static boolean canGenerateInBlock(World world, int x, int y, int z, WeightedRandomBlock[] mat) {
if (mat == null || mat.length == 0)
return true;
Block block = world.getBlock(x, y, z);
for (int j = 0, e = mat.length; j < e; ++j) {
WeightedRandomBlock genBlock = mat[j];
if ((-1 == genBlock.metadata || genBlock.metadata == world.getBlockMetadata(x, y, z)) &&
(block.isReplaceableOreGen(world, x, y, z, genBlock.block) || block.isAssociatedBlock(genBlock.block))) {
return true;
}
}
return false;
}
public static boolean generateBlock(World world, int x, int y, int z, WeightedRandomBlock[] mat, List<WeightedRandomBlock> o) {
if (mat == null || mat.length == 0)
return generateBlock(world, x, y, z, o);
if (canGenerateInBlock(world, x, y, z, mat)) {
return generateBlock(world, x, y, z, o);
}
return false;
}
public static boolean generateBlock(World world, int x, int y, int z, List<WeightedRandomBlock> o) {
WeightedRandomBlock ore = (WeightedRandomBlock) WeightedRandom.getRandomItem(world.rand, o);
return world.setBlock(x, y, z, ore.block, ore.metadata, 2);
}
public static WeightedRandomBlock selectBlock(World world, List<WeightedRandomBlock> o) {
return (WeightedRandomBlock) WeightedRandom.getRandomItem(world.rand, o);
}
}