* Initial commit.

:)
This commit is contained in:
iProgramInCpp
2023-07-30 22:22:02 +03:00
commit 9642818a88
613 changed files with 151754 additions and 0 deletions

View File

@@ -0,0 +1,219 @@
#include "Biome.hpp"
#include "Tile.hpp"
Biome
* Biome::rainForest,
* Biome::swampland,
* Biome::seasonalForest,
* Biome::forest,
* Biome::savanna,
* Biome::shrubland,
* Biome::taiga,
* Biome::desert,
* Biome::plains,
* Biome::iceDesert,
* Biome::tundra;
Biome* Biome::map[4096];
Biome* Biome::_getBiome(float hum, float temp)
{
if (temp < 0.1)
return tundra;
float ht = hum * temp;
if (ht < 0.2f)
{
if (temp >= 0.5f)
{
if (temp >= 0.95f)
return desert;
return savanna;
}
return tundra;
}
if (ht > 0.5f && ht < 0.7f)
return swampland;
if (temp < 0.5f)
return taiga;
if (temp >= 0.97f)
{
if (ht < 0.45f)
return plains;
if (ht < 0.9f)
return seasonalForest;
return rainForest;
}
if (temp >= 0.35f)
return forest;
return shrubland;
}
float Biome::adjustDepth(float f)
{
return f;
}
float Biome::adjustScale(float f)
{
return f;
}
int Biome::getSkyColor(float x)
{
return 0x80808080;
}
Biome* Biome::getBiome(float hum, float temp)
{
int i = int(63.0f * hum);
int j = int(63.0f * temp);
return map[i + j * 64];
}
void Biome::recalc()
{
for (int i = 0; i < 64; i++)
{
for (int j = 0; j < 64; j++)
{
map[i + j * 64] = _getBiome(float(i) / 63, float(j) / 63);
}
}
desert->field_20 = desert->field_21 = Tile::sand->m_ID;
iceDesert->field_20 = iceDesert->field_21 = Tile::sand->m_ID;
}
Biome::Biome()
{
m_name = "";
field_20 = Tile::grass->m_ID;
field_21 = Tile::dirt->m_ID;
}
Biome::~Biome()
{
}
Feature* Biome::getTreeFeature(Random* pRandom)
{
pRandom->genrand_int32(); //! unused result
return new TreeFeature;
}
Biome* Biome::setSnowCovered()
{
return this;
}
Biome* Biome::setColor(int color)
{
m_Color = color;
return this;
}
Biome* Biome::setLeafColor(int color)
{
m_LeafColor = color;
return this;
}
Biome* Biome::setName(const std::string & name)
{
m_name = name;
return this;
}
void Biome::initBiomes()
{
rainForest = (new RainforestBiome)
->setColor(0x08FA36)
->setName("Rainforest")
->setLeafColor(0x1FF458);
swampland = (new SwampBiome)
->setColor(0x07F9B2)
->setName("Swampland")
->setLeafColor(0x8BAF48);
seasonalForest = (new Biome)
->setColor(0x9BE023)
->setName("Seasonal Forest");
forest = (new ForestBiome)
->setColor(0x056621)
->setName("Forest")
->setLeafColor(0x4EBA31);
savanna = (new FlatBiome)
->setColor(0xD9E023)
->setName("Savanna");
shrubland = (new Biome)
->setColor(0xA1AD20)
->setName("Shrubland");
taiga = (new TaigaBiome)
->setColor(0x2EB153)
->setName("Taiga")
->setSnowCovered()
->setLeafColor(0x7BB731);
desert = (new FlatBiome)
->setColor(0xFA9418)
->setName("Desert");
plains = (new FlatBiome)
->setColor(0xFFD910)
->setName("Plains");
iceDesert = (new FlatBiome)
->setColor(0xFFED93)
->setName("Ice Desert")
->setSnowCovered()
->setLeafColor(0xC4D339);
tundra = (new Biome)
->setColor(0x57EBF9)
->setName("Tundra")
->setSnowCovered()
->setLeafColor(0xC4D339);
recalc();
}
Feature* RainforestBiome::getTreeFeature(Random* pRandom)
{
pRandom->genrand_int32(); //! unused result
return new TreeFeature;
}
Feature* ForestBiome::getTreeFeature(Random* pRandom)
{
if (pRandom->nextInt(5) == 0)
return new BirchFeature;
// @NOTE: Here would be code for a big tree with random->nextInt(3).
// But PE doesn't have big trees
return new TreeFeature;
}
Feature* TaigaBiome::getTreeFeature(Random* pRandom)
{
if (pRandom->nextInt(3) == 0)
return new PineFeature;
return new SpruceFeature;
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include <string>
#include "Feature.hpp"
#include "PerlinNoise.hpp"
#include "Utils.hpp"
class Biome
{
public: // Static Functions
static Biome* getBiome(float hum, float temp);
static void initBiomes();
public: // Virtual functions
virtual float adjustDepth(float depth);
virtual float adjustScale(float scale);
virtual int getSkyColor(float x);
virtual Feature* getTreeFeature(Random*);
public: // Instance Functions
Biome();
virtual ~Biome();
Biome* setColor(int color);
Biome* setLeafColor(int color);
Biome* setName(const std::string& name);
Biome* setSnowCovered();
public: // Static Variables
static Biome
* rainForest,
* swampland,
* seasonalForest,
* forest,
* savanna,
* shrubland,
* taiga,
* desert,
* plains,
* iceDesert,
* tundra;
static Biome* map[4096];
private: // Private Functions
static Biome* _getBiome(float hum, float temp);
static void recalc();
public: // Instance Variables
std::string m_name = "";
int m_Color = 0;
TileID field_20 = 0;
TileID field_21 = 0;
int m_LeafColor = 0;
};
class RainforestBiome : public Biome
{
public:
Feature* getTreeFeature(Random*) override;
};
class SwampBiome : public Biome
{
};
class ForestBiome : public Biome
{
public:
Feature* getTreeFeature(Random*) override;
};
class TaigaBiome : public Biome
{
public:
Feature* getTreeFeature(Random*) override;
};
class FlatBiome : public Biome
{
};

View File

@@ -0,0 +1,117 @@
#include "BiomeSource.hpp"
#include "Level.hpp"
BiomeSource::BiomeSource()
{
field_20 = new Biome*[256];
}
BiomeSource::BiomeSource(Level* pLevel) :
m_Random1(pLevel->getSeed() * 9871),
m_Random2(pLevel->getSeed() * 39811),
m_Random3(pLevel->getSeed() * 543321)
{
m_pPerlinNoise[0] = new PerlinNoise(&m_Random1, 4);
m_pPerlinNoise[1] = new PerlinNoise(&m_Random2, 4);
m_pPerlinNoise[2] = new PerlinNoise(&m_Random3, 2);
field_20 = new Biome*[256];
field_4 = new float[256];
}
Biome* BiomeSource::getBiome(ChunkPos& pos)
{
// @BUG: Shifting right by 4 instead of left. In Java Edition, a shift left by 4 is performed instead.
return getBiome(pos.x >> 4, pos.z >> 4);
}
Biome* BiomeSource::getBiome(int a, int b)
{
return *getBiomeBlock(a, b, 1, 1);
}
Biome** BiomeSource::getBiomeBlock(int a, int b, int c, int d)
{
return getBiomeBlock(field_20, a, b, c, d);
}
Biome** BiomeSource::getBiomeBlock(Biome** pBiomes, int a, int b, int c, int d)
{
field_4 = m_pPerlinNoise[0]->getRegion(field_4, a, b, c, c, 0.025f, 0.025f, 0.25f);
field_8 = m_pPerlinNoise[1]->getRegion(field_8, a, b, c, c, 0.05f, 0.05f, 0.3333f);
field_C = m_pPerlinNoise[2]->getRegion(field_C, a, b, c, c, 0.25f, 0.25f, 0.588f);
int index = 0;
for (int i = 0; i < c; i++)
{
for (int j = 0; j < d; j++)
{
float d = field_C[index] * 1.1f + 0.5f;
float d1 = 0.01f;
float d2 = 1.0f - d1;
float d3 = (field_4[index] * 0.15f + 0.7f) * d2 + d * d1;
d1 = 0.002f;
d2 = 1.0f - d1;
float d4 = (field_8[index] * 0.15f + 0.5f) * d2 + d * d1;
d3 = 1.0f - (1.0f - d3) * (1.0f - d3);
if (d3 < 0.0f) d3 = 0.0f;
if (d4 < 0.0f) d4 = 0.0f;
if (d3 > 1.0f) d3 = 1.0f;
if (d4 > 1.0f) d4 = 1.0f;
field_4[index] = d3;
field_8[index] = d4;
field_20[index++] = Biome::getBiome(d3, d4);
}
}
return field_20;
}
float* BiomeSource::getTemperatureBlock(int a, int b, int c, int d)
{
field_4 = m_pPerlinNoise[0]->getRegion(field_4, a, b, c, d, 0.025f, 0.025f, 0.25f);
field_C = m_pPerlinNoise[2]->getRegion(field_C, a, b, c, d, 0.25f, 0.25f, 0.588f);
int index = 0;
for (int i = 0; i < c; i++)
{
for (int j = 0; j < d; j++)
{
float d = field_C[index] * 1.1f + 0.5f;
float d1 = 0.01f;
float d2 = 1.0f - d1;
float d3 = (field_4[index] * 0.15f + 0.7f) * d2 + d * d1;
d3 = 1.0f - (1.0f - d3) * (1.0f - d3);
if (d3 < 0.0f)
d3 = 0.0f;
if (d3 > 1.0f)
d3 = 1.0f;
field_4[index++] = d3;
}
}
return field_4;
}
BiomeSource::~BiomeSource()
{
for (int i = 0; i < 3; i++)
if (m_pPerlinNoise[i])
delete m_pPerlinNoise[i];
if (field_4)
delete[] field_4;
if (field_8)
delete[] field_8;
if (field_C)
delete[] field_C;
if (field_20)
delete[] field_20;
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include "Biome.hpp"
class Level;
class BiomeSource
{
public:
BiomeSource(Level*);
BiomeSource();
virtual ~BiomeSource();
virtual Biome* getBiome(ChunkPos&);
virtual Biome* getBiome(int, int);
virtual Biome** getBiomeBlock(int, int, int, int);
virtual Biome** getBiomeBlock(Biome**, int, int, int, int);
virtual float* getTemperatureBlock(int, int, int, int);
public:
float* field_4 = nullptr;
float* field_8 = nullptr;
float* field_C = nullptr;
int field_10 = 0;
int field_14 = 0;
int field_18 = 0;
int field_1C = 0;
Biome** field_20 = nullptr;
PerlinNoise* m_pPerlinNoise[3] = { nullptr };
Random m_Random1;
Random m_Random2;
Random m_Random3;
};

View File

@@ -0,0 +1,100 @@
#include "Feature.hpp"
#include "Level.hpp"
bool BirchFeature::place(Level* level, Random* random, int x, int y, int z)
{
if (y <= C_MIN_Y)
return false;
int treeHeight = int(random->nextInt(3) + 5); // Between 5 and 7 blocks tall.
if (y + treeHeight >= C_MAX_Y)
return false;
// Ensure that we can place this tree
bool bCanPlace = true;
for (int ay = y; ay <= y + treeHeight; ay++)
{
int x1 = 1;
if (y + treeHeight - 1 <= ay)
x1 = 2;
else if (ay == y)
x1 = 0;
for (int ax = x - x1; ax <= x + x1 && bCanPlace; ax++)
{
for (int az = z - x1; az <= z + x1 && bCanPlace; az++)
{
if (ay < C_MIN_Y || ay >= C_MAX_Y)
{
bCanPlace = false;
break;
}
TileID tile = level->getTile(ax, ay, az);
// other trees can overlap with this one, apparently
if (tile != TILE_AIR && tile != Tile::leaves->m_ID)
{
bCanPlace = false;
break;
}
}
}
}
// If not, let our caller know.
if (!bCanPlace)
return false;
TileID tileBelow = level->getTile(x, y - 1, z);
// If grass or dirt aren't below us, we can't possibly grow!
if (tileBelow != Tile::grass->m_ID && tileBelow != Tile::dirt->m_ID)
return false;
// @NOTE: Redundant check
if (y >= C_MAX_Y - treeHeight)
return false;
level->setTileNoUpdate(x, y - 1, z, Tile::dirt->m_ID);
int upperY = y + treeHeight;
int lowerY = y + treeHeight - 3;
int diff = lowerY - upperY;
for (int i = lowerY; i <= upperY; i++, diff = i - upperY)
{
int c1 = 1 - diff / 2;
int c2 = diff / 2 - 1;
for (int ax = x - c1; ax <= x + c1; ax++)
{
int c3 = c2;
int c4 = diff / 2 - 1;
if (c2 < 0)
c3 = -c2;
int c5 = c3;
for (int az = z - c1; az <= z + c1; az++, c4++)
{
if ((abs(ax - x) != c1 || abs(az - z) != c1 || random->nextInt(2) != 0 && diff != 0) && !Tile::solid[level->getTile(ax, i, az)])
{
level->setTileAndDataNoUpdate(ax, i, az, Tile::leaves->m_ID, 2);
}
}
}
}
for (int i = 0; i < treeHeight; i++)
{
int y1 = i + y;
TileID tile = level->getTile(x, y + i, z);
if (tile && tile != Tile::leaves->m_ID)
continue;
level->setTileAndDataNoUpdate(x, y + i, z, Tile::treeTrunk->m_ID, 2);
}
return true;
}

View File

@@ -0,0 +1,214 @@
#include "LevelChunk.hpp"
#include "ChunkCache.hpp"
#include "Level.hpp"
ChunkCache::ChunkCache(Level* pLevel, ChunkStorage* pStor, ChunkSource* pSrc)
{
m_pChunkSource = pSrc;
m_pChunkStorage = pStor;
m_pLevel = pLevel;
m_pEmptyChunk = new EmptyLevelChunk(pLevel, nullptr, 0, 0);
m_pEmptyChunk->field_236 = true;
memset(m_chunkMap, 0, sizeof(m_chunkMap));
}
LevelChunk* ChunkCache::create(int x, int z)
{
return getChunk(x, z);
}
LevelChunk* ChunkCache::getChunk(int x, int z)
{
// get the last chunk quickly if needed
if (m_LastChunkX == x && m_LastChunkZ == z)
{
if (m_pLastChunk)
return m_pLastChunk;
}
if (x < 0 || z < 0 || x >= C_MAX_CHUNKS_Z || z >= C_MAX_CHUNKS_X)
return m_pEmptyChunk;
if (!hasChunk(x, z))
{
LevelChunk* pOldChunk = m_chunkMap[z][x];
if (pOldChunk)
{
pOldChunk->unload();
save(pOldChunk);
if (m_pChunkStorage)
m_pChunkStorage->saveEntities(m_pLevel, pOldChunk);
}
LevelChunk* pChunk = load(x, z);
if (pChunk)
{
m_chunkMap[z][x] = pChunk;
pChunk->lightLava();
int globalX = x * 16, globalZ = z * 16;
for (int i = globalX, m = 0; m < 16; i++, m++)
{
for (int j = globalZ, n = 0; n < 16; j++, n++)
{
int height = m_pLevel->getHeightmap(i, j);
if (height >= 0)
{
for (int k = height; k > 0; k--)
{
m_pLevel->updateLight(LightLayer::Sky, globalX, k, globalZ, globalX, k, globalZ);
m_pLevel->updateLight(LightLayer::Block, globalX-1, k, globalZ-1, globalX+1, k, globalZ+1);
}
}
}
}
}
else
{
pChunk = m_pEmptyChunk;
if (m_pChunkSource)
pChunk = m_pChunkSource->getChunk(x, z);
m_chunkMap[z][x] = pChunk;
pChunk->lightLava();
}
pChunk = m_chunkMap[z][x];
if (pChunk)
pChunk->load();
if (!pChunk->field_234 && hasChunk(x + 1, z + 1) && hasChunk(x, z + 1) && hasChunk(x + 1, z))
postProcess(this, x, z);
//@OVERSIGHT: redundant call twice to hasChunk(x-1, z), hasChunk(x,z-1), and hasChunk(x-1,z-1)
if (hasChunk(x - 1, z) && !getChunk(x - 1, z)->field_234 && hasChunk(x - 1, z + 1) && hasChunk(x, z + 1) && hasChunk(x - 1, z))
postProcess(this, x - 1, z);
if (hasChunk(x, z - 1) && !getChunk(x, z - 1)->field_234 && hasChunk(x + 1, z - 1) && hasChunk(x + 1, z) && hasChunk(x, z - 1))
postProcess(this, x, z - 1);
if (hasChunk(x - 1, z - 1) && !getChunk(x - 1, z - 1)->field_234 && hasChunk(x - 1, z - 1) && hasChunk(x, z - 1) && hasChunk(x - 1, z))
postProcess(this, x - 1, z - 1);
}
m_LastChunkX = x;
m_LastChunkZ = z;
m_pLastChunk = m_chunkMap[z][x];
return m_chunkMap[z][x];
}
bool ChunkCache::hasChunk(int x, int z)
{
if (x < 0 || z < 0)
return true;
if (x >= C_MAX_CHUNKS_X || z >= C_MAX_CHUNKS_Z)
return true;
if (m_LastChunkX == x && m_LastChunkZ == z)
return true;
LevelChunk* pChunk = m_chunkMap[z][x];
if (!pChunk)
return false;
if (pChunk == m_pEmptyChunk)
return true;
return pChunk->isAt(x, z);
}
int ChunkCache::tick()
{
if (m_pChunkStorage)
m_pChunkStorage->tick();
return m_pChunkSource->tick();
}
void ChunkCache::postProcess(ChunkSource* pChkSrc, int x, int z)
{
if (x < 0 || z < 0 || x >= C_MAX_CHUNKS_X || z >= C_MAX_CHUNKS_Z)
return;
LevelChunk* pChunk = getChunk(x, z);
if (!pChunk->field_234)
{
pChunk->field_234 = 1;
if (m_pChunkSource)
{
m_pChunkSource->postProcess(m_pChunkSource, x, z);
pChunk->clearUpdateMap();
}
}
}
void ChunkCache::save(LevelChunk* pChunk)
{
if (m_pChunkStorage)
{
pChunk->field_23C = m_pLevel->getTime();
m_pChunkStorage->save(m_pLevel, pChunk);
}
}
void ChunkCache::saveAll()
{
if (!m_pChunkStorage) return;
std::vector<LevelChunk*> chunksToSave;
for (int i = 0; i < C_MAX_CHUNKS_Z; i++)
{
for (int j = 0; j < C_MAX_CHUNKS_X; j++)
{
chunksToSave.push_back(m_pLevel->getChunk(j, i));
}
}
m_pChunkStorage->saveAll(m_pLevel, chunksToSave);
}
bool ChunkCache::shouldSave()
{
return true;
}
std::string ChunkCache::gatherStats()
{
return "ChunkCache: 1024";
}
ChunkCache::~ChunkCache()
{
SAFE_DELETE(m_pChunkSource);
SAFE_DELETE(m_pEmptyChunk);
for (int i = 0; i < 16; i++)
for (int j = 0; j < 16; j++)
{
LevelChunk* pChk = m_chunkMap[i][j];
if (pChk)
{
pChk->deleteBlockData();
SAFE_DELETE(pChk);
}
}
}
LevelChunk* ChunkCache::load(int x, int z)
{
if (!m_pChunkStorage)
return m_pEmptyChunk;
if (x < 0 || z < 0 || x >= C_MAX_CHUNKS_X || z >= C_MAX_CHUNKS_Z)
return m_pEmptyChunk;
LevelChunk* pChk = m_pChunkStorage->load(m_pLevel, x, z);
if (pChk)
pChk->field_23C = m_pLevel->getTime();
return pChk;
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include "ChunkSource.hpp"
#include "ChunkStorage.hpp"
class Level;
class ChunkCache : public ChunkSource
{
public:
ChunkCache(Level*, ChunkStorage*, ChunkSource*);
virtual ~ChunkCache();
LevelChunk* create(int x, int z) override;
LevelChunk* getChunk(int x, int z) override;
bool hasChunk(int x, int z) override;
std::string gatherStats() override;
void postProcess(ChunkSource*, int, int) override;
bool shouldSave() override;
void saveAll() override;
int tick() override;
LevelChunk* load(int, int);
void save(LevelChunk*);
public:
bool field_4 = true;
LevelChunk* m_pEmptyChunk = nullptr;
ChunkSource* m_pChunkSource = nullptr;
ChunkStorage* m_pChunkStorage = nullptr;
LevelChunk* m_chunkMap[C_MAX_CHUNKS_Z][C_MAX_CHUNKS_X];
Level* m_pLevel = nullptr;
LevelChunk* m_pLastChunk = nullptr;
int m_LastChunkX = -999999999;
int m_LastChunkZ = -999999999;
};

View File

@@ -0,0 +1,11 @@
#include "ChunkSource.hpp"
#include "Level.hpp"
void ChunkSource::saveAll()
{
}
ChunkSource::~ChunkSource()
{
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <string>
class Level;
class LevelChunk;
class ChunkSource
{
public:
virtual ~ChunkSource();
virtual bool hasChunk(int, int) = 0;
virtual LevelChunk* getChunk(int, int) = 0;
virtual LevelChunk* create(int, int) = 0;
virtual void postProcess(ChunkSource*, int, int) = 0;
virtual int tick() = 0;
virtual bool shouldSave() = 0;
virtual void saveAll();
virtual std::string gatherStats() = 0;
};

View File

@@ -0,0 +1,67 @@
#include "Feature.hpp"
#include "Level.hpp"
ClayFeature::ClayFeature(int id, int count)
{
m_ID = id;
m_count = count;
}
bool ClayFeature::place(Level* level, Random* random, int x, int y, int z)
{
if (level->getMaterial(x, y, z) != Material::water)
{
return false;
}
// @NOTE: This appears to be pretty much the same as the ore feature.
float fAng = random->nextFloat() * float(M_PI);
float d0 = float(x + 8) + 0.125f * float(m_count) * Mth::sin(fAng);
float d1 = float(x + 8) - 0.125f * float(m_count) * Mth::sin(fAng);
float d2 = float(z + 8) - 0.125f * float(m_count) * Mth::cos(fAng);
float d3 = float(z + 8) - 0.125f * float(m_count) * Mth::cos(fAng);
float d4 = float(y + random->nextInt(3) + 2);
float d5 = float(y + random->nextInt(3) + 2);
for (int i = 0; i <= m_count; i++)
{
float d6 = d0 + ((d1 - d0) * float(i)) / float(m_count);
float d7 = d4 + ((d5 - d4) * float(i)) / float(m_count);
float d8 = d2 + ((d3 - d2) * float(i)) / float(m_count);
float d9 = (random->nextFloat() * float(m_count)) / 16.0f;
// @NOTE: seems to be calculated twice??
float radius_1 = float(Mth::sin((float(i) * float(M_PI)) / float(m_count)) + 1.0f) * d9 + 1.0f;
float radius_2 = float(Mth::sin((float(i) * float(M_PI)) / float(m_count)) + 1.0f) * d9 + 1.0f;
int minX = int(d6 - radius_1 / 2.0f);
int maxX = int(d6 + radius_1 / 2.0f);
int minY = int(d7 - radius_2 / 2.0f);
int maxY = int(d7 + radius_2 / 2.0f);
int minZ = int(d6 - radius_1 / 2.0f);
int maxZ = int(d6 + radius_1 / 2.0f);
for (int cx = minX; cx <= maxX; cx++)
{
for (int cy = minY; cy <= maxY; cy++)
{
for (int cz = minZ; cz <= maxZ; cz++)
{
float d12 = ((float(cx) + 0.5f) - d6) / (radius_1 / 2.0f);
float d13 = ((float(cy) + 0.5f) - d7) / (radius_2 / 2.0f);
float d14 = ((float(cz) + 0.5f) - d8) / (radius_1 / 2.0f);
if (d12 * d12 + d13 * d13 + d14 * d14 >= 1.0f)
continue;
if (level->getTile(cx, cy, cz) == Tile::sand->m_ID)
level->setTileNoUpdate(cx, cy, cz, m_ID);
}
}
}
}
return true;
}

View File

@@ -0,0 +1,11 @@
#include "Feature.hpp"
void Feature::init(float a, float b, float c)
{
}
Feature::~Feature()
{
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include "Random.hpp"
class Level;
class Feature
{
public:
virtual ~Feature();
virtual bool place(Level*, Random*, int, int, int) = 0;
virtual void init(float, float, float);
};
class TreeFeature : public Feature
{
public:
bool place(Level*, Random*, int x, int y, int z) override;
};
class BirchFeature : public Feature
{
public:
bool place(Level*, Random*, int x, int y, int z) override;
};
class SpruceFeature : public Feature
{
public:
bool place(Level*, Random*, int x, int y, int z) override;
};
class PineFeature : public Feature
{
public:
bool place(Level*, Random*, int x, int y, int z) override;
};
class FlowerFeature : public Feature
{
public:
FlowerFeature(int id);
bool place(Level*, Random*, int x, int y, int z) override;
private:
int m_ID;
};
class SpringFeature : public Feature
{
public:
SpringFeature(int id);
bool place(Level*, Random*, int x, int y, int z) override;
private:
int m_ID;
};
class ClayFeature : public Feature
{
public:
ClayFeature(int id, int count);
bool place(Level*, Random*, int x, int y, int z) override;
private:
int m_ID;
int m_count;
};
class OreFeature : public Feature
{
public:
OreFeature(int id, int count);
bool place(Level*, Random*, int x, int y, int z) override;
private:
int m_ID;
int m_count;
};

View File

@@ -0,0 +1,22 @@
#include "Feature.hpp"
#include "Level.hpp"
FlowerFeature::FlowerFeature(int id)
{
m_ID = id;
}
bool FlowerFeature::place(Level* level, Random* random, int x, int y, int z)
{
for (int i = 0; i < 64; i++)
{
int nX = x + random->nextInt(8) - random->nextInt(8);
int nY = y + random->nextInt(4) - random->nextInt(4);
int nZ = z + random->nextInt(8) - random->nextInt(8);
if (level->isEmptyTile(nX, nY, nZ) && Tile::tiles[m_ID]->canSurvive(level, nX, nY, nZ))
level->setTileNoUpdate(nX, nY, nZ, m_ID);
}
return true;
}

View File

@@ -0,0 +1,67 @@
#include "Feature.hpp"
#include "Level.hpp"
OreFeature::OreFeature(int id, int count)
{
m_ID = id;
m_count = count;
}
bool OreFeature::place(Level* level, Random* random, int x, int y, int z)
{
float fAng = random->nextFloat() * float(M_PI);
float d0 = float(x + 8) + 0.125f * float(m_count) * Mth::sin(fAng);
float d1 = float(x + 8) - 0.125f * float(m_count) * Mth::sin(fAng);
float d2 = float(z + 8) - 0.125f * float(m_count) * Mth::cos(fAng);
float d3 = float(z + 8) - 0.125f * float(m_count) * Mth::cos(fAng);
float d4 = float(random->nextInt(3) + y + 2);
float d5 = float(random->nextInt(3) + y + 2);
// @NOTE: 1 extra block
for (int i = 0; i <= m_count; i++)
{
float d6 = d0 + ((d1 - d0) * float(i)) / float(m_count);
float d7 = d4 + ((d5 - d4) * float(i)) / float(m_count);
float d8 = d2 + ((d3 - d2) * float(i)) / float(m_count);
float d9 = (random->nextFloat() * float(m_count)) / 16.0f;
// @NOTE: seems to be calculated twice??
float radius_1 = (Mth::sin((float(M_PI * i)) / float(m_count)) + 1.0f) * d9 + 1.0f;
float radius_2 = (Mth::sin((float(M_PI * i)) / float(m_count)) + 1.0f) * d9 + 1.0f;
int minX = int(d6 - radius_1 / 2.0f);
int minY = int(d7 - radius_2 / 2.0f);
int minZ = int(d8 - radius_1 / 2.0f);
int maxX = int(d6 + radius_1 / 2.0f);
int maxY = int(d7 + radius_2 / 2.0f);
int maxZ = int(d8 + radius_1 / 2.0f);
for (int cx = minX; cx <= maxX; cx++)
{
float distX = ((float(cx) + 0.5f) - d6) / (radius_1 / 2.0f);
if (distX * distX >= 1.0f)
continue;
for (int cy = minY; cy <= maxY; cy++)
{
float distY = ((float(cy) + 0.5f) - d7) / (radius_2 / 2.0f);
if (distX * distX + distY * distY >= 1.0f)
continue;
for (int cz = minZ; cz <= maxZ; cz++)
{
float distZ = ((float(cz) + 0.5f) - d8) / (radius_1 / 2.0f);
if (distX * distX + distY * distY + distZ * distZ >= 1.0f)
continue;
if (level->getTile(cx, cy, cz) == Tile::rock->m_ID)
level->setTileNoUpdate(cx, cy, cz, m_ID);
}
}
}
}
return true;
}

View File

@@ -0,0 +1,71 @@
#include "PerformanceTestChunkSource.hpp"
#include "Level.hpp"
LevelChunk* PerformanceTestChunkSource::create(int x, int z)
{
TileID* pData = new TileID[0x8000u];
memset(pData, 0, 0x8000u * sizeof(TileID));
for (int i = 0; i != 65; i++)
{
if (i > 59)
{
// alternating columns of dirt
for (int j = 0; j < 16; j += 2)
{
for (int k = 0; k < 16; k += 2)
{
pData[i | (j << 11) | (k << 7)] = 3; // dirt
}
}
}
else
{
// alternating checkerboard patterns of dirt
for (int j = (~i) & 1; j < 16; j += 2)
{
for (int k = i & 1; k < 16; k += 2)
{
pData[i | (j << 11) | (k << 7)] = 3; // dirt
}
}
}
}
LevelChunk* pChunk = new LevelChunk(m_pLevel, pData, x, z);
pChunk->recalcHeightmap();
return pChunk;
}
std::string PerformanceTestChunkSource::gatherStats()
{
return "PerformanceTestChunkSource";
}
// this sucks because it creates a new chunk every call to this function...
LevelChunk* PerformanceTestChunkSource::getChunk(int x, int z)
{
return create(x, z);
}
bool PerformanceTestChunkSource::hasChunk(int x, int z)
{
return true;
}
void PerformanceTestChunkSource::postProcess(ChunkSource* a, int b, int c)
{
}
bool PerformanceTestChunkSource::shouldSave()
{
return false;
}
int PerformanceTestChunkSource::tick()
{
return 0;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "ChunkSource.hpp"
class Level;
class PerformanceTestChunkSource : public ChunkSource
{
public:
LevelChunk* create(int x, int z) override;
LevelChunk* getChunk(int x, int z) override;
bool hasChunk(int x, int z) override;
std::string gatherStats() override;
void postProcess(ChunkSource*, int, int) override;
bool shouldSave() override;
int tick() override;
public:
Level* m_pLevel = nullptr;
};

View File

@@ -0,0 +1,95 @@
#include "Feature.hpp"
#include "Level.hpp"
bool PineFeature::place(Level* level, Random* random, int x, int y, int z)
{
if (y <= C_MIN_Y)
return false;
int height = random->nextInt(5) + 7;
if (height + y >= C_MAX_Y)
return false;
int x1 = height - random->nextInt(2) - 3;
int p1 = height - x1;
int x2 = 1 + random->nextInt(1 + x1);
int upperY = y + 1 + height;
bool bCanPlace = true;
for (int i = 0, cy = y; cy <= upperY && bCanPlace; i++, cy++)
{
int range = 1;
if (cy - y < x1)
range = 0;
else
range = x2;
for (int cx = x - range; cx <= x + range && bCanPlace; cx++)
{
for (int cz = z - range; cz <= z + range && bCanPlace; cz++)
{
if (cy >= C_MAX_Y || cy < C_MIN_Y)
{
bCanPlace = false;
break;
}
TileID tile = level->getTile(cx, cy, cz);
if (tile != TILE_AIR && tile != Tile::leaves->m_ID)
bCanPlace = false;
}
}
}
if (!bCanPlace)
return false;
TileID tileBelow = level->getTile(x, y - 1, z);
if (tileBelow != Tile::grass->m_ID && tileBelow != Tile::dirt->m_ID)
return false;
if (y >= C_MAX_Y - 1 - height)
return false;
level->setTileNoUpdate(x, y - 1, z, Tile::dirt->m_ID);
int range = 0;
for (int cy = 0; cy <= height - x1; cy++)
{
int b1 = height + y - cy;
for (int cx = x - range; cx <= x + range; cx++)
{
int dx = cx - x;
for (int cz = z - range; cz <= z + range; cz++)
{
int dz = cz - z;
if ((abs(dx) != range || abs(dz) != range || range <= 0) && !Tile::solid[level->getTile(cx, b1, cz)])
level->setTileAndDataNoUpdate(cx, b1, cz, Tile::leaves->m_ID, 1);
}
}
if (range >= 1 && cy == y + x1 + 1)
{
range--;
continue;
}
if (range < x2)
{
range++;
continue;
}
}
int mheight = height - 1;
for (int yd = 0; yd < mheight; yd++)
{
int cy = yd + y;
TileID tile = level->getTile(x, cy, z);
if (tile == TILE_AIR || tile == Tile::leaves->m_ID)
level->setTileAndDataNoUpdate(x, cy, z, Tile::treeTrunk->m_ID, 1);
}
return true;
}

View File

@@ -0,0 +1,546 @@
#include "RandomLevelSource.hpp"
#include "Level.hpp"
const float RandomLevelSource::SNOW_CUTOFF = 0.5f;
const float RandomLevelSource::SNOW_SCALE = 0.3f;
float g_timeSpentInPostProcessing = 0;
RandomLevelSource::RandomLevelSource(Level* level, long 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)
{
LogMsg("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;
printf("random.get : %d\n", 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);
auto 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({ 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.
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)
{
//return;
m_pLevel->field_12 = true;
SandTile::instaFall = true;
int x16 = x * 16, z16 = z * 16;
Biome* pBiome = m_pLevel->getBiomeSource()->getBiome(x16 + 16, z16 + 16);
long seed = m_pLevel->getSeed();
int xo, yo, zo;
m_random.setSeed(seed);
long x1 = 1 + 2 * (m_random.nextInt() / 2);
long x2 = 1 + 2 * (m_random.nextInt() / 2);
m_random.setSeed((long(x) * x1 + long(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, 8).place(m_pLevel, &m_random, x16 + xo, yo, z16 + zo);
}
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);
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 += 2 + t3;
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 < 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->field_12 = 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";
}

View File

@@ -0,0 +1,63 @@
#pragma once
// @NOTE: This class appears to reference a mythical "std::hashtable", but I couldn't find any traces of it.
// I doubt they used C++11 (since it came out in 2011), but this is weird...
// We'll use std::unordered_map instead.
#include <unordered_map>
#include "ChunkSource.hpp"
#include "PerlinNoise.hpp"
#include "Utils.hpp"
#include "BiomeSource.hpp"
#include "Feature.hpp"
class RandomLevelSource : public ChunkSource
{
public:
RandomLevelSource(Level*, long seed, int);
int tick() override;
bool shouldSave() override;
bool hasChunk(int x, int z) override;
LevelChunk* create(int x, int z) override;
LevelChunk* getChunk(int x, int z) override;
std::string gatherStats() override;
void postProcess(ChunkSource*, int, int) override;
float* getHeights(float*, int, int, int, int, int, int);
void prepareHeights(int x, int z, TileID*, void*, float*);
void buildSurfaces (int x, int z, TileID*, Biome**);
public:
bool field_4 = false;
// @TODO: LargeCaveFeature
int field_9D8[1024];
std::unordered_map<int, LevelChunk*> m_chunks;
float field_19F0 = 1.0f;
Random m_random;
PerlinNoise m_perlinNoise1;
PerlinNoise m_perlinNoise2;
PerlinNoise m_perlinNoise3;
PerlinNoise m_perlinNoise4;
PerlinNoise m_perlinNoise5;
PerlinNoise m_perlinNoise6;
PerlinNoise m_perlinNoise7;
PerlinNoise m_perlinNoise8;
Level* m_pLevel = nullptr;
float* field_7280 = nullptr;
float field_7284[256];
float field_7684[256];
float field_7A84[256];
// @TODO
float* field_7E84 = nullptr;
float* field_7E88 = nullptr;
float* field_7E8C = nullptr;
float* field_7E90 = nullptr;
float* field_7E94 = nullptr;
// @TODO
static const float SNOW_CUTOFF;
static const float SNOW_SCALE;
};

View File

@@ -0,0 +1,42 @@
#include "Feature.hpp"
#include "Level.hpp"
SpringFeature::SpringFeature(int id)
{
m_ID = id;
}
bool SpringFeature::place(Level* level, Random* random, int x, int y, int z)
{
if (level->getTile(x, y + 1, z) != Tile::rock->m_ID)
return false;
if (level->getTile(x, y - 1, z) != Tile::rock->m_ID)
return false;
if (level->getTile(x, y, z) && level->getTile(x, y, z) != Tile::rock->m_ID)
return false;
int nRockTiles = 0;
int nEmptyTiles = 0;
if (level->getTile(x - 1, y, z) == Tile::rock->m_ID) nRockTiles++;
if (level->getTile(x + 1, y, z) == Tile::rock->m_ID) nRockTiles++;
if (level->getTile(x, y, z - 1) == Tile::rock->m_ID) nRockTiles++;
if (level->getTile(x, y, z + 1) == Tile::rock->m_ID) nRockTiles++;
if (level->isEmptyTile(x - 1, y, z)) nEmptyTiles++;
if (level->isEmptyTile(x + 1, y, z)) nEmptyTiles++;
if (level->isEmptyTile(x, y, z - 1)) nEmptyTiles++;
if (level->isEmptyTile(x, y, z + 1)) nEmptyTiles++;
if (nEmptyTiles != 1) return true;
if (nRockTiles != 3) return true;
level->setTile(x, y, z, m_ID);
level->field_10 = true;
Tile::tiles[m_ID]->tick(level, x, y, z, random);
level->field_10 = false;
return true;
}

View File

@@ -0,0 +1,95 @@
#include "Feature.hpp"
#include "Level.hpp"
bool SpruceFeature::place(Level* level, Random* random, int x, int y, int z)
{
if (y <= C_MIN_Y)
return false;
int height = random->nextInt(4) + 6;
if (height + y >= C_MAX_Y)
return false;
int x1 = random->nextInt(2) + 1;
int x2 = random->nextInt(2) + 2;
int upperY = y + 1 + height;
bool bCanPlace = true;
for (int i = 0, cy = y; cy <= upperY && bCanPlace; i++, cy++)
{
int range = x2;
if (x1 <= i)
range = 0;
for (int cx = x - range; cx <= x + range && bCanPlace; cx++)
{
for (int cz = z - range; cz <= z + range && bCanPlace; cz++)
{
if (cy >= C_MAX_Y || cy < C_MIN_Y)
{
bCanPlace = false;
break;
}
TileID tile = level->getTile(cx, cy, cz);
if (tile != TILE_AIR && tile != Tile::leaves->m_ID)
bCanPlace = false;
}
}
}
if (!bCanPlace)
return false;
TileID tileBelow = level->getTile(x, y - 1, z);
if (tileBelow != Tile::grass->m_ID && tileBelow != Tile::dirt->m_ID)
return false;
if (y >= C_MAX_Y - 1 - height)
return false;
level->setTileNoUpdate(x, y - 1, z, Tile::dirt->m_ID);
int range = random->nextInt(2);
int b2 = 1, b3 = 0;
for (int cy = 0; cy <= height - x1; cy++)
{
int b1 = height + y - cy;
for (int cx = x - range; cx <= x + range; cx++)
{
int dx = cx - x;
for (int cz = z - range; cz <= z + range; cz++)
{
int dz = cz - z;
if ((abs(dx) != range || abs(dz) != range || range <= 0) && !Tile::solid[level->getTile(cx, b1, cz)])
level->setTileAndDataNoUpdate(cx, b1, cz, Tile::leaves->m_ID, 1);
}
}
if (range >= b2)
{
b2++;
range = b3;
if (b2 > x2)
b2 = x2;
b3 = 1;
}
else
{
range++;
}
}
int mheight = height - random->nextInt(3);
for (int yd = 0; yd < mheight; yd++)
{
int cy = yd + y;
TileID tile = level->getTile(x, cy, z);
if (tile == TILE_AIR || tile == Tile::leaves->m_ID)
level->setTileAndDataNoUpdate(x, cy, z, Tile::treeTrunk->m_ID, 1);
}
return true;
}

View File

@@ -0,0 +1,169 @@
#include "TestChunkSource.hpp"
#include "Level.hpp"
TestChunkSource::TestChunkSource(Level* pLevel)
{
m_pLevel = pLevel;
memset(m_chunkMap, 0, sizeof m_chunkMap);
m_pEmptyChunk = new EmptyLevelChunk(pLevel, nullptr, 0, 0);
m_lastChunkX = -99999;
m_lastChunkZ = -99999;
m_pLastChunk = m_pEmptyChunk;
}
TestChunkSource::~TestChunkSource()
{
for (int z = 0; z < C_MAX_CHUNKS_Z; z++)
{
for (int x = 0; x < C_MAX_CHUNKS_X; x++)
{
if (m_chunkMap[z][x])
delete m_chunkMap[z][x];
}
}
}
LevelChunk* TestChunkSource::generateChunk(int x, int z)
{
//@NOTE: This gets called a dangerous amount of recursions (for the entire damn world!)
// We really should fix this...
if (x < 0 || z < 0 || x >= C_MAX_CHUNKS_X || z >= C_MAX_CHUNKS_Z)
return nullptr;
if (m_chunkMap[z][x])
return m_chunkMap[z][x];
TileID* pData = new TileID[0x8000u];
memset(pData, 0, 0x8000u * sizeof(TileID));
for (int i = 0; i != 66; i++)
{
for (int j = 0; j < 16; j++)
{
for (int k = 0; k < 16; k++)
{
TileID* p = &pData[i | (j << 11) | (k << 7)];
if (i == 0)
*p = TILE_BEDROCK;
//else if (j == 0 || k == 0)
// *p = TILE_AIR;
else if (i == 65) {
/*if (rand() % 10 == 0)
*p = TILE_ROSE;
else if (rand() % 10 == 0)
*p = TILE_FLOWER;
else if (rand() % 10 == 0)
*p = TILE_TOPSNOW;*/
}
else if (i > 64)
*p = TILE_AIR;
else if (i == 64) {
//if ((j + k) % 2 == 0)
*p = TILE_GRASS;
}
else if (i > 60)
*p = TILE_DIRT;
else if (i > 3)
*p = TILE_STONE;
else
*p = TILE_STONEBRICK;
// generate a hole at (128,0,128) for testing
//if (i == 0 && j == 0 && k == 0 && x == 8 && z == 8)
// *p = TILE_WOOD;
}
}
}
LevelChunk* pChunk = new LevelChunk(m_pLevel, pData, x, z);
m_chunkMap[z][x] = pChunk;
/*
// block light updates during level generation
bool bOldState = m_pLevel->m_bUpdateLights;
m_pLevel->m_bUpdateLights = false;
// encode this chunk's X/Z coordinate by setting bits in it
pChunk->setTile(0, 65, 0, TILE_WOOD);
pChunk->setTile(0, 65, 1, TILE_STONE);
pChunk->setTile(3+1, 65, 0, (x & (1 << 0)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(2+1, 65, 0, (x & (1 << 1)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(1+1, 65, 0, (x & (1 << 2)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(0+1, 65, 0, (x & (1 << 3)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(3+1, 65, 1, (z & (1 << 0)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(2+1, 65, 1, (z & (1 << 1)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(1+1, 65, 1, (z & (1 << 2)) ? TILE_STONEBRICK : TILE_BEDROCK);
pChunk->setTile(0+1, 65, 1, (z & (1 << 3)) ? TILE_STONEBRICK : TILE_BEDROCK);
m_pLevel->m_bUpdateLights = bOldState;
pChunk->setBrightness(LightLayer::Block, 0, 0, 0, 3);
pChunk->setBrightness(LightLayer::Block, 0, 1, 0, 4);
pChunk->setBrightness(LightLayer::Block, 0, 2, 0, 5);
pChunk->setBrightness(LightLayer::Block, 0, 3, 0, 6);
int br1 = pChunk->getBrightness(LightLayer::Block, 0, 0, 0);
int br2 = pChunk->getBrightness(LightLayer::Block, 0, 1, 0);
int br3 = pChunk->getBrightness(LightLayer::Block, 0, 2, 0);
int br4 = pChunk->getBrightness(LightLayer::Block, 0, 3, 0);
assert(br1 == 3);
assert(br2 == 4);
assert(br3 == 5);
assert(br4 == 6);
*/
pChunk->recalcHeightmap();
return pChunk;
}
LevelChunk* TestChunkSource::create(int x, int z)
{
if (m_lastChunkX == x && m_lastChunkZ == z)
return m_pLastChunk;
LevelChunk* pChk = generateChunk(x, z);
if (!pChk)
pChk = m_pEmptyChunk;
m_lastChunkX = x;
m_lastChunkZ = z;
m_pLastChunk = pChk;
return pChk;
}
std::string TestChunkSource::gatherStats()
{
return "TestChunkSource";
}
LevelChunk* TestChunkSource::getChunk(int x, int z)
{
return create(x, z);
}
bool TestChunkSource::hasChunk(int x, int z)
{
return true;
}
void TestChunkSource::postProcess(ChunkSource* a, int b, int c)
{
}
bool TestChunkSource::shouldSave()
{
return false;
}
int TestChunkSource::tick()
{
return 0;
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Utils.hpp"
#include "ChunkSource.hpp"
class Level;
class TestChunkSource : public ChunkSource
{
public:
TestChunkSource(Level*);
virtual ~TestChunkSource();
LevelChunk* generateChunk(int x, int z);
LevelChunk* create(int x, int z) override;
LevelChunk* getChunk(int x, int z) override;
bool hasChunk(int x, int z) override;
std::string gatherStats() override;
void postProcess(ChunkSource*, int, int) override;
bool shouldSave() override;
int tick() override;
public:
Level* m_pLevel = nullptr;
LevelChunk* m_chunkMap[C_MAX_CHUNKS_Z][C_MAX_CHUNKS_X];
LevelChunk* m_pEmptyChunk;
LevelChunk* m_pLastChunk;
int m_lastChunkX, m_lastChunkZ;
};

View File

@@ -0,0 +1,100 @@
#include "Feature.hpp"
#include "Level.hpp"
bool TreeFeature::place(Level* level, Random* random, int x, int y, int z)
{
if (y <= C_MIN_Y)
return false;
int treeHeight = int(random->nextInt(3) + 4); // Between 4 and 6 blocks tall.
if (y + treeHeight >= C_MAX_Y)
return false;
// Ensure that we can place this tree
bool bCanPlace = true;
for (int ay = y; ay <= y + treeHeight; ay++)
{
int x1 = 1;
if (y + treeHeight - 1 <= ay)
x1 = 2;
else if (ay == y)
x1 = 0;
for (int ax = x - x1; ax <= x + x1 && bCanPlace; ax++)
{
for (int az = z - x1; az <= z + x1 && bCanPlace; az++)
{
if (ay < C_MIN_Y || ay >= C_MAX_Y)
{
bCanPlace = false;
break;
}
TileID tile = level->getTile(ax, ay, az);
// other trees can overlap with this one, apparently
if (tile != TILE_AIR && tile != Tile::leaves->m_ID)
{
bCanPlace = false;
break;
}
}
}
}
// If not, let our caller know.
if (!bCanPlace)
return false;
TileID tileBelow = level->getTile(x, y - 1, z);
// If grass or dirt aren't below us, we can't possibly grow!
if (tileBelow != Tile::grass->m_ID && tileBelow != Tile::dirt->m_ID)
return false;
// @NOTE: Redundant check
if (y >= C_MAX_Y - treeHeight)
return false;
level->setTileNoUpdate(x, y - 1, z, Tile::dirt->m_ID);
int upperY = y + treeHeight;
int lowerY = y + treeHeight - 3;
int diff = lowerY - upperY;
for (int i = lowerY; i <= upperY; i++, diff = i - upperY)
{
int c1 = 1 - diff / 2;
int c2 = diff / 2 - 1;
for (int ax = x - c1; ax <= x + c1; ax++)
{
int c3 = c2;
int c4 = diff / 2 - 1;
if (c2 < 0)
c3 = -c2;
int c5 = c3;
for (int az = z - c1; az <= z + c1; az++, c4++)
{
if ((abs(ax - x) != c1 || abs(az - z) != c1 || random->nextInt(2) != 0 && diff != 0) && !Tile::solid[level->getTile(ax, i, az)])
{
level->setTileNoUpdate(ax, i, az, Tile::leaves->m_ID);
}
}
}
}
for (int i = 0; i < treeHeight; i++)
{
int y1 = i + y;
TileID tile = level->getTile(x, y + i, z);
if (tile && tile != Tile::leaves->m_ID)
continue;
level->setTileNoUpdate(x, y + i, z, Tile::treeTrunk->m_ID);
}
return true;
}