package net.glowstone.generator.objects;
import net.glowstone.constants.GlowBiomeClimate;
import org.bukkit.DoublePlantSpecies;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.material.DoublePlant;
import java.util.Arrays;
import java.util.Random;
public class Lake {
private static final double MAX_DIAMETER = 16.0D;
private static final double MAX_HEIGHT = 8.0D;
private static final int MAX_BLOCKS = (int) (MAX_DIAMETER * MAX_DIAMETER * MAX_HEIGHT);
private static final Material[] PLANT_TYPES = {Material.LONG_GRASS, Material.YELLOW_FLOWER, Material.RED_ROSE,
Material.DOUBLE_PLANT, Material.BROWN_MUSHROOM, Material.RED_MUSHROOM};
private static final Biome[] MYCEL_BIOMES = {Biome.MUSHROOM_ISLAND, Biome.MUSHROOM_SHORE};
private final Material type;
public Lake(Material type) {
this.type = type;
}
public void generate(World world, Random random, int sourceX, int sourceY, int sourceZ) {
sourceY -= (int) MAX_HEIGHT / 2;
final byte[] lakeMap = new byte[MAX_BLOCKS];
for (int n = 0; n < random.nextInt(4) + 4; n++) {
double sizeX = random.nextDouble() * 6.0D + 3;
double sizeY = random.nextDouble() * 4.0D + 2;
double sizeZ = random.nextDouble() * 6.0D + 3;
double dX = random.nextDouble() * (MAX_DIAMETER - sizeX - 2) + 1 + sizeX / 2.0D;
double dY = random.nextDouble() * (MAX_HEIGHT - sizeY - 4) + 2 + sizeY / 2.0D;
double dZ = random.nextDouble() * (MAX_DIAMETER - sizeZ - 2) + 1 + sizeZ / 2.0D;
for (int x = 1; x < (int) MAX_DIAMETER - 1; x++) {
for (int z = 1; z < (int) MAX_DIAMETER - 1; z++) {
for (int y = 1; y < (int) MAX_HEIGHT - 1; y++) {
double nX = (x - dX) / (sizeX / 2.0D);
nX *= nX;
double nY = (y - dY) / (sizeY / 2.0D);
nY *= nY;
double nZ = (z - dZ) / (sizeZ / 2.0D);
nZ *= nZ;
if (nX + nY + nZ < 1.0D) {
setLakeBlock(lakeMap, x, y, z);
}
}
}
}
}
if (!canPlace(lakeMap, world, sourceX, sourceY, sourceZ)) {
return;
}
final Biome biome = world.getBiome(sourceX + 8 + (int) MAX_DIAMETER / 2, sourceZ + 8 + (int) MAX_DIAMETER / 2);
boolean mycelBiome = Arrays.asList(MYCEL_BIOMES).contains(biome) ? true : false;
for (int x = 0; x < (int) MAX_DIAMETER; x++) {
for (int z = 0; z < (int) MAX_DIAMETER; z++) {
for (int y = 0; y < (int) MAX_HEIGHT; y++) {
if (isLakeBlock(lakeMap, x, y, z)) {
Material type = this.type;
final Block block = world.getBlockAt(sourceX + x, sourceY + y, sourceZ + z);
final Block blockAbove = block.getRelative(BlockFace.UP);
if ((block.getType() == Material.DIRT &&
(blockAbove.getType() == Material.LOG || blockAbove.getType() == Material.LOG_2)) ||
(block.getType() == Material.LOG || block.getType() == Material.LOG_2)) {
continue;
}
if (y >= (int) MAX_HEIGHT / 2) {
type = Material.AIR;
for (Material mat : PLANT_TYPES) {
if (blockAbove.getType() == mat) {
if (mat == Material.DOUBLE_PLANT) {
final Block blockAboveBlock = blockAbove.getRelative(BlockFace.UP);
if (blockAboveBlock.getState().getData() instanceof DoublePlant &&
((DoublePlant) blockAboveBlock.getState().getData()).getSpecies() == DoublePlantSpecies.PLANT_APEX) {
blockAboveBlock.setType(Material.AIR);
}
}
blockAbove.setType(Material.AIR);
break;
}
}
if (type == Material.STATIONARY_WATER && (block.getType() == Material.ICE || block.getType() == Material.PACKED_ICE)) {
type = block.getType();
}
} else if (y == MAX_HEIGHT / 2 - 1) {
if (type == Material.STATIONARY_WATER && GlowBiomeClimate.isCold(world.getBiome(sourceX + x, sourceZ + z),
sourceX + x, y, sourceZ + z)) {
type = Material.ICE;
}
}
block.setType(type);
}
}
}
}
for (int x = 0; x < (int) MAX_DIAMETER; x++) {
for (int z = 0; z < (int) MAX_DIAMETER; z++) {
for (int y = (int) MAX_HEIGHT / 2; y < (int) MAX_HEIGHT; y++) {
if (isLakeBlock(lakeMap, x, y, z)) {
final Block block = world.getBlockAt(sourceX + x, sourceY + y - 1, sourceZ + z);
if (block.getType() == Material.DIRT && !block.getRelative(BlockFace.UP).getType().isOccluding() &&
block.getRelative(BlockFace.UP).getLightLevel() > 0) {
block.setType(mycelBiome ? Material.MYCEL : Material.GRASS);
}
}
}
}
}
}
private boolean canPlace(byte[] lakeMap, World world, int sourceX, int sourceY, int sourceZ) {
for (int x = 0; x < MAX_DIAMETER; x++) {
for (int z = 0; z < MAX_DIAMETER; z++) {
for (int y = 0; y < MAX_HEIGHT; y++) {
if (!isLakeBlock(lakeMap, x, y, z) &&
((x < MAX_DIAMETER - 1 && isLakeBlock(lakeMap, x + 1, y, z)) ||
(x > 0 && isLakeBlock(lakeMap, x - 1, y, z)) ||
(z < MAX_DIAMETER - 1 && isLakeBlock(lakeMap, x, y, z + 1)) ||
(z > 0 && isLakeBlock(lakeMap, x, y, z - 1)) ||
(z < MAX_HEIGHT - 1 && isLakeBlock(lakeMap, x, y + 1, z)) ||
(z > 0 && isLakeBlock(lakeMap, x, y - 1, z)))) {
final Block block = world.getBlockAt(sourceX + x, sourceY + y, sourceZ + z);
if (y >= MAX_HEIGHT / 2 && (block.isLiquid() || block.getType() == Material.ICE)) {
return false; // there's already some liquids above
} else if (y < MAX_HEIGHT / 2 && !block.getType().isSolid() && block.getType() != type) {
return false; // bottom must be solid and do not overlap with another liquid type
}
}
}
}
}
return true;
}
private boolean isLakeBlock(byte[] lakeMap, int x, int y, int z) {
return lakeMap[(x * (int) MAX_DIAMETER + z) * (int) MAX_HEIGHT + y] != 0;
}
private void setLakeBlock(byte[] lakeMap, int x, int y, int z) {
lakeMap[(int) ((x * (int) MAX_DIAMETER + z) * (int) MAX_HEIGHT + y)] = 1;
}
}