Override default genlayer procedure to enable better control over world generation and biome placement - added 2 varieties of biome arrangement in addition to the vanilla system, random and latitude (which can be selected via a config file)

This commit is contained in:
Cheeserolls 2015-05-27 02:35:48 +01:00
parent 45b15e6d7f
commit 2b9a7c30f0
4 changed files with 506 additions and 1 deletions

View file

@ -8,10 +8,33 @@
package biomesoplenty.common.world;
import java.io.File;
import biomesoplenty.common.util.config.BOPConfig;
import biomesoplenty.common.world.layer.GenLayerHeatLatitude;
import biomesoplenty.common.world.layer.GenLayerHeatRandom;
import biomesoplenty.common.world.layer.GenLayerHillsBOP;
import biomesoplenty.core.BiomesOPlenty;
import net.minecraft.world.World;
import net.minecraft.world.WorldType;
import net.minecraft.world.biome.WorldChunkManager;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.GenLayerAddIsland;
import net.minecraft.world.gen.layer.GenLayerAddMushroomIsland;
import net.minecraft.world.gen.layer.GenLayerAddSnow;
import net.minecraft.world.gen.layer.GenLayerDeepOcean;
import net.minecraft.world.gen.layer.GenLayerEdge;
import net.minecraft.world.gen.layer.GenLayerFuzzyZoom;
import net.minecraft.world.gen.layer.GenLayerIsland;
import net.minecraft.world.gen.layer.GenLayerRareBiome;
import net.minecraft.world.gen.layer.GenLayerRemoveTooMuchOcean;
import net.minecraft.world.gen.layer.GenLayerRiver;
import net.minecraft.world.gen.layer.GenLayerRiverInit;
import net.minecraft.world.gen.layer.GenLayerRiverMix;
import net.minecraft.world.gen.layer.GenLayerShore;
import net.minecraft.world.gen.layer.GenLayerSmooth;
import net.minecraft.world.gen.layer.GenLayerVoronoiZoom;
import net.minecraft.world.gen.layer.GenLayerZoom;
public class WorldChunkManagerBOP extends WorldChunkManager
{
@ -19,7 +42,8 @@ public class WorldChunkManagerBOP extends WorldChunkManager
{
super();
this.field_180301_f = chunkProviderSettings;
GenLayer[] agenlayer = GenLayer.initializeAllBiomeGenerators(seed, worldType, chunkProviderSettings);
BOPConfig.IConfigObj worldConfig = new BOPConfig.ConfigFileObj(new File(BiomesOPlenty.configDirectory, "world.json"));
GenLayer[] agenlayer = setupGenLayersVanilla(seed, worldType, chunkProviderSettings, worldConfig);
agenlayer = getModdedBiomeGenerators(worldType, seed, agenlayer);
this.genBiomes = agenlayer[0];
this.biomeIndexLayer = agenlayer[1];
@ -29,4 +53,131 @@ public class WorldChunkManagerBOP extends WorldChunkManager
{
this(world.getSeed(), world.getWorldInfo().getTerrainType(), world.getWorldInfo().getGeneratorOptions());
}
// generate the regions of land and sea
public static GenLayer initialLandAndSeaLayer()
{
GenLayer stack = new GenLayerIsland(1L);
stack = new GenLayerFuzzyZoom(2000L, stack);
stack = new GenLayerAddIsland(1L, stack);
stack = new GenLayerZoom(2001L, stack);
stack = new GenLayerAddIsland(2L, stack);
stack = new GenLayerAddIsland(50L, stack);
stack = new GenLayerAddIsland(70L, stack);
stack = new GenLayerRemoveTooMuchOcean(2L, stack);
return stack;
}
public enum TemperatureVariationScheme {VANILLA, RANDOM, LATITUDE}
// superimpose hot and cold regions an a land and sea layer
public static GenLayer addHotAndColdRegions(GenLayer landAndSea, TemperatureVariationScheme scheme, long worldSeed)
{
GenLayer stack;
switch (scheme)
{
// The 'random' scheme places small hot and cold regions all over the map completely at random
// this results in biomes scattered randomly like in Minecraft before v1.7
case RANDOM:
stack = new GenLayerAddIsland(3L, landAndSea);
stack = new GenLayerZoom(2002L, stack);
stack = new GenLayerZoom(2002L, stack);
stack = new GenLayerHeatRandom(2L, stack);
stack = new GenLayerEdge(3L, stack, GenLayerEdge.Mode.SPECIAL);
break;
// The 'latitude' scheme causes temperature to depend on latitude (as it does on Earth)
// the result is bands of temperature in the East-West direction
// travelling North/South you find different temperatures, travelling East/West you find different biomes of a similar temperature
case LATITUDE:
stack = new GenLayerAddIsland(3L, landAndSea);
stack = new GenLayerZoom(2002L, stack);
stack = new GenLayerHeatLatitude(2L, stack, 8, worldSeed);
stack = new GenLayerEdge(3L, stack, GenLayerEdge.Mode.SPECIAL);
stack = new GenLayerZoom(2002L, stack);
break;
// The 'vanilla' scheme results in large temperature regions, arranged semi-randomly (extremes of temperature rarely touch)
// this is the minecraft default scheme and causes biomes for similar temperatures to be clustered together
case VANILLA: default:
stack = new GenLayerAddSnow(2L, landAndSea);
stack = new GenLayerAddIsland(3L, stack);
stack = new GenLayerEdge(2L, stack, GenLayerEdge.Mode.COOL_WARM);
stack = new GenLayerEdge(2L, stack, GenLayerEdge.Mode.HEAT_ICE);
stack = new GenLayerEdge(3L, stack, GenLayerEdge.Mode.SPECIAL);
stack = new GenLayerZoom(2002L, stack);
stack = new GenLayerZoom(2003L, stack);
break;
}
return stack;
}
public static GenLayer allocateBiomes(long worldSeed, WorldType worldType, String chunkProviderSettingsJson, GenLayer hotAndCold, GenLayer hillsInit)
{
// allocate the (low) biomes
GenLayer stack = worldType.getBiomeLayer(worldSeed, hotAndCold, chunkProviderSettingsJson);
// use the hillsInit layer to change some to hill biomes
stack = new GenLayerHillsBOP(1000L, stack, GenLayerZoom.magnify(1000L, hillsInit, 2));
return stack;
}
public static GenLayer[] setupGenLayersVanilla(long worldSeed, WorldType worldType, String chunkProviderSettingsJson, BOPConfig.IConfigObj conf)
{
int biomeSize = 4;
int riverSize = 4;
// first few layers just create areas of land and sea, continents and islands
GenLayer mainBranch = initialLandAndSeaLayer();
// now add hot and cold regions (and two zooms)
TemperatureVariationScheme scheme = conf.getEnum("temperatureVariationScheme", TemperatureVariationScheme.VANILLA, TemperatureVariationScheme.class);
mainBranch = addHotAndColdRegions(mainBranch, scheme, worldSeed);
// add mushroom islands and deep oceans
mainBranch = new GenLayerAddIsland(4L, mainBranch);
mainBranch = new GenLayerAddMushroomIsland(5L, mainBranch);
mainBranch = new GenLayerDeepOcean(4L, mainBranch);
// fork off a new branch as a seed for rivers and hills
GenLayer riversAndHillsInit = new GenLayerRiverInit(100L, mainBranch);
// allocate the biomes
mainBranch = allocateBiomes(worldSeed, worldType, chunkProviderSettingsJson, mainBranch, riversAndHillsInit);
// do a bit more zooming, depending on biomeSize
mainBranch = new GenLayerRareBiome(1001L, mainBranch);
for (int i = 0; i < biomeSize; ++i)
{
mainBranch = new GenLayerZoom((long)(1000 + i), mainBranch);
if (i == 0) {mainBranch = new GenLayerAddIsland(3L, mainBranch);}
if (i == 1 || biomeSize == 1) {mainBranch = new GenLayerShore(1000L, mainBranch);}
}
mainBranch = new GenLayerSmooth(1000L, mainBranch);
// develop the rivers branch
GenLayer riversBranch = GenLayerZoom.magnify(1000L, riversAndHillsInit, 2);
riversBranch = GenLayerZoom.magnify(1000L, riversBranch, riverSize);
riversBranch = new GenLayerRiver(1L, riversBranch);
riversBranch = new GenLayerSmooth(1000L, riversBranch);
// mix rivers into main branch
GenLayer riverMixFinal = new GenLayerRiverMix(100L, mainBranch, riversBranch);
// finish biomes with Voronoi zoom
GenLayer biomesFinal = new GenLayerVoronoiZoom(10L, riverMixFinal);
riverMixFinal.initWorldGenSeed(worldSeed);
biomesFinal.initWorldGenSeed(worldSeed);
return new GenLayer[] {riverMixFinal, biomesFinal, riverMixFinal};
}
}

View file

@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright 2015, the Biomes O' Plenty Team
*
* This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License.
*
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/.
******************************************************************************/
package biomesoplenty.common.world.layer;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.IntCache;
public class GenLayerHeatLatitude extends GenLayer
{
private double period;
private double halfPeriod;
private int offset;
private double offsetVariation;
private double amplitude;
public GenLayerHeatLatitude(long seed, GenLayer parent, double halfPeriod, long worldSeed)
{
super(seed);
this.parent = parent;
this.period = halfPeriod * 2.0D;
this.halfPeriod = halfPeriod;
this.offset = (int)(worldSeed % ((int)(this.period * 2)));
this.offsetVariation = halfPeriod / 7.0F;
this.amplitude = 5.999D / halfPeriod;
}
@Override
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight)
{
int x0 = areaX - 1;
int y0 = areaY - 1;
int x1 = areaWidth + 2;
int y1 = areaHeight + 2;
int[] parentVals = this.parent.getInts(x0, y0, x1, y1);
int[] out = IntCache.getIntCache(areaWidth * areaHeight);
for (int y = 0; y < areaHeight; ++y)
{
for (int x = 0; x < areaWidth; ++x)
{
int parentVal = parentVals[x + 1 + (y + 1) * x1];
// x and y are local coordinates, but we need global coordinates, which we'll call X and Y
int X = x + areaX;
int Y = y + areaY;
this.initChunkSeed((long)X, (long)Y);
if (parentVal == 0)
{
// ocean stays as ocean
out[x + y * areaWidth] = 0;
}
else
{
// set value between 1 and 4 which is periodic in Y (with some random variation)
double Yoffset = Y + this.offset + ((this.nextInt(1001) - 500) * this.offsetVariation / 500.0D);
int scaledVal = (int)Math.floor(this.amplitude * Math.abs((Math.abs(Yoffset % period) - halfPeriod)));
switch (scaledVal)
{
case 0:
out[x + y * areaWidth] = 1;
break;
case 1: case 2:
out[x + y * areaWidth] = 2;
break;
case 3: case 4:
out[x + y * areaWidth] = 3;
break;
default:
out[x + y * areaWidth] = 4;
break;
}
}
}
}
return out;
}
}

View file

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright 2015, the Biomes O' Plenty Team
*
* This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License.
*
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/.
******************************************************************************/
package biomesoplenty.common.world.layer;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.IntCache;
public class GenLayerHeatRandom extends GenLayer
{
public GenLayerHeatRandom(long seed, GenLayer parent)
{
super(seed);
this.parent = parent;
}
@Override
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight)
{
int x0 = areaX - 1;
int y0 = areaY - 1;
int x1 = areaWidth + 2;
int y1 = areaHeight + 2;
int[] parentVals = this.parent.getInts(x0, y0, x1, y1);
int[] out = IntCache.getIntCache(areaWidth * areaHeight);
for (int y = 0; y < areaHeight; ++y)
{
for (int x = 0; x < areaWidth; ++x)
{
int parentVal = parentVals[x + 1 + (y + 1) * x1];
this.initChunkSeed((long)(x + areaX), (long)(y + areaY));
if (parentVal == 0)
{
// ocean stays as ocean
out[x + y * areaWidth] = 0;
}
else
{
// otherwise choose a random heat value
out[x + y * areaWidth] = this.nextInt(4) + 1;
}
}
}
return out;
}
}

View file

@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright 2015, the Biomes O' Plenty Team
*
* This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License.
*
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/.
******************************************************************************/
package biomesoplenty.common.world.layer;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.IntCache;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// This is the same as vanilla minecraft's GenLayerHills code, just de-obfuscated a bit - might change it slightly later to support hilly versions of BOP biomes
public class GenLayerHillsBOP extends GenLayer
{
private static final Logger logger = LogManager.getLogger();
private GenLayer riversInitLayer;
public GenLayerHillsBOP(long seed, GenLayer biomesLayer, GenLayer riversInitLayer)
{
super(seed);
this.parent = biomesLayer;
this.riversInitLayer = riversInitLayer;
}
@Override
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight)
{
int[] biomeIds = this.parent.getInts(areaX - 1, areaY - 1, areaWidth + 2, areaHeight + 2);
int[] riverVals = this.riversInitLayer.getInts(areaX - 1, areaY - 1, areaWidth + 2, areaHeight + 2);
int[] out = IntCache.getIntCache(areaWidth * areaHeight);
for (int z = 0; z < areaHeight; ++z)
{
for (int x = 0; x < areaWidth; ++x)
{
this.initChunkSeed((long)(x + areaX), (long)(z + areaY));
int biomeID = biomeIds[x + 1 + (z + 1) * (areaWidth + 2)];
int l1 = riverVals[x + 1 + (z + 1) * (areaWidth + 2)];
boolean flag = (l1 - 2) % 29 == 0;
if (biomeID > 255)
{
logger.debug("old! " + biomeID);
}
if (biomeID != 0 && l1 >= 2 && (l1 - 2) % 29 == 1 && biomeID < 128)
{
if (BiomeGenBase.getBiome(biomeID + 128) != null)
{
out[x + z * areaWidth] = biomeID + 128;
}
else
{
out[x + z * areaWidth] = biomeID;
}
}
else if (this.nextInt(3) != 0 && !flag)
{
out[x + z * areaWidth] = biomeID;
}
else
{
int i2 = biomeID;
int j2;
if (biomeID == BiomeGenBase.desert.biomeID)
{
i2 = BiomeGenBase.desertHills.biomeID;
}
else if (biomeID == BiomeGenBase.forest.biomeID)
{
i2 = BiomeGenBase.forestHills.biomeID;
}
else if (biomeID == BiomeGenBase.birchForest.biomeID)
{
i2 = BiomeGenBase.birchForestHills.biomeID;
}
else if (biomeID == BiomeGenBase.roofedForest.biomeID)
{
i2 = BiomeGenBase.plains.biomeID;
}
else if (biomeID == BiomeGenBase.taiga.biomeID)
{
i2 = BiomeGenBase.taigaHills.biomeID;
}
else if (biomeID == BiomeGenBase.megaTaiga.biomeID)
{
i2 = BiomeGenBase.megaTaigaHills.biomeID;
}
else if (biomeID == BiomeGenBase.coldTaiga.biomeID)
{
i2 = BiomeGenBase.coldTaigaHills.biomeID;
}
else if (biomeID == BiomeGenBase.plains.biomeID)
{
if (this.nextInt(3) == 0)
{
i2 = BiomeGenBase.forestHills.biomeID;
}
else
{
i2 = BiomeGenBase.forest.biomeID;
}
}
else if (biomeID == BiomeGenBase.icePlains.biomeID)
{
i2 = BiomeGenBase.iceMountains.biomeID;
}
else if (biomeID == BiomeGenBase.jungle.biomeID)
{
i2 = BiomeGenBase.jungleHills.biomeID;
}
else if (biomeID == BiomeGenBase.ocean.biomeID)
{
i2 = BiomeGenBase.deepOcean.biomeID;
}
else if (biomeID == BiomeGenBase.extremeHills.biomeID)
{
i2 = BiomeGenBase.extremeHillsPlus.biomeID;
}
else if (biomeID == BiomeGenBase.savanna.biomeID)
{
i2 = BiomeGenBase.savannaPlateau.biomeID;
}
else if (biomesEqualOrMesaPlateau(biomeID, BiomeGenBase.mesaPlateau_F.biomeID))
{
i2 = BiomeGenBase.mesa.biomeID;
}
else if (biomeID == BiomeGenBase.deepOcean.biomeID && this.nextInt(3) == 0)
{
j2 = this.nextInt(2);
if (j2 == 0)
{
i2 = BiomeGenBase.plains.biomeID;
}
else
{
i2 = BiomeGenBase.forest.biomeID;
}
}
if (flag && i2 != biomeID)
{
if (BiomeGenBase.getBiome(i2 + 128) != null)
{
i2 += 128;
}
else
{
i2 = biomeID;
}
}
if (i2 == biomeID)
{
out[x + z * areaWidth] = biomeID;
}
else
{
j2 = biomeIds[x + 1 + (z + 1 - 1) * (areaWidth + 2)];
int k2 = biomeIds[x + 1 + 1 + (z + 1) * (areaWidth + 2)];
int l2 = biomeIds[x + 1 - 1 + (z + 1) * (areaWidth + 2)];
int i3 = biomeIds[x + 1 + (z + 1 + 1) * (areaWidth + 2)];
int j3 = 0;
if (biomesEqualOrMesaPlateau(j2, biomeID))
{
++j3;
}
if (biomesEqualOrMesaPlateau(k2, biomeID))
{
++j3;
}
if (biomesEqualOrMesaPlateau(l2, biomeID))
{
++j3;
}
if (biomesEqualOrMesaPlateau(i3, biomeID))
{
++j3;
}
if (j3 >= 3)
{
out[x + z * areaWidth] = i2;
}
else
{
out[x + z * areaWidth] = biomeID;
}
}
}
}
}
return out;
}
}