Files
mcpe/source/world/level/levelgen/chunk/RandomLevelSource.cpp
Brent 5c1ea03747 Logging cleanup (#69)
* Mac OS X 10.6 & More C++03 Support

* Fix SDL2 options.txt loading for C++03

* Output/Logging Overhaul
* Added StandardOut class
* Renamed LOGX macros to LOG_X
* Removed LogMsg macros in favor of LOG_X
* Added console window for debug Windows builds

* Updated Xcode Project
+ StandardOut.hpp
+ StandardOut.cpp

* StandardOut_windows
* Replaced the Windows #ifdefs in StandardOut with StandardOut_windows

---------

Co-authored-by: Brent Da Mage <BrentDaMage@users.noreply.github.com>
2023-08-28 10:55:41 +03:00

601 lines
15 KiB
C++

/********************************************************************
Minecraft: Pocket Edition - Decompilation Project
Copyright (C) 2023 iProgramInCpp
The following code is licensed under the BSD 1 clause license.
SPDX-License-Identifier: BSD-1-Clause
********************************************************************/
#include "RandomLevelSource.hpp"
#include "world/level/Level.hpp"
#include "world/tile/SandTile.hpp"
//#define TEST_CAVES
const float RandomLevelSource::SNOW_CUTOFF = 0.5f;
const float RandomLevelSource::SNOW_SCALE = 0.3f;
float g_timeSpentInPostProcessing = 0;
RandomLevelSource::RandomLevelSource(Level* level, TLong seed, int x) :
m_random(seed),
m_perlinNoise1(&m_random, 16),
m_perlinNoise2(&m_random, 16),
m_perlinNoise3(&m_random, 8),
m_perlinNoise4(&m_random, 4),
m_perlinNoise5(&m_random, 4),
m_perlinNoise6(&m_random, 10),
m_perlinNoise7(&m_random, 16),
m_perlinNoise8(&m_random, 8),
m_pLevel(level)
{
field_4 = false;
field_19F0 = 1.0f;
field_7280 = nullptr;
field_7E84 = nullptr;
field_7E88 = nullptr;
field_7E8C = nullptr;
field_7E90 = nullptr;
field_7E94 = nullptr;
LOG_I("Generating world with seed: %d", seed);
for (int a = 0; a < 32; a++)
{
for (int b = 0; b < 32; b++)
{
field_9D8[a * 32 + b] = 0;
}
}
field_7280 = new float[1024];
Random random = m_random;
LOG_I("random.get : %d", random.genrand_int32() >> 1);
}
// @BUG: Potential collisions.
inline int GetChunkHash(int x, int z)
{
int v6 = (z & 0x7FFF) | ((x & 0x7FFF) << 16) | (x & 0x80000000), v7;
if (z >= 0)
v7 = 0;
else
v7 = 0x8000;
return v6 | v7;
}
LevelChunk* RandomLevelSource::getChunk(int x, int z)
{
int hashCode = GetChunkHash(x, z);
std::map<int, LevelChunk*>::iterator iter = m_chunks.find(hashCode);
if (iter != m_chunks.end())
return iter->second;
// have to generate the chunk
m_random.init_genrand(341872712 * x + 132899541 * z);
TileID* pLevelData = new TileID[32768];
LevelChunk* pChunk = new LevelChunk(m_pLevel, pLevelData, x, z);
m_chunks.insert(std::pair<int, LevelChunk*>(hashCode, pChunk));
Biome** pBiomeBlock = m_pLevel->getBiomeSource()->getBiomeBlock(16 * x, 16 * z, 16, 16);
prepareHeights(x, z, pLevelData, nullptr, m_pLevel->getBiomeSource()->field_4);
buildSurfaces(x, z, pLevelData, pBiomeBlock);
pChunk->recalcHeightmap();
// @NOTE: Java Edition Beta 1.6 uses the m_largeCaveFeature.
#ifdef TEST_CAVES
m_largeCaveFeature.apply(this, m_pLevel, x, z, pLevelData, 0);
#endif
return pChunk;
}
LevelChunk* RandomLevelSource::getChunkDontCreate(int x, int z)
{
int hashCode = GetChunkHash(x, z);
std::map<int, LevelChunk*>::iterator iter = m_chunks.find(hashCode);
if (iter != m_chunks.end())
return iter->second;
// have to create the chunk. Create an empty one
TileID* pLevelData = new TileID[32768];
memset (pLevelData, 0, sizeof *pLevelData * 32768);
LevelChunk* pChunk = new LevelChunk(m_pLevel, pLevelData, x, z);
m_chunks.insert(std::pair<int, LevelChunk*>(hashCode, pChunk));
return pChunk;
}
float* RandomLevelSource::getHeights(float* fptr, int a3, int a4, int a5, int a6, int a7, int a8)
{
if (fptr == nullptr)
{
fptr = new float[a6 * a7 * a8];
}
float* bsf4 = m_pLevel->getBiomeSource()->field_4;
float* bsf8 = m_pLevel->getBiomeSource()->field_8;
constexpr float C_MAGIC_1 = 684.412f;
field_7E90 = m_perlinNoise6.getRegion(field_7E90, a3, a5, a6, a8, 1.121f, 1.121f, 0.5f);
field_7E94 = m_perlinNoise7.getRegion(field_7E94, a3, a5, a6, a8, 200.0f, 200.0f, 0.5f);
field_7E84 = m_perlinNoise3.getRegion(field_7E84, float(a3), float(a4), float(a5), a6, a7, a8, 8.5552f, 4.2776f, 8.5552f);
field_7E88 = m_perlinNoise1.getRegion(field_7E88, float(a3), float(a4), float(a5), a6, a7, a8, C_MAGIC_1, C_MAGIC_1, C_MAGIC_1);
field_7E8C = m_perlinNoise2.getRegion(field_7E8C, float(a3), float(a4), float(a5), a6, a7, a8, C_MAGIC_1, C_MAGIC_1, C_MAGIC_1);
int k1 = 0;
int l1 = 0;
int i2 = 16 / a6;
for (int j2 = 0; j2 < a6; j2++)
{
int k2 = j2 * i2 + i2 / 2;
for (int l2 = 0; l2 < a8; l2++)
{
int i3 = l2 * i2 + i2 / 2;
float d2 = bsf4[k2 * 16 + i3];
float d3 = bsf8[k2 * 16 + i3] * d2;
float d4 = 1.0f - d3;
d4 *= d4;
d4 *= d4;
d4 = 1.0f - d4;
float d5 = (field_7E90[l1] + 256.0f) / 512.0f;
d5 *= d4;
if (d5 > 1.0f)
d5 = 1.0f;
float d6 = field_7E94[l1] / 8000.0f;
if (d6 < 0.0f)
{
d6 = -d6 * 0.3f;
}
d6 = d6 * 3.0f - 2.0f;
if (d6 < 0.0f)
{
d6 /= 2.0f;
if (d6 < -1.0f)
d6 = -1.0f;
d6 /= 1.4f;
d6 /= 2.0f;
d5 = 0.0f;
}
else
{
if (d6 > 1.0f)
d6 = 1.0f;
d6 /= 8.0f;
}
if (d5 < 0.0f)
d5 = 0.0f;
d5 += 0.5f;
d6 = (d6 * (float)a7) / 16.0f;
float d7 = (float)a7 / 2.0f + d6 * 4.0f;
l1++;
for (int j3 = 0; j3 < a7; j3++)
{
float d8 = 0.0f;
float d9 = (((float)j3 - d7) * 12.0f) / d5;
if (d9 < 0.0f)
{
d9 *= 4.0f;
}
float d10 = field_7E88[k1] / 512.0f;
float d11 = field_7E8C[k1] / 512.0f;
float d12 = (field_7E84[k1] / 10.0f + 1.0f) / 2.0f;
if (d12 < 0.0f)
d8 = d10;
else if (d12 > 1.0f)
d8 = d11;
else
d8 = d10 + (d11 - d10) * d12;
d8 -= d9;
if (j3 > a7 - 4)
{
float d13 = (float)(j3 - (a7 - 4)) / 3.0f;
d8 = d8 * (1.0f - d13) + -10.0f * d13;
}
fptr[k1] = d8;
k1++;
}
}
}
return fptr;
}
void RandomLevelSource::prepareHeights(int x, int z, TileID* tiles, void* huh, float* fptr)
{
field_7280 = getHeights(field_7280, x * 4, 0, z * 4, 5, 17, 5);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 16; k++)
{
float v24 = field_7280[85 * i + 17 * j + k];
float v23 = field_7280[85 * i + 17 + 17 * j + k];
float v22 = field_7280[85 * i + 85 + 17 * j + k];
float v21 = field_7280[85 * i + 102 + 17 * j + k];
float v20 = (field_7280[85 * i + 1 + 17 * j + k] - v24) * 0.125f;
float v19 = (field_7280[85 * i + 18 + 17 * j + k] - v23) * 0.125f;
float v18 = (field_7280[85 * i + 86 + 17 * j + k] - v22) * 0.125f;
float v17 = (field_7280[85 * i + 103 + 17 * j + k] - v21) * 0.125f;
for (int l = 0; l < 8; l++)
{
float v15 = v24;
float v14 = v23;
for (int m = 0; m < 4; m++)
{
int v12 = ((m + 4 * i) << 11) | (j << 9) | (l + 8 * k);
float v11 = v15;
for (int n = 0; n < 4; n++)
{
TileID tile = TILE_AIR;
if (8 * k + l < 64)
{
if (fptr[64 * i + 16 * m + 4 * j + n] < 0.5f && 8 * k + l == 63)
tile = Tile::ice->m_ID;
else
tile = Tile::calmWater->m_ID;
}
if (v11 > 0.0f)
tile = Tile::rock->m_ID;
tiles[v12] = tile;
v12 += 128;
v11 = v11 + (v14 - v15) * 0.25f;
}
v15 = v15 + (v22 - v24) * 0.25f;
v14 = v14 + (v21 - v23) * 0.25f;
}
v24 = v24 + v20;
v23 = v23 + v19;
v22 = v22 + v18;
v21 = v21 + v17;
}
}
}
}
}
void RandomLevelSource::buildSurfaces(int x, int z, TileID* tiles, Biome** biomes)
{
//return;
m_perlinNoise4.getRegion(field_7284, float(x) * 16.0f, float(z) * 16.0f, 0.0f, 16, 16, 1, 1.0f / 32.0f, 1.0f / 32.0f, 1.0f);
m_perlinNoise4.getRegion(field_7684, float(x) * 16.0f, 109.01f, float(z) * 16.0f, 16, 1, 16, 1.0f / 32.0f, 1.0f, 1.0f / 32.0f);
m_perlinNoise5.getRegion(field_7A84, float(x) * 16.0f, float(z) * 16.0f, 0.0f, 16, 16, 1, 1.0f / 16.0f, 1.0f / 16.0f, 1.0f / 16.0f);
// @NOTE: Again, extracted from Java Beta 1.6. Probably accurate
constexpr int byte0 = 64;
//constexpr float d = 0.03125f;
for (int k = 0; k < 16; k++)
{
for (int l = 0; l < 16; l++)
{
Biome* pBiome = biomes[k + l * 16];
bool flag = field_7284[k + l * 16] + m_random.nextFloat() * 0.2f > 0.0f;
bool flag1 = field_7684[k + l * 16] + m_random.nextFloat() * 0.2f > 3.0f;
int i1 = (int)(field_7A84[k + l * 16] / 3.0f + 3.0f + m_random.nextFloat() * 0.25f);
int j1 = -1;
TileID byte1 = pBiome->field_20;
TileID byte2 = pBiome->field_21;
for (int k1 = 127; k1 >= 0; k1--)
{
int l1 = (l * 16 + k) * 128 + k1;
if (k1 <= int(0 + m_random.nextInt(5)))
{
tiles[l1] = Tile::unbreakable->m_ID;
continue;
}
TileID byte3 = tiles[l1];
if (byte3 == 0)
{
j1 = -1;
continue;
}
if (byte3 != Tile::rock->m_ID)
continue;
if (j1 == -1)
{
if (i1 <= 0)
{
byte1 = 0;
byte2 = Tile::rock->m_ID;
}
else if (k1 >= byte0 - 4 && k1 <= byte0 + 1)
{
byte1 = pBiome->field_20;
byte2 = pBiome->field_21;
if (flag1) {
byte1 = 0;
byte2 = Tile::gravel->m_ID;
}
if (flag) {
byte1 = Tile::sand->m_ID;
byte2 = Tile::sand->m_ID;
}
}
if (k1 < byte0 && byte1 == 0)
byte1 = Tile::calmWater->m_ID;
j1 = i1;
if (k1 >= byte0 - 1)
tiles[l1] = byte1;
else
tiles[l1] = byte2;
continue;
}
if (j1 <= 0)
continue;
j1--;
tiles[l1] = byte2;
if (j1 == 0 && byte2 == Tile::sand->m_ID)
{
j1 = m_random.nextInt(4);
byte2 = Tile::sandStone->m_ID;
}
}
}
}
}
void RandomLevelSource::postProcess(ChunkSource* src, int x, int z)
{
m_pLevel->m_bPostProcessing = true;
SandTile::instaFall = true;
int x16 = x * 16, z16 = z * 16;
Biome* pBiome = m_pLevel->getBiomeSource()->getBiome(x16 + 16, z16 + 16);
TLong seed = m_pLevel->getSeed();
int xo, yo, zo;
m_random.setSeed(seed);
TLong x1 = 1 + 2 * (m_random.nextInt() / 2);
TLong x2 = 1 + 2 * (m_random.nextInt() / 2);
m_random.setSeed((TLong(x) * x1 + TLong(z) * x2) ^ seed);
// @NOTE: I can't put the random calls _in_ the argument list - args are evaluated right to left I believe
for (int i = 0; i < 10; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(128);
zo = m_random.nextInt(16);
ClayFeature(Tile::clay->m_ID, 32).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
// Start of ore generation
for (int i = 0; i < 20; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(128);
zo = m_random.nextInt(16);
OreFeature(Tile::dirt->m_ID, 32).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 10; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(128);
zo = m_random.nextInt(16);
OreFeature(Tile::gravel->m_ID, 32).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 20; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(128);
zo = m_random.nextInt(16);
OreFeature(Tile::coalOre->m_ID, 16).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 20; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(64);
zo = m_random.nextInt(16);
OreFeature(Tile::ironOre->m_ID, 8).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 2; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(32);
zo = m_random.nextInt(16);
OreFeature(Tile::goldOre->m_ID, 8).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 8; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(16);
zo = m_random.nextInt(16);
OreFeature(Tile::redStoneOre->m_ID, 7).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 1; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(16);
zo = m_random.nextInt(16);
OreFeature(Tile::emeraldOre->m_ID, 7).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
for (int i = 0; i < 1; i++)
{
xo = m_random.nextInt(16);
yo = m_random.nextInt(16) + m_random.nextInt(16);
zo = m_random.nextInt(16);
OreFeature(Tile::lapisOre->m_ID, 6).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
// End of ore generation
// Start of tree generation
float t1 = m_perlinNoise8.getValue(float(x16) / 2.0f, float(z16) / 2.0f);
float t2 = m_random.nextFloat();
int t3 = int((4.0f + t2 * 4.0f + t1 * 0.125f) / 3.0f);
int treeCount = 0;
if (m_random.nextInt(10) == 0)
treeCount++;
if (pBiome == Biome::forest)
treeCount += t3 + 2;
if (pBiome == Biome::rainForest)
treeCount += t3 + 2;
if (pBiome == Biome::seasonalForest)
treeCount += t3 + 1;
if (pBiome == Biome::taiga)
treeCount += t3 + 1;
if (pBiome == Biome::desert)
treeCount -= 20;
if (pBiome == Biome::tundra)
treeCount -= 20;
if (pBiome == Biome::plains)
treeCount -= 20;
for (int i = 0; i < treeCount; i++)
{
int rngX = m_random.nextInt(16) + x16 + 8;
int rngZ = m_random.nextInt(16) + z16 + 8;
int height = m_pLevel->getHeightmap(rngX, rngZ);
Feature* pTreeFeature = pBiome->getTreeFeature(&m_random);
if (pTreeFeature)
{
pTreeFeature->init(1.0f, 1.0f, 1.0f);
pTreeFeature->place(m_pLevel, &m_random, rngX, height, rngZ);
delete pTreeFeature;
}
}
for (int i = 0; i < 2; i++)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(128);
int zo = m_random.nextInt(16);
FlowerFeature(Tile::flower->m_ID).place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
if (m_random.nextInt(2) == 0)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(128);
int zo = m_random.nextInt(16);
FlowerFeature(Tile::rose->m_ID).place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
if (m_random.nextInt(4) == 0)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(128);
int zo = m_random.nextInt(16);
FlowerFeature(Tile::mushroom1->m_ID).place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
if (m_random.nextInt(8) == 0)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(128);
int zo = m_random.nextInt(16);
FlowerFeature(Tile::mushroom2->m_ID).place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
for (int i = 0; i < 10; i++)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(128);
int zo = m_random.nextInt(16);
ReedsFeature().place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
for (int i = 0; i < 50; i++)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(m_random.nextInt(120) + 8);
int zo = m_random.nextInt(16);
SpringFeature(Tile::water->m_ID).place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
for (int i = 0; i < 20; i++)
{
int xo = m_random.nextInt(16);
int yo = m_random.nextInt(m_random.nextInt(m_random.nextInt(112) + 8) + 8);
int zo = m_random.nextInt(16);
SpringFeature(Tile::lava->m_ID).place(m_pLevel, &m_random, x16 + 8 + xo, yo, z16 + 8 + zo);
}
float* tempBlock = m_pLevel->getBiomeSource()->getTemperatureBlock(x16 + 8, z16 + 8, 16, 16);
for (int j19 = x16 + 8; j19 < x16 + 8 + 16; j19++)
{
for (int j22 = z16 + 8; j22 < z16 + 8 + 16; j22++)
{
int i24 = j19 - (x16 + 8);
int j25 = j22 - (z16 + 8);
int tsb = m_pLevel->getTopSolidBlock(j19, j22);
if (SNOW_CUTOFF > (tempBlock[i24 * 16 + j25] - SNOW_SCALE * (float(tsb - 64) / 64.0f)))
{
if (tsb >= 0 && tsb < C_MAX_Y && m_pLevel->isEmptyTile(j19, tsb, j22))
{
if (m_pLevel->getMaterial(j19, tsb - 1, j22)->blocksMotion() &&
m_pLevel->getMaterial(j19, tsb - 1, j22) != Material::ice)
{
m_pLevel->setTile(j19, tsb, j22, Tile::topSnow->m_ID);
}
}
}
}
}
SandTile::instaFall = false;
m_pLevel->m_bPostProcessing = false;
}
int RandomLevelSource::tick()
{
return 0;
}
bool RandomLevelSource::shouldSave()
{
return true;
}
bool RandomLevelSource::hasChunk(int x, int z)
{
return true;
}
LevelChunk* RandomLevelSource::create(int x, int z)
{
return getChunk(x, z);
}
std::string RandomLevelSource::gatherStats()
{
return "RandomLevelSource";
}