summaryrefslogtreecommitdiffstats
path: root/src/Generating
diff options
context:
space:
mode:
Diffstat (limited to 'src/Generating')
-rw-r--r--src/Generating/Caves.cpp10
-rw-r--r--src/Generating/ChunkGenerator.cpp49
-rw-r--r--src/Generating/ChunkGenerator.h47
-rw-r--r--src/Generating/CompoGen.cpp12
-rw-r--r--src/Generating/ComposableGenerator.cpp10
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp27
-rw-r--r--src/Generating/FinishGen.cpp328
-rw-r--r--src/Generating/FinishGen.h86
-rw-r--r--src/Generating/MineShafts.cpp5
-rw-r--r--src/Generating/Noise3DGenerator.cpp5
10 files changed, 498 insertions, 81 deletions
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
index fc925a150..e4735cb83 100644
--- a/src/Generating/Caves.cpp
+++ b/src/Generating/Caves.cpp
@@ -692,8 +692,14 @@ static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise)
float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4;
oct1 = oct1 * oct1 * oct1;
- if (oct1 < 0.f) oct1 = PI_2;
- if (oct1 > PI_2) oct1 = PI_2;
+ if (oct1 < 0.f)
+ {
+ oct1 = PI_2;
+ }
+ if (oct1 > PI_2)
+ {
+ oct1 = PI_2;
+ }
return oct1;
}
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 3ee02c767..854aac60d 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -6,7 +6,7 @@
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
-#include "../MersenneTwister.h"
+#include "FastRandom.h"
@@ -110,29 +110,19 @@ void cChunkGenerator::Stop(void)
-void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate)
+void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback)
{
ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ));
{
cCSLock Lock(m_CS);
- // Check if it is already in the queue:
- for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
- {
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
- {
- // Already in the queue, bail out
- return;
- }
- } // for itr - m_Queue[]
-
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
- m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate));
+ m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback});
}
m_Event.Set();
@@ -242,9 +232,9 @@ void cChunkGenerator::Execute(void)
continue;
}
- cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue
+ cQueueItem item = m_Queue.front(); // Get next chunk from the queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
- m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue
+ m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
@@ -258,22 +248,35 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
- if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
+ // Skip the chunk if it's already generated and regeneration is not forced:
+ if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ))
{
- LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
- // Already generated, ignore request
+ LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
continue;
}
- if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
+ // Skip the chunk if the generator is overloaded:
+ if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ))
{
- LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
+ LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
continue;
}
- LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
- DoGenerate(coords.m_ChunkX, coords.m_ChunkZ);
-
+ // Generate the chunk:
+ LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
+ DoGenerate(item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
NumChunksGenerated++;
} // while (!bStop)
}
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
index 190d9e616..4af486ae1 100644
--- a/src/Generating/ChunkGenerator.h
+++ b/src/Generating/ChunkGenerator.h
@@ -119,8 +119,12 @@ public:
bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
- /// Queues the chunk for generation; removes duplicate requests
- void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate);
+ /** Queues the chunk for generation
+ If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink.
+ a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating.
+ It is legal to set the callback to nullptr, no callback is called then.
+ If the generator becomes overloaded and skips this chunk, the callback is still called. */
+ void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
@@ -131,22 +135,46 @@ public:
int GetSeed(void) const { return m_Seed; }
- /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome
+ /** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
- /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure.
+ /** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */
static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
private:
+ struct cQueueItem
+ {
+ /** The chunk coords */
+ int m_ChunkX, m_ChunkZ;
+
+ /** Force the regeneration of an already existing chunk */
+ bool m_ForceGenerate;
+
+ /** Callback to call after generating.*/
+ cChunkCoordCallback * m_Callback;
+ };
+
+ typedef std::list<cQueueItem> cGenQueue;
+
+
+ /** Seed used for the generator. */
int m_Seed;
- cCriticalSection m_CS;
- cChunkCoordsWithBoolList m_Queue;
- cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
- cEvent m_evtRemoved; ///< Set when an item is removed from the queue
+ /** CS protecting access to the queue. */
+ cCriticalSection m_CS;
+
+ /** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */
+ cGenQueue m_Queue;
+
+ /** Set when an item is added to the queue or the thread should terminate. */
+ cEvent m_Event;
+
+ /** Set when an item is removed from the queue. */
+ cEvent m_evtRemoved;
- cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
+ /** The actual generator engine used to generate chunks. */
+ cGenerator * m_Generator;
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
@@ -158,6 +186,7 @@ private:
// cIsThread override:
virtual void Execute(void) override;
+ /** Generates the specified chunk and sets it into the chunksink. */
void DoGenerate(int a_ChunkX, int a_ChunkZ);
};
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
index 6c3b50b4e..cb9c04fd7 100644
--- a/src/Generating/CompoGen.cpp
+++ b/src/Generating/CompoGen.cpp
@@ -290,17 +290,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc:
BLOCKTYPE Block = E_BLOCK_AIR;
if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air.
{
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(BaseX + x)) / 8;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(BaseZ + z)) / 8;
- NOISE_DATATYPE CompBlock = m_Noise1.CubicNoise3D(NoiseX, (float) (y + Segment) / 2, NoiseY);
- if (CompBlock < -0.5)
- {
- Block = E_BLOCK_SOULSAND;
- }
- else
- {
- Block = E_BLOCK_NETHERRACK;
- }
+ Block = E_BLOCK_NETHERRACK;
}
a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
}
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index 4192dfa72..bda45ad92 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -294,7 +294,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
- if (NoCaseCompare(*itr, "BottomLava") == 0)
+ if (NoCaseCompare(*itr, "Animals") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension)));
+ }
+ else if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
@@ -570,6 +574,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
GridSize, MaxOffset
)));
}
+ else if (NoCaseCompare(*itr, "SoulsandRims") == 0)
+ {
+ m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed)));
+ }
else if (NoCaseCompare(*itr, "Snow") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow));
diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp
index 7ab22c2c5..c4bf8839e 100644
--- a/src/Generating/DungeonRoomsFinisher.cpp
+++ b/src/Generating/DungeonRoomsFinisher.cpp
@@ -7,6 +7,7 @@
#include "DungeonRoomsFinisher.h"
#include "../FastRandom.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
@@ -57,6 +58,22 @@ public:
int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ);
+
+ // Choose what the mobspawner will spawn.
+ // 25% chance for a spider, 25% for a skeleton and 50% chance to get a zombie spawer.
+ int MobType = (a_Noise.IntNoise3DInt(a_OriginX, m_FloorHeight, a_OriginZ) / 7) % 100;
+ if (MobType <= 25)
+ {
+ m_MonsterType = mtSkeleton;
+ }
+ else if (MobType <= 50)
+ {
+ m_MonsterType = mtSpider;
+ }
+ else
+ {
+ m_MonsterType = mtZombie;
+ }
}
protected:
@@ -76,6 +93,8 @@ protected:
/** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest2;
+ /** The monster type for the mobspawner entity. */
+ eMonsterType m_MonsterType;
/** Decodes the position index along the room walls into a proper 2D position for a chest.
@@ -112,8 +131,8 @@ protected:
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
- int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
+ int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width);
+ int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
for (int y = a_StartY; y < a_EndY; y++)
@@ -246,7 +265,9 @@ protected:
)
{
a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: Set the spawned mob
+ cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(CenterX, b, CenterZ));
+ ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
+ MobSpawner->SetEntity(m_MonsterType);
}
}
} ;
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index 1e8a67dd0..e10cb00f8 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -26,6 +26,8 @@
#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
+#define DEF_ANIMAL_SPAWN_PERCENT 10
+#define DEF_NO_ANIMALS 0
@@ -65,7 +67,7 @@ void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
-
+
// Choose what block to use.
NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ);
if (BlockType < -0.7)
@@ -195,10 +197,10 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
-
+
// Get the top block + 1. This is the place where the grass would finaly be placed:
int y = a_ChunkDesc.GetHeight(x, z) + 1;
-
+
if (y >= 255)
{
continue;
@@ -281,7 +283,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
{
return false;
}
-
+
// All conditions met, place a sugarcane here:
a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
return true;
@@ -294,7 +296,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Generate small foliage (1-block):
-
+
// TODO: Update heightmap with 1-block-tall foliage
for (int z = 0; z < cChunkDef::Width; z++)
{
@@ -319,7 +321,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
// WEIRD, since we're using heightmap, so there should NOT be anything above it
continue;
}
-
+
const float xx = (float)BlockX;
float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f);
float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
@@ -359,7 +361,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
}
break;
} // case E_BLOCK_GRASS
-
+
case E_BLOCK_SAND:
{
int y = Top + 1;
@@ -370,7 +372,8 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
(a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) &&
- (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR)
+ (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) &&
+ IsDesertVariant(a_ChunkDesc.GetBiome(x, z))
)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
@@ -391,6 +394,72 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
+bool cFinishGenSprinkleFoliage::IsDesertVariant(EMCSBiome a_Biome)
+{
+ return
+ (
+ (a_Biome == biDesertHills) ||
+ (a_Biome == biDesert) ||
+ (a_Biome == biDesertM)
+ );
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSoulsandRims
+
+void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
+
+ for (int x = 0; x < 16; x++)
+ {
+ int xx = ChunkX + x;
+ for (int z = 0; z < 16; z++)
+ {
+ int zz = ChunkZ + z;
+
+ // Place soulsand rims when netherrack gets thin
+ for (int y = 2; y < MaxHeight - 2; y++)
+ {
+ // The current block is air. Let's bail ut.
+ BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
+ if (Block == E_BLOCK_AIR)
+ {
+ continue;
+ }
+
+ if (
+ ((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) &&
+ ( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) ||
+ ((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) &&
+ ( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR))
+ )
+ {
+ continue;
+ }
+
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(xx)) / 32;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(zz)) / 32;
+ NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, (float) (y) / 4, NoiseY);
+ if (CompBlock < 0)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cFinishGenSnow:
@@ -716,7 +785,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
} // for y
} // for x
} // for z
-
+
// Turn fluid at the chunk edges into non-stationary fluid:
for (int y = 0; y < cChunkDef::Height; y++)
{
@@ -808,12 +877,12 @@ void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
// Not in this chunk
return;
}
-
+
// Get the height at which to try:
int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
Height %= m_HeightDistribution.GetSum();
Height = m_HeightDistribution.MapValue(Height);
-
+
// Try adding the spring at the height, if unsuccessful, move lower:
for (int y = Height; y > 1; y--)
{
@@ -851,7 +920,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
-
+
static const struct
{
int x, y, z;
@@ -882,7 +951,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
-
+
// Has exactly one air neighbor, place a spring:
a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
return true;
@@ -891,3 +960,236 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
+
+////////////////////////////////////////////////////////////////////////////////
+// cFinishGenPassiveMobs:
+
+cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) :
+ m_Noise(a_Seed)
+{
+ AString SectionName = "Animals";
+ int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
+ switch (a_Dimension)
+ {
+ case dimOverworld:
+ {
+ DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
+ break;
+ }
+ case dimNether:
+ case dimEnd: // No nether or end animals (currently)
+ {
+ DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled world dimension");
+ DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
+ break;
+ }
+ } // switch (dimension)
+ m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
+ if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
+ {
+ LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
+ m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
+ }
+}
+
+
+
+
+
+void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ int chunkX = a_ChunkDesc.GetChunkX();
+ int chunkZ = a_ChunkDesc.GetChunkZ();
+ int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
+ if (ChanceRnd > m_AnimalProbability)
+ {
+ return;
+ }
+
+ eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
+ if (RandomMob == mtInvalidType)
+ {
+ // No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well.
+ return;
+ }
+
+ // Try spawning a pack center 10 times, should get roughly the same probability
+ for (int Tries = 0; Tries < 10; Tries++)
+ {
+ int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
+ int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
+ if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
+ int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
+ TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
+ }
+ return;
+
+ } // if pack center spawn successful
+ } // for tries
+}
+
+
+
+
+
+bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
+{
+ if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
+ {
+ return false;
+ }
+
+ BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
+ BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
+ BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);
+
+ // Check block below (opaque, grass, water), and above (air)
+ if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
+ {
+ return false;
+ }
+ if (
+ (AnimalToSpawn != mtSquid) &&
+ (BlockAtHead != E_BLOCK_AIR) &&
+ (BlockAtFeet != E_BLOCK_AIR) &&
+ (!cBlockInfo::IsTransparent(BlockUnderFeet))
+ )
+ {
+ return false;
+ }
+ if (
+ (BlockUnderFeet != E_BLOCK_GRASS) &&
+ ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
+ )
+ {
+ return false;
+ }
+ if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
+ {
+ return false;
+ }
+
+ double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5);
+ double AnimalY = a_RelY;
+ double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
+
+ cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
+ NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
+ a_ChunkDesc.GetEntities().push_back(NewMob);
+ LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);
+
+ return true;
+}
+
+
+
+
+
+eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc)
+{
+
+ std::set<eMonsterType> ListOfSpawnables;
+ int chunkX = a_ChunkDesc.GetChunkX();
+ int chunkZ = a_ChunkDesc.GetChunkZ();
+ int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
+ int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;
+
+ // Check biomes first to get a list of animals
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ // No animals in deserts or non-overworld dimensions
+ case biNether:
+ case biEnd:
+ case biDesertHills:
+ case biDesert:
+ case biDesertM:
+ {
+ return mtInvalidType;
+ }
+
+ // Mooshroom only - no other mobs on mushroom islands
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ return mtMooshroom;
+ }
+
+ // Add squid in ocean biomes
+ case biOcean:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biRiver:
+ case biDeepOcean:
+ {
+ ListOfSpawnables.insert(mtSquid);
+ break;
+ }
+
+ // Add ocelots in jungle biomes
+ case biJungle:
+ case biJungleHills:
+ case biJungleEdge:
+ case biJungleM:
+ case biJungleEdgeM:
+ {
+ ListOfSpawnables.insert(mtOcelot);
+ break;
+ }
+
+ // Add horses in plains-like biomes
+ case biPlains:
+ case biSunflowerPlains:
+ case biSavanna:
+ case biSavannaPlateau:
+ case biSavannaM:
+ case biSavannaPlateauM:
+ {
+ ListOfSpawnables.insert(mtHorse);
+ break;
+ }
+
+ // Add wolves in forest and spruce forests
+ case biForest:
+ case biTaiga:
+ case biMegaTaiga:
+ case biColdTaiga:
+ case biColdTaigaM:
+ {
+ ListOfSpawnables.insert(mtWolf);
+ break;
+ }
+ // Nothing special about this biome
+ default:
+ {
+ break;
+ }
+ }
+ ListOfSpawnables.insert(mtChicken);
+ ListOfSpawnables.insert(mtCow);
+ ListOfSpawnables.insert(mtPig);
+ ListOfSpawnables.insert(mtSheep);
+
+ if (ListOfSpawnables.empty())
+ {
+ return mtInvalidType;
+ }
+
+ int RandMob = (m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size();
+ auto MobIter = ListOfSpawnables.begin();
+ std::advance(MobIter, RandMob);
+
+ return *MobIter;
+}
+
+
+
+
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
index 991a85787..ae6dee590 100644
--- a/src/Generating/FinishGen.h
+++ b/src/Generating/FinishGen.h
@@ -18,6 +18,7 @@
#include "ComposableGenerator.h"
#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"
+#include "../Mobs/Monster.h"
@@ -117,19 +118,41 @@ protected:
+class cFinishGenSoulsandRims :
+ public cFinishGen
+{
+public:
+ cFinishGenSoulsandRims(int a_Seed) :
+ m_Noise(a_Seed)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
class cFinishGenSprinkleFoliage :
public cFinishGen
{
public:
cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
-
+
protected:
cNoise m_Noise;
int m_Seed;
-
+
/// Tries to place sugarcane at the coords specified, returns true if successful
bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ);
-
+
+ // Returns true is the specified biome is a desert or its variant
+ static bool IsDesertVariant(EMCSBiome a_biome);
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -167,31 +190,31 @@ public:
{
m_IsAllowedBelow[idx] = false;
}
-
+
// Load the allowed blocks into m_IsAllowedBelow
for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr)
{
m_IsAllowedBelow[*itr] = true;
}
-
+
// Initialize all the biome types.
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx)
{
m_IsBiomeAllowed[idx] = false;
}
-
+
// Load the allowed biomes into m_IsBiomeAllowed
for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr)
{
m_IsBiomeAllowed[*itr] = true;
}
}
-
+
protected:
cNoise m_Noise;
BLOCKTYPE m_BlockType;
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
-
+
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
// Returns true if the given biome is a biome that is allowed.
@@ -206,7 +229,7 @@ protected:
return m_IsAllowedBelow[a_BlockBelow];
}
-
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -223,11 +246,11 @@ public:
m_Level(a_Level)
{
}
-
+
int GetLevel(void) const { return m_Level; }
protected:
int m_Level;
-
+
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@@ -241,7 +264,7 @@ class cFinishGenPreSimulator :
{
public:
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava);
-
+
protected:
bool m_PreSimulateFallingBlocks;
@@ -253,7 +276,7 @@ protected:
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
);
-
+
/** For each fluid block:
- if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top)
- all fluid on the chunk's edge is made flowing
@@ -278,7 +301,7 @@ class cFinishGenFluidSprings :
{
public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
-
+
protected:
cNoise m_Noise;
@@ -289,10 +312,43 @@ protected:
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
- /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
+ /** Tries to place a spring at the specified coords, checks neighbors. Returns true if successful. */
bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
} ;
+
+/** This class populates generated chunks with packs of biome-dependant animals
+Animals: cows, sheep, pigs, mooshrooms, squid, horses, wolves, ocelots */
+class cFinishGenPassiveMobs :
+ public cFinishGen
+{
+public:
+
+ cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension);
+
+protected:
+
+ /** The noise used as the source of randomness */
+ cNoise m_Noise;
+
+ /** Chance, [0..100], that an animal pack will be generated in a chunk */
+ int m_AnimalProbability;
+
+
+ // cFinishGen override:
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+
+ /** Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and returns true */
+ bool TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn);
+
+ /** Picks a random animal from biome-dependant list for a random position in the chunk.
+ Returns the chosen mob type, or mtInvalid if no mob chosen. */
+ eMonsterType GetRandomMob(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp
index 55b3b64dd..65588ce4b 100644
--- a/src/Generating/MineShafts.cpp
+++ b/src/Generating/MineShafts.cpp
@@ -20,6 +20,7 @@ in a depth-first processing. Each of the descendants will branch randomly, if no
#include "MineShafts.h"
#include "../Cuboid.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/MobSpawnerEntity.h"
@@ -875,7 +876,9 @@ void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
)
{
a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
+ cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ));
+ ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
+ MobSpawner->SetEntity(mtCaveSpider);
}
}
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index 57e23809e..b43a1a6de 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -6,7 +6,6 @@
#include "Globals.h"
#include "Noise3DGenerator.h"
#include "../OSSupport/File.h"
-#include "../OSSupport/Timer.h"
#include "../IniFile.h"
#include "../LinearInterpolation.h"
#include "../LinearUpscale.h"
@@ -739,7 +738,7 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
- case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break;
+ case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
case biFrozenOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
@@ -764,7 +763,7 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
case biNether: a_HeightAmp = 0.01f; a_MidPoint = 64; break;
case biOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
- case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break;
+ case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
case biRoofedForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biRoofedForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biSavanna: a_HeightAmp = 0.3f; a_MidPoint = 62; break;