mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-04-25 12:13:40 +00:00
576 lines
19 KiB
C++
576 lines
19 KiB
C++
#include "../../Platform/stdafx.h"
|
|
#include "../../Headers/net.minecraft.world.level.h"
|
|
#include "../../Headers/net.minecraft.world.level.storage.h"
|
|
#include "../../Headers/net.minecraft.world.level.biome.h"
|
|
#include "../../Headers/net.minecraft.world.level.newbiome.layer.h"
|
|
#include "../../Platform/System.h"
|
|
#include "BiomeSource.h"
|
|
#include "../../../Minecraft.Client/Minecraft.h"
|
|
#include "../../../Minecraft.Client/Rendering/EntityRenderers/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_t 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_t 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 {
|
|
// 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 {
|
|
// 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 {
|
|
|
|
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 {
|
|
// 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]];
|
|
#if !defined(_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 {
|
|
// 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. //4jcraft made it array delete
|
|
// 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 {
|
|
// 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] = (uint8_t)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,
|
|
std::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,
|
|
std::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;
|
|
for (unsigned int i = 0; i < w * h; 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
|
|
int64_t BiomeSource::findSeed(LevelType* generator)
|
|
{
|
|
|
|
int64_t bestSeed = 0;
|
|
|
|
ProgressRenderer* mcprogress = Minecraft::GetInstance()->progressRenderer;
|
|
mcprogress->progressStage(IDS_PROGRESS_NEW_WORLD_SEED);
|
|
|
|
#if !defined(_CONTENT_PACKAGE)
|
|
if (app.DebugSettingsOn() &&
|
|
app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()) &
|
|
(1L << eDebugSetting_EnableBiomeOverride)) {
|
|
// Do nothing
|
|
} else
|
|
#endif
|
|
{
|
|
#if defined(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_t 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);
|
|
} while (!matchFound);
|
|
|
|
// Clean up
|
|
delete pr;
|
|
delete[] indices.data;
|
|
|
|
#if defined(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);
|
|
}
|