mirror of
https://github.com/celisej567/mcpe.git
synced 2026-09-23 20:09:46 +03:00
* Initial commit.
:)
This commit is contained in:
219
source/World/Generator/Biome.cpp
Normal file
219
source/World/Generator/Biome.cpp
Normal 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;
|
||||
}
|
||||
80
source/World/Generator/Biome.hpp
Normal file
80
source/World/Generator/Biome.hpp
Normal 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
|
||||
{
|
||||
};
|
||||
117
source/World/Generator/BiomeSource.cpp
Normal file
117
source/World/Generator/BiomeSource.cpp
Normal 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;
|
||||
}
|
||||
33
source/World/Generator/BiomeSource.hpp
Normal file
33
source/World/Generator/BiomeSource.hpp
Normal 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;
|
||||
};
|
||||
|
||||
100
source/World/Generator/BirchFeature.cpp
Normal file
100
source/World/Generator/BirchFeature.cpp
Normal 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;
|
||||
}
|
||||
214
source/World/Generator/ChunkCache.cpp
Normal file
214
source/World/Generator/ChunkCache.cpp
Normal 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;
|
||||
}
|
||||
36
source/World/Generator/ChunkCache.hpp
Normal file
36
source/World/Generator/ChunkCache.hpp
Normal 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;
|
||||
};
|
||||
|
||||
11
source/World/Generator/ChunkSource.cpp
Normal file
11
source/World/Generator/ChunkSource.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "ChunkSource.hpp"
|
||||
#include "Level.hpp"
|
||||
|
||||
void ChunkSource::saveAll()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ChunkSource::~ChunkSource()
|
||||
{
|
||||
}
|
||||
19
source/World/Generator/ChunkSource.hpp
Normal file
19
source/World/Generator/ChunkSource.hpp
Normal 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;
|
||||
};
|
||||
|
||||
67
source/World/Generator/ClayFeature.cpp
Normal file
67
source/World/Generator/ClayFeature.cpp
Normal 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;
|
||||
}
|
||||
11
source/World/Generator/Feature.cpp
Normal file
11
source/World/Generator/Feature.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "Feature.hpp"
|
||||
|
||||
void Feature::init(float a, float b, float c)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Feature::~Feature()
|
||||
{
|
||||
|
||||
}
|
||||
80
source/World/Generator/Feature.hpp
Normal file
80
source/World/Generator/Feature.hpp
Normal 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;
|
||||
};
|
||||
|
||||
22
source/World/Generator/FlowerFeature.cpp
Normal file
22
source/World/Generator/FlowerFeature.cpp
Normal 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;
|
||||
}
|
||||
67
source/World/Generator/OreFeature.cpp
Normal file
67
source/World/Generator/OreFeature.cpp
Normal 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;
|
||||
}
|
||||
71
source/World/Generator/PerformanceTestChunkSource.cpp
Normal file
71
source/World/Generator/PerformanceTestChunkSource.cpp
Normal 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;
|
||||
}
|
||||
|
||||
19
source/World/Generator/PerformanceTestChunkSource.hpp
Normal file
19
source/World/Generator/PerformanceTestChunkSource.hpp
Normal 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;
|
||||
};
|
||||
95
source/World/Generator/PineFeature.cpp
Normal file
95
source/World/Generator/PineFeature.cpp
Normal 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;
|
||||
}
|
||||
546
source/World/Generator/RandomLevelSource.cpp
Normal file
546
source/World/Generator/RandomLevelSource.cpp
Normal 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";
|
||||
}
|
||||
63
source/World/Generator/RandomLevelSource.hpp
Normal file
63
source/World/Generator/RandomLevelSource.hpp
Normal 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;
|
||||
};
|
||||
|
||||
42
source/World/Generator/SpringFeature.cpp
Normal file
42
source/World/Generator/SpringFeature.cpp
Normal 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;
|
||||
}
|
||||
95
source/World/Generator/SpruceFeature.cpp
Normal file
95
source/World/Generator/SpruceFeature.cpp
Normal 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;
|
||||
}
|
||||
169
source/World/Generator/TestChunkSource.cpp
Normal file
169
source/World/Generator/TestChunkSource.cpp
Normal 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;
|
||||
}
|
||||
|
||||
29
source/World/Generator/TestChunkSource.hpp
Normal file
29
source/World/Generator/TestChunkSource.hpp
Normal 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;
|
||||
};
|
||||
100
source/World/Generator/TreeFeature.cpp
Normal file
100
source/World/Generator/TreeFeature.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user