the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "net.minecraft.world.level.h"
3#include "net.minecraft.world.level.biome.h"
4#include "net.minecraft.world.level.levelgen.h"
5#include "net.minecraft.world.level.levelgen.feature.h"
6#include "net.minecraft.world.level.levelgen.structure.h"
7#include "net.minecraft.world.level.levelgen.synth.h"
8#include "net.minecraft.world.level.tile.h"
9#include "net.minecraft.world.level.storage.h"
10#include "net.minecraft.world.entity.h"
11#include "CustomLevelSource.h"
12
13const double CustomLevelSource::SNOW_SCALE = 0.3;
14const double CustomLevelSource::SNOW_CUTOFF = 0.5;
15
16CustomLevelSource::CustomLevelSource(Level *level, __int64 seed, bool generateStructures) : generateStructures( generateStructures )
17{
18#ifdef _OVERRIDE_HEIGHTMAP
19 m_XZSize = level->getLevelData()->getXZSize();
20
21 m_heightmapOverride = byteArray( (m_XZSize*16) * (m_XZSize*16) );
22
23#ifdef _UNICODE
24 wstring path = L"GAME:\\GameRules\\heightmap.bin";
25
26#else
27#ifdef _WINDOWS64
28 string path = "GameRules\\heightmap.bin";
29#else
30 string path = "GAME:\\GameRules\\heightmap.bin";
31#endif
32#endif
33 HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
34 if( file == INVALID_HANDLE_VALUE )
35 {
36 app.FatalLoadError();
37 DWORD error = GetLastError();
38 assert(false);
39 }
40 else
41 {
42
43#ifdef _DURANGO
44 __debugbreak(); // TODO
45 DWORD bytesRead,dwFileSize = 0;
46#else
47 DWORD bytesRead,dwFileSize = GetFileSize(file,NULL);
48#endif
49 if(dwFileSize > m_heightmapOverride.length)
50 {
51 app.DebugPrintf("Heightmap binary is too large!!\n");
52 __debugbreak();
53 }
54 BOOL bSuccess = ReadFile(file,m_heightmapOverride.data,dwFileSize,&bytesRead,NULL);
55
56 if(bSuccess==FALSE)
57 {
58 app.FatalLoadError();
59 }
60 CloseHandle(file);
61 }
62
63 m_waterheightOverride = byteArray( (m_XZSize*16) * (m_XZSize*16) );
64
65#ifdef _UNICODE
66 wstring waterHeightPath = L"GAME:\\GameRules\\waterheight.bin";
67
68#else
69#ifdef _WINDOWS64
70 string waterHeightPath = "GameRules\\waterheight.bin";
71#else
72 string waterHeightPath = "GAME:\\GameRules\\waterheight.bin";
73#endif
74#endif
75 file = CreateFile(waterHeightPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
76 if( file == INVALID_HANDLE_VALUE )
77 {
78 DWORD error = GetLastError();
79 //assert(false);
80 memset(m_waterheightOverride.data, level->seaLevel, m_waterheightOverride.length);
81 }
82 else
83 {
84
85#ifdef _DURANGO
86 __debugbreak(); // TODO
87 DWORD bytesRead,dwFileSize = 0;
88#else
89 DWORD bytesRead,dwFileSize = GetFileSize(file,NULL);
90#endif
91 if(dwFileSize > m_waterheightOverride.length)
92 {
93 app.DebugPrintf("waterheight binary is too large!!\n");
94 __debugbreak();
95 }
96 BOOL bSuccess = ReadFile(file,m_waterheightOverride.data,dwFileSize,&bytesRead,NULL);
97
98 if(bSuccess==FALSE)
99 {
100 app.FatalLoadError();
101 }
102 CloseHandle(file);
103 }
104
105 caveFeature = new LargeCaveFeature();
106 strongholdFeature = new StrongholdFeature();
107 villageFeature = new VillageFeature(m_XZSize);
108 mineShaftFeature = new MineShaftFeature();
109 scatteredFeature = new RandomScatteredLargeFeature();
110 canyonFeature = new CanyonFeature();
111
112 this->level = level;
113
114 random = new Random(seed);
115 pprandom = new Random(seed); // 4J - added, so that we can have a separate random for doing post-processing in parallel with creation
116 perlinNoise3 = new PerlinNoise(random, 4);
117#endif
118}
119
120CustomLevelSource::~CustomLevelSource()
121{
122#ifdef _OVERRIDE_HEIGHTMAP
123 delete caveFeature;
124 delete strongholdFeature;
125 delete villageFeature;
126 delete mineShaftFeature;
127 delete canyonFeature;
128
129 this->level = level;
130
131 delete random;
132 delete perlinNoise3;
133#endif
134}
135
136void CustomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks)
137{
138#ifdef _OVERRIDE_HEIGHTMAP
139 int xChunks = 16 / CHUNK_WIDTH;
140 int yChunks = Level::maxBuildHeight / CHUNK_HEIGHT;
141 int waterHeight = level->seaLevel;
142
143 int xSize = xChunks + 1;
144 int ySize = Level::maxBuildHeight / CHUNK_HEIGHT + 1;
145 int zSize = xChunks + 1;
146
147 int xMapStart = xOffs + m_XZSize/2;
148 int zMapStart = zOffs + m_XZSize/2;
149 for (int xc = 0; xc < xChunks; xc++)
150 {
151 for (int zc = 0; zc < xChunks; zc++)
152 {
153 for (int yc = 0; yc < yChunks; yc++)
154 {
155 for (int y = 0; y < CHUNK_HEIGHT; y++)
156 {
157 for (int x = 0; x < CHUNK_WIDTH; x++)
158 {
159 for (int z = 0; z < CHUNK_WIDTH; z++)
160 {
161 int mapIndex = (zMapStart * 16 + z + ( zc * CHUNK_WIDTH )) * (m_XZSize * 16) + (xMapStart * 16 + x + ( xc * CHUNK_WIDTH ));
162 int mapHeight = m_heightmapOverride[mapIndex];
163 waterHeight = m_waterheightOverride[mapIndex];
164 //app.DebugPrintf("MapHeight = %d, y = %d\n", mapHeight, yc * CHUNK_HEIGHT + y);
165 ///////////////////////////////////////////////////////////////////
166 // 4J - add this chunk of code to make land "fall-off" at the edges of
167 // a finite world - size of that world is currently hard-coded in here
168 const int worldSize = m_XZSize * 16;
169 const int falloffStart = 32; // chunks away from edge were we start doing fall-off
170 const float falloffMax = 128.0f; // max value we need to get to falloff by the edge of the map
171
172 int xxx = ( ( xOffs * 16 ) + x + ( xc * CHUNK_WIDTH ) );
173 int zzz = ( ( zOffs * 16 ) + z + ( zc * CHUNK_WIDTH ) );
174
175 // Get distance to edges of world in x
176 int xxx0 = xxx + ( worldSize / 2 );
177 if( xxx0 < 0 ) xxx0 = 0;
178 int xxx1 = ( ( worldSize / 2 ) - 1 ) - xxx;
179 if( xxx1 < 0 ) xxx1 = 0;
180
181 // Get distance to edges of world in z
182 int zzz0 = zzz + ( worldSize / 2 );
183 if( zzz0 < 0 ) zzz0 = 0;
184 int zzz1 = ( ( worldSize / 2 ) - 1 ) - zzz;
185 if( zzz1 < 0 ) zzz1 = 0;
186
187 // Get min distance to any edge
188 int emin = xxx0;
189 if (xxx1 < emin ) emin = xxx1;
190 if (zzz0 < emin ) emin = zzz0;
191 if (zzz1 < emin ) emin = zzz1;
192
193 float comp = 0.0f;
194
195 // Calculate how much we want the world to fall away, if we're in the defined region to do so
196 if( emin < falloffStart )
197 {
198 int falloff = falloffStart - emin;
199 comp = ((float)falloff / (float)falloffStart ) * falloffMax;
200 }
201 // 4J - end of extra code
202 ///////////////////////////////////////////////////////////////////
203 int tileId = 0;
204 // 4J - this comparison used to just be with 0.0f but is now varied by block above
205 if (yc * CHUNK_HEIGHT + y < mapHeight)
206 {
207 tileId = (byte) Tile::stone_Id;
208 }
209 else if (yc * CHUNK_HEIGHT + y < waterHeight)
210 {
211 tileId = (byte) Tile::calmWater_Id;
212 }
213
214 // 4J - more extra code to make sure that the column at the edge of the world is just water & rock, to match the infinite sea that
215 // continues on after the edge of the world.
216
217 if( emin == 0 )
218 {
219 // This matches code in MultiPlayerChunkCache that makes the geometry which continues at the edge of the world
220 if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::stone_Id;
221 else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
222 }
223
224 int indexY = (yc * CHUNK_HEIGHT + y);
225 int offsAdjustment = 0;
226 if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
227 {
228 indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
229 offsAdjustment = Level::COMPRESSED_CHUNK_SECTION_TILES;
230 }
231 int offs = ( (x + xc * CHUNK_WIDTH) << Level::genDepthBitsPlusFour | (z + zc * CHUNK_WIDTH) << Level::genDepthBits | indexY) + offsAdjustment;
232 blocks[offs] = tileId;
233 }
234 }
235 }
236 }
237 }
238 }
239#endif
240}
241
242
243void CustomLevelSource::buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes)
244{
245#ifdef _OVERRIDE_HEIGHTMAP
246 int waterHeight = level->seaLevel;
247 int xMapStart = xOffs + m_XZSize/2;
248 int zMapStart = zOffs + m_XZSize/2;
249
250 double s = 1 / 32.0;
251
252 doubleArray depthBuffer(16*16); // 4J - used to be declared with class level scope but moved here for thread safety
253
254 depthBuffer = perlinNoise3->getRegion(depthBuffer, xOffs * 16, zOffs * 16, 0, 16, 16, 1, s * 2, s * 2, s * 2);
255
256 for (int x = 0; x < 16; x++)
257 {
258 for (int z = 0; z < 16; z++)
259 {
260 int mapIndex = (zMapStart * 16 + z) * (m_XZSize * 16) + (xMapStart * 16 + x);
261 waterHeight = m_waterheightOverride[mapIndex];
262
263 Biome *b = biomes[z + x * 16];
264 float temp = b->getTemperature();
265 int runDepth = (int) (depthBuffer[x + z * 16] / 3 + 3 + random->nextDouble() * 0.25);
266
267 int run = -1;
268
269 byte top = b->topMaterial;
270 byte material = b->material;
271
272 LevelGenerationOptions *lgo = app.getLevelGenerationOptions();
273 if(lgo != NULL)
274 {
275 lgo->getBiomeOverride(b->id,material,top);
276 }
277
278 for (int y = Level::maxBuildHeight - 1; y >= 0; y--)
279 {
280
281 int indexY = y;
282 int offsAdjustment = 0;
283 if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
284 {
285 indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
286 offsAdjustment = Level::COMPRESSED_CHUNK_SECTION_TILES;
287 }
288 int offs = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | indexY) + offsAdjustment;
289
290 if (y <= 1 + random->nextInt(2)) // 4J - changed to make the bedrock not have bits you can get stuck in
291 // if (y <= 0 + random->nextInt(5))
292 {
293 blocks[offs] = (byte) Tile::unbreakable_Id;
294 }
295 else
296 {
297 int old = blocks[offs];
298
299 if (old == 0)
300 {
301 run = -1;
302 }
303 else if (old == Tile::stone_Id)
304 {
305 if (run == -1)
306 {
307 if (runDepth <= 0)
308 {
309 top = 0;
310 material = (byte) Tile::stone_Id;
311 }
312 else if (y >= waterHeight - 4 && y <= waterHeight + 1)
313 {
314 top = b->topMaterial;
315 material = b->material;
316 if(lgo != NULL)
317 {
318 lgo->getBiomeOverride(b->id,material,top);
319 }
320 }
321
322 if (y < waterHeight && top == 0)
323 {
324 if (temp < 0.15f) top = (byte) Tile::ice_Id;
325 else top = (byte) Tile::calmWater_Id;
326 }
327
328 run = runDepth;
329 if (y >= waterHeight - 1) blocks[offs] = top;
330 else blocks[offs] = material;
331 }
332 else if (run > 0)
333 {
334 run--;
335 blocks[offs] = material;
336
337 // place a few sandstone blocks beneath sand
338 // runs
339 if (run == 0 && material == Tile::sand_Id)
340 {
341 run = random->nextInt(4);
342 material = (byte) Tile::sandStone_Id;
343 }
344 }
345 }
346 }
347 }
348 }
349 }
350
351 delete [] depthBuffer.data;
352#endif
353}
354
355LevelChunk *CustomLevelSource::create(int x, int z)
356{
357#ifdef _OVERRIDE_HEIGHTMAP
358 return getChunk(x,z);
359#else
360 return NULL;
361#endif
362}
363
364LevelChunk *CustomLevelSource::getChunk(int xOffs, int zOffs)
365{
366#ifdef _OVERRIDE_HEIGHTMAP
367 random->setSeed(xOffs * 341873128712l + zOffs * 132897987541l);
368
369 // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed
370 int blocksSize = Level::maxBuildHeight * 16 * 16;
371 byte *tileData = (byte *)XPhysicalAlloc(blocksSize, MAXULONG_PTR, 4096, PAGE_READWRITE);
372 XMemSet128(tileData,0,blocksSize);
373 byteArray blocks = byteArray(tileData,blocksSize);
374 // byteArray blocks = byteArray(16 * level->depth * 16);
375
376 // LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); // 4J - moved to below
377
378 prepareHeights(xOffs, zOffs, blocks);
379
380 // 4J - Some changes made here to how biomes, temperatures and downfalls are passed around for thread safety
381 BiomeArray biomes;
382 level->getBiomeSource()->getBiomeBlock(biomes, xOffs * 16, zOffs * 16, 16, 16, true);
383
384 buildSurfaces(xOffs, zOffs, blocks, biomes);
385
386 delete [] biomes.data;
387
388 caveFeature->apply(this, level, xOffs, zOffs, blocks);
389 // 4J Stu Design Change - 1.8 gen goes stronghold, mineshaft, village, canyon
390 // this changed in 1.2 to canyon, mineshaft, village, stronghold
391 // This change makes sense as it stops canyons running through other structures
392 canyonFeature->apply(this, level, xOffs, zOffs, blocks);
393 if (generateStructures)
394 {
395 mineShaftFeature->apply(this, level, xOffs, zOffs, blocks);
396 villageFeature->apply(this, level, xOffs, zOffs, blocks);
397 strongholdFeature->apply(this, level, xOffs, zOffs, blocks);
398 scatteredFeature->apply(this, level, xOffs, zOffs, blocks);
399 }
400 // canyonFeature.apply(this, level, xOffs, zOffs, blocks);
401 // townFeature.apply(this, level, xOffs, zOffs, blocks);
402 // addCaves(xOffs, zOffs, blocks);
403 // addTowns(xOffs, zOffs, blocks);
404
405 // levelChunk->recalcHeightmap(); // 4J - removed & moved into its own method
406
407 // 4J - this now creates compressed block data from the blocks array passed in, so moved it until after the blocks are actually finalised. We also
408 // now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore.
409 LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs);
410 XPhysicalFree(tileData);
411
412 return levelChunk;
413#else
414 return NULL;
415#endif
416}
417
418// 4J - removed & moved into its own method from getChunk, so we can call recalcHeightmap after the chunk is added into the cache. Without
419// doing this, then loads of the lightgaps() calls will fail to add any lights, because adding a light checks if the cache has this chunk in.
420// lightgaps also does light 1 block into the neighbouring chunks, and maybe that is somehow enough to get lighting to propagate round the world,
421// but this just doesn't seem right - this isn't a new fault in the 360 version, have checked that java does the same.
422void CustomLevelSource::lightChunk(LevelChunk *lc)
423{
424#ifdef _OVERRIDE_HEIGHTMAP
425 lc->recalcHeightmap();
426#endif
427}
428
429bool CustomLevelSource::hasChunk(int x, int y)
430{
431 return true;
432}
433
434void CustomLevelSource::calcWaterDepths(ChunkSource *parent, int xt, int zt)
435{
436#ifdef _OVERRIDE_HEIGHTMAP
437 int xo = xt * 16;
438 int zo = zt * 16;
439 for (int x = 0; x < 16; x++)
440 {
441 int y = level->getSeaLevel();
442 for (int z = 0; z < 16; z++)
443 {
444 int xp = xo + x + 7;
445 int zp = zo + z + 7;
446 int h = level->getHeightmap(xp, zp);
447 if (h <= 0)
448 {
449 if (level->getHeightmap(xp - 1, zp) > 0 || level->getHeightmap(xp + 1, zp) > 0 || level->getHeightmap(xp, zp - 1) > 0 || level->getHeightmap(xp, zp + 1) > 0)
450 {
451 bool hadWater = false;
452 if (hadWater || (level->getTile(xp - 1, y, zp) == Tile::calmWater_Id && level->getData(xp - 1, y, zp) < 7)) hadWater = true;
453 if (hadWater || (level->getTile(xp + 1, y, zp) == Tile::calmWater_Id && level->getData(xp + 1, y, zp) < 7)) hadWater = true;
454 if (hadWater || (level->getTile(xp, y, zp - 1) == Tile::calmWater_Id && level->getData(xp, y, zp - 1) < 7)) hadWater = true;
455 if (hadWater || (level->getTile(xp, y, zp + 1) == Tile::calmWater_Id && level->getData(xp, y, zp + 1) < 7)) hadWater = true;
456 if (hadWater)
457 {
458 for (int x2 = -5; x2 <= 5; x2++)
459 {
460 for (int z2 = -5; z2 <= 5; z2++)
461 {
462 int d = (x2 > 0 ? x2 : -x2) + (z2 > 0 ? z2 : -z2);
463
464 if (d <= 5)
465 {
466 d = 6 - d;
467 if (level->getTile(xp + x2, y, zp + z2) == Tile::calmWater_Id)
468 {
469 int od = level->getData(xp + x2, y, zp + z2);
470 if (od < 7 && od < d)
471 {
472 level->setData(xp + x2, y, zp + z2, d, Tile::UPDATE_CLIENTS);
473 }
474 }
475 }
476 }
477 }
478 if (hadWater)
479 {
480 level->setTileAndData(xp, y, zp, Tile::calmWater_Id, 7, Tile::UPDATE_CLIENTS);
481 for (int y2 = 0; y2 < y; y2++)
482 {
483 level->setTileAndData(xp, y2, zp, Tile::calmWater_Id, 8, Tile::UPDATE_CLIENTS);
484 }
485 }
486 }
487 }
488 }
489 }
490 }
491#endif
492}
493
494// 4J - changed this to used pprandom rather than random, so that we can run it concurrently with getChunk
495void CustomLevelSource::postProcess(ChunkSource *parent, int xt, int zt)
496{
497#ifdef _OVERRIDE_HEIGHTMAP
498 HeavyTile::instaFall = true;
499 int xo = xt * 16;
500 int zo = zt * 16;
501
502 Biome *biome = level->getBiome(xo + 16, zo + 16);
503
504 if (CustomLevelSource::FLOATING_ISLANDS)
505 {
506 calcWaterDepths(parent, xt, zt);
507 }
508
509 pprandom->setSeed(level->getSeed());
510 __int64 xScale = pprandom->nextLong() / 2 * 2 + 1;
511 __int64 zScale = pprandom->nextLong() / 2 * 2 + 1;
512 pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed());
513
514 bool hasVillage = false;
515
516 PIXBeginNamedEvent(0,"Structure postprocessing");
517 if (generateStructures)
518 {
519 mineShaftFeature->postProcess(level, pprandom, xt, zt);
520 hasVillage = villageFeature->postProcess(level, pprandom, xt, zt);
521 strongholdFeature->postProcess(level, pprandom, xt, zt);
522 scatteredFeature->postProcess(level, random, xt, zt);
523 }
524 PIXEndNamedEvent();
525
526#if 0
527 PIXBeginNamedEvent(0,"Lakes");
528 if (!hasVillage && pprandom->nextInt(4) == 0)
529 {
530 int x = xo + pprandom->nextInt(16) + 8;
531 int y = pprandom->nextInt(Level::maxBuildHeight);
532 int z = zo + pprandom->nextInt(16) + 8;
533
534 LakeFeature *calmWater = new LakeFeature(Tile::calmWater_Id);
535 calmWater->place(level, pprandom, x, y, z);
536 delete calmWater;
537 }
538 PIXEndNamedEvent();
539
540 PIXBeginNamedEvent(0,"Lava");
541 if (!hasVillage && pprandom->nextInt(8) == 0)
542 {
543 int x = xo + pprandom->nextInt(16) + 8;
544 int y = pprandom->nextInt(pprandom->nextInt(Level::maxBuildHeight - 8) + 8);
545 int z = zo + pprandom->nextInt(16) + 8;
546 if (y < level->seaLevel || pprandom->nextInt(10) == 0)
547 {
548 LakeFeature *calmLava = new LakeFeature(Tile::calmLava_Id);
549 calmLava->place(level, pprandom, x, y, z);
550 delete calmLava;
551 }
552 }
553 PIXEndNamedEvent();
554#endif
555
556 PIXBeginNamedEvent(0,"Monster rooms");
557 for (int i = 0; i < 8; i++) {
558 int x = xo + pprandom->nextInt(16) + 8;
559 int y = pprandom->nextInt(Level::maxBuildHeight);
560 int z = zo + pprandom->nextInt(16) + 8;
561 MonsterRoomFeature *mrf = new MonsterRoomFeature();
562 if (mrf->place(level, pprandom, x, y, z))
563 {
564 }
565 delete mrf;
566 }
567 PIXEndNamedEvent();
568
569 PIXBeginNamedEvent(0,"Biome decorate");
570 biome->decorate(level, pprandom, xo, zo);
571 PIXEndNamedEvent();
572
573 app.processSchematics(parent->getChunk(xt,zt));
574
575 MobSpawner::postProcessSpawnMobs(level, biome, xo + 8, zo + 8, 16, 16, pprandom);
576
577 // 4J - brought forward from 1.2.3 to get snow back in taiga biomes
578 xo += 8;
579 zo += 8;
580 for (int x = 0; x < 16; x++)
581 {
582 for (int z = 0; z < 16; z++)
583 {
584 int y = level->getTopRainBlock(xo + x, zo + z);
585
586 if (level->shouldFreezeIgnoreNeighbors(x + xo, y - 1, z + zo))
587 {
588 level->setTileAndData(x + xo, y - 1, z + zo, Tile::ice_Id,0, Tile::UPDATE_INVISIBLE); // 4J - changed from setTile, otherwise we end up creating a *lot* of dynamic water tiles as these ice tiles are set
589 }
590 if (level->shouldSnow(x + xo, y, z + zo))
591 {
592 level->setTileAndData(x + xo, y, z + zo, Tile::topSnow_Id,0, Tile::UPDATE_CLIENTS);
593 }
594 }
595 }
596
597 HeavyTile::instaFall = false;
598#endif
599}
600
601bool CustomLevelSource::save(bool force, ProgressListener *progressListener)
602{
603 return true;
604}
605
606bool CustomLevelSource::tick()
607{
608 return false;
609}
610
611bool CustomLevelSource::shouldSave()
612{
613 return true;
614}
615
616wstring CustomLevelSource::gatherStats()
617{
618 return L"CustomLevelSource";
619}
620
621vector<Biome::MobSpawnerData *> *CustomLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z)
622{
623#ifdef _OVERRIDE_HEIGHTMAP
624 Biome *biome = level->getBiome(x, z);
625 if (biome == NULL)
626 {
627 return NULL;
628 }
629 if (mobCategory == MobCategory::monster && scatteredFeature->isSwamphut(x, y, z))
630 {
631 return scatteredFeature->getSwamphutEnemies();
632 }
633 return biome->getMobs(mobCategory);
634#else
635 return NULL;
636#endif
637}
638
639TilePos *CustomLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z)
640{
641#ifdef _OVERRIDE_HEIGHTMAP
642 if (LargeFeature::STRONGHOLD == featureName && strongholdFeature != NULL)
643 {
644 return strongholdFeature->getNearestGeneratedFeature(level, x, y, z);
645 }
646#endif
647 return NULL;
648}
649
650void CustomLevelSource::recreateLogicStructuresForChunk(int chunkX, int chunkZ)
651{
652 if (generateStructures)
653 {
654#ifdef _OVERRIDE_HEIGHTMAP
655 mineShaftFeature->apply(this, level, chunkX, chunkZ, NULL);
656 villageFeature->apply(this, level, chunkX, chunkZ, NULL);
657 strongholdFeature->apply(this, level, chunkX, chunkZ, NULL);
658 scatteredFeature->apply(this, level, chunkX, chunkZ, NULL);
659#endif
660 }
661}