Cuberite
A lightweight, fast and extensible game server for Minecraft
FinishGen.cpp
Go to the documentation of this file.
1 
2 // FinishGen.cpp
3 
4 /* Implements the various finishing generators:
5  - cFinishGenSnow
6  - cFinishGenIce
7  - cFinishGenSprinkleFoliage
8 */
9 
10 #include "Globals.h"
11 
12 #include "FinishGen.h"
13 #include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
14 #include "../Simulator/FireSimulator.h"
15 #include "../IniFile.h"
16 #include "../MobSpawner.h"
17 #include "../BlockInfo.h"
18 
19 
20 
21 
22 
23 #define DEF_NETHER_WATER_SPRINGS "0, 1; 255, 1"
24 #define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0"
25 #define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0"
26 #define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
27 #define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
28 #define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
29 #define DEF_ANIMAL_SPAWN_PERCENT 10
30 #define DEF_NO_ANIMALS 0
31 
32 
33 
34 
35 
36 static inline bool IsWater(BLOCKTYPE a_BlockType)
37 {
38  return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER);
39 }
40 
41 
42 
43 
44 
46 // cFinishGenNetherClumpFoliage:
47 
49 {
50  int ChunkX = a_ChunkDesc.GetChunkX();
51  int ChunkZ = a_ChunkDesc.GetChunkZ();
52 
53  int Val1 = m_Noise.IntNoise2DInt(ChunkX ^ ChunkZ, ChunkZ + ChunkX);
54  int Val2 = m_Noise.IntNoise2DInt(ChunkZ ^ ChunkX, ChunkZ - ChunkX);
55 
56  int PosX = Val1 % 16;
57  int PosZ = Val2 % 16;
58 
59  for (int y = 1; y < cChunkDef::Height; y++)
60  {
61  if (a_ChunkDesc.GetBlockType(PosX, y, PosZ) != E_BLOCK_AIR)
62  {
63  continue;
64  }
65 
66  if (!cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(PosX, y - 1, PosZ))) // Only place on solid blocks
67  {
68  continue;
69  }
70 
71  // Choose what block to use.
72  NOISE_DATATYPE BlockType = m_Noise.IntNoise3D(static_cast<int>(ChunkX), y, static_cast<int>(ChunkZ));
73  if (BlockType < -0.7)
74  {
75  TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_BROWN_MUSHROOM);
76  }
77  else if (BlockType < 0)
78  {
79  TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_RED_MUSHROOM);
80  }
81  else if (BlockType < 0.7)
82  {
83  TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_FIRE);
84  }
85  }
86 }
87 
88 
89 
90 
91 
92 void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
93 {
94  bool IsFireBlock = a_Block == E_BLOCK_FIRE;
95 
96  int MinX = a_RelX - 4;
97  if (MinX < 0) // Check if the coordinate is outside the chunk. If it it then adjust it.
98  {
99  MinX = 0;
100  }
101 
102  int MaxX = a_RelX + 4;
103  if (MaxX > cChunkDef::Width) // Check if the coordinate is outside the chunk. If it it then adjust it.
104  {
105  MaxX = cChunkDef::Width;
106  }
107 
108  int MinZ = a_RelZ - 4;
109  if (MinZ < 0) // Check if the coordinate is outside the chunk. If it it then adjust it.
110  {
111  MinZ = 0;
112  }
113 
114  int MaxZ = a_RelZ + 4;
115  if (MaxZ > cChunkDef::Width) // Check if the coordinate is outside the chunk. If it it then adjust it.
116  {
117  MaxZ = cChunkDef::Width;
118  }
119 
120  int MinY = a_RelY - 2;
121  if (MinY < 0) // Check if the coordinate is outside the chunk. If it it then adjust it.
122  {
123  MinY = 0;
124  }
125 
126  int MaxY = a_RelY + 2;
127  if (MaxY > cChunkDef::Height) // Check if the coordinate is outside the chunk. If it it then adjust it.
128  {
129  MaxY = cChunkDef::Height;
130  }
131 
132  for (int x = MinX; x < MaxX; x++)
133  {
134  int xx = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
135  for (int z = MinZ; z < MaxZ; z++)
136  {
137  int zz = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
138  for (int y = MinY; y < MaxY; y++)
139  {
140  if (
141  ((x < 0) || (x >= cChunkDef::Width)) ||
142  ((y < 0) || (y >= cChunkDef::Height)) ||
143  ((z < 0) || (z >= cChunkDef::Width))
144  )
145  {
146  continue;
147  }
148 
149  if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) // Don't replace non air blocks.
150  {
151  continue;
152  }
153 
154  BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, y - 1, z);
155  if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow)) // Only place on solid blocks
156  {
157  continue;
158  }
159 
160  if (IsFireBlock) // don't place fire on non-forever burning blocks.
161  {
162  if (!cFireSimulator::DoesBurnForever(BlockBelow))
163  {
164  continue;
165  }
166  }
167 
168  NOISE_DATATYPE Val = m_Noise.IntNoise2D(xx, zz);
169  if (Val < -0.5)
170  {
171  a_ChunkDesc.SetBlockType(x, y, z, a_Block);
172  }
173  }
174  }
175  }
176 }
177 
178 
179 
180 
181 
183 // cFinishGenClumpTopBlock
184 
186 {
187  int ChunkX = a_ChunkDesc.GetChunkX();
188  int ChunkZ = a_ChunkDesc.GetChunkZ();
189 
190  int NoiseVal = m_Noise.IntNoise2DInt(ChunkX, ChunkZ);
191  EMCSBiome Biome = a_ChunkDesc.GetBiome(cChunkDef::Width / 2, cChunkDef::Width / 2);
192  BiomeInfo info = m_FlowersPerBiome[static_cast<size_t>(Biome)];
193 
194  const auto & PossibleBlocks = info.m_Blocks;
195  if (PossibleBlocks.empty())
196  {
197  // No need to go any further. This biome can't generate any blocks.
198  return;
199  }
200 
201  int NumClumps = info.m_MaxNumClumpsPerChunk - info.m_MinNumClumpsPerChunk;
202  if (NumClumps == 0)
203  {
204  NumClumps = 1;
205  }
206 
207  NumClumps = NoiseVal % NumClumps + info.m_MinNumClumpsPerChunk;
208  for (int i = 0; i < NumClumps; i++)
209  {
210  int Val1 = m_Noise.IntNoise2DInt(ChunkX * ChunkZ * i, ChunkZ + ChunkX + i);
211  int Val2 = m_Noise.IntNoise2DInt(ChunkZ * ChunkX + i, ChunkZ - ChunkX * i);
212  int BlockVal = m_Noise.IntNoise2DInt(Val1, Val2);
213 
214  int PosX = Val1 % (cChunkDef::Width - RANGE_FROM_CENTER * 2) + RANGE_FROM_CENTER;
215  int PosZ = Val2 % (cChunkDef::Width - RANGE_FROM_CENTER * 2) + RANGE_FROM_CENTER;
216 
217  int TotalWeight = 0;
218  for (const auto & Block : PossibleBlocks)
219  {
220  TotalWeight += Block.m_Weight;
221  }
222 
223  // Prevent division by 0
224  TotalWeight = (TotalWeight != 0) ? TotalWeight : 1;
225  int Weight = BlockVal % TotalWeight;
226  for (const auto & Block : PossibleBlocks)
227  {
228  Weight -= Block.m_Weight;
229  if (Weight < 0)
230  {
231  TryPlaceFoliageClump(a_ChunkDesc, PosX, PosZ, Block.m_BlockType, Block.m_BlockMeta, Block.m_BlockType == E_BLOCK_BIG_FLOWER);
232  break;
233  }
234  }
235  }
236 }
237 
238 
239 
240 
241 
242 void cFinishGenClumpTopBlock::TryPlaceFoliageClump(cChunkDesc & a_ChunkDesc, int a_CenterX, int a_CenterZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_IsDoubleTall)
243 {
244  int ChunkX = a_ChunkDesc.GetChunkX();
245  int ChunkZ = a_ChunkDesc.GetChunkZ();
246 
247  int NumBlocks = m_Noise.IntNoise2DInt(a_CenterX + ChunkX * 16, a_CenterZ + ChunkZ * 16) % (MAX_NUM_FOLIAGE - MIN_NUM_FOLIAGE) + MIN_NUM_FOLIAGE + 1;
248  for (int i = 1; i < NumBlocks; i++)
249  {
250  int Rnd = m_Noise.IntNoise2DInt(ChunkX + ChunkZ + i, ChunkX - ChunkZ - i) / 59;
251  int x = a_CenterX + (((Rnd % 59) % RANGE_FROM_CENTER * 2) - RANGE_FROM_CENTER);
252  int z = a_CenterZ + (((Rnd / 97) % RANGE_FROM_CENTER * 2) - RANGE_FROM_CENTER);
253  int Top = a_ChunkDesc.GetHeight(x, z);
254 
255  // Doesn't place if the blocks can't be placed. Checked value also depends on a_IsDoubleTall
256  if (Top + 1 + (a_IsDoubleTall ? 1 : 0) >= cChunkDef::Height)
257  {
258  continue;
259  }
260 
261  auto GroundBlockType = a_ChunkDesc.GetBlockType(x, Top, z);
262  if (
263  (GroundBlockType == E_BLOCK_GRASS) || (
264  (GroundBlockType == E_BLOCK_MYCELIUM) && ((a_BlockType == E_BLOCK_RED_MUSHROOM) || (a_BlockType == E_BLOCK_BROWN_MUSHROOM))
265  )
266  )
267  {
268  a_ChunkDesc.SetBlockTypeMeta(x, Top + 1, z, a_BlockType, a_BlockMeta);
269  if (a_IsDoubleTall)
270  {
271  a_ChunkDesc.SetBlockTypeMeta(x, Top + 2, z, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
272  a_ChunkDesc.SetHeight(x, z, static_cast<HEIGHTTYPE>(Top + 2));
273  }
274  else
275  {
276  a_ChunkDesc.SetHeight(x, z, static_cast<HEIGHTTYPE>(Top + 1));
277  }
278  }
279  }
280 
281 }
282 
283 
284 
285 
286 
287 void cFinishGenClumpTopBlock::ParseConfigurationString(const AString & a_RawClumpInfo, std::vector<BiomeInfo> & a_Output)
288 {
289  // Initialize the vector for all biomes.
290  for (int i = static_cast<int>(a_Output.size()); i <= static_cast<int>(biMaxVariantBiome); i++)
291  {
292  // Push empty BiomeInfo structure to be later directly accessed by index:
293  a_Output.emplace_back();
294  }
295 
296  AStringVector ClumpInfo = StringSplitAndTrim(a_RawClumpInfo, "=");
297 
298  // Information about a clump is divided in 2 parts. The biomes they can be in and the blocks that can be placed.
299  if (ClumpInfo.size() != 2)
300  {
301  LOGWARNING("OverworldClumpFoliage: Data missing for \"%s\". Please divide biome and blocks with a semi colon", a_RawClumpInfo.c_str());
302  return;
303  }
304 
305  AStringVector Biomes = StringSplitAndTrim(ClumpInfo[0], ";");
306  AStringVector Blocks = StringSplitAndTrim(ClumpInfo[1], ";");
307 
308  for (const auto & RawBiomeInfo : Biomes)
309  {
310  const AStringVector BiomeInfo = StringSplitAndTrim(RawBiomeInfo, ",");
311  const AString & BiomeName = BiomeInfo[0];
312  const EMCSBiome Biome = StringToBiome(BiomeName);
313  if (Biome == biInvalidBiome)
314  {
315  LOGWARNING("Biome \"%s\" is invalid.", BiomeName.c_str());
316  continue;
317  }
318 
319  // The index of Biome in the output vector.
320  const size_t BiomeIndex = static_cast<size_t>(Biome);
321 
322  if (BiomeInfo.size() == 2)
323  {
324  // Only the minimum amount of clumps per chunk is changed.
325  int MinNumClump = 1;
326  if (!StringToInteger(BiomeInfo[1], MinNumClump))
327  {
328  LOGWARNING("OverworldClumpFoliage: Invalid data in \"%s\". Second parameter is either not existing or a number", RawBiomeInfo.c_str());
329  continue;
330  }
331  a_Output[BiomeIndex].m_MinNumClumpsPerChunk = MinNumClump;
332 
333  // In case the minimum number is higher than the current maximum value we change the max to the minimum value.
334  a_Output[BiomeIndex].m_MaxNumClumpsPerChunk = std::max(MinNumClump, a_Output[BiomeIndex].m_MaxNumClumpsPerChunk);
335  }
336  else if (BiomeInfo.size() == 3)
337  {
338  // Both the minimum and maximum amount of clumps per chunk is changed.
339  int MinNumClumps = 0, MaxNumClumps = 1;
340  if (!StringToInteger(BiomeInfo[1], MinNumClumps) || !StringToInteger(BiomeInfo[2], MaxNumClumps))
341  {
342  LOGWARNING("Invalid data in \"%s\". Second parameter is either not existing or a number", RawBiomeInfo.c_str());
343  continue;
344  }
345 
346  a_Output[BiomeIndex].m_MaxNumClumpsPerChunk = MaxNumClumps + 1;
347  a_Output[BiomeIndex].m_MinNumClumpsPerChunk = MinNumClumps;
348  }
349 
350  // TODO: Make the weight configurable.
351  for (const auto & BlockName : Blocks)
352  {
353  cItem Block;
354  if (!StringToItem(BlockName, Block) || !IsValidBlock(Block.m_ItemType))
355  {
356  LOGWARNING("Block \"%s\" is invalid", BlockName.c_str());
357  continue;
358  }
359 
360  // Construct the FoliageInfo:
361  a_Output[BiomeIndex].m_Blocks.emplace_back(
362  static_cast<BLOCKTYPE>(Block.m_ItemType), static_cast<NIBBLETYPE>(Block.m_ItemDamage), 100
363  );
364  }
365  }
366 }
367 
368 
369 
370 
371 
372 std::vector<cFinishGenClumpTopBlock::BiomeInfo> cFinishGenClumpTopBlock::ParseIniFile(cIniFile & a_IniFile, const AString & a_ClumpPrefix)
373 {
374  std::vector<cFinishGenClumpTopBlock::BiomeInfo> Foliage;
375  int NumGeneratorValues = a_IniFile.GetNumValues("Generator");
376  int GeneratorKeyId = a_IniFile.FindKey("Generator");
377  for (int i = 0; i < NumGeneratorValues; i++)
378  {
379  AString ValueName = a_IniFile.GetValueName("Generator", i);
380  if (ValueName.substr(0, a_ClumpPrefix.size()) == a_ClumpPrefix)
381  {
382  AString RawClump = a_IniFile.GetValue(GeneratorKeyId, i);
384  }
385  }
386 
387  if (Foliage.empty())
388  {
389  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-1", "Forest, -2, 2; ForestHills, -3, 2; FlowerForest = yellowflower; redflower; lilac; rosebush"), Foliage);
390  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-2", "Plains, -2, 1; SunflowerPlains = yellowflower; redflower; azurebluet; redtulip; orangetulip; whitetulip; pinktulip; oxeyedaisy"), Foliage);
391  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-3", "SunflowerPlains, 1, 2 = sunflower"), Foliage);
392  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-4", "FlowerForest, 2, 5 = allium; redtulip; orangetulip; whitetulip; pinktulip; oxeyedaisy"), Foliage);
393  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-5", "Swampland; SwamplandM = brownmushroom; redmushroom; blueorchid"), Foliage);
394  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-6", "MushroomIsland; MushroomShore; MegaTaiga; MegaTaigaHills; MegaSpruceTaiga; MegaSpruceTaigaHills = brownmushroom; redmushroom"), Foliage);
395  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-7", "RoofedForest, 1, 5; RoofedForestM, 1, 5 = rosebush; peony; lilac; grass"), Foliage);
396  cFinishGenClumpTopBlock::ParseConfigurationString(a_IniFile.GetValueSet("Generator", a_ClumpPrefix + "-8", "MegaTaiga; MegaTaigaHills = deadbush"), Foliage);
397  }
398 
399  return Foliage;
400 }
401 
402 
403 
404 
405 
407 // cFinishGenGlowStone:
408 
410 {
411  int ChunkX = a_ChunkDesc.GetChunkX();
412  int ChunkZ = a_ChunkDesc.GetChunkZ();
413 
414  // Change the number of attempts to create a vein depending on the maximum height of the chunk. A standard Nether could have 5 veins at most.
415  int NumGlowStone = m_Noise.IntNoise2DInt(ChunkX, ChunkZ) % a_ChunkDesc.GetMaxHeight() / 23;
416 
417  for (int i = 1; i <= NumGlowStone; i++)
418  {
419  // The maximum size for a string of glowstone can get 3 - 5 blocks long
420  int Size = 3 + m_Noise.IntNoise3DInt(ChunkX, i, ChunkZ) % 3;
421 
422  // Generate X / Z coordinates.
423  int X = Size + (m_Noise.IntNoise2DInt(i, Size) % (cChunkDef::Width - Size * 2));
424  int Z = Size + (m_Noise.IntNoise2DInt(X, i) % (cChunkDef::Width - Size * 2));
425 
426  int Height = a_ChunkDesc.GetHeight(X, Z);
427  for (int y = Height; y > Size; y--)
428  {
429  if (!cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(X, y, Z)))
430  {
431  // Current block isn't solid, bail out
432  continue;
433  }
434 
435  if (a_ChunkDesc.GetBlockType(X, y - 1, Z) != E_BLOCK_AIR)
436  {
437  // The block below isn't air, bail out
438  continue;
439  }
440 
441  if ((m_Noise.IntNoise3DInt(X, y, Z) % 100) < 95)
442  {
443  // Have a 5% chance of creating the glowstone
444  continue;
445  }
446 
447  TryPlaceGlowstone(a_ChunkDesc, X, y, Z, Size, 5 + m_Noise.IntNoise3DInt(X, y, Z) % 7);
448  break;
449  }
450  }
451 }
452 
453 
454 
455 
456 
457 void cFinishGenGlowStone::TryPlaceGlowstone(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings)
458 {
459  // The starting point of every glowstone string
460  Vector3i StartPoint = Vector3i(a_RelX, a_RelY, a_RelZ);
461 
462  // Array with possible directions for a string of glowstone to go to.
463  const Vector3i AvailableDirections[] =
464  {
465  { -1, 0, 0 }, { 1, 0, 0 },
466  { 0, -1, 0 }, // Don't let the glowstone go up
467  { 0, 0, -1 }, { 0, 0, 1 },
468 
469  // Diagonal direction. Only X or Z with Y.
470  // If all were changed the glowstone string looks awkward
471  { 0, -1, 1 }, { 1, -1, 0 },
472  { 0, -1, -1 }, { -1, -1, 0 },
473 
474  };
475 
476  for (int i = 1; i <= a_NumStrings; i++)
477  {
478  // The current position of the string that is being generated
479  Vector3i CurrentPos = Vector3i(StartPoint);
480 
481  // A vector where the previous direction of a glowstone string is stored.
482  // This is used to make the strings change direction when going one block further
483  Vector3i PreviousDirection = Vector3i();
484 
485  for (int j = 0; j < a_Size; j++)
486  {
487  Vector3i Direction = AvailableDirections[static_cast<size_t>(m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i, CurrentPos.z)) % ARRAYCOUNT(AvailableDirections)];
488  int Attempts = 2; // multiply by 1 would make no difference, so multiply by 2 instead
489 
490  while (Direction.Equals(PreviousDirection))
491  {
492  // To make the glowstone branches look better we want to make the direction change every time.
493  Direction = AvailableDirections[static_cast<size_t>(m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i * Attempts, CurrentPos.z)) % ARRAYCOUNT(AvailableDirections)];
494  Attempts++;
495  }
496 
497  // Update the previous direction variable
498  PreviousDirection = Direction;
499 
500  // Update the position of the glowstone string
501  CurrentPos += Direction;
502  if (cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z)) && (a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z) != E_BLOCK_GLOWSTONE))
503  {
504  // The glowstone hit something solid, and it wasn't glowstone. Stop the string.
505  break;
506  }
507 
508  // Place a glowstone block.
509  a_ChunkDesc.SetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z, E_BLOCK_GLOWSTONE);
510  }
511  }
512 }
513 
514 
515 
516 
517 
519 // cFinishGenTallGrass:
520 
522 {
523  for (int x = 0; x < cChunkDef::Width; x++)
524  {
525  int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width;
526  for (int z = 0; z < cChunkDef::Width; z++)
527  {
528  int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
529  int BiomeDensity = GetBiomeDensity(a_ChunkDesc.GetBiome(x, z));
530 
531  // Choose if we want to place long grass here. If not then bail out:
532  if ((m_Noise.IntNoise2DInt(xx + m_Noise.IntNoise1DInt(xx), zz + m_Noise.IntNoise1DInt(zz)) / 7 % 100) > BiomeDensity)
533  {
534  continue;
535  }
536 
537  // Get the top block + 1. This is the place where the grass would finaly be placed:
538  int y = a_ChunkDesc.GetHeight(x, z) + 1;
539 
540  if (y >= cChunkDef::Height - 1)
541  {
542  continue;
543  }
544 
545  // Walk below trees:
546  auto BlockBelow = a_ChunkDesc.GetBlockType(x, y - 1, z);
547  bool failed = false; // marker if the search for a valid position was successful
548 
549  while (
550  (BlockBelow == E_BLOCK_LEAVES) ||
551  (BlockBelow == E_BLOCK_NEW_LEAVES) ||
552  (BlockBelow == E_BLOCK_LOG) ||
553  (BlockBelow == E_BLOCK_NEW_LOG) ||
554  (BlockBelow == E_BLOCK_AIR)
555  )
556  {
557  y--;
558  if (!cChunkDef::IsValidHeight({x, y - 1, z}))
559  {
560  failed = true;
561  break;
562  }
563  BlockBelow = a_ChunkDesc.GetBlockType(x, y - 1, z);
564  }
565 
566  if (failed)
567  {
568  continue;
569  }
570 
571  // Check if long grass can be placed:
572  if (
573  (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) ||
574  ((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_GRASS) && (a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_DIRT))
575 
576  )
577  {
578  continue;
579  }
580 
581  // Choose what long grass meta we should use:
582  int GrassType = m_Noise.IntNoise2DInt(xx * 50, zz * 50) / 7 % 100;
583  if ((GrassType < 60) && CanGrassGrow(a_ChunkDesc.GetBiome(x, z)))
584  {
586  }
587  else if ((GrassType < 90) && CanFernGrow(a_ChunkDesc.GetBiome(x, z)))
588  {
590  }
591  else if (!IsBiomeVeryCold(a_ChunkDesc.GetBiome(x, z)))
592  {
593  // If double long grass we have to choose what type we should use:
594  if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR)
595  {
596  NIBBLETYPE Meta;
597  if (CanGrassGrow(a_ChunkDesc.GetBiome(x, z)))
598  {
599  Meta = (m_Noise.IntNoise2DInt(xx * 100, zz * 100) / 7 % 100) > 25 ? E_META_BIG_FLOWER_DOUBLE_TALL_GRASS : E_META_BIG_FLOWER_LARGE_FERN;
600  }
601  else
602  {
604  }
605 
606  if ((Meta != E_META_BIG_FLOWER_LARGE_FERN) || CanLargeFernGrow(a_ChunkDesc.GetBiome(x, z)))
607  {
608  a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_BIG_FLOWER, Meta);
609  a_ChunkDesc.SetBlockTypeMeta(x, y + 1, z, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
610  a_ChunkDesc.SetHeight(x, z, static_cast<HEIGHTTYPE>(y + 1));
611  }
612  }
613  }
614  else
615  {
616  NIBBLETYPE meta = static_cast<NIBBLETYPE>((m_Noise.IntNoise2DInt(xx * 50, zz * 50) / 7 % 2) + 1);
617  a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_TALL_GRASS, meta);
618  a_ChunkDesc.SetHeight(x, z, static_cast<HEIGHTTYPE>(y));
619  }
620  }
621  }
622 }
623 
624 
625 
626 
627 
629 {
630  switch (a_Biome)
631  {
632  case biJungle:
633  case biJungleEdge:
634  case biJungleEdgeM:
635  case biJungleHills:
636  case biJungleM:
637  {
638  return true;
639  }
640 
641  default:
642  {
643  return CanLargeFernGrow(a_Biome);
644  }
645  }
646 }
647 
648 
649 
650 
651 
653 {
654  switch (a_Biome)
655  {
656  case biColdTaiga:
657  case biColdTaigaHills:
658  case biColdTaigaM:
659 
660  case biTaiga:
661  case biTaigaHills:
662  case biTaigaM:
663 
664  case biMegaSpruceTaiga:
666  case biMegaTaiga:
667  case biMegaTaigaHills:
668  {
669  return true;
670  }
671 
672  default:
673  {
674  return false;
675  }
676  }
677 }
678 
679 
680 
681 
682 
684 {
685  switch (a_Biome)
686  {
687  case biSavanna:
688  case biSavannaM:
689  case biSavannaPlateau:
690  case biSavannaPlateauM:
691  case biPlains:
692  {
693  return 70;
694  }
695 
696  case biExtremeHillsEdge:
697  case biExtremeHillsPlus:
698  case biExtremeHills:
699  case biExtremeHillsPlusM:
700  case biExtremeHillsM:
701  case biIceMountains:
702  {
703  return 3;
704  }
705 
706  default:
707  {
708  return 20;
709  }
710  }
711 }
712 
713 
714 
715 
716 
718 {
719  switch (a_Biome)
720  {
721  case biMegaTaiga:
722  case biMegaTaigaHills:
723  {
724  return false;
725  }
726  default:
727  {
728  return true;
729  }
730  }
731 }
732 
733 
734 
735 
736 
738 // cFinishGenVines
739 
741 {
742  switch (a_Biome)
743  {
744  case biJungle:
745  case biJungleEdge:
746  case biJungleEdgeM:
747  case biJungleHills:
748  case biJungleM:
749  {
750  return true;
751  }
752  default:
753  {
754  return false;
755  }
756  }
757 }
758 
759 
760 
761 
762 
764 {
765  for (int x = 0; x < cChunkDef::Width; x++)
766  {
767  int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width;
768  for (int z = 0; z < cChunkDef::Width; z++)
769  {
770  int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
771  if (!IsJungleVariant(a_ChunkDesc.GetBiome(x, z)))
772  {
773  // Current biome isn't a jungle
774  continue;
775  }
776 
777  if ((m_Noise.IntNoise2DInt(xx, zz) % 101) < 50)
778  {
779  continue;
780  }
781 
782  int Height = a_ChunkDesc.GetHeight(x, z);
783  for (int y = Height; y > m_Level; y--)
784  {
785  if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
786  {
787  // Can't place vines in non-air blocks
788  continue;
789  }
790 
791  if ((m_Noise.IntNoise3DInt(xx, y, zz) % 101) < 50)
792  {
793  continue;
794  }
795 
796  std::vector<NIBBLETYPE> Places;
797  if ((x + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x + 1, y, z)))
798  {
799  Places.push_back(8);
800  }
801 
802  if ((x - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x - 1, y, z)))
803  {
804  Places.push_back(2);
805  }
806 
807  if ((z + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z + 1)))
808  {
809  Places.push_back(1);
810  }
811 
812  if ((z - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z - 1)))
813  {
814  Places.push_back(4);
815  }
816 
817  if (Places.size() == 0)
818  {
819  continue;
820  }
821 
822  NIBBLETYPE Meta = Places[static_cast<size_t>(m_Noise.IntNoise3DInt(xx, y, zz)) % Places.size()];
823  a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_VINES, Meta);
824  }
825  }
826  }
827 }
828 
829 
830 
831 
832 
834 // cFinishGenSprinkleFoliage:
835 
836 bool cFinishGenSprinkleFoliage::TryAddCactus(cChunkDesc & a_ChunkDesc, int a_RelX, HEIGHTTYPE & a_RelY, int a_RelZ)
837 {
838  if (!IsDesertVariant(a_ChunkDesc.GetBiome(a_RelX, a_RelZ)))
839  {
840  return false;
841  }
842 
843  int CactusHeight = 1 + (m_Noise.IntNoise2DInt(a_RelX, a_RelZ) % m_MaxCactusHeight);
844 
845  // We'll be doing comparison with blocks above, so the coords should be 1 block away from chunk top
846  if (a_RelY + CactusHeight >= cChunkDef::Height - 1)
847  {
848  CactusHeight = cChunkDef::Height - a_RelY - 1;
849  }
850 
851  // We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges:
852  if (
853  (a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) ||
854  (a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1)
855  )
856  {
857  return false;
858  }
859 
860  for (int i = 0; i < CactusHeight; i++)
861  {
862  const bool cactusExists = i != 0;
863 
864  const int y = a_RelY + 1;
865  if (
866  cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(a_RelX + 1, y, a_RelZ)) ||
867  cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(a_RelX - 1, y, a_RelZ)) ||
868  cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(a_RelX, y, a_RelZ + 1)) ||
869  cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(a_RelX, y, a_RelZ - 1))
870  )
871  {
872  return cactusExists;
873  }
874 
875  // All conditions are met, we can place a cactus here
876  a_ChunkDesc.SetBlockType(a_RelX, ++a_RelY, a_RelZ, E_BLOCK_CACTUS);
877  }
878 
879  return true;
880 }
881 
883 // cFinishGenSprinkleFoliage:
884 
885 bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, HEIGHTTYPE & a_RelY, int a_RelZ)
886 {
887  int SugarcaneHeight = 1 + (m_Noise.IntNoise2DInt(a_RelX, a_RelZ) % m_MaxSugarcaneHeight);
888 
889  // Only allow dirt, grass, sand and sugarcane below sugarcane:
890  switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ))
891  {
892  case E_BLOCK_DIRT:
893  case E_BLOCK_GRASS:
894  case E_BLOCK_SAND:
895  case E_BLOCK_SUGARCANE:
896  {
897  break;
898  }
899  default:
900  {
901  return false;
902  }
903  }
904 
905  // We'll be doing comparison with blocks above, so the coords should be 1 block away from chunk top
906  if (a_RelY + SugarcaneHeight >= cChunkDef::Height - 1)
907  {
908  SugarcaneHeight = cChunkDef::Height - a_RelY - 1;
909  }
910 
911  // We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges:
912  if (
913  (a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) ||
914  (a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1)
915  )
916  {
917  return false;
918  }
919 
920  // Water is required next to the block below the sugarcane (if the block below isn't sugarcane already)
921  if (
922  !IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) &&
923  !IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) &&
924  !IsWater(a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ - 1)) &&
925  !IsWater(a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ + 1)) &&
926  a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ) != E_BLOCK_SUGARCANE
927  )
928  {
929  return false;
930  }
931 
932  for (int i = 0; i < SugarcaneHeight; i++)
933  {
934  // All conditions met, place a sugarcane here
935  a_ChunkDesc.SetBlockType(a_RelX, ++a_RelY, a_RelZ, E_BLOCK_SUGARCANE);
936  }
937 
938  return true;
939 }
940 
941 
942 
943 
944 
946 {
947  // Generate small foliage (1-block):
948 
949  // TODO: Update heightmap with 1-block-tall foliage
950  for (int z = 0; z < cChunkDef::Width; z++)
951  {
952  int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
953  const float zz = static_cast<float>(BlockZ);
954  for (int x = 0; x < cChunkDef::Width; x++)
955  {
956  int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
957  if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124)
958  {
959  continue;
960  }
961  HEIGHTTYPE Top = a_ChunkDesc.GetHeight(x, z);
962  if (Top > 250)
963  {
964  // Nothing grows above Y=250
965  continue;
966  }
967  if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR)
968  {
969  // Space already taken by something else, don't grow here
970  // WEIRD, since we're using heightmap, so there should NOT be anything above it
971  continue;
972  }
973 
974  const float xx = static_cast<float>(BlockX);
975  float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f);
976  float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
977  switch (a_ChunkDesc.GetBlockType(x, Top, z))
978  {
979  case E_BLOCK_GRASS:
980  {
981  if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
982  {
983  // Checks and block placing are handled in the TryAddSugarcane method
984  }
985  else if ((val1 > 0.5) && (val2 < -0.5))
986  {
987  float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10);
988  a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, static_cast<unsigned>(val3 * 8) % 4);
989  }
990  break;
991  } // case E_BLOCK_GRASS
992 
993  case E_BLOCK_SAND:
994  {
995  if (val1 + val2 > 0.5f)
996  {
997  if (!TryAddCactus(a_ChunkDesc, x, Top, z))
998  {
999  TryAddSugarcane(a_ChunkDesc, x, Top, z);
1000  }
1001  }
1002  else
1003  {
1004  TryAddSugarcane(a_ChunkDesc, x, Top, z);
1005  }
1006  break;
1007  }
1008  } // switch (TopBlock)
1009  a_ChunkDesc.SetHeight(x, z, Top);
1010  } // for x
1011  } // for z
1012 }
1013 
1014 
1015 
1016 
1017 
1019 {
1020  return
1021  (
1022  (a_Biome == biDesertHills) ||
1023  (a_Biome == biDesert) ||
1024  (a_Biome == biDesertM)
1025  );
1026 }
1027 
1028 
1029 
1030 
1031 
1033 // cFinishGenSoulsandRims
1034 
1036 {
1037  int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
1038  int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
1039  HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
1040 
1041  for (int x = 0; x < 16; x++)
1042  {
1043  int xx = ChunkX + x;
1044  for (int z = 0; z < 16; z++)
1045  {
1046  int zz = ChunkZ + z;
1047 
1048  // Place soulsand rims when netherrack gets thin
1049  for (int y = 2; y < MaxHeight - 2; y++)
1050  {
1051  // The current block is air. Let's bail ut.
1052  BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
1053  if (Block != E_BLOCK_NETHERRACK)
1054  {
1055  continue;
1056  }
1057 
1058  if (
1059  ((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) &&
1060  ( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) ||
1061  ((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) &&
1062  ( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR))
1063  )
1064  {
1065  continue;
1066  }
1067 
1068  NOISE_DATATYPE NoiseX = (static_cast<NOISE_DATATYPE>(xx)) / 32;
1069  NOISE_DATATYPE NoiseY = (static_cast<NOISE_DATATYPE>(zz)) / 32;
1070  NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, static_cast<float>(y) / 4, NoiseY);
1071  if (CompBlock < 0)
1072  {
1073  a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND);
1074  }
1075  }
1076  }
1077  }
1078 }
1079 
1080 
1081 
1082 
1083 
1085 // cFinishGenSnow:
1086 
1088 {
1089  // Add a snow block in snowy biomes onto blocks that can be snowed over
1090  for (int z = 0; z < cChunkDef::Width; z++)
1091  {
1092  for (int x = 0; x < cChunkDef::Width; x++)
1093  {
1094  HEIGHTTYPE Height = a_ChunkDesc.GetHeight(x, z);
1095  if (GetSnowStartHeight(a_ChunkDesc.GetBiome(x, z)) > Height)
1096  {
1097  // Height isn't high enough for snow to start forming.
1098  continue;
1099  }
1100 
1101  if (!cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) || (Height >= cChunkDef::Height - 1))
1102  {
1103  // The top block can't be snown over.
1104  continue;
1105  }
1106 
1107  a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
1108  a_ChunkDesc.SetHeight(x, z, Height + 1);
1109  } // for x
1110  } // for z
1111 }
1112 
1113 
1114 
1115 
1116 
1118 // cFinishGenIce:
1119 
1121 {
1122  // Turn surface water into ice in icy biomes
1123  for (int z = 0; z < cChunkDef::Width; z++)
1124  {
1125  for (int x = 0; x < cChunkDef::Width; x++)
1126  {
1127  int Height = a_ChunkDesc.GetHeight(x, z);
1128  if (GetSnowStartHeight(a_ChunkDesc.GetBiome(x, z)) > Height)
1129  {
1130  // Height isn't high enough for snow to start forming.
1131  continue;
1132  }
1133 
1134  if (!IsBlockWater(a_ChunkDesc.GetBlockType(x, Height, z)))
1135  {
1136  // The block isn't a water block.
1137  continue;
1138  }
1139 
1140  if (a_ChunkDesc.GetBlockMeta(x, Height, z) != 0)
1141  {
1142  // The water block isn't a source block.
1143  continue;
1144  }
1145 
1146  a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE);
1147  } // for x
1148  } // for z
1149 }
1150 
1151 
1152 
1153 
1154 
1156 // cFinishGenSingleTopBlock:
1157 
1159 {
1160  int res = 0;
1161  for (size_t i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
1162  {
1163  if (IsAllowedBiome(a_BiomeMap[i]))
1164  {
1165  res++;
1166  }
1167  } // for i - a_BiomeMap[]
1168  return m_Amount * res / 256;
1169 }
1170 
1171 
1172 
1173 
1174 
1176 {
1177  int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap());
1178  int ChunkX = a_ChunkDesc.GetChunkX();
1179  int ChunkZ = a_ChunkDesc.GetChunkZ();
1180  for (int i = 0; i < NumToGen; i++)
1181  {
1182  int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width;
1183  int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width;
1184 
1185  // Place the block at {x, z} if possible:
1186  EMCSBiome Biome = a_ChunkDesc.GetBiome(x, z);
1187  if (!IsAllowedBiome(Biome))
1188  {
1189  // Incorrect biome
1190  continue;
1191  }
1192 
1193  HEIGHTTYPE Height = a_ChunkDesc.GetHeight(x, z);
1194  if (Height >= cChunkDef::Height - 1)
1195  {
1196  // Too high up
1197  continue;
1198  }
1199  if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR)
1200  {
1201  // Not an empty block
1202  continue;
1203  }
1204 
1205  BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z);
1206  if (!IsAllowedBlockBelow(BlockBelow))
1207  {
1208  continue;
1209  }
1210 
1211  a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
1212  a_ChunkDesc.SetHeight(x, z, Height + 1);
1213  }
1214 }
1215 
1216 
1217 
1218 
1219 
1221 // cFinishGenBottomLava:
1222 
1224 {
1225  cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
1226  for (int y = m_Level; y > 0; y--)
1227  {
1228  for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
1229  {
1230  const auto Index = cChunkDef::MakeIndex(x, y, z);
1231  if (BlockTypes[Index] == E_BLOCK_AIR)
1232  {
1233  BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA;
1234  }
1235  } // for x, for z
1236  } // for y
1237 }
1238 
1239 
1240 
1241 
1242 
1244 // cFinishGenPreSimulator:
1245 
1246 cFinishGenPreSimulator::cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava) :
1247  m_PreSimulateFallingBlocks(a_PreSimulateFallingBlocks),
1248  m_PreSimulateWater(a_PreSimulateWater),
1249  m_PreSimulateLava(a_PreSimulateLava)
1250 {
1251  // Nothing needed yet
1252 }
1253 
1254 
1255 
1256 
1257 
1259 {
1261  {
1262  CollapseSandGravel(a_ChunkDesc);
1263  }
1264 
1265  if (m_PreSimulateWater)
1266  {
1268  }
1269 
1270  if (m_PreSimulateLava)
1271  {
1273  }
1274  // TODO: other operations
1275 }
1276 
1277 
1278 
1279 
1280 
1282 {
1283  for (int z = 0; z < cChunkDef::Width; z++)
1284  {
1285  for (int x = 0; x < cChunkDef::Width; x++)
1286  {
1287  int LastY = -1;
1288  int HeightY = 0;
1289  for (int y = 0; y < cChunkDef::Height; y++)
1290  {
1291  BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
1292  switch (Block)
1293  {
1294  default:
1295  {
1296  // Set the last block onto which stuff can fall to this height:
1297  LastY = y;
1298  HeightY = y;
1299  break;
1300  }
1301  case E_BLOCK_AIR:
1302  {
1303  // Do nothing
1304  break;
1305  }
1306  case E_BLOCK_FIRE:
1307  case E_BLOCK_WATER:
1309  case E_BLOCK_LAVA:
1311  {
1312  // Do nothing, only remember this height as potentially highest
1313  HeightY = y;
1314  break;
1315  }
1316  case E_BLOCK_SAND:
1317  case E_BLOCK_GRAVEL:
1318  {
1319  if (LastY < y - 1)
1320  {
1321  auto BlockMeta = a_ChunkDesc.GetBlockMeta(x, y, z);
1322  a_ChunkDesc.SetBlockTypeMeta(x, LastY + 1, z, Block, BlockMeta);
1323  a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
1324  }
1325  LastY++;
1326  if (LastY > HeightY)
1327  {
1328  HeightY = LastY;
1329  }
1330  break;
1331  }
1332  } // switch (GetBlock)
1333  } // for y
1334  a_ChunkDesc.SetHeight(x, z, static_cast<HEIGHTTYPE>(HeightY));
1335  } // for x
1336  } // for z
1337 }
1338 
1339 
1340 
1341 
1342 
1344  cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
1345  cChunkDef::HeightMap & a_HeightMap, // Height map to read
1346  BLOCKTYPE a_Fluid,
1347  BLOCKTYPE a_StationaryFluid
1348 )
1349 {
1350  // Turn fluid in the middle to stationary, unless it has air or washable block next to it:
1351  for (int z = 1; z < cChunkDef::Width - 1; z++)
1352  {
1353  for (int x = 1; x < cChunkDef::Width - 1; x++)
1354  {
1355  for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--)
1356  {
1357  BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
1358  if ((Block != a_Fluid) && (Block != a_StationaryFluid))
1359  {
1360  continue;
1361  }
1362  static const struct
1363  {
1364  int x, y, z;
1365  } Coords[] =
1366  {
1367  {1, 0, 0},
1368  {-1, 0, 0},
1369  {0, 0, 1},
1370  {0, 0, -1},
1371  {0, -1, 0}
1372  } ;
1373  BLOCKTYPE BlockToSet = a_StationaryFluid; // By default, don't simulate this block
1374  for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
1375  {
1376  if ((y == 0) && (Coords[i].y < 0))
1377  {
1378  continue;
1379  }
1380  BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z);
1381  if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor))
1382  {
1383  // There is an air / washable neighbor, simulate this block
1384  BlockToSet = a_Fluid;
1385  break;
1386  }
1387  } // for i - Coords[]
1388  cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet);
1389  } // for y
1390  } // for x
1391  } // for z
1392 
1393  // Turn fluid at the chunk edges into non-stationary fluid:
1394  for (int y = 0; y < cChunkDef::Height; y++)
1395  {
1396  for (int i = 0; i < cChunkDef::Width; i++) // i stands for both x and z here
1397  {
1398  if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid)
1399  {
1400  cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid);
1401  }
1402  if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid)
1403  {
1404  cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid);
1405  }
1406  if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid)
1407  {
1408  cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid);
1409  }
1410  if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid)
1411  {
1412  cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid);
1413  }
1414  }
1415  }
1416 }
1417 
1418 
1419 
1420 
1421 
1423 // cFinishGenFluidSprings:
1424 
1425 cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension) :
1426  m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other
1427  m_HeightDistribution(cChunkDef::Height - 1),
1428  m_Fluid(a_Fluid)
1429 {
1430  bool IsWater = (a_Fluid == E_BLOCK_WATER);
1431  AString SectionName = IsWater ? "WaterSprings" : "LavaSprings";
1432  AString DefaultHeightDistribution;
1433  int DefaultChance = 0;
1434  switch (a_Dimension)
1435  {
1436  case dimNether:
1437  {
1438  DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS;
1439  DefaultChance = IsWater ? 0 : 15;
1440  break;
1441  }
1442  case dimOverworld:
1443  {
1444  DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS;
1445  DefaultChance = IsWater ? 24 : 9;
1446  break;
1447  }
1448  case dimEnd:
1449  {
1450  DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS;
1451  DefaultChance = 0;
1452  break;
1453  }
1454  default:
1455  {
1456  ASSERT(!"Unhandled world dimension");
1457  break;
1458  }
1459  } // switch (dimension)
1460  AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution);
1461  if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0))
1462  {
1463  LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".",
1464  (a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava",
1465  DefaultHeightDistribution.c_str()
1466  );
1467  m_HeightDistribution.SetDefString(DefaultHeightDistribution);
1468  }
1469  m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance);
1470 }
1471 
1472 
1473 
1474 
1475 
1477 {
1478  int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100;
1479  if (ChanceRnd > m_Chance)
1480  {
1481  // Not in this chunk
1482  return;
1483  }
1484 
1485  // Get the height at which to try:
1486  int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
1487  Height %= m_HeightDistribution.GetSum();
1488  Height = m_HeightDistribution.MapValue(Height);
1489 
1490  // Try adding the spring at the height, if unsuccessful, move lower:
1491  for (int y = Height; y > 1; y--)
1492  {
1493  // TODO: randomize the order in which the coords are being checked
1494  for (int z = 1; z < cChunkDef::Width - 1; z++)
1495  {
1496  for (int x = 1; x < cChunkDef::Width - 1; x++)
1497  {
1498  switch (a_ChunkDesc.GetBlockType(x, y, z))
1499  {
1500  case E_BLOCK_NETHERRACK:
1501  case E_BLOCK_STONE:
1502  {
1503  if (TryPlaceSpring(a_ChunkDesc, x, y, z))
1504  {
1505  // Succeeded, bail out
1506  return;
1507  }
1508  }
1509  } // switch (BlockType)
1510  } // for x
1511  } // for y
1512  } // for y
1513 }
1514 
1515 
1516 
1517 
1518 
1519 bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z)
1520 {
1521  // In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air
1522  // Also, its neighbor on top of it must be non-air
1523  if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR)
1524  {
1525  return false;
1526  }
1527 
1528  static const struct
1529  {
1530  int x, y, z;
1531  } Coords[] =
1532  {
1533  {-1, 0, 0},
1534  { 1, 0, 0},
1535  { 0, -1, 0},
1536  { 0, 0, -1},
1537  { 0, 0, 1},
1538  } ;
1539  int NumAirNeighbors = 0;
1540  for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
1541  {
1542  switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z))
1543  {
1544  case E_BLOCK_AIR:
1545  {
1546  NumAirNeighbors += 1;
1547  if (NumAirNeighbors > 1)
1548  {
1549  return false;
1550  }
1551  }
1552  }
1553  }
1554  if (NumAirNeighbors == 0)
1555  {
1556  return false;
1557  }
1558 
1559  // Has exactly one air neighbor, place a spring:
1560  a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
1561  return true;
1562 }
1563 
1564 
1565 
1566 
1567 
1569 // cFinishGenPassiveMobs:
1570 
1572  m_Noise(a_Seed)
1573 {
1574  AString SectionName = "Animals";
1575  int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
1576  switch (a_Dimension)
1577  {
1578  case dimOverworld:
1579  {
1580  DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
1581  break;
1582  }
1583  case dimNether:
1584  case dimEnd: // No nether or end animals (currently)
1585  {
1586  DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
1587  break;
1588  }
1589  default:
1590  {
1591  ASSERT(!"Unhandled world dimension");
1592  DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
1593  break;
1594  }
1595  } // switch (dimension)
1596  m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
1597  if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
1598  {
1599  LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
1600  m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
1601  }
1602 }
1603 
1604 
1605 
1606 
1607 
1609 {
1610  int chunkX = a_ChunkDesc.GetChunkX();
1611  int chunkZ = a_ChunkDesc.GetChunkZ();
1612  int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
1613  if (ChanceRnd > m_AnimalProbability)
1614  {
1615  return;
1616  }
1617 
1618  eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
1619  if (RandomMob == mtInvalidType)
1620  {
1621  // No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well.
1622  return;
1623  }
1624 
1625  // Try spawning a pack center 10 times, should get roughly the same probability
1626  for (int Tries = 0; Tries < 10; Tries++)
1627  {
1628  int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
1629  int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
1630  if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
1631  {
1632  for (int i = 0; i < 3; i++)
1633  {
1634  int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
1635  int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
1636  TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
1637  }
1638  return;
1639 
1640  } // if pack center spawn successful
1641  } // for tries
1642 }
1643 
1644 
1645 
1646 
1647 
1648 bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
1649 {
1650  if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
1651  {
1652  return false;
1653  }
1654 
1655  BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
1656  BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
1657  BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);
1658 
1659  // Check block below (opaque, grass, water), and above (air)
1660  if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
1661  {
1662  return false;
1663  }
1664  if (
1665  (AnimalToSpawn != mtSquid) &&
1666  (BlockAtHead != E_BLOCK_AIR) &&
1667  (BlockAtFeet != E_BLOCK_AIR) &&
1668  (!cBlockInfo::IsTransparent(BlockUnderFeet))
1669  )
1670  {
1671  return false;
1672  }
1673  if (
1674  (BlockUnderFeet != E_BLOCK_GRASS) &&
1675  ((AnimalToSpawn == mtWolf) || (AnimalToSpawn == mtRabbit) || (AnimalToSpawn == mtCow) || (AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
1676  )
1677  {
1678  return false;
1679  }
1680  if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
1681  {
1682  return false;
1683  }
1684 
1685  double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5);
1686  double AnimalY = a_RelY;
1687  double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
1688 
1689  auto NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
1690  NewMob->SetHealth(NewMob->GetMaxHealth());
1691  NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
1692  FLOGD("Spawning {0} #{1} at {2:.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), NewMob->GetPosition());
1693  a_ChunkDesc.GetEntities().emplace_back(std::move(NewMob));
1694 
1695  return true;
1696 }
1697 
1698 
1699 
1700 
1701 
1703 {
1704  std::vector<eMonsterType> ListOfSpawnables;
1705  int chunkX = a_ChunkDesc.GetChunkX();
1706  int chunkZ = a_ChunkDesc.GetChunkZ();
1707  int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
1708  int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;
1709 
1710  for (auto MobType : cMobSpawner::GetAllowedMobTypes(a_ChunkDesc.GetBiome(x, z)))
1711  {
1712  if (cMonster::FamilyFromType(MobType) == cMonster::eFamily::mfPassive)
1713  {
1714  ListOfSpawnables.push_back(MobType);
1715  }
1716  }
1717 
1718  if (ListOfSpawnables.empty())
1719  {
1720  return mtInvalidType;
1721  }
1722 
1723  auto RandMob = (static_cast<size_t>(m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size());
1724  return ListOfSpawnables[RandMob];
1725 }
1726 
1727 
1728 
1729 
1730 
1732 // cFinishGenOres:
1733 
1735 {
1736  int seq = 1;
1737 
1738  // Generate the ores from the ore list.
1739  for (const auto & ore: m_OreInfos)
1740  {
1741  GenerateOre(
1742  a_ChunkDesc,
1743  ore.m_BlockType, ore.m_BlockMeta,
1744  ore.m_MaxHeight, ore.m_NumNests, ore.m_NestSize,
1745  seq
1746  );
1747  seq++;
1748  }
1749 }
1750 
1751 
1752 
1753 
1754 
1756 {
1757  static OreInfos res
1758  {
1759  // OreType, OreMeta, MaxHeight, NumNests, NestSize
1760  {E_BLOCK_COAL_ORE, 0, 127, 20, 16},
1761  {E_BLOCK_IRON_ORE, 0, 64, 20, 8},
1762  {E_BLOCK_GOLD_ORE, 0, 32, 2, 8},
1763  {E_BLOCK_REDSTONE_ORE, 0, 16, 8, 7},
1764  {E_BLOCK_DIAMOND_ORE, 0, 15, 1, 7},
1765  {E_BLOCK_LAPIS_ORE, 0, 30, 1, 6},
1766  {E_BLOCK_EMERALD_ORE, 0, 32, 11, 1},
1767  {E_BLOCK_SILVERFISH_EGG, 0, 64, 7, 9},
1768  };
1769  return res;
1770 }
1771 
1772 
1773 
1774 
1775 
1777 {
1778  static OreInfos res
1779  {
1780  // OreType, OreMeta, MaxHeight, NumNests, NestSize
1781  {E_BLOCK_NETHER_QUARTZ_ORE, 0, 127, 20, 8},
1782  };
1783  return res;
1784 }
1785 
1786 
1787 
1788 
1789 
1791 {
1792  static OreInfos res
1793  {
1794  // OreType, OreMeta, MaxHeight, NumNests, NestSize
1795  {E_BLOCK_DIRT, 0, 127, 20, 32},
1796  {E_BLOCK_GRAVEL, 0, 127, 10, 32},
1797  {E_BLOCK_STONE, E_META_STONE_GRANITE, 127, 20, 32},
1798  {E_BLOCK_STONE, E_META_STONE_DIORITE, 127, 20, 32},
1799  {E_BLOCK_STONE, E_META_STONE_ANDESITE, 127, 20, 32},
1800  };
1801  return res;
1802 }
1803 
1804 
1805 
1806 
1807 
1809 {
1810  // The string is expected to be formatted as "<OreInfo1> | <OreInfo2> | <OreInfo3> | ..."
1811  // Each OreInfo is expected to be formatted as "<OreType> : <OreMeta> : <MaxHeight> : <NumNests> : <NestSize>"
1812 
1813  OreInfos res;
1814  auto ores = StringSplitAndTrim(a_OreInfosString, "|");
1815  for (const auto & ore: ores)
1816  {
1817  auto parts = StringSplitAndTrim(ore, ":");
1818  if (parts.size() != 5)
1819  {
1820  LOGWARNING("Cannot parse ore information from string, not enough OreInfo members (exp 5, got %d). Offending item: \"%s\".",
1821  static_cast<unsigned>(parts.size()), ore.c_str()
1822  );
1823  continue;
1824  }
1825  auto oreType = BlockStringToType(parts[0]);
1826  if (oreType < 0)
1827  {
1828  LOGWARNING("Cannot parse ore information from string, invalid OreType: \"%s\".", parts[0].c_str());
1829  continue;
1830  }
1831  NIBBLETYPE oreMeta;
1832  int maxHeight, numNests, nestSize;
1833  if (
1834  !StringToInteger(parts[1], oreMeta) ||
1835  !StringToInteger(parts[2], maxHeight) ||
1836  !StringToInteger(parts[3], numNests) ||
1837  !StringToInteger(parts[4], nestSize)
1838  )
1839  {
1840  LOGWARNING("Cannot parse ore information from string, invalid number in OreInfo \"%s\".", ore.c_str());
1841  continue;
1842  }
1843  res.emplace_back(static_cast<BLOCKTYPE>(oreType), oreMeta, maxHeight, numNests, nestSize);
1844  } // for i - split[]
1845  return res;
1846 }
1847 
1848 
1849 
1850 
1851 
1853 {
1854  AString res;
1855  for (const auto & ore: a_OreInfos)
1856  {
1857  if (!res.empty())
1858  {
1859  res.append(" | ");
1860  }
1861  res.append(fmt::format(FMT_STRING("{}:{}:{}:{}:{}"),
1862  ItemTypeToString(ore.m_BlockType), ore.m_BlockMeta,
1863  ore.m_MaxHeight, ore.m_NumNests, ore.m_NestSize
1864  ));
1865  } // for ore - a_OreInfos[]
1866  return res;
1867 }
1868 
1869 
1870 
1871 
1872 
1873 void cFinishGenOres::SetSeed(int a_Seed)
1874 {
1875  m_Noise.SetSeed(a_Seed);
1876 }
1877 
1878 
1879 
1880 
1881 
1883 // cFinishGenOreNests:
1884 
1886  cChunkDesc & a_ChunkDesc,
1887  BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta,
1888  int a_MaxHeight, int a_NumNests, int a_NestSize,
1889  int a_Seq
1890 )
1891 {
1892  // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
1893  // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
1894  // Only "terraformable" blocks get replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).
1895 
1896  // If there is an attempt to generate Emerald ores in a chunk with no mountains biome abort
1897  // There are just four points sampled to avoid searching all 16 * 16 blocks:
1898  if (a_OreType == E_BLOCK_EMERALD_ORE)
1899  {
1900  const auto BiomeSampleOne = a_ChunkDesc.GetBiome( 4, 4);
1901  const auto BiomeSampleTwo = a_ChunkDesc.GetBiome( 4, 12);
1902  const auto BiomeSampleThree = a_ChunkDesc.GetBiome(12, 4);
1903  const auto BiomeSampleFour = a_ChunkDesc.GetBiome(12, 12);
1904 
1905  if (
1906  !IsBiomeMountain(BiomeSampleOne) &&
1907  !IsBiomeMountain(BiomeSampleTwo) &&
1908  !IsBiomeMountain(BiomeSampleThree) &&
1909  !IsBiomeMountain(BiomeSampleFour)
1910  )
1911  {
1912  return;
1913  }
1914  }
1915 
1916  // Gold ores are generated more often in Mesa-Type-Biomes:
1917  // https://minecraft.wiki/w/Gold_Ore
1918  if (a_OreType == E_BLOCK_GOLD_ORE)
1919  {
1920  const auto BiomeSampleOne = a_ChunkDesc.GetBiome( 4, 4);
1921  const auto BiomeSampleTwo = a_ChunkDesc.GetBiome( 4, 12);
1922  const auto BiomeSampleThree = a_ChunkDesc.GetBiome(12, 4);
1923  const auto BiomeSampleFour = a_ChunkDesc.GetBiome(12, 12);
1924 
1925  if (
1926  IsBiomeMesa(BiomeSampleOne) ||
1927  IsBiomeMesa(BiomeSampleTwo) ||
1928  IsBiomeMesa(BiomeSampleThree) ||
1929  IsBiomeMesa(BiomeSampleFour)
1930  )
1931  {
1932  a_MaxHeight = 76;
1933  a_NumNests = 22; // 2 times default + 20 times mesa bonus
1934  }
1935  }
1936 
1937  if (a_OreType == E_BLOCK_SILVERFISH_EGG)
1938  {
1939  const auto BiomeSampleOne = a_ChunkDesc.GetBiome( 4, 4);
1940  const auto BiomeSampleTwo = a_ChunkDesc.GetBiome( 4, 12);
1941  const auto BiomeSampleThree = a_ChunkDesc.GetBiome(12, 4);
1942  const auto BiomeSampleFour = a_ChunkDesc.GetBiome(12, 12);
1943 
1944  if (
1945  !IsBiomeMountain(BiomeSampleOne) &&
1946  !IsBiomeMountain(BiomeSampleTwo) &&
1947  !IsBiomeMountain(BiomeSampleThree) &&
1948  !IsBiomeMountain(BiomeSampleFour)
1949  )
1950  {
1951  return;
1952  }
1953  }
1954 
1955  auto chunkX = a_ChunkDesc.GetChunkX();
1956  auto chunkZ = a_ChunkDesc.GetChunkZ();
1957  auto & blockTypes = a_ChunkDesc.GetBlockTypes();
1958  auto & blockMetas = a_ChunkDesc.GetBlockMetasUncompressed();
1959  for (int i = 0; i < a_NumNests; i++)
1960  {
1961  int nestRnd = m_Noise.IntNoise3DInt(chunkX + i, a_Seq, chunkZ + 64 * i) / 8;
1962  int BaseX = nestRnd % cChunkDef::Width;
1963  nestRnd /= cChunkDef::Width;
1964  int BaseZ = nestRnd % cChunkDef::Width;
1965  nestRnd /= cChunkDef::Width;
1966  int BaseY = nestRnd % a_MaxHeight;
1967  nestRnd /= a_MaxHeight;
1968  int NestSize = a_NestSize + (nestRnd % (std::max(a_NestSize, 4) / 4)); // The actual nest size may be up to 1 / 4 larger
1969  int Num = 0;
1970  while (Num < NestSize)
1971  {
1972  // Put a cuboid around [BaseX, BaseY, BaseZ]
1973  int rnd = m_Noise.IntNoise3DInt(chunkX + 64 * i, 2 * a_Seq + Num, chunkZ + 32 * i) / 8;
1974  int xsize = rnd % 2;
1975  int ysize = (rnd / 4) % 2;
1976  int zsize = (rnd / 16) % 2;
1977  rnd >>= 8;
1978  for (int x = xsize; x >= 0; --x)
1979  {
1980  int BlockX = BaseX + x;
1981  if (!cChunkDef::IsValidWidth(BlockX))
1982  {
1983  Num++; // So that the cycle finishes even if the base coords wander away from the chunk
1984  continue;
1985  }
1986  for (int y = ysize; y >= 0; --y)
1987  {
1988  int BlockY = BaseY + y;
1989  if (!cChunkDef::IsValidHeight({BlockX, BlockY, BaseZ}))
1990  {
1991  Num++; // So that the cycle finishes even if the base coords wander away from the chunk
1992  continue;
1993  }
1994  for (int z = zsize; z >= 0; --z)
1995  {
1996  int BlockZ = BaseZ + z;
1997  if (!cChunkDef::IsValidWidth(BlockZ))
1998  {
1999  Num++; // So that the cycle finishes even if the base coords wander away from the chunk
2000  continue;
2001  }
2002 
2003  const auto Index = cChunkDef::MakeIndex(BlockX, BlockY, BlockZ);
2004  const auto blockType = blockTypes[Index];
2005  if ((blockType == E_BLOCK_STONE) || (blockType == E_BLOCK_NETHERRACK))
2006  {
2007  blockTypes[Index] = a_OreType;
2008  blockMetas[Index] = a_OreMeta;
2009  }
2010  Num++;
2011  } // for z
2012  } // for y
2013  } // for x
2014 
2015  // Move the base to a neighbor voxel
2016  switch (rnd % 4)
2017  {
2018  case 0: BaseX--; break;
2019  case 1: BaseX++; break;
2020  }
2021  switch ((rnd >> 3) % 4)
2022  {
2023  case 0: BaseY--; break;
2024  case 1: BaseY++; break;
2025  }
2026  switch ((rnd >> 6) % 4)
2027  {
2028  case 0: BaseZ--; break;
2029  case 1: BaseZ++; break;
2030  }
2031  } // while (Num < NumBlocks)
2032  } // for i - NumNests
2033 }
2034 
2035 
2036 
2037 
2038 
2040 // cFinishGenOrePockets:
2041 
2042 void cFinishGenOrePockets::Initialize(cIniFile & a_IniFile, const AString & a_GenName)
2043 {
2044  // Read the OreInfos configuration:
2045  auto valueName = a_GenName + "Blocks";
2046  auto pocketCfg = a_IniFile.GetValue("Generator", valueName, "");
2047  if (pocketCfg.empty())
2048  {
2049  // There's no config currently stored in the INI file. Store the defaults as the config:
2050  a_IniFile.SetValue("Generator", valueName, OreInfosToString(m_OreInfos));
2051  }
2052  else
2053  {
2054  m_OreInfos = OreInfosFromString(pocketCfg);
2055  }
2056 
2057  // Read the optional seed configuration (but do not store the default):
2058  valueName = a_GenName + "Seed";
2059  SetSeed(a_IniFile.GetValueI("Generator", valueName, m_Noise.GetSeed()));
2060 }
2061 
2062 
2063 
2064 
2065 
2067  cChunkDesc & a_ChunkDesc,
2068  BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta,
2069  int a_MaxHeight, int a_NumNests, int a_NestSize,
2070  int a_Seq
2071 )
2072 {
2073  // This function generates several "pockets" of the specified ore
2074  // Each chunk can contain only pockets that are generated for that chunk, or for its XM / ZM neighbors.
2075 
2076  // Generate for the 3 neighbors in the XP / ZP direction as well, so that pockets crossing the boundaries are accounted for as well:
2077  int chunkZ = a_ChunkDesc.GetChunkZ();
2078  int chunkX = a_ChunkDesc.GetChunkX();
2079  imprintChunkOrePockets(chunkX - 1, chunkZ - 1, a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
2080  imprintChunkOrePockets(chunkX - 1, chunkZ, a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
2081  imprintChunkOrePockets(chunkX, chunkZ - 1, a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
2082  imprintChunkOrePockets(chunkX, chunkZ, a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
2083 }
2084 
2085 
2086 
2087 
2088 
2090  int a_ChunkX, int a_ChunkZ,
2091  cChunkDesc & a_ChunkDesc,
2092  BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta,
2093  int a_MaxHeight, int a_NumNests, int a_NestSize,
2094  int a_Seq
2095 )
2096 {
2097  // Pick a starting coord for each nest:
2098  int baseBlockX = a_ChunkX * cChunkDef::Width;
2099  int baseBlockZ = a_ChunkZ * cChunkDef::Width;
2100  for (int i = 0; i < a_NumNests; i++)
2101  {
2102  int nestRnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 7;
2103  int baseX = (nestRnd % cChunkDef::Width) + baseBlockX;
2104  nestRnd /= cChunkDef::Width;
2105  int baseZ = (nestRnd % cChunkDef::Width) + baseBlockZ;
2106  nestRnd /= cChunkDef::Width;
2107  int baseY = nestRnd % a_MaxHeight;
2108  nestRnd /= a_MaxHeight;
2109  imprintPocket(
2110  a_ChunkDesc,
2111  baseX, baseY, baseZ,
2112  a_NestSize, i + 200 * a_Seq,
2113  a_OreType, a_OreMeta
2114  );
2115  } // for i - NumNests
2116 }
2117 
2118 
2119 
2120 
2121 
2123  cChunkDesc & a_ChunkDesc,
2124  int a_MinPocketX, int a_PocketY, int a_MinPocketZ,
2125  int a_NestSize, int a_Seq,
2126  BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta
2127 )
2128 {
2129  // A line segment in a random direction is chosen. Then, several spheres are formed along this line segment,
2130  // with their diameters diminishing towards the line ends (one half of a sinusoid)
2131 
2132  double x1 = static_cast<double>(a_MinPocketX) + 0.5;
2133  double y1 = static_cast<double>(a_PocketY) + 0.5;
2134  double z1 = static_cast<double>(a_MinPocketZ) + 0.5;
2135  int rnd = m_Noise.IntNoise2DInt(a_MinPocketX + 7 * a_Seq, a_MinPocketZ + a_PocketY * 11) / 7;
2136  double angle = static_cast<double>(rnd % 256) / (256.0 * M_PI / 2.0); // range [0 .. pi / 2]
2137  rnd /= 256;
2138  double length = static_cast<double>(a_NestSize) / 4.0;
2139  double x2 = x1 + sin(angle) * length; // Always larger than x1
2140  double z2 = z1 + cos(angle) * length; // Always larger than z1
2141  double y2 = y1 + static_cast<double>((rnd % 3) - 1); // Up to 1 block away from y1
2142 
2143  // Iterate over the line segment in a total of a_NestSize steps:
2144  double stepX = (x2 - x1) / static_cast<double>(a_NestSize);
2145  double stepY = (y2 - y1) / static_cast<double>(a_NestSize);
2146  double stepZ = (z2 - z1) / static_cast<double>(a_NestSize);
2147  double stepR = M_PI / static_cast<double>(a_NestSize);
2148  double size = static_cast<double>(a_NestSize) / 16.0;
2149  for (int i = 0; i < a_NestSize; ++i)
2150  {
2151  double iDbl = static_cast<double>(i);
2152  double sphereX = x1 + stepX * iDbl;
2153  double sphereY = y1 + stepY * iDbl;
2154  double sphereZ = z1 + stepZ * iDbl;
2155  double radius = (sin(stepR * iDbl) + 1.0) * size + 1.0;
2156  imprintSphere(a_ChunkDesc, sphereX, sphereY, sphereZ, radius, a_OreType, a_OreMeta);
2157  } // for i
2158 }
2159 
2160 
2161 
2162 
2163 
2165  cChunkDesc & a_ChunkDesc,
2166  double a_SphereX, double a_SphereY, double a_SphereZ, double a_Radius,
2167  BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta
2168 )
2169 {
2170  // Get the sphere's bounding box, unioned with the chunk's bounding box (possibly empty):
2171  int baseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
2172  int baseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
2173  int minX = std::max(FloorC(a_SphereX - a_Radius), baseX);
2174  int minY = std::max(FloorC(a_SphereY - a_Radius), 0);
2175  int minZ = std::max(FloorC(a_SphereZ - a_Radius), baseZ);
2176  int maxX = std::min(CeilC(a_SphereX + a_Radius), baseX + cChunkDef::Width - 1);
2177  int maxY = std::min(CeilC(a_SphereY + a_Radius), cChunkDef::Height - 1);
2178  int maxZ = std::min(CeilC(a_SphereZ + a_Radius), baseZ + cChunkDef::Width - 1);
2179 
2180  /*
2181  // DEBUG:
2182  int blockX = FloorC(a_SphereX);
2183  int blockY = FloorC(a_SphereY);
2184  int blockZ = FloorC(a_SphereZ);
2185  if (
2186  (blockX >= baseX) && (blockX < baseX + cChunkDef::Width) &&
2187  (blockY >= 0) && (blockY < cChunkDef::Height) &&
2188  (blockZ >= baseZ) && (blockZ < baseZ + cChunkDef::Width)
2189  )
2190  {
2191  // FLOGD("Imprinting a sphere center at {0}", Vector3i{blockX, blockY, blockZ});
2192  a_ChunkDesc.SetBlockTypeMeta(blockX - baseX, blockY, blockZ - baseZ, a_OreType, a_OreMeta);
2193  }
2194  return;
2195  //*/
2196 
2197  // Imprint the parts of the sphere intersecting the chunk:
2198  double radiusSq = a_Radius * a_Radius / 4.0;
2199  for (int y = minY; y <= maxY; y++)
2200  {
2201  double relY = static_cast<double>(y) + 0.5 - a_SphereY;
2202  double relYSq = relY * relY;
2203  if (relYSq > radiusSq)
2204  {
2205  // outside the sphere, bail out
2206  continue;
2207  }
2208  for (int z = minZ; z <= maxZ; z++)
2209  {
2210  double relZ = static_cast<double>(z) + 0.5 - a_SphereZ;
2211  double relZSq = relZ * relZ;
2212  if (relZSq + relYSq > radiusSq)
2213  {
2214  // outside the sphere, bail out
2215  continue;
2216  }
2217  for (int x = minX; x <= maxX; x++)
2218  {
2219  double relX = static_cast<double>(x) + 0.5 - a_SphereX;
2220  double relXSq = relX * relX;
2221  if (relZSq + relYSq + relXSq > radiusSq)
2222  {
2223  // outside the sphere, bail out
2224  continue;
2225  }
2226  int bX = x - baseX;
2227  int bZ = z - baseZ;
2228  auto blockType = a_ChunkDesc.GetBlockType(bX, y, bZ);
2229  if ((blockType == E_BLOCK_STONE) || (blockType == E_BLOCK_NETHERRACK))
2230  {
2231  a_ChunkDesc.SetBlockTypeMeta(bX, y, bZ, a_OreType, a_OreMeta);
2232  }
2233  } // for x
2234  } // for z
2235  } // for y
2236 }
2237 
2238 
2239 
2240 
2241 
2242 cFinishGenForestRocks::cFinishGenForestRocks(int a_Seed, cIniFile & a_IniFile) : m_Noise(a_Seed)
2243 {
2244 }
2245 
2246 
2247 
2248 
2249 
2251 {
2252  // Choose random position in chunk and place boulder around it
2253  auto Pos = Vector3i(
2254  m_Noise.IntNoise2DInt(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()) % cChunkDef::Width,
2255  0,
2256  m_Noise.IntNoise2DInt(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()) % cChunkDef::Width
2257  );
2258  Pos.y = a_ChunkDesc.GetHeight(Pos.x, Pos.z) % cChunkDef::Height;
2259 
2260  auto Biome = a_ChunkDesc.GetBiome(Pos.x, Pos.z);
2261  if ((Biome != biMegaTaiga) && (Biome != biMegaTaigaHills))
2262  {
2263  return;
2264  }
2265 
2266  // Determines the size of the boulder
2267  const int TwoLimit = 70;
2268  const int ThreeLimit = 90;
2269 
2270  auto RadiusChance = m_Noise.IntNoise2DInt(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()) % 100;
2271  int Radius = 1;
2272  if (RadiusChance > TwoLimit && RadiusChance <= ThreeLimit)
2273  {
2274  Radius = 2;
2275  }
2276  else if (RadiusChance > ThreeLimit)
2277  {
2278  Radius = 3;
2279  }
2280 
2281  Pos.x = Clamp(Pos.x, Radius, cChunkDef::Width - Radius - 1);
2282  Pos.z = Clamp(Pos.z, Radius, cChunkDef::Width - Radius - 1);
2283 
2284  auto StartBlock = a_ChunkDesc.GetBlockType(Pos.x, Pos.y, Pos.z);
2285  while (!((StartBlock == E_BLOCK_DIRT) || (StartBlock == E_BLOCK_GRASS)))
2286  {
2287  Pos.y -= 1;
2288  if (!cChunkDef::IsValidRelPos(Pos.addedY(-Radius)))
2289  {
2290  return;
2291  }
2292  StartBlock = a_ChunkDesc.GetBlockType(Pos.x, Pos.y, Pos.z);
2293  }
2294 
2295 
2296  Pos.y -= Radius - 1;
2297  // Pos.y = Clamp(Pos.y - m_Noise.IntNoise2DInt(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()) % Radius + 1, 0, cChunkDef::Height);
2298 
2299  for (int x = -Radius; x <= Radius; x++)
2300  {
2301  for (int y = -Radius; y <= Radius; y++)
2302  {
2303  for (int z = -Radius; z <= Radius; z++)
2304  {
2305  if (!cChunkDef::IsValidRelPos({ Pos.x + x, Pos.y + y, Pos.z + z }))
2306  {
2307  continue;
2308  }
2309 
2310  if (Vector3d(x, y, z).SqrLength() > Radius * Radius + 1)
2311  {
2312  continue;
2313  }
2314 
2315  a_ChunkDesc.SetBlockTypeMeta(Pos.x + x, Pos.y + y, Pos.z + z, E_BLOCK_MOSSY_COBBLESTONE, 0);
2316  }
2317  }
2318  }
2319 }
int GetSnowStartHeight(EMCSBiome a_Biome)
Returns the height when a biome when a biome starts snowing.
Definition: BiomeDef.cpp:275
bool IsBiomeVeryCold(EMCSBiome a_Biome)
Returns true if the biome is very cold (has snow on ground everywhere, turns top water to ice,...
Definition: BiomeDef.cpp:169
bool IsBiomeMountain(EMCSBiome a_Biome)
Returns true if the biome is mountainous (mutations of the extreme hills biome)
Definition: BiomeDef.cpp:228
EMCSBiome StringToBiome(const AString &a_BiomeString)
Translates a biome string to biome enum.
Definition: BiomeDef.cpp:94
bool IsBiomeMesa(EMCSBiome a_Biome)
Returns true if the biome is Mesa or one of its mutations.
Definition: BiomeDef.cpp:251
EMCSBiome
Biome IDs The first batch corresponds to the clientside biomes, used by MineCraft.
Definition: BiomeDef.h:18
@ biJungleEdge
Definition: BiomeDef.h:50
@ biIceMountains
Definition: BiomeDef.h:38
@ biExtremeHillsM
Definition: BiomeDef.h:79
@ biDesert
Definition: BiomeDef.h:24
@ biExtremeHillsPlusM
Definition: BiomeDef.h:92
@ biColdTaigaHills
Definition: BiomeDef.h:58
@ biInvalidBiome
Definition: BiomeDef.h:19
@ biMegaSpruceTaiga
Definition: BiomeDef.h:90
@ biSavanna
Definition: BiomeDef.h:62
@ biTaigaHills
Definition: BiomeDef.h:44
@ biExtremeHillsPlus
Definition: BiomeDef.h:61
@ biJungle
Definition: BiomeDef.h:46
@ biMegaTaiga
Definition: BiomeDef.h:59
@ biJungleHills
Definition: BiomeDef.h:47
@ biDesertHills
Definition: BiomeDef.h:42
@ biJungleEdgeM
Definition: BiomeDef.h:85
@ biTaigaM
Definition: BiomeDef.h:81
@ biJungleM
Definition: BiomeDef.h:84
@ biMaxVariantBiome
Definition: BiomeDef.h:100
@ biExtremeHillsEdge
Definition: BiomeDef.h:45
@ biSavannaPlateauM
Definition: BiomeDef.h:94
@ biPlains
Definition: BiomeDef.h:23
@ biMegaSpruceTaigaHills
Definition: BiomeDef.h:91
@ biSavannaM
Definition: BiomeDef.h:93
@ biExtremeHills
Definition: BiomeDef.h:25
@ biColdTaiga
Definition: BiomeDef.h:57
@ biMegaTaigaHills
Definition: BiomeDef.h:60
@ biTaiga
Definition: BiomeDef.h:27
@ biDesertM
Definition: BiomeDef.h:78
@ biSavannaPlateau
Definition: BiomeDef.h:63
@ biColdTaigaM
Definition: BiomeDef.h:89
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:10
int BlockStringToType(const AString &a_BlockTypeString)
Translates a blocktype string into blocktype.
Definition: BlockType.cpp:212
bool StringToItem(const AString &a_ItemTypeString, cItem &a_Item)
Translates an itemtype string into an item.
Definition: BlockType.cpp:228
AString ItemTypeToString(short a_ItemType)
Translates itemtype into a string.
Definition: BlockType.cpp:252
@ E_META_TALL_GRASS_FERN
Definition: BlockType.h:932
@ E_META_TALL_GRASS_GRASS
Definition: BlockType.h:931
@ E_META_BIG_FLOWER_TOP
Definition: BlockType.h:562
@ E_META_BIG_FLOWER_DOUBLE_TALL_GRASS
Definition: BlockType.h:557
@ E_META_STONE_GRANITE
Definition: BlockType.h:906
@ E_META_BIG_FLOWER_LARGE_FERN
Definition: BlockType.h:558
@ E_META_STONE_ANDESITE
Definition: BlockType.h:910
@ E_META_STONE_DIORITE
Definition: BlockType.h:908
@ E_BLOCK_NEW_LEAVES
Definition: BlockType.h:180
@ E_BLOCK_WATER
Definition: BlockType.h:18
@ E_BLOCK_STATIONARY_LAVA
Definition: BlockType.h:21
@ E_BLOCK_REDSTONE_ORE
Definition: BlockType.h:87
@ E_BLOCK_COAL_ORE
Definition: BlockType.h:26
@ E_BLOCK_DIAMOND_ORE
Definition: BlockType.h:66
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_LEAVES
Definition: BlockType.h:28
@ E_BLOCK_MOSSY_COBBLESTONE
Definition: BlockType.h:58
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_BLOCK_LAPIS_ORE
Definition: BlockType.h:31
@ E_BLOCK_GRASS
Definition: BlockType.h:12
@ E_BLOCK_EMERALD_ORE
Definition: BlockType.h:144
@ E_BLOCK_CACTUS
Definition: BlockType.h:95
@ E_BLOCK_ICE
Definition: BlockType.h:93
@ E_BLOCK_SILVERFISH_EGG
Definition: BlockType.h:112
@ E_BLOCK_NEW_LOG
Definition: BlockType.h:181
@ E_BLOCK_SOULSAND
Definition: BlockType.h:103
@ E_BLOCK_MYCELIUM
Definition: BlockType.h:125
@ E_BLOCK_SNOW
Definition: BlockType.h:92
@ E_BLOCK_BROWN_MUSHROOM
Definition: BlockType.h:49
@ E_BLOCK_IRON_ORE
Definition: BlockType.h:25
@ E_BLOCK_GRAVEL
Definition: BlockType.h:23
@ E_BLOCK_PUMPKIN
Definition: BlockType.h:101
@ E_BLOCK_BIG_FLOWER
Definition: BlockType.h:194
@ E_BLOCK_SUGARCANE
Definition: BlockType.h:97
@ E_BLOCK_VINES
Definition: BlockType.h:121
@ E_BLOCK_GOLD_ORE
Definition: BlockType.h:24
@ E_BLOCK_STONE
Definition: BlockType.h:11
@ E_BLOCK_NETHER_QUARTZ_ORE
Definition: BlockType.h:170
@ E_BLOCK_RED_MUSHROOM
Definition: BlockType.h:50
@ E_BLOCK_DIRT
Definition: BlockType.h:13
@ E_BLOCK_NETHERRACK
Definition: BlockType.h:102
@ E_BLOCK_LOG
Definition: BlockType.h:27
@ E_BLOCK_GLOWSTONE
Definition: BlockType.h:104
@ E_BLOCK_SAND
Definition: BlockType.h:22
@ E_BLOCK_STATIONARY_WATER
Definition: BlockType.h:19
@ E_BLOCK_LAVA
Definition: BlockType.h:20
@ E_BLOCK_TALL_GRASS
Definition: BlockType.h:41
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:44
unsigned char HEIGHTTYPE
The type used by the heightmap.
Definition: ChunkDef.h:47
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
bool IsValidBlock(int a_BlockType)
Returns true if the specified block type is valid (known).
Definition: Defines.cpp:160
eDimension
Dimension of a world.
Definition: Defines.h:231
@ dimEnd
Definition: Defines.h:234
@ dimNether
Definition: Defines.h:232
@ dimOverworld
Definition: Defines.h:233
static bool IsWater(BLOCKTYPE a_BlockType)
Definition: FinishGen.cpp:36
#define DEF_END_LAVA_SPRINGS
Definition: FinishGen.cpp:28
#define DEF_NETHER_LAVA_SPRINGS
Definition: FinishGen.cpp:24
#define DEF_OVERWORLD_LAVA_SPRINGS
Definition: FinishGen.cpp:26
#define DEF_OVERWORLD_WATER_SPRINGS
Definition: FinishGen.cpp:25
#define DEF_NO_ANIMALS
Definition: FinishGen.cpp:30
#define DEF_ANIMAL_SPAWN_PERCENT
Definition: FinishGen.cpp:29
#define DEF_NETHER_WATER_SPRINGS
Definition: FinishGen.cpp:23
#define DEF_END_WATER_SPRINGS
Definition: FinishGen.cpp:27
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:231
T Clamp(T a_Value, T a_Min, T a_Max)
Clamp X to the specified range.
Definition: Globals.h:336
std::enable_if< std::is_arithmetic< T >::value, C >::type CeilC(T a_Value)
Ceils a value, then casts it to C (an int by default).
Definition: Globals.h:354
#define ASSERT(x)
Definition: Globals.h:276
std::enable_if< std::is_arithmetic< T >::value, C >::type FloorC(T a_Value)
Floors a value, then casts it to C (an int by default).
Definition: Globals.h:347
#define FLOGD
Definition: LoggerSimple.h:91
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
eMonsterType
Identifies individual monster type.
Definition: MonsterTypes.h:11
@ mtSheep
Definition: MonsterTypes.h:56
@ mtWolf
Definition: MonsterTypes.h:77
@ mtRabbit
Definition: MonsterTypes.h:53
@ mtPig
Definition: MonsterTypes.h:47
@ mtCow
Definition: MonsterTypes.h:20
@ mtMooshroom
Definition: MonsterTypes.h:41
@ mtSquid
Definition: MonsterTypes.h:64
@ mtInvalidType
Definition: MonsterTypes.h:12
@ mtChicken
Definition: MonsterTypes.h:18
float NOISE_DATATYPE
The datatype used by all the noise generators.
Definition: Noise.h:9
BlockType
Definition: BlockTypes.h:4
Direction
AStringVector StringSplitAndTrim(const AString &str, const AString &delim)
Split the string at any of the listed delimiters and trim each value.
std::vector< AString > AStringVector
Definition: StringUtils.h:12
std::string AString
Definition: StringUtils.h:11
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
Definition: StringUtils.h:143
Vector3< double > Vector3d
Definition: Vector3.h:485
Vector3< int > Vector3i
Definition: Vector3.h:487
static bool IsSolid(BLOCKTYPE Block)
Is this block solid (player cannot walk through)?
Definition: BlockInfo.cpp:892
static bool IsSnowable(BLOCKTYPE Block)
Definition: BlockInfo.cpp:879
static bool FullyOccupiesVoxel(BLOCKTYPE Block)
Does this block fully occupy its voxel - is it a 'full' block?
Definition: BlockInfo.cpp:606
static bool IsTransparent(BLOCKTYPE Block)
Is a block transparent? (https://minecraft.wiki/w/Opacity)
Definition: BlockInfo.cpp:961
Constants used throughout the code, useful typedefs and utility functions.
Definition: ChunkDef.h:120
static bool IsValidHeight(Vector3i a_BlockPosition)
Validates a height-coordinate.
Definition: ChunkDef.h:185
BLOCKTYPE BlockTypes[NumBlocks]
The type used for block type operations and storage, AXIS_ORDER ordering.
Definition: ChunkDef.h:140
static bool IsValidWidth(int a_Width)
Validates a width-coordinate.
Definition: ChunkDef.h:192
HEIGHTTYPE HeightMap[Width *Width]
The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the hig...
Definition: ChunkDef.h:132
static BLOCKTYPE GetBlock(const BLOCKTYPE *a_BlockTypes, Vector3i a_RelPos)
Definition: ChunkDef.h:280
static bool IsValidRelPos(Vector3i a_RelPos)
Validates a chunk relative coordinate.
Definition: ChunkDef.h:199
static size_t MakeIndex(int x, int y, int z)
Definition: ChunkDef.h:227
static const int Width
Definition: ChunkDef.h:124
static const int Height
Definition: ChunkDef.h:125
static HEIGHTTYPE GetHeight(const HeightMap &a_HeightMap, int a_X, int a_Z)
Definition: ChunkDef.h:303
static void SetBlock(BLOCKTYPE *a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type)
Definition: ChunkDef.h:264
EMCSBiome BiomeMap[Width *Width]
The type used for any biomemap operations and storage inside Cuberite, using Cuberite biomes (need no...
Definition: ChunkDef.h:137
HEIGHTTYPE GetHeight(int a_RelX, int a_RelZ) const
Definition: ChunkDesc.cpp:144
EMCSBiome GetBiome(int a_RelX, int a_RelZ) const
Definition: ChunkDesc.cpp:126
void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
Definition: ChunkDesc.cpp:81
cEntityList & GetEntities(void)
Definition: ChunkDesc.h:240
int GetChunkX() const
Definition: ChunkDesc.h:49
void SetHeight(int a_RelX, int a_RelZ, HEIGHTTYPE a_Height)
Definition: ChunkDesc.cpp:135
void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Definition: ChunkDesc.cpp:63
cChunkDef::BlockTypes & GetBlockTypes(void)
Definition: ChunkDesc.h:235
cChunkDef::HeightMap & GetHeightMap(void)
Definition: ChunkDesc.h:239
BlockNibbleBytes & GetBlockMetasUncompressed(void)
Definition: ChunkDesc.h:238
NIBBLETYPE GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: ChunkDesc.cpp:99
BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ) const
Definition: ChunkDesc.cpp:90
cChunkDef::BiomeMap & GetBiomeMap(void)
Definition: ChunkDesc.h:234
int GetChunkZ() const
Definition: ChunkDesc.h:50
HEIGHTTYPE GetMaxHeight(void) const
Returns the maximum height value in the heightmap.
Definition: ChunkDesc.cpp:397
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1087
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1120
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:48
void TryPlaceClump(cChunkDesc &a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
Definition: FinishGen.cpp:92
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:185
static std::vector< BiomeInfo > ParseIniFile(cIniFile &a_IniFile, const AString &a_ClumpPrefix)
Parses an inifile in search for all clumps.
Definition: FinishGen.cpp:372
const int MIN_NUM_FOLIAGE
The minimum number of foliage per clump.
Definition: FinishGen.h:136
const int MAX_NUM_FOLIAGE
The maximum number of foliage per clump.
Definition: FinishGen.h:133
std::vector< BiomeInfo > m_FlowersPerBiome
Definition: FinishGen.h:130
void TryPlaceFoliageClump(cChunkDesc &a_ChunkDesc, int a_RelX, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_IsDoubleTall)
Definition: FinishGen.cpp:242
const int RANGE_FROM_CENTER
The maximum range a foliage can be placed from the center of the clump.
Definition: FinishGen.h:139
static void ParseConfigurationString(const AString &a_String, std::vector< BiomeInfo > &a_Output)
Parses a string and puts a vector with a length of biMaxVariantBiome in a_Output.
Definition: FinishGen.cpp:287
std::vector< FoliageInfo > m_Blocks
Definition: FinishGen.h:98
void TryPlaceGlowstone(cChunkDesc &a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings)
Definition: FinishGen.cpp:457
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:409
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:521
static bool CanFernGrow(EMCSBiome a_Biome)
Definition: FinishGen.cpp:628
static bool CanGrassGrow(EMCSBiome a_Biome)
Definition: FinishGen.cpp:717
static int GetBiomeDensity(EMCSBiome a_Biome)
Definition: FinishGen.cpp:683
static bool CanLargeFernGrow(EMCSBiome a_Biome)
Definition: FinishGen.cpp:652
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:763
cNoise m_Noise
Definition: FinishGen.h:207
bool IsJungleVariant(EMCSBiome a_Biome)
Definition: FinishGen.cpp:740
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1035
bool TryAddSugarcane(cChunkDesc &a_ChunkDesc, int a_RelX, HEIGHTTYPE &a_RelY, int a_RelZ)
Tries to place sugarcane at the coords specified, returns true if successful, updates the top variabl...
Definition: FinishGen.cpp:885
static bool IsDesertVariant(EMCSBiome a_biome)
Definition: FinishGen.cpp:1018
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:945
bool TryAddCactus(cChunkDesc &a_ChunkDesc, int a_RelX, HEIGHTTYPE &a_RelY, int a_RelZ)
Tries to place cactus at the coords specified, returns true if successful, updates the top variable (...
Definition: FinishGen.cpp:836
int m_Amount
Relative amount of blocks to try adding.
Definition: FinishGen.h:323
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1175
bool IsAllowedBlockBelow(BLOCKTYPE a_BlockBelow)
Returns true if the given blocktype may be below m_BlockType.
Definition: FinishGen.h:335
bool IsAllowedBiome(EMCSBiome a_Biome)
Returns true if the given biome is a biome that is allowed.
Definition: FinishGen.h:329
int GetNumToGen(const cChunkDef::BiomeMap &a_BiomeMap)
Definition: FinishGen.cpp:1158
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1223
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava)
Definition: FinishGen.cpp:1246
void CollapseSandGravel(cChunkDesc &a_ChunkDesc)
Drops hanging sand and gravel down to the ground, recalculates heightmap.
Definition: FinishGen.cpp:1281
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1258
void StationarizeFluid(cChunkDef::BlockTypes &a_BlockTypes, cChunkDef::HeightMap &a_HeightMap, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid)
For each fluid block:
Definition: FinishGen.cpp:1343
bool TryPlaceSpring(cChunkDesc &a_ChunkDesc, int x, int y, int z)
Tries to place a spring at the specified coords, checks neighbors.
Definition: FinishGen.cpp:1519
cProbabDistrib m_HeightDistribution
Definition: FinishGen.h:412
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile &a_IniFile, eDimension a_Dimension)
Definition: FinishGen.cpp:1425
int m_Chance
Chance, [0..100], that a spring will be generated in a chunk.
Definition: FinishGen.h:414
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1476
bool TrySpawnAnimals(cChunkDesc &a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn)
Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and ...
Definition: FinishGen.cpp:1648
int m_AnimalProbability
Chance, [0..100], that an animal pack will be generated in a chunk.
Definition: FinishGen.h:442
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1608
eMonsterType GetRandomMob(cChunkDesc &a_ChunkDesc)
Picks a random animal from biome-dependant list for a random position in the chunk.
Definition: FinishGen.cpp:1702
cFinishGenPassiveMobs(int a_Seed, cIniFile &a_IniFile, eDimension a_Dimension)
Definition: FinishGen.cpp:1571
cNoise m_Noise
The noise used as the source of randomness.
Definition: FinishGen.h:439
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:1734
static const OreInfos & DefaultOverworldOres(void)
Returns a vector of OreInfo structures describing the default Overworld ores, usable in the construct...
Definition: FinishGen.cpp:1755
static OreInfos OreInfosFromString(const AString &a_OreInfosString)
Parses the parameter string into OreInfos array.
Definition: FinishGen.cpp:1808
OreInfos m_OreInfos
All the ores enabled in this generator.
Definition: FinishGen.h:536
static const OreInfos & DefaultNetherOres(void)
Returns a vector of OreInfo structures describing the default Nether ores, usable in the constructor.
Definition: FinishGen.cpp:1776
cNoise m_Noise
The noise used for generating.
Definition: FinishGen.h:533
static AString OreInfosToString(const OreInfos &a_OreInfos)
Returns a string that represents the OreInfos given as the parameter.
Definition: FinishGen.cpp:1852
static const OreInfos & DefaultNaturalPatches(void)
Returns a vector of OreInfo structures describing the default Overworld non-ore pockets (dirt,...
Definition: FinishGen.cpp:1790
std::vector< OreInfo > OreInfos
Definition: FinishGen.h:498
virtual void GenerateOre(cChunkDesc &a_ChunkDesc, BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta, int a_MaxHeight, int a_NumNests, int a_NestSize, int a_Seq)=0
Generates a single ore in the specified chunk image.
void SetSeed(int a_Seed)
(Re-)sets the seed used by the internal generating mechanisms.
Definition: FinishGen.cpp:1873
virtual void GenerateOre(cChunkDesc &a_ChunkDesc, BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta, int a_MaxHeight, int a_NumNests, int a_NestSize, int a_Seq) override
Generates a single ore in the specified chunk image.
Definition: FinishGen.cpp:1885
virtual void GenerateOre(cChunkDesc &a_ChunkDesc, BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta, int a_MaxNestHeight, int a_NumNests, int a_NestSize, int a_Seq) override
Generates a single ore in the specified chunk image.
Definition: FinishGen.cpp:2066
void imprintChunkOrePockets(int a_ChunkX, int a_ChunkZ, cChunkDesc &a_ChunkDesc, BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta, int a_MaxHeight, int a_NumNests, int a_NestSize, int a_Seq)
Calculates the pockets for the specified chunk and imprints them into the specified ChunkDesc (not ne...
Definition: FinishGen.cpp:2089
void imprintPocket(cChunkDesc &a_ChunkDesc, int a_MinPocketX, int a_PocketY, int a_MinPocketZ, int a_NestSize, int a_Seq, BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta)
Imprints a single pocket of the specified ore at the specified coords into the chunk.
Definition: FinishGen.cpp:2122
void imprintSphere(cChunkDesc &a_ChunkDesc, double a_SphereX, double a_SphereY, double a_SphereZ, double a_Radius, BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta)
Imprints a single sphere of the specified ore at the specified coords.
Definition: FinishGen.cpp:2164
void Initialize(cIniFile &a_IniFile, const AString &a_GenName)
Reads the configuration from the specified INI file.
Definition: FinishGen.cpp:2042
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: FinishGen.cpp:2250
cFinishGenForestRocks(int a_Seed, cIniFile &a_IniFile)
Definition: FinishGen.cpp:2242
int FindKey(const AString &keyname) const
Returns index of specified key, or noID if not found.
Definition: IniFile.cpp:243
AString GetValue(const AString &keyname, const AString &valuename, const AString &defValue="") const override
Get the value at the specified key and value, returns defValue on failure.
Definition: IniFile.cpp:485
int GetNumValues(const AString &keyname) const
Definition: IniFile.cpp:322
AString GetValueName(const AString &keyname, const int valueID) const
Definition: IniFile.cpp:349
int GetValueI(const AString &keyname, const AString &valuename, const int defValue=0) const
Definition: IniFile.cpp:506
int GetValueSetI(const AString &keyname, const AString &valuename, const int defValue=0) override
Definition: IniFile.cpp:559
bool SetValue(const int keyID, const int valueID, const AString &value)
Definition: IniFile.cpp:397
AString GetValueSet(const AString &keyname, const AString &valuename, const AString &defValue="") override
Gets the value; if not found, write the default to the repository.
Definition: IniFile.cpp:526
Definition: Item.h:37
static std::unique_ptr< cMonster > NewMonsterFromType(eMonsterType a_MobType)
Creates a new object of the specified mob.
Definition: Monster.cpp:1252
static eFamily FamilyFromType(eMonsterType a_MobType)
Returns the mob family based on the type.
Definition: Monster.cpp:1102
static std::set< eMonsterType > GetAllowedMobTypes(EMCSBiome a_Biome)
Returns all mob types that can spawn that biome.
Definition: MobSpawner.cpp:355
int IntNoise1DInt(int a_X) const
Definition: Noise.h:233
NOISE_DATATYPE CubicNoise2D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const
Definition: Noise.cpp:593
NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const
Definition: Noise.h:198
NOISE_DATATYPE CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const
Definition: Noise.cpp:621
int GetSeed(void) const
Definition: Noise.h:53
int IntNoise3DInt(int a_X, int a_Y, int a_Z) const
Definition: Noise.h:254
void SetSeed(int a_Seed)
Definition: Noise.h:52
int IntNoise2DInt(int a_X, int a_Y) const
Definition: Noise.h:243
NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const
Definition: Noise.h:210
bool SetDefString(const AString &a_DefString)
Sets the distribution curve using a definition string; returns true on successful parse.
int GetSum(void) const
Definition: ProbabDistrib.h:62
int MapValue(int a_OrigValue) const
Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability.
static bool DoesBurnForever(BLOCKTYPE a_BlockType)
static bool CanWashAway(BLOCKTYPE a_BlockType)
T x
Definition: Vector3.h:17
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17