aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/BiomeSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Minecraft.World/BiomeSource.cpp')
-rw-r--r--Minecraft.World/BiomeSource.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/Minecraft.World/BiomeSource.cpp b/Minecraft.World/BiomeSource.cpp
new file mode 100644
index 00000000..b16efd31
--- /dev/null
+++ b/Minecraft.World/BiomeSource.cpp
@@ -0,0 +1,638 @@
+#include "stdafx.h"
+#include "net.minecraft.world.level.h"
+#include "net.minecraft.world.level.storage.h"
+#include "net.minecraft.world.level.biome.h"
+#include "net.minecraft.world.level.newbiome.layer.h"
+#include "System.h"
+#include "BiomeSource.h"
+#include "..\Minecraft.Client\Minecraft.h"
+#include "..\Minecraft.Client\ProgressRenderer.h"
+
+// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
+void BiomeSource::_init()
+{
+ layer = nullptr;
+ zoomedLayer = nullptr;
+
+ cache = new BiomeCache(this);
+
+ playerSpawnBiomes.push_back(Biome::forest);
+ playerSpawnBiomes.push_back(Biome::taiga);
+ // 4J-PB - Moving forward plains as a spawnable biome (mainly for the Superflat world)
+ playerSpawnBiomes.push_back(Biome::plains);
+ playerSpawnBiomes.push_back(Biome::taigaHills);
+ playerSpawnBiomes.push_back(Biome::forestHills);
+ playerSpawnBiomes.push_back(Biome::jungle);
+ playerSpawnBiomes.push_back(Biome::jungleHills);
+}
+
+void BiomeSource::_init(__int64 seed, LevelType *generator)
+{
+ _init();
+
+ LayerArray layers = Layer::getDefaultLayers(seed, generator);
+ layer = layers[0];
+ zoomedLayer = layers[1];
+
+ delete [] layers.data;
+}
+
+BiomeSource::BiomeSource()
+{
+ _init();
+}
+
+// 4J added
+BiomeSource::BiomeSource(__int64 seed, LevelType *generator)
+{
+ _init(seed, generator);
+}
+
+// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
+BiomeSource::BiomeSource(Level *level)
+{
+ _init(level->getSeed(), level->getLevelData()->getGenerator());
+}
+
+BiomeSource::~BiomeSource()
+{
+ delete cache;
+}
+
+Biome *BiomeSource::getBiome(ChunkPos *cp)
+{
+ return getBiome(cp->x << 4, cp->z << 4);
+}
+
+Biome *BiomeSource::getBiome(int x, int z)
+{
+ return cache->getBiome(x, z);
+}
+
+float BiomeSource::getDownfall(int x, int z) const
+{
+ return cache->getDownfall(x, z);
+}
+
+// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only.
+floatArray BiomeSource::getDownfallBlock(int x, int z, int w, int h) const
+{
+ floatArray downfalls;
+ getDownfallBlock(downfalls, x, z, w, h);
+ return downfalls;
+}
+
+// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only.
+// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
+void BiomeSource::getDownfallBlock(floatArray &downfalls, int x, int z, int w, int h) const
+{
+ IntCache::releaseAll();
+ //if (downfalls == NULL || downfalls->length < w * h)
+ if (downfalls.data == NULL || downfalls.length < w * h)
+ {
+ if(downfalls.data != NULL) delete [] downfalls.data;
+ downfalls = floatArray(w * h);
+ }
+
+ intArray result = zoomedLayer->getArea(x, z, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ float d = (float) Biome::biomes[result[i]]->getDownfallInt() / 65536.0f;
+ if (d > 1) d = 1;
+ downfalls[i] = d;
+ }
+}
+
+BiomeCache::Block *BiomeSource::getBlockAt(int x, int y)
+{
+ return cache->getBlockAt(x, y);
+}
+
+float BiomeSource::getTemperature(int x, int y, int z) const
+{
+ return scaleTemp(cache->getTemperature(x, z), y);
+}
+
+// 4J - brought forward from 1.2.3
+float BiomeSource::scaleTemp(float temp, int y ) const
+{
+ return temp;
+}
+
+floatArray BiomeSource::getTemperatureBlock(int x, int z, int w, int h) const
+{
+ floatArray temperatures;
+ getTemperatureBlock(temperatures, x, z, w, h);
+ return temperatures;
+}
+
+// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only.
+// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
+void BiomeSource::getTemperatureBlock(floatArray& temperatures, int x, int z, int w, int h) const
+{
+ IntCache::releaseAll();
+ //if (temperatures == null || temperatures.length < w * h) {
+ if (temperatures.data == NULL || temperatures.length < w * h)
+ {
+ if( temperatures.data != NULL ) delete [] temperatures.data;
+ temperatures = floatArray(w * h);
+ }
+
+ intArray result = zoomedLayer->getArea(x, z, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ float t = (float) Biome::biomes[result[i]]->getTemperatureInt() / 65536.0f;
+ if (t > 1) t = 1;
+ temperatures[i] = t;
+ }
+}
+
+BiomeArray BiomeSource::getRawBiomeBlock(int x, int z, int w, int h) const
+{
+ BiomeArray biomes;
+ getRawBiomeBlock(biomes, x, z, w, h);
+ return biomes;
+}
+
+// 4J added
+void BiomeSource::getRawBiomeIndices(intArray &biomes, int x, int z, int w, int h) const
+{
+ IntCache::releaseAll();
+
+ intArray result = layer->getArea(x, z, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ biomes[i] = result[i];
+ }
+}
+
+void BiomeSource::getRawBiomeBlock(BiomeArray &biomes, int x, int z, int w, int h) const
+{
+ IntCache::releaseAll();
+ //if (biomes == null || biomes.length < w * h)
+ if (biomes.data == NULL || biomes.length < w * h)
+ {
+ if(biomes.data != NULL) delete [] biomes.data;
+ biomes = BiomeArray(w * h);
+ }
+
+ intArray result = layer->getArea(x, z, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ biomes[i] = Biome::biomes[result[i]];
+#ifndef _CONTENT_PACKAGE
+ if(biomes[i] == NULL)
+ {
+ app.DebugPrintf("Tried to assign null biome %d\n", result[i]);
+ __debugbreak();
+ }
+#endif
+ }
+}
+
+
+
+BiomeArray BiomeSource::getBiomeBlock(int x, int z, int w, int h) const
+{
+ if (w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0)
+ {
+ return cache->getBiomeBlockAt(x, z);
+ }
+ BiomeArray biomes;
+ getBiomeBlock(biomes, x, z, w, h, true);
+ return biomes;
+}
+
+// 4J - caller is responsible for deleting biomes array
+void BiomeSource::getBiomeBlock(BiomeArray& biomes, int x, int z, int w, int h, bool useCache) const
+{
+ IntCache::releaseAll();
+ //if (biomes == null || biomes.length < w * h)
+ if (biomes.data == NULL || biomes.length < w * h)
+ {
+ if(biomes.data != NULL) delete [] biomes.data;
+ biomes = BiomeArray(w * h);
+ }
+
+ if (useCache && w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0)
+ {
+ BiomeArray tmp = cache->getBiomeBlockAt(x, z);
+ System::arraycopy(tmp, 0, &biomes, 0, w * h);
+ delete tmp.data; // MGH - added, the caching creates this array from the indices now.
+ //return biomes;
+ }
+
+ intArray result = zoomedLayer->getArea(x, z, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ biomes[i] = Biome::biomes[result[i]];
+ }
+}
+
+
+
+
+byteArray BiomeSource::getBiomeIndexBlock(int x, int z, int w, int h) const
+{
+ if (w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0)
+ {
+ return cache->getBiomeIndexBlockAt(x, z);
+ }
+ byteArray biomeIndices;
+ getBiomeIndexBlock(biomeIndices, x, z, w, h, true);
+ return biomeIndices;
+}
+
+// 4J - caller is responsible for deleting biomes array
+void BiomeSource::getBiomeIndexBlock(byteArray& biomeIndices, int x, int z, int w, int h, bool useCache) const
+{
+ IntCache::releaseAll();
+ //if (biomes == null || biomes.length < w * h)
+ if (biomeIndices.data == NULL || biomeIndices.length < w * h)
+ {
+ if(biomeIndices.data != NULL) delete [] biomeIndices.data;
+ biomeIndices = byteArray(w * h);
+ }
+
+ if (useCache && w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0)
+ {
+ byteArray tmp = cache->getBiomeIndexBlockAt(x, z);
+ System::arraycopy(tmp, 0, &biomeIndices, 0, w * h);
+ //return biomes;
+ }
+
+ intArray result = zoomedLayer->getArea(x, z, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ biomeIndices[i] = (byte)result[i];
+ }
+}
+
+/**
+* Checks if an area around a block contains only the specified biomes.
+* Useful for placing elements like towns.
+*
+* This is a bit of a rough check, to make it as fast as possible. To ensure
+* NO other biomes, add a margin of at least four blocks to the radius
+*/
+bool BiomeSource::containsOnly(int x, int z, int r, vector<Biome *> allowed)
+{
+ int x0 = ((x - r) >> 2);
+ int z0 = ((z - r) >> 2);
+ int x1 = ((x + r) >> 2);
+ int z1 = ((z + r) >> 2);
+
+ int w = x1 - x0 + 1;
+ int h = z1 - z0 + 1;
+
+ intArray biomes = layer->getArea(x0, z0, w, h);
+ for (int i = 0; i < w * h; i++)
+ {
+ Biome *b = Biome::biomes[biomes[i]];
+ if (find(allowed.begin(), allowed.end(), b) == allowed.end()) return false;
+ }
+
+ return true;
+}
+
+/**
+* Checks if an area around a block contains only the specified biome.
+* Useful for placing elements like towns.
+*
+* This is a bit of a rough check, to make it as fast as possible. To ensure
+* NO other biomes, add a margin of at least four blocks to the radius
+*/
+bool BiomeSource::containsOnly(int x, int z, int r, Biome *allowed)
+{
+ int x0 = ((x - r) >> 2);
+ int z0 = ((z - r) >> 2);
+ int x1 = ((x + r) >> 2);
+ int z1 = ((z + r) >> 2);
+
+ int w = x1 - x0;
+ int h = z1 - z0;
+ int biomesCount = w*h;
+ intArray biomes = layer->getArea(x0, z0, w, h);
+ for (unsigned int i = 0; i < biomesCount; i++)
+ {
+ Biome *b = Biome::biomes[biomes[i]];
+ if (allowed != b) return false;
+ }
+
+ return true;
+}
+
+/**
+* Finds the specified biome within the radius. This will return a random
+* position if several are found. This test is fairly rough.
+*
+* Returns null if the biome wasn't found
+*/
+TilePos *BiomeSource::findBiome(int x, int z, int r, Biome *toFind, Random *random)
+{
+ int x0 = ((x - r) >> 2);
+ int z0 = ((z - r) >> 2);
+ int x1 = ((x + r) >> 2);
+ int z1 = ((z + r) >> 2);
+
+ int w = x1 - x0 + 1;
+ int h = z1 - z0 + 1;
+ intArray biomes = layer->getArea(x0, z0, w, h);
+ TilePos *res = NULL;
+ int found = 0;
+ int biomesCount = w*h;
+ for (unsigned int i = 0; i < biomesCount; i++)
+ {
+ int xx = x0 + i % w;
+ int zz = z0 + i / w;
+ Biome *b = Biome::biomes[biomes[i]];
+ if (b == toFind)
+ {
+ if (res == NULL || random->nextInt(found + 1) == 0)
+ {
+ res = new TilePos(xx, 0, zz);
+ found++;
+ }
+ }
+ }
+
+ return res;
+}
+
+/**
+* Finds one of the specified biomes within the radius. This will return a
+* random position if several are found. This test is fairly rough.
+*
+* Returns null if the biome wasn't found
+*/
+TilePos *BiomeSource::findBiome(int x, int z, int r, vector<Biome *> allowed, Random *random)
+{
+ int x0 = ((x - r) >> 2);
+ int z0 = ((z - r) >> 2);
+ int x1 = ((x + r) >> 2);
+ int z1 = ((z + r) >> 2);
+
+ int w = x1 - x0 + 1;
+ int h = z1 - z0 + 1;
+ MemSect(50);
+ intArray biomes = layer->getArea(x0, z0, w, h);
+ TilePos *res = NULL;
+ int found = 0;
+ int biomesCount = w*h;
+ for (unsigned int i = 0; i < biomesCount; i++)
+ {
+ int xx = (x0 + i % w) << 2;
+ int zz = (z0 + i / w) << 2;
+ Biome *b = Biome::biomes[biomes[i]];
+ if (find(allowed.begin(), allowed.end(), b) != allowed.end())
+ {
+ if (res == NULL || random->nextInt(found + 1) == 0)
+ {
+ delete res;
+ res = new TilePos(xx, 0, zz);
+ found++;
+ }
+ }
+ }
+ MemSect(0);
+
+ return res;
+}
+
+void BiomeSource::update()
+{
+ cache->update();
+}
+
+//#define DEBUG_SEEDS 50
+
+// 4J added - find a seed for this biomesource that matches certain criteria
+#ifdef __PSVITA__
+__int64 BiomeSource::findSeed(LevelType *generator, bool* pServerRunning) // MGH - added pRunning, so we can early out of this on Vita as it can take up to 60 secs
+#else
+__int64 BiomeSource::findSeed(LevelType *generator)
+#endif
+{
+
+ __int64 bestSeed = 0;
+
+ ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer;
+ mcprogress->progressStage(IDS_PROGRESS_NEW_WORLD_SEED);
+
+#ifndef _CONTENT_PACKAGE
+ if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_EnableHeightWaterBiomeOverride))
+ {
+ // Do nothing
+ }
+ else
+#endif
+ {
+#ifdef DEBUG_SEEDS
+ for( int k = 0; k < DEBUG_SEEDS; k++ )
+#endif
+ {
+ // Try and genuinely random this search up
+ Random *pr = new Random(System::nanoTime());
+
+ // Raw biome data has one result per 4x4 group of tiles.
+ // Removing a border of 8 from each side since we'll be doing special things at the edge to turn our world into an island, and so don't want to count things
+ // in the edge region in case they later get removed
+ static const int biomeWidth = ( 54 * 4 ) - 16; // Should be even so we can offset evenly
+ static const int biomeOffset = -( biomeWidth / 2 );
+
+ // Storage for our biome indices
+ intArray indices = intArray( biomeWidth * biomeWidth );
+
+ // Storage for the fractional amounts of each biome that will be calculated
+ float toCompare[Biome::BIOME_COUNT];
+
+ bool matchFound = false;
+ int tryCount = 0;
+
+ // Just keeping trying to generate seeds until we find one that matches our criteria
+ do
+ {
+ __int64 seed = pr->nextLong();
+ BiomeSource *biomeSource = new BiomeSource(seed,generator);
+
+ biomeSource->getRawBiomeIndices(indices, biomeOffset, biomeOffset, biomeWidth, biomeWidth);
+ getFracs(indices, toCompare);
+
+ matchFound = getIsMatch( toCompare );
+
+ if( matchFound ) bestSeed = seed;
+
+ delete biomeSource;
+ tryCount++;
+
+ mcprogress->progressStagePercentage( tryCount % 100 );
+#ifdef __PSVITA__
+ } while (!matchFound && *pServerRunning);
+#else
+ } while (!matchFound);
+#endif
+
+ // Clean up
+ delete pr;
+ delete indices.data;
+
+#ifdef DEBUG_SEEDS
+ app.DebugPrintf("%d: %d tries taken, seed used is %lld\n", k, tryCount, bestSeed);
+
+ BiomeSource *biomeSource = new BiomeSource(bestSeed);
+ BiomeArray biomes = biomeSource->getBiomeBlock(-27 * 16, -27 * 16, 54 * 16, 54 * 16);
+
+ unsigned int *pixels = new unsigned int[54 * 16 * 54 * 16];
+ for(int i = 0; i < 54 * 16 * 54 * 16; i++ )
+ {
+ int id = biomes[i]->id;
+
+ // Create following colours:
+ // 0 ocean 0000 black
+ // 1 plains 0001 pastel cyan
+ // 2 desert 0010 green
+ // 3 extreme hills 0011 yellow
+ // 4 forest 0100 blue
+ // 5 taiga 0101 magenta
+ // 6 swamps 0110 cyan
+ // 7 river 0111 white
+ // 8 hell 1000 grey
+ // 9 end biome 1001 white
+ // 10 frozen ocean 1010 pastel green
+ // 11 frozen river 1011 pastel yellow
+ // 12 ice flats 1100 pastel blue
+ // 13 ice mountains 1101 pastel magenta
+ // 14 mushroom island 1110 red
+ // 15 mushroom shore 1111 pastel red
+
+ if( id == 1 ) id = 14;
+ else if ( id == 14 ) id = 1;
+ else if( id == 9 ) id = 15;
+ else if( id == 15 ) id = 9;
+ pixels[i] = 0xff000000;
+ if( id & 1 ) pixels[i] |= 0x00ff0000;
+ if( id & 2 ) pixels[i] |= 0x0000ff00;
+ if( id & 4 ) pixels[i] |= 0x000000ff;
+ if( id & 8 ) pixels[i] |= 0x00808080;
+ }
+ D3DXIMAGE_INFO srcInfo;
+ srcInfo.Format = D3DFMT_LIN_A8R8G8B8;
+ srcInfo.ImageFileFormat = D3DXIFF_BMP;
+ srcInfo.Width = 54 * 16;
+ srcInfo.Height = 54 * 16;
+
+ char buf[256];
+ sprintf(buf,"GAME:\\BiomeTest%d.bmp",k);
+ RenderManager.SaveTextureData(buf, &srcInfo, (int *)pixels);
+
+ delete [] pixels;
+ delete biomes.data;
+ delete biomeSource;
+#endif
+ }
+ }
+
+ return bestSeed;
+}
+
+// 4J added - get the fractional amounts of each biome type in the given indices
+void BiomeSource::getFracs(intArray indices, float *fracs)
+{
+ for( int i = 0; i < Biome::BIOME_COUNT; i++ )
+ {
+ fracs[i] = 0.0f;
+ }
+
+ for( int i = 0; i < indices.length; i++ )
+ {
+ fracs[indices[i]] += 1.0f;
+ }
+
+ for( int i = 0; i < Biome::BIOME_COUNT; i++ )
+ {
+ fracs[i] /= (float)(indices.length);
+ }
+}
+
+
+
+// 4J added - determine if this particular set of fractional amounts of biome types matches are requirements
+bool BiomeSource::getIsMatch(float *frac)
+{
+ // A true for a particular biome type here marks it as one that *has* to be present
+ static const bool critical[Biome::BIOME_COUNT] = {
+ true, // ocean
+ true, // plains
+ true, // desert
+ false, // extreme hills
+ true, // forest
+ true, // taiga
+ true, // swamps
+ false, // river
+ false, // hell
+ false, // end biome
+ false, // frozen ocean
+ false, // frozen river
+ false, // ice flats
+ false, // ice mountains
+ true, // mushroom island / shore
+ false, // mushroom shore (combined with above)
+ false, // beach
+ false, // desert hills (combined with desert)
+ false, // forest hills (combined with forest)
+ false, // taiga hills (combined with taga)
+ false, // small extreme hills
+ true, // jungle
+ false, // jungle hills (combined with jungle)
+ };
+
+
+ // Don't want more than 15% ocean
+ if( frac[0] > 0.15f )
+ {
+ return false;
+ }
+
+ // Consider mushroom shore & islands as the same by finding max
+ frac[14] = ( ( frac[15] > frac[14] ) ? frac[15] : frac[14] );
+
+ // Merge desert and desert hills
+ frac[2] = ( ( frac[17] > frac[2] ) ? frac[17] : frac[2] );
+
+ // Merge forest and forest hills
+ frac[4] = ( ( frac[18] > frac[4] ) ? frac[18] : frac[4] );
+
+ // Merge taiga and taiga hills
+ frac[5] = ( ( frac[19] > frac[5] ) ? frac[19] : frac[5] );
+
+ // Merge jungle and jungle hills
+ frac[21] = ( ( frac[22] > frac[21] ) ? frac[22] : frac[21] );
+
+ // Loop through all biome types, and:
+ // (1) count them
+ // (2) give up if one of the critical ones is missing
+
+ int typeCount = 0;
+ for( int i = 0; i < Biome::BIOME_COUNT; i++ )
+ {
+ // We want to skip some where we have merged with another type
+ if(i == 15 || i == 17 || i == 18 || i == 19 || i == 22) continue;
+
+ // Consider 0.1% as being "present" - this equates an area of about 3 chunks
+ if( frac[i] > 0.001f )
+ {
+ typeCount++;
+ }
+ else
+ {
+ // If a critical biome is missing, just give up
+ if( critical[i] )
+ {
+ return false;
+ }
+ }
+ }
+
+ // Consider as suitable if we've got all the critical ones, and in total 9 or more - currently there's 8 critical so this just forces at least 1 more others
+ return ( typeCount >= 9 );
+} \ No newline at end of file