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 "System.h"
3#include "BasicTypeContainers.h"
4#include "File.h"
5#include "ProgressListener.h"
6#include "net.minecraft.h"
7#include "net.minecraft.world.h"
8#include "net.minecraft.world.entity.ai.village.h"
9#include "net.minecraft.world.entity.h"
10#include "net.minecraft.world.entity.global.h"
11#include "net.minecraft.world.entity.player.h"
12#include "net.minecraft.world.level.biome.h"
13#include "net.minecraft.world.level.chunk.h"
14#include "net.minecraft.world.level.dimension.h"
15#include "net.minecraft.world.level.tile.h"
16#include "net.minecraft.world.level.tile.entity.h"
17#include "net.minecraft.world.level.h"
18#include "net.minecraft.world.level.levelgen.h"
19#include "net.minecraft.world.level.storage.h"
20#include "net.minecraft.world.level.pathfinder.h"
21#include "net.minecraft.world.level.redstone.h"
22#include "net.minecraft.world.scores.h"
23#include "net.minecraft.world.phys.h"
24#include "Explosion.h"
25#include "LevelListener.h"
26#include "Level.h"
27#include "ThreadName.h"
28#include "WeighedRandom.h"
29
30#include "ConsoleSaveFile.h"
31#include <xuiapp.h>
32#include "..\Minecraft.Client\Minecraft.h"
33#include "..\Minecraft.Client\LevelRenderer.h"
34#include "SoundTypes.h"
35#include "SparseLightStorage.h"
36#include "..\Minecraft.Client\Textures.h"
37#include "..\Minecraft.Client\TexturePackRepository.h"
38#include "..\Minecraft.Client\DLCTexturePack.h"
39#include "..\Minecraft.Client\Common\DLC\DLCPack.h"
40#include "..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.h"
41#include "..\Minecraft.Client\MinecraftServer.h"
42
43
44DWORD Level::tlsIdx = TlsAlloc();
45DWORD Level::tlsIdxLightCache = TlsAlloc();
46
47// 4J : WESTY : Added for time played stats.
48#include "net.minecraft.stats.h"
49
50// 4J - Caching of lighting data added. This is implemented as a 16x16x16 cache of ints (ie 16K storage in total). The index of the element to be used in the array is determined by the lower
51// four bits of each x/y/z position, and the upper 7/4/7 bits of the x/y/z positions are stored within the element itself along with the cached values etc. The cache can be enabled per thread by
52// calling enableLightingCache, otherwise standard non-cached accesses are performed. General method for using caching if enabled on a thread is:
53// (1) Call initCache, this invalidates any previous data in the cache
54// (2) Use setBrightnessCached, getBrightnessCached, getEmissionCached, getBlockingCached methods to get and set data
55// (3) Call flushCache, which writes through any dirty values in cache
56
57#ifdef _LARGE_WORLDS
58// Packing for cache entries in large worlds is as follows ( 64 bits per entry)
59// Add the extra x and z data into the top 32 bits, to keep all the masks and code for everything else the same
60// xxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzWEBLllllbbbbeeeexxxxxxyyyyzzzzzz
61//
62// xxxxxx - middle 6 bits of x position
63// yyyy - top 4 bits of y position
64// zzzzzz - middle 6 bits of z position
65// eeee - light emission
66// bbbb - light blocking
67// llll - light level
68// L - light value valid
69// B - blocking value valid
70// E - emission value valid
71// W - lighting value requires write
72// xxxxxxxxxxxxxxxx - top 16 bits of x position
73// zzzzzzzzzzzzzzzz - top 16 bits of z position
74#else
75// Packing for cache entries is as follows ( 32 bits per entry)
76// WEBLllllbbbbeeeexxxxxxyyyyzzzzzz
77//
78// xxxxxx - top 6 bits of x position
79// yyyy - top 4 bits of y position
80// zzzzzz - top 6 bits of z position
81// eeee - light emission
82// bbbb - light blocking
83// llll - light level
84// L - light value valid
85// B - blocking value valid
86// E - emission value valid
87// W - lighting value requires write
88#endif
89
90
91void Level::enableLightingCache()
92{
93 // Allocate 16K (needs 32K for large worlds) for a 16x16x16x4 byte cache of results, plus 128K required for toCheck array. Rounding up to 256 to keep as multiple of alignement - aligning to 128K boundary for possible cache locking.
94 void *cache = (unsigned char *)XPhysicalAlloc(256 * 1024, MAXULONG_PTR, 128 * 1024, PAGE_READWRITE | MEM_LARGE_PAGES);
95 TlsSetValue(tlsIdxLightCache,cache);
96}
97
98void Level::destroyLightingCache()
99{
100 lightCache_t *cache = (lightCache_t *)TlsGetValue(tlsIdxLightCache);
101 XPhysicalFree(cache);
102}
103
104inline int GetIndex(int x, int y, int z)
105{
106 return ( ( x & 15 ) << 8 ) | ( ( y & 15 ) << 4 ) | ( z & 15 );
107}
108
109void Level::initCachePartial(lightCache_t *cache, int xc, int yc, int zc)
110{
111 cachewritten = false;
112 if( cache == NULL ) return;
113
114 int idx;
115 if( !(yc & 0xffffff00) )
116 {
117 idx = GetIndex(xc, yc, zc);
118 cache[idx] = 0;
119 idx = GetIndex(xc - 1, yc, zc);
120 cache[idx] = 0;
121 idx = GetIndex(xc + 1, yc, zc);
122 cache[idx] = 0;
123 idx = GetIndex(xc, yc, zc - 1);
124 cache[idx] = 0;
125 idx = GetIndex(xc, yc, zc + 1);
126 cache[idx] = 0;
127 }
128 if( !((yc-1) & 0xffffff00) )
129 {
130 idx = GetIndex(xc, yc - 1, zc);
131 cache[idx] = 0;
132 }
133 if( !((yc+1) & 0xffffff00) )
134 {
135 idx = GetIndex(xc, yc + 1, zc);
136 cache[idx] = 0;
137 }
138}
139
140void Level::initCacheComplete(lightCache_t *cache, int xc, int yc, int zc)
141{
142 lightCache_t old[7];
143 if( !(yc & 0xffffff00) )
144 {
145 old[0] = cache[GetIndex(xc, yc, zc)];
146 old[1] = cache[GetIndex(xc - 1, yc, zc)];
147 old[2] = cache[GetIndex(xc + 1, yc, zc)];
148 old[5] = cache[GetIndex(xc, yc, zc - 1)];
149 old[6] = cache[GetIndex(xc, yc, zc + 1)];
150 }
151 if( !((yc-1) & 0xffffff00) )
152 {
153 old[3] = cache[GetIndex(xc, yc - 1, zc)];
154 }
155 if( !((yc+1) & 0xffffff00) )
156 {
157 old[4] = cache[GetIndex(xc, yc + 1, zc)];
158 }
159
160 XMemSet128(cache,0,16*16*16*sizeof(lightCache_t));
161
162 if( !(yc & 0xffffff00) )
163 {
164 cache[GetIndex(xc, yc, zc)] = old[0];
165 cache[GetIndex(xc - 1, yc, zc)] = old[1];
166 cache[GetIndex(xc + 1, yc, zc)] = old[2];
167 cache[GetIndex(xc, yc, zc - 1)] = old[5];
168 cache[GetIndex(xc, yc, zc + 1)] = old[6];
169 }
170 if( !((yc-1) & 0xffffff00) )
171 {
172 cache[GetIndex(xc, yc - 1, zc)] = old[3];
173 }
174 if( !((yc+1) & 0xffffff00) )
175 {
176 cache[GetIndex(xc, yc + 1, zc)] = old[4];
177 }
178}
179
180// Set a brightness value, going through the cache if enabled for this thread
181void inline Level::setBrightnessCached(lightCache_t *cache, __uint64 *cacheUse, LightLayer::variety layer, int x, int y, int z, int brightness)
182{
183 if( cache == NULL )
184 {
185 setBrightness(layer, x, y, z, brightness, true);
186 return;
187 }
188 if( y & 0xffffff00 ) return; // Eliminate -ve ys and values > 255
189
190 int idx = ( ( x & 15 ) << 8 ) |
191 ( ( y & 15 ) << 4 ) |
192 ( z & 15 );
193 lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) |
194 ( ( y & 0x0f0 ) << 2 ) |
195 ( ( z & 0x3f0 ) >> 4 );
196#ifdef _LARGE_WORLDS
197 // Add in the higher bits for x and z
198 posbits |= ( ( ((__uint64)x) & 0x3FFFC00L) << 38) |
199 ( ( ((__uint64)z) & 0x3FFFC00L) << 22);
200#endif
201
202 lightCache_t cacheValue = cache[idx];
203
204 // If this cache entry doesn't refer to the same thing...
205 if( ( cacheValue & POSITION_MASK ) != posbits )
206 {
207 /// and it has been written to...
208 if( cacheValue & LIGHTING_WRITEBACK )
209 {
210 // Then we need to flush
211 int val = ( cacheValue >> LIGHTING_SHIFT ) & 15;
212 int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 );
213#ifdef _LARGE_WORLDS
214 xx |= ( (cacheValue >> 38) & 0x3FFFC00);
215 xx = ( xx << 6 ) >> 6; // sign extend
216#else
217 xx = ( xx << 22 ) >> 22; // sign extend
218#endif
219 int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 );
220 int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 );
221#ifdef _LARGE_WORLDS
222 zz |= ( (cacheValue >> 22) & 0x3FFFC00);
223 zz = ( zz << 6 ) >> 6; // sign extend
224#else
225 zz = ( zz << 22 ) >> 22; // sign extend
226#endif
227 setBrightness(layer, xx, yy, zz, val, true);
228 }
229 cacheValue = posbits;
230 }
231
232 // Just written to it, so value is valid & requires writing back
233 cacheValue &= ~(15 << LIGHTING_SHIFT );
234 cacheValue |= brightness << LIGHTING_SHIFT;
235 cacheValue |= ( LIGHTING_WRITEBACK | LIGHTING_VALID );
236
237 // cacheUse has a single bit for each x, y and z to say whether anything with that x, y or z has been written to
238 (*cacheUse) |= ( ( 1LL << ( x & 15 ) ) | ( 0x10000LL << ( y & 15 ) ) | ( 0x100000000LL << ( z & 15 ) ) );
239
240 cache[idx] = cacheValue;
241}
242
243// Get a brightness value, going through the cache if enabled for this thread
244inline int Level::getBrightnessCached(lightCache_t *cache, LightLayer::variety layer, int x, int y, int z)
245{
246 if( cache == NULL ) return getBrightness(layer, x, y, z);
247 if( y & 0xffffff00 ) return getBrightness(layer, x, y, z); // Fall back on original method for out-of-bounds y
248
249 int idx = ( ( x & 15 ) << 8 ) |
250 ( ( y & 15 ) << 4 ) |
251 ( z & 15 );
252 lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) |
253 ( ( y & 0x0f0 ) << 2 ) |
254 ( ( z & 0x3f0 ) >> 4 );
255#ifdef _LARGE_WORLDS
256 // Add in the higher bits for x and z
257 posbits |= ( ( ((__uint64)x) & 0x3FFFC00L) << 38) |
258 ( ( ((__uint64)z) & 0x3FFFC00L) << 22);
259#endif
260
261 lightCache_t cacheValue = cache[idx];
262
263 if( ( cacheValue & POSITION_MASK ) != posbits )
264 {
265 // Position differs - need to evict this cache entry
266 if( cacheValue & LIGHTING_WRITEBACK )
267 {
268 // Then we need to flush
269 int val = ( cacheValue >> LIGHTING_SHIFT ) & 15;
270 int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 );
271#ifdef _LARGE_WORLDS
272 xx |= ( (cacheValue >> 38) & 0x3FFFC00);
273 xx = ( xx << 6 ) >> 6; // sign extend
274#else
275 xx = ( xx << 22 ) >> 22; // sign extend
276#endif
277 int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 );
278 int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 );
279#ifdef _LARGE_WORLDS
280 zz |= ( (cacheValue >> 22) & 0x3FFFC00);
281 zz = ( zz << 6 ) >> 6; // sign extend
282#else
283 zz = ( zz << 22 ) >> 22; // sign extend
284#endif
285 setBrightness(layer, xx, yy, zz, val, true);
286 }
287 cacheValue = posbits | LIGHTING_VALID;
288 int val = getBrightness(layer, x, y, z);
289 cacheValue |= val << LIGHTING_SHIFT;
290 }
291 else
292 {
293 // The position matches - will incurr a read miss if the lighting value isn't valid
294 if( ( cacheValue & LIGHTING_VALID ) == 0 )
295 {
296 int val = getBrightness(layer, x, y, z);
297 cacheValue |= val << LIGHTING_SHIFT;
298 cacheValue |= LIGHTING_VALID;
299 }
300 else
301 {
302 // All valid - just return value
303 return ( cacheValue >> LIGHTING_SHIFT ) & 15;
304 }
305 }
306
307 cache[idx] = cacheValue;
308 return ( cacheValue >> LIGHTING_SHIFT ) & 15;
309}
310
311// Get a block emission value, going through the cache if enabled for this thread
312inline int Level::getEmissionCached(lightCache_t *cache, int ct, int x, int y, int z)
313{
314 if( cache == NULL ) return Tile::lightEmission[ct];
315
316 int idx = ( ( x & 15 ) << 8 ) |
317 ( ( y & 15 ) << 4 ) |
318 ( z & 15 );
319 lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) |
320 ( ( y & 0x0f0 ) << 2 ) |
321 ( ( z & 0x3f0 ) >> 4 );
322#ifdef _LARGE_WORLDS
323 // Add in the higher bits for x and z
324 posbits |= ( ( ((__uint64)x) & 0x3FFFC00) << 38) |
325 ( ( ((__uint64)z) & 0x3FFFC00) << 22);
326#endif
327
328 lightCache_t cacheValue = cache[idx];
329
330 if( ( cacheValue & POSITION_MASK ) != posbits )
331 {
332 // Position differs - need to evict this cache entry
333 if( cacheValue & LIGHTING_WRITEBACK )
334 {
335 // Then we need to flush
336 int val = ( cacheValue >> LIGHTING_SHIFT ) & 15;
337 int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 );
338#ifdef _LARGE_WORLDS
339 xx |= ( (cacheValue >> 38) & 0x3FFFC00);
340 xx = ( xx << 6 ) >> 6; // sign extend
341#else
342 xx = ( xx << 22 ) >> 22; // sign extend
343#endif
344 int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 );
345 int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 );
346#ifdef _LARGE_WORLDS
347 zz |= ( (cacheValue >> 22) & 0x3FFFC00);
348 zz = ( zz << 6 ) >> 6; // sign extend
349#else
350 zz = ( zz << 22 ) >> 22; // sign extend
351#endif
352 setBrightness(LightLayer::Block, xx, yy, zz, val, true);
353 }
354
355 // Update both emission & blocking values whilst we are here
356 cacheValue = posbits | EMISSION_VALID | BLOCKING_VALID;
357 int t = getTile(x,y,z);
358 cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT;
359 cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT;
360 }
361 else
362 {
363 // The position matches - will incurr a read miss if the lighting value isn't valid
364 if( ( cacheValue & EMISSION_VALID ) == 0 )
365 {
366 // Update both emission & blocking values whilst we are here
367 cacheValue |= EMISSION_VALID | BLOCKING_VALID;
368 int t = getTile(x,y,z);
369 cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT;
370 cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT;
371 }
372 else
373 {
374 // All valid - just return value
375 return ( cacheValue >> EMISSION_SHIFT ) & 15;
376 }
377 }
378 cache[idx] = cacheValue;
379 return ( cacheValue >> EMISSION_SHIFT ) & 15;
380}
381
382// Get a tile light blocking value, going through cache if enabled for this thread
383inline int Level::getBlockingCached(lightCache_t *cache, LightLayer::variety layer, int *ct, int x, int y, int z)
384{
385 if( cache == NULL )
386 {
387 int t = getTile(x,y,z);
388 if(ct) *ct = t;
389 return Tile::lightBlock[t];
390 }
391
392 int idx = ( ( x & 15 ) << 8 ) |
393 ( ( y & 15 ) << 4 ) |
394 ( z & 15 );
395 lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) |
396 ( ( y & 0x0f0 ) << 2 ) |
397 ( ( z & 0x3f0 ) >> 4 );
398#ifdef _LARGE_WORLDS
399 // Add in the higher bits for x and z
400 posbits |= ( ( ((__uint64)x) & 0x3FFFC00L) << 38) |
401 ( ( ((__uint64)z) & 0x3FFFC00L) << 22);
402#endif
403
404 lightCache_t cacheValue = cache[idx];
405
406 if( ( cacheValue & POSITION_MASK ) != posbits )
407 {
408 // Position differs - need to evict this cache entry
409 if( cacheValue & LIGHTING_WRITEBACK )
410 {
411 // Then we need to flush
412 int val = ( cacheValue >> LIGHTING_SHIFT ) & 15;
413 int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 );
414#ifdef _LARGE_WORLDS
415 xx |= ( (cacheValue >> 38) & 0x3FFFC00);
416 xx = ( xx << 6 ) >> 6; // sign extend
417#else
418 xx = ( xx << 22 ) >> 22; // sign extend
419#endif
420 int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 );
421 int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 );
422#ifdef _LARGE_WORLDS
423 zz |= ( (cacheValue >> 22) & 0x3FFFC00);
424 zz = ( zz << 6 ) >> 6; // sign extend
425#else
426 zz = ( zz << 22 ) >> 22; // sign extend
427#endif
428 setBrightness(layer, xx, yy, zz, val, true);
429 }
430
431 // Update both emission & blocking values whilst we are here
432 cacheValue = posbits | EMISSION_VALID | BLOCKING_VALID;
433 int t = getTile(x,y,z);
434 cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT;
435 cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT;
436 }
437 else
438 {
439 // The position matches - will incurr a read miss if the lighting value isn't valid
440 if( ( cacheValue & EMISSION_VALID ) == 0 )
441 {
442 // Update both emission & blocking values whilst we are here
443 cacheValue |= EMISSION_VALID | BLOCKING_VALID;
444 int t = getTile(x,y,z);
445 cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT;
446 cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT;
447 }
448 else
449 {
450 // All valid - just return value
451 return ( cacheValue >> BLOCKING_SHIFT ) & 15;
452 }
453 }
454
455 cache[idx] = cacheValue;
456 return ( cacheValue >> BLOCKING_SHIFT ) & 15;
457}
458
459// Write back any dirty entries in the lighting cache. Also calls the setTilesDirty method on the region which has been updated during this lighting update, since
460// this hasn't been updated (for client threads) for each individual lighting update as would have been the case with the non-cached lighting. There's two reasons for this
461// (1) it's more efficient, since we aren't doing so many individual calls to the level listener to let the renderer know what has been updated
462// (2) it lets the lighting actually complete before we get any visual representation of the update, otherwise we end up seeing some strange partial updates
463void Level::flushCache(lightCache_t *cache, __uint64 cacheUse, LightLayer::variety layer)
464{
465 // cacheUse has a single bit for each x, y and z to say whether anything with that x, y or z has been written to
466 if( cacheUse == 0 ) return;
467 if( cache )
468 {
469 lightCache_t *pcache = cache;
470 for( int x = 0; x < 16; x++ )
471 {
472 if( ( cacheUse & ( 1LL << x ) ) == 0 )
473 {
474 pcache += 16 * 16;
475 continue;
476 }
477 for( int y = 0; y < 16; y++ )
478 {
479 if( ( cacheUse & ( 0x10000LL << y ) ) == 0 )
480 {
481 pcache += 16;
482 continue;
483 }
484 for( int z = 0; z < 16; z++ )
485 {
486 if( ( cacheUse & ( 0x100000000LL << z ) ) == 0 )
487 {
488 pcache++;
489 continue;
490 }
491 lightCache_t cacheValue = *pcache++;
492 if( cacheValue & LIGHTING_WRITEBACK )
493 {
494 int val = ( cacheValue >> LIGHTING_SHIFT ) & 15;
495 int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 );
496#ifdef _LARGE_WORLDS
497 xx |= ( (cacheValue >> 38) & 0x3FFFC00);
498 xx = ( xx << 6 ) >> 6; // sign extend
499#else
500 xx = ( xx << 22 ) >> 22; // sign extend
501#endif
502 int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 );
503 int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 );
504#ifdef _LARGE_WORLDS
505 zz |= ( (cacheValue >> 22) & 0x3FFFC00);
506 zz = ( zz << 6 ) >> 6; // sign extend
507#else
508 zz = ( zz << 22 ) >> 22; // sign extend
509#endif
510 setBrightness(layer, xx, yy, zz, val, true);
511 }
512 }
513 }
514 }
515 }
516 // For client side (which has the renderer attached) we haven't been updating with each individual update, but have been gathering them up.
517 // Let the renderer know now the region that has been updated.
518 if( isClientSide && cachewritten)
519 {
520 setTilesDirty(cacheminx, cacheminy, cacheminz,cachemaxx,cachemaxy,cachemaxz);
521 }
522}
523
524// 4J - added following 2 functions to move instaBuild flag from being a class member, to TLS
525bool Level::getInstaTick()
526{
527 return ((size_t)TlsGetValue(tlsIdx)) != 0;
528}
529
530void Level::setInstaTick(bool enable)
531{
532 void *value = 0;
533 if( enable ) value = (void *)1;
534 TlsSetValue(tlsIdx,value);
535}
536
537// 4J - added
538bool Level::hasEntitiesToRemove()
539{
540 return !entitiesToRemove.empty();
541}
542
543void Level::_init()
544{
545 cloudColor = 0xffffff;
546
547 skyDarken = 0;
548
549 randValue = (new Random())->nextInt();
550
551 addend = 1013904223;
552
553 oRainLevel = rainLevel = 0.0f;
554
555 oThunderLevel = thunderLevel = 0.0f;
556
557 skyFlashTime = 0;
558
559 difficulty = 0;
560
561 random = new Random();
562 isNew = false;
563
564 dimension = NULL;
565
566 chunkSource = NULL;
567
568 levelStorage = nullptr;
569
570 levelData = NULL;
571
572 isFindingSpawn = false;
573
574 savedDataStorage = NULL;
575
576 spawnEnemies = true;
577
578 spawnFriendlies = true;
579
580 delayUntilNextMoodSound = random->nextInt(20 * 60 * 10);
581
582 isClientSide = false;
583
584 InitializeCriticalSection(&m_entitiesCS);
585 InitializeCriticalSection(&m_tileEntityListCS);
586
587 updatingTileEntities = false;
588
589 villageSiege = new VillageSiege(this);
590 scoreboard = new Scoreboard();
591
592 toCheckLevel = new int[ 32 * 32 * 32]; // 4J - brought forward from 1.8.2
593 InitializeCriticalSectionAndSpinCount(&m_checkLightCS, 5120); // 4J - added for 1.8.2 lighting
594
595 // 4J Added
596 m_bDisableAddNewTileEntities = false;
597 m_iHighestY=-1000;
598 m_unsavedChunkCount = 0;
599}
600
601// 4J - brought forward from 1.8.2
602Biome *Level::getBiome(int x, int z)
603{
604 if (hasChunkAt(x, 0, z))
605 {
606 LevelChunk *lc = getChunkAt(x, z);
607 if (lc != NULL)
608 {
609 // Water chunks at the edge of the world return NULL for their biome as they can't store it, so should fall back on the normal method below
610 Biome *biome = lc->getBiome(x & 0xf, z & 0xf, dimension->biomeSource);
611 if( biome ) return biome;
612 }
613 }
614 return dimension->biomeSource->getBiome(x, z);
615}
616
617BiomeSource *Level::getBiomeSource()
618{
619 return dimension->biomeSource;
620}
621
622Level::Level(shared_ptr<LevelStorage> levelStorage, const wstring& name, Dimension *dimension, LevelSettings *levelSettings, bool doCreateChunkSource)
623 : seaLevel(constSeaLevel)
624{
625 _init();
626 this->levelStorage = levelStorage;//shared_ptr<LevelStorage>(levelStorage);
627 this->dimension = dimension;
628 levelData = new LevelData(levelSettings, name);
629 if( !this->levelData->useNewSeaLevel() ) seaLevel = Level::genDepth / 2; // 4J added - sea level is one unit lower since 1.8.2, maintain older height for old levels
630 savedDataStorage = new SavedDataStorage(levelStorage.get());
631
632 shared_ptr<Villages> savedVillages = dynamic_pointer_cast<Villages>(savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID));
633 if (savedVillages == NULL)
634 {
635 villages = shared_ptr<Villages>(new Villages(this));
636 savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages);
637 }
638 else
639 {
640 villages = savedVillages;
641 villages->setLevel(this);
642 }
643
644 dimension->init(this);
645 chunkSource = NULL; // 4J - added flag so chunk source can be called from derived class instead
646
647 updateSkyBrightness();
648 prepareWeather();
649}
650
651Level::Level(shared_ptr<LevelStorage>levelStorage, const wstring& levelName, LevelSettings *levelSettings)
652 : seaLevel( constSeaLevel )
653{
654 _init(levelStorage, levelName, levelSettings, NULL, true);
655}
656
657
658Level::Level(shared_ptr<LevelStorage>levelStorage, const wstring& levelName, LevelSettings *levelSettings, Dimension *fixedDimension, bool doCreateChunkSource)
659 : seaLevel( constSeaLevel )
660{
661 _init( levelStorage, levelName, levelSettings, fixedDimension, doCreateChunkSource );
662}
663
664void Level::_init(shared_ptr<LevelStorage>levelStorage, const wstring& levelName, LevelSettings *levelSettings, Dimension *fixedDimension, bool doCreateChunkSource)
665{
666 _init();
667 this->levelStorage = levelStorage;//shared_ptr<LevelStorage>(levelStorage);
668 savedDataStorage = new SavedDataStorage(levelStorage.get());
669
670 shared_ptr<Villages> savedVillages = dynamic_pointer_cast<Villages>(savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID));
671 if (savedVillages == NULL)
672 {
673 villages = shared_ptr<Villages>(new Villages(this));
674 savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages);
675 }
676 else
677 {
678 villages = savedVillages;
679 villages->setLevel(this);
680 }
681
682 levelData = levelStorage->prepareLevel();
683 isNew = levelData == NULL;
684
685 if (fixedDimension != NULL)
686 {
687 dimension = fixedDimension;
688 }
689 // 4J Remove TU9 as getDimensions was never accurate. This path was never used anyway as we always set fixedDimension
690 //else if (levelData != NULL && levelData->getDimension() != 0)
691 //{
692 // dimension = Dimension::getNew(levelData->getDimension());
693 //}
694 else
695 {
696 dimension = Dimension::getNew(0);
697 }
698
699 if (levelData == NULL)
700 {
701 levelData = new LevelData(levelSettings, levelName);
702 }
703 else
704 {
705 levelData->setLevelName(levelName);
706 }
707 if( !this->levelData->useNewSeaLevel() ) seaLevel = Level::genDepth / 2; // 4J added - sea level is one unit lower since 1.8.2, maintain older height for old levels
708
709 ((Dimension *) dimension)->init( this );
710
711 chunkSource = doCreateChunkSource ? createChunkSource() : NULL; // 4J - added flag so chunk source can be called from derived class instead
712
713 // 4J Stu- Moved to derived classes
714 //if (!levelData->isInitialized())
715 //{
716 // initializeLevel(levelSettings);
717 // levelData->setInitialized(true);
718 //}
719
720 updateSkyBrightness();
721 prepareWeather();
722
723}
724
725Level::~Level()
726{
727 delete random;
728 delete dimension;
729 delete chunkSource;
730 delete levelData;
731 delete toCheckLevel;
732 delete scoreboard;
733 delete villageSiege;
734
735 if( !isClientSide )
736 {
737 NotGateTile::removeLevelReferences(this); // 4J added
738 }
739
740 DeleteCriticalSection(&m_checkLightCS);
741
742 // 4J-PB - savedDataStorage is shared between overworld and nether levels in the server, so it will already have been deleted on the first level delete
743 if(savedDataStorage!=NULL) delete savedDataStorage;
744
745 DeleteCriticalSection(&m_entitiesCS);
746 DeleteCriticalSection(&m_tileEntityListCS);
747
748 // 4J Stu - At least one of the listeners is something we cannot delete, the LevelRenderer
749 /*
750 for(int i = 0; i < listeners.size(); i++)
751 delete listeners[i];
752 */
753}
754
755void Level::initializeLevel(LevelSettings *settings)
756{
757 levelData->setInitialized(true);
758}
759
760void Level::validateSpawn()
761{
762 setSpawnPos(8, 64, 8);
763}
764
765int Level::getTopTile(int x, int z)
766{
767 // 4J added - was breaking spawning as not finding ground in superflat worlds
768 if( levelData->getGenerator() == LevelType::lvl_flat )
769 {
770 return Tile::grass_Id;
771 }
772
773 int y = seaLevel;
774 while (!isEmptyTile(x, y + 1, z))
775 {
776 y++;
777 }
778 return getTile(x, y, z);
779}
780int Level::getTile(int x, int y, int z)
781{
782 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
783 {
784 return 0;
785 }
786 if (y < minBuildHeight) return 0;
787 if (y >= maxBuildHeight) return 0;
788 return getChunk(x >> 4, z >> 4)->getTile(x & 15, y, z & 15);
789}
790
791int Level::getTileLightBlock(int x, int y, int z)
792{
793 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
794 {
795 return 0;
796 }
797 if (y < minBuildHeight) return 0;
798 if (y >= maxBuildHeight) return 0;
799 return getChunk(x >> 4, z >> 4)->getTileLightBlock(x & 15, y, z & 15);
800}
801
802bool Level::isEmptyTile(int x, int y, int z)
803{
804 return getTile(x, y, z) == 0;
805}
806
807bool Level::isEntityTile(int x, int y, int z)
808{
809 int t = getTile(x, y, z);
810 if (Tile::tiles[t] != NULL && Tile::tiles[t]->isEntityTile())
811 {
812 return true;
813 }
814 return false;
815}
816
817int Level::getTileRenderShape(int x, int y, int z)
818{
819 int t = getTile(x, y, z);
820 if (Tile::tiles[t] != NULL)
821 {
822 return Tile::tiles[t]->getRenderShape();
823 }
824 return Tile::SHAPE_INVISIBLE;
825}
826
827// 4J Added to slightly optimise and avoid getTile call if we already know the tile
828int Level::getTileRenderShape(int t)
829{
830 if (Tile::tiles[t] != NULL)
831 {
832 return Tile::tiles[t]->getRenderShape();
833 }
834 return Tile::SHAPE_INVISIBLE;
835}
836
837bool Level::hasChunkAt(int x, int y, int z)
838{
839 if (y < minBuildHeight || y >= maxBuildHeight) return false;
840 return hasChunk(x >> 4, z >> 4);
841}
842
843// 4J added
844bool Level::reallyHasChunkAt(int x, int y, int z)
845{
846 if (y < minBuildHeight || y >= maxBuildHeight) return false;
847 return reallyHasChunk(x >> 4, z >> 4);
848}
849
850bool Level::hasChunksAt(int x, int y, int z, int r)
851{
852 return hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r);
853}
854
855// 4J added
856bool Level::reallyHasChunksAt(int x, int y, int z, int r)
857{
858 return reallyHasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r);
859}
860
861
862bool Level::hasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1)
863{
864 if (y1 < minBuildHeight || y0 >= maxBuildHeight) return false;
865
866 x0 >>= 4;
867 z0 >>= 4;
868 x1 >>= 4;
869 z1 >>= 4;
870
871 for (int x = x0; x <= x1; x++)
872 for (int z = z0; z <= z1; z++)
873 if (!hasChunk(x, z)) return false;
874
875 return true;
876}
877
878// 4J added
879bool Level::reallyHasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1)
880{
881 x0 >>= 4;
882 z0 >>= 4;
883 x1 >>= 4;
884 z1 >>= 4;
885
886 for (int x = x0; x <= x1; x++)
887 for (int z = z0; z <= z1; z++)
888 if (!reallyHasChunk(x, z)) return false;
889
890 return true;
891}
892
893bool Level::hasChunk(int x, int z)
894{
895 return this->chunkSource->hasChunk(x, z);
896}
897
898// 4J added
899bool Level::reallyHasChunk(int x, int z)
900{
901 return this->chunkSource->reallyHasChunk(x, z);
902}
903
904
905LevelChunk *Level::getChunkAt(int x, int z)
906{
907 return getChunk(x >> 4, z >> 4);
908}
909
910
911LevelChunk *Level::getChunk(int x, int z)
912{
913 return this->chunkSource->getChunk(x, z);
914}
915
916bool Level::setTileAndData(int x, int y, int z, int tile, int data, int updateFlags)
917{
918 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
919 {
920 return false;
921 }
922 if (y < 0) return false;
923 if (y >= maxBuildHeight) return false;
924 LevelChunk *c = getChunk(x >> 4, z >> 4);
925
926 int oldTile = 0;
927 if ((updateFlags & Tile::UPDATE_NEIGHBORS) != 0)
928 {
929 oldTile = c->getTile(x & 15, y, z & 15);
930 }
931 bool result;
932#ifndef _CONTENT_PACKAGE
933 int old = c->getTile(x & 15, y, z & 15);
934 int olddata = c->getData( x & 15, y, z & 15);
935#endif
936 result = c->setTileAndData(x & 15, y, z & 15, tile, data);
937 if( updateFlags != Tile::UPDATE_INVISIBLE_NO_LIGHT)
938 {
939#ifndef _CONTENT_PACKAGE
940 PIXBeginNamedEvent(0,"Checking light %d %d %d",x,y,z);
941 PIXBeginNamedEvent(0,"was %d, %d now %d, %d",old,olddata,tile,data);
942#endif
943 checkLight(x, y, z);
944 PIXEndNamedEvent();
945 PIXEndNamedEvent();
946 }
947 if (result)
948 {
949 if ((updateFlags & Tile::UPDATE_CLIENTS) != 0 && !(isClientSide && (updateFlags & Tile::UPDATE_INVISIBLE) != 0))
950 {
951 sendTileUpdated(x, y, z);
952 }
953 if (!isClientSide && (updateFlags & Tile::UPDATE_NEIGHBORS) != 0)
954 {
955 tileUpdated(x, y, z, oldTile);
956 Tile *tobj = Tile::tiles[tile];
957 if (tobj != NULL && tobj->hasAnalogOutputSignal()) updateNeighbourForOutputSignal(x, y, z, tile);
958 }
959 }
960 return result;
961}
962
963Material *Level::getMaterial(int x, int y, int z)
964{
965 int t = getTile(x, y, z);
966 if (t == 0) return Material::air;
967 return Tile::tiles[t]->material;
968}
969
970int Level::getData(int x, int y, int z)
971{
972 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
973 {
974 return 0;
975 }
976 if (y < 0) return 0;
977 if (y >= maxBuildHeight) return 0;
978 LevelChunk *c = getChunk(x >> 4, z >> 4);
979 x &= 15;
980 z &= 15;
981 return c->getData(x, y, z);
982}
983
984bool Level::setData(int x, int y, int z, int data, int updateFlags, bool forceUpdate/*=false*/) // 4J added forceUpdate)
985{
986 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
987 {
988 return false;
989 }
990 if (y < 0) return false;
991 if (y >= maxBuildHeight) return false;
992 LevelChunk *c = getChunk(x >> 4, z >> 4);
993 int cx = x & 15;
994 int cz = z & 15;
995 // 4J - have changed _sendTileData to encode a bitfield of which bits are important to be sent. This will be zero where the original flag was false, and non-zero where the original
996 // flag was true - hence recreating the original flag as sendTileData here. For nearly all tiles this will be 15 for the case where this used to be true (ie all bits are important) so
997 // there should be absolutely to change in behaviour. However, for leaf tiles, bits have been masked so we don't bother doing sendTileUpdated if a non-visual thing has changed in the data
998 unsigned char importantMask = Tile::_sendTileData[c->getTile(cx, y, cz) & Tile::TILE_NUM_MASK];
999 bool sendTileData = importantMask != 0;
1000
1001 bool maskedBitsChanged;
1002 bool result = c->setData(cx, y, cz, data, importantMask, &maskedBitsChanged);
1003 if (result || forceUpdate)
1004 {
1005 int tile = c->getTile(cx, y, cz);
1006 if (forceUpdate || ((updateFlags & Tile::UPDATE_CLIENTS) != 0 && !(isClientSide && (updateFlags & Tile::UPDATE_INVISIBLE) != 0)))
1007 {
1008 sendTileUpdated(x, y, z);
1009 }
1010 if (!isClientSide && (forceUpdate || (updateFlags & Tile::UPDATE_NEIGHBORS) != 0) )
1011 {
1012 tileUpdated(x, y, z, tile);
1013 Tile *tobj = Tile::tiles[tile];
1014 if (tobj != NULL && tobj->hasAnalogOutputSignal()) updateNeighbourForOutputSignal(x, y, z, tile);
1015 }
1016 }
1017 return result;
1018}
1019
1020/**
1021* Sets a tile to air without dropping resources or showing any animation.
1022*
1023* @param x
1024* @param y
1025* @param z
1026* @return
1027*/
1028bool Level::removeTile(int x, int y, int z)
1029{
1030 return setTileAndData(x, y, z, 0, 0, Tile::UPDATE_ALL);
1031}
1032
1033/**
1034* Sets a tile to air and plays a destruction animation, with option to also
1035* drop resources.
1036*
1037* @param x
1038* @param y
1039* @param z
1040* @param dropResources
1041* @return True if anything was changed
1042*/
1043bool Level::destroyTile(int x, int y, int z, bool dropResources)
1044{
1045 int tile = getTile(x, y, z);
1046 if (tile > 0)
1047 {
1048 int data = getData(x, y, z);
1049 levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, tile + (data << Tile::TILE_NUM_SHIFT));
1050 if (dropResources)
1051 {
1052 Tile::tiles[tile]->spawnResources(this, x, y, z, data, 0);
1053 }
1054 return setTileAndData(x, y, z, 0, 0, Tile::UPDATE_ALL);
1055 }
1056 return false;
1057}
1058
1059bool Level::setTileAndUpdate(int x, int y, int z, int tile)
1060{
1061 return setTileAndData(x, y, z, tile, 0, Tile::UPDATE_ALL);
1062}
1063
1064void Level::sendTileUpdated(int x, int y, int z)
1065{
1066 AUTO_VAR(itEnd, listeners.end());
1067 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1068 {
1069 (*it)->tileChanged(x, y, z);
1070 }
1071}
1072
1073void Level::tileUpdated(int x, int y, int z, int tile)
1074{
1075 updateNeighborsAt(x, y, z, tile);
1076}
1077
1078void Level::lightColumnChanged(int x, int z, int y0, int y1)
1079{
1080 PIXBeginNamedEvent(0,"LightColumnChanged (%d,%d) %d to %d",x,z,y0,y1);
1081 if (y0 > y1)
1082 {
1083 int tmp = y1;
1084 y1 = y0;
1085 y0 = tmp;
1086 }
1087
1088 if (!dimension->hasCeiling)
1089 {
1090 PIXBeginNamedEvent(0,"Checking lights");
1091 for (int y = y0; y <= y1; y++)
1092 {
1093 PIXBeginNamedEvent(0,"Checking light %d", y);
1094 checkLight(LightLayer::Sky, x, y, z);
1095 PIXEndNamedEvent();
1096 }
1097 PIXEndNamedEvent();
1098 }
1099 PIXBeginNamedEvent(0,"Setting tiles dirty");
1100 setTilesDirty(x, y0, z, x, y1, z);
1101 PIXEndNamedEvent();
1102 PIXEndNamedEvent();
1103}
1104
1105
1106void Level::setTileDirty(int x, int y, int z)
1107{
1108 AUTO_VAR(itEnd, listeners.end());
1109 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1110 {
1111 (*it)->setTilesDirty(x, y, z, x, y, z, this);
1112 }
1113}
1114
1115
1116void Level::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1)
1117{
1118 AUTO_VAR(itEnd, listeners.end());
1119 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1120 {
1121 (*it)->setTilesDirty(x0, y0, z0, x1, y1, z1, this);
1122 }
1123}
1124
1125void Level::updateNeighborsAt(int x, int y, int z, int tile)
1126{
1127 neighborChanged(x - 1, y, z, tile);
1128 neighborChanged(x + 1, y, z, tile);
1129 neighborChanged(x, y - 1, z, tile);
1130 neighborChanged(x, y + 1, z, tile);
1131 neighborChanged(x, y, z - 1, tile);
1132 neighborChanged(x, y, z + 1, tile);
1133}
1134
1135void Level::updateNeighborsAtExceptFromFacing(int x, int y, int z, int tile, int skipFacing)
1136{
1137 if (skipFacing != Facing::WEST) neighborChanged(x - 1, y, z, tile);
1138 if (skipFacing != Facing::EAST) neighborChanged(x + 1, y, z, tile);
1139 if (skipFacing != Facing::DOWN) neighborChanged(x, y - 1, z, tile);
1140 if (skipFacing != Facing::UP) neighborChanged(x, y + 1, z, tile);
1141 if (skipFacing != Facing::NORTH) neighborChanged(x, y, z - 1, tile);
1142 if (skipFacing != Facing::SOUTH) neighborChanged(x, y, z + 1, tile);
1143}
1144
1145void Level::neighborChanged(int x, int y, int z, int type)
1146{
1147 if (isClientSide) return;
1148 int id = getTile(x, y, z);
1149 Tile *tile = Tile::tiles[id];
1150
1151 if (tile != NULL)
1152 {
1153 tile->neighborChanged(this, x, y, z, type);
1154 }
1155}
1156
1157bool Level::isTileToBeTickedAt(int x, int y, int z, int tileId)
1158{
1159 return false;
1160}
1161
1162bool Level::canSeeSky(int x, int y, int z)
1163{
1164 return getChunk(x >> 4, z >> 4)->isSkyLit(x & 15, y, z & 15);
1165}
1166
1167
1168int Level::getDaytimeRawBrightness(int x, int y, int z)
1169{
1170 if (y < 0) return 0;
1171 if (y >= maxBuildHeight) y = maxBuildHeight - 1;
1172 return getChunk(x >> 4, z >> 4)->getRawBrightness(x & 15, y, z & 15, 0);
1173}
1174
1175
1176int Level::getRawBrightness(int x, int y, int z)
1177{
1178 return getRawBrightness(x, y, z, true);
1179}
1180
1181
1182int Level::getRawBrightness(int x, int y, int z, bool propagate)
1183{
1184 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
1185 {
1186 return MAX_BRIGHTNESS;
1187 }
1188
1189 if (propagate)
1190 {
1191 int id = getTile(x, y, z);
1192 if (Tile::propagate[id])
1193 {
1194 int br = getRawBrightness(x, y + 1, z, false);
1195 int br1 = getRawBrightness(x + 1, y, z, false);
1196 int br2 = getRawBrightness(x - 1, y, z, false);
1197 int br3 = getRawBrightness(x, y, z + 1, false);
1198 int br4 = getRawBrightness(x, y, z - 1, false);
1199 if (br1 > br) br = br1;
1200 if (br2 > br) br = br2;
1201 if (br3 > br) br = br3;
1202 if (br4 > br) br = br4;
1203 return br;
1204 }
1205 }
1206
1207 if (y < 0) return 0;
1208 if (y >= maxBuildHeight) y = maxBuildHeight - 1;
1209
1210 LevelChunk *c = getChunk(x >> 4, z >> 4);
1211 x &= 15;
1212 z &= 15;
1213 return c->getRawBrightness(x, y, z, skyDarken);
1214}
1215
1216
1217bool Level::isSkyLit(int x, int y, int z)
1218{
1219 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
1220 {
1221 return false;
1222 }
1223 if (dimension->hasCeiling) return false;
1224
1225 if (y < 0) return false;
1226 if (y >= maxBuildHeight) return true;
1227 if (!hasChunk(x >> 4, z >> 4)) return false;
1228
1229 LevelChunk *c = getChunk(x >> 4, z >> 4);
1230 x &= 15;
1231 z &= 15;
1232 return c->isSkyLit(x, y, z);
1233}
1234
1235
1236int Level::getHeightmap(int x, int z)
1237{
1238 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
1239 {
1240 return 0;
1241 }
1242 if (!hasChunk(x >> 4, z >> 4)) return 0;
1243
1244 LevelChunk *c = getChunk(x >> 4, z >> 4);
1245 return c->getHeightmap(x & 15, z & 15);
1246}
1247
1248int Level::getLowestHeightmap(int x, int z)
1249{
1250 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
1251 {
1252 return 0;
1253 }
1254 if (!hasChunk(x >> 4, z >> 4)) return 0;
1255
1256 LevelChunk *c = getChunk(x >> 4, z >> 4);
1257 return c->lowestHeightmap;
1258}
1259
1260void Level::updateLightIfOtherThan(LightLayer::variety layer, int x, int y, int z, int expected)
1261{
1262 if (dimension->hasCeiling && layer == LightLayer::Sky) return;
1263
1264 if (!hasChunkAt(x, y, z)) return;
1265
1266 if (layer == LightLayer::Sky)
1267 {
1268 if (isSkyLit(x, y, z)) expected = 15;
1269 }
1270 else if (layer == LightLayer::Block)
1271 {
1272 int t = getTile(x, y, z);
1273 if (Tile::lightEmission[t] > expected) expected = Tile::lightEmission[t];
1274 }
1275
1276 if (getBrightness(layer, x, y, z) != expected)
1277 {
1278 setBrightness(layer, x, y, z, expected);
1279 }
1280}
1281
1282// 4J - update brought forward from 1.8.2
1283int Level::getBrightnessPropagate(LightLayer::variety layer, int x, int y, int z, int tileId)
1284{
1285 if (dimension->hasCeiling && layer == LightLayer::Sky) return 0;
1286
1287 if (y < 0) y = 0;
1288 if (y >= maxBuildHeight && layer == LightLayer::Sky)
1289 {
1290 // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
1291 // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
1292 // it to an int
1293 return (int)layer;
1294 }
1295 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
1296 {
1297 // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
1298 // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
1299 // it to an int
1300 return (int)layer;
1301 }
1302 int xc = x >> 4;
1303 int zc = z >> 4;
1304 if (!hasChunk(xc, zc)) return (int)layer;
1305
1306 {
1307 int id = tileId > -1 ? tileId : getTile(x,y,z);
1308 if (Tile::propagate[id])
1309 {
1310 int br = getBrightness(layer, x, y + 1, z);
1311 int br1 = getBrightness(layer, x + 1, y, z);
1312 int br2 = getBrightness(layer, x - 1, y, z);
1313 int br3 = getBrightness(layer, x, y, z + 1);
1314 int br4 = getBrightness(layer, x, y, z - 1);
1315 if (br1 > br) br = br1;
1316 if (br2 > br) br = br2;
1317 if (br3 > br) br = br3;
1318 if (br4 > br) br = br4;
1319 return br;
1320 }
1321 }
1322
1323 LevelChunk *c = getChunk(xc, zc);
1324 return c->getBrightness(layer, x & 15, y, z & 15);
1325}
1326
1327int Level::getBrightness(LightLayer::variety layer, int x, int y, int z)
1328{
1329 // 4J - optimised. Not doing checks on x/z that are no longer necessary, and directly checking the cache within
1330 // the ServerChunkCache/MultiplayerChunkCache rather than going through wrappers & virtual functions.
1331 int xc = x >> 4;
1332 int zc = z >> 4;
1333
1334 int ix = xc + (chunkSourceXZSize/2);
1335 int iz = zc + (chunkSourceXZSize/2);
1336
1337 if( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) return 0;
1338 if( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) return 0;
1339 int idx = ix * chunkSourceXZSize + iz;
1340 LevelChunk *c = chunkSourceCache[idx];
1341
1342 if( c == NULL ) return (int)layer;
1343
1344 if (y < 0) y = 0;
1345 if (y >= maxBuildHeight) y = maxBuildHeight - 1;
1346
1347 return c->getBrightness(layer, x & 15, y, z & 15);
1348}
1349
1350// 4J added as optimisation - if all the neighbouring brightesses are going to be in the one chunk, just get
1351// the level chunk once
1352void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z)
1353{
1354 if( ( ( ( x & 15 ) == 0 ) || ( ( x & 15 ) == 15 ) ) ||
1355 ( ( ( z & 15 ) == 0 ) || ( ( z & 15 ) == 15 ) ) ||
1356 ( ( y <= 0 ) || ( y >= 127 ) ) )
1357 {
1358 // We're spanning more than one chunk, just fall back on original java method here
1359 brightnesses[0] = getBrightness(layer, x - 1, y, z);
1360 brightnesses[1] = getBrightness(layer, x + 1, y, z);
1361 brightnesses[2] = getBrightness(layer, x, y - 1, z);
1362 brightnesses[3] = getBrightness(layer, x, y + 1, z);
1363 brightnesses[4] = getBrightness(layer, x, y, z - 1);
1364 brightnesses[5] = getBrightness(layer, x, y, z + 1);
1365 }
1366 else
1367 {
1368 // All in one chunk - just get the chunk once, and do a single call to get the results
1369 int xc = x >> 4;
1370 int zc = z >> 4;
1371
1372 int ix = xc + (chunkSourceXZSize/2);
1373 int iz = zc + (chunkSourceXZSize/2);
1374
1375 // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
1376 // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
1377 // it to an int
1378 if( ( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) ||
1379 ( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) )
1380 {
1381 for( int i = 0; i < 6; i++ )
1382 {
1383 brightnesses[i] = (int)layer;
1384 }
1385 return;
1386 }
1387
1388 int idx = ix * chunkSourceXZSize + iz;
1389 LevelChunk *c = chunkSourceCache[idx];
1390
1391 // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
1392 // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
1393 // it to an int
1394 if( c == NULL )
1395 {
1396 for( int i = 0; i < 6; i++ )
1397 {
1398 brightnesses[i] = (int)layer;
1399 }
1400 return;
1401 }
1402
1403 // Single call to the levelchunk too to avoid overhead of virtual fn calls
1404 c->getNeighbourBrightnesses(brightnesses, layer, x & 15, y, z & 15);
1405 }
1406}
1407
1408void Level::setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness, bool noUpdateOnClient/*=false*/) // 4J added noUpdateOnClient
1409{
1410 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
1411 {
1412 return;
1413 }
1414 if (y < 0) return;
1415 if (y >= maxBuildHeight) return;
1416 if (!hasChunk(x >> 4, z >> 4)) return;
1417 LevelChunk *c = getChunk(x >> 4, z >> 4);
1418
1419 c->setBrightness(layer, x & 15, y, z & 15, brightness);
1420
1421 // 4J added
1422 if( isClientSide && noUpdateOnClient )
1423 {
1424 if( cachewritten )
1425 {
1426 if( x < cacheminx ) cacheminx = x;
1427 if( x > cachemaxx ) cachemaxx = x;
1428 if( y < cacheminy ) cacheminy = y;
1429 if( y > cachemaxy ) cachemaxy = y;
1430 if( z < cacheminz ) cacheminz = z;
1431 if( z > cachemaxz ) cachemaxz = z;
1432 }
1433 else
1434 {
1435 cachewritten = true;
1436 cacheminx = x;
1437 cachemaxx = x;
1438 cacheminy = y;
1439 cachemaxy = y;
1440 cacheminz = z;
1441 cachemaxz = z;
1442 }
1443 }
1444 else
1445 {
1446 AUTO_VAR(itEnd, listeners.end());
1447 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1448 {
1449 (*it)->tileLightChanged(x, y, z);
1450 }
1451 }
1452}
1453
1454void Level::setTileBrightnessChanged(int x, int y, int z)
1455{
1456 AUTO_VAR(itEnd, listeners.end());
1457 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1458 {
1459 (*it)->tileLightChanged(x, y, z);
1460 }
1461}
1462
1463int Level::getLightColor(int x, int y, int z, int emitt, int tileId/*=-1*/)
1464{
1465 int s = getBrightnessPropagate(LightLayer::Sky, x, y, z, tileId);
1466 int b = getBrightnessPropagate(LightLayer::Block, x, y, z, tileId);
1467 if (b < emitt) b = emitt;
1468 return s << 20 | b << 4;
1469}
1470
1471float Level::getBrightness(int x, int y, int z, int emitt)
1472{
1473 int n = getRawBrightness(x, y, z);
1474 if (n < emitt) n = emitt;
1475 return dimension->brightnessRamp[n];
1476}
1477
1478
1479float Level::getBrightness(int x, int y, int z)
1480{
1481 return dimension->brightnessRamp[getRawBrightness(x, y, z)];
1482}
1483
1484
1485bool Level::isDay()
1486{
1487 return skyDarken < 4;
1488}
1489
1490
1491HitResult *Level::clip(Vec3 *a, Vec3 *b)
1492{
1493 return clip(a, b, false, false);
1494}
1495
1496
1497HitResult *Level::clip(Vec3 *a, Vec3 *b, bool liquid)
1498{
1499 return clip(a, b, liquid, false);
1500}
1501
1502
1503HitResult *Level::clip(Vec3 *a, Vec3 *b, bool liquid, bool solidOnly)
1504{
1505 if (Double::isNaN(a->x) || Double::isNaN(a->y) || Double::isNaN(a->z)) return NULL;
1506 if (Double::isNaN(b->x) || Double::isNaN(b->y) || Double::isNaN(b->z)) return NULL;
1507
1508 int xTile1 = Mth::floor(b->x);
1509 int yTile1 = Mth::floor(b->y);
1510 int zTile1 = Mth::floor(b->z);
1511
1512 int xTile0 = Mth::floor(a->x);
1513 int yTile0 = Mth::floor(a->y);
1514 int zTile0 = Mth::floor(a->z);
1515
1516 {
1517 int t = getTile(xTile0, yTile0, zTile0);
1518 int data = getData(xTile0, yTile0, zTile0);
1519 Tile *tile = Tile::tiles[t];
1520 if (solidOnly && tile != NULL && tile->getAABB(this, xTile0, yTile0, zTile0) == NULL)
1521 {
1522 // No collision
1523
1524 }
1525 else if (t > 0 && tile->mayPick(data, liquid))
1526 {
1527 HitResult *r = tile->clip(this, xTile0, yTile0, zTile0, a, b);
1528 if (r != NULL) return r;
1529 }
1530 }
1531
1532 int maxIterations = 200;
1533 while (maxIterations-- >= 0)
1534 {
1535 if (Double::isNaN(a->x) || Double::isNaN(a->y) || Double::isNaN(a->z)) return NULL;
1536 if (xTile0 == xTile1 && yTile0 == yTile1 && zTile0 == zTile1) return NULL;
1537
1538 bool xClipped = true;
1539 bool yClipped = true;
1540 bool zClipped = true;
1541
1542 double xClip = 999;
1543 double yClip = 999;
1544 double zClip = 999;
1545
1546 if (xTile1 > xTile0) xClip = xTile0 + 1.000;
1547 else if (xTile1 < xTile0) xClip = xTile0 + 0.000;
1548 else xClipped = false;
1549
1550 if (yTile1 > yTile0) yClip = yTile0 + 1.000;
1551 else if (yTile1 < yTile0) yClip = yTile0 + 0.000;
1552 else yClipped = false;
1553
1554 if (zTile1 > zTile0) zClip = zTile0 + 1.000;
1555 else if (zTile1 < zTile0) zClip = zTile0 + 0.000;
1556 else zClipped = false;
1557
1558 double xDist = 999;
1559 double yDist = 999;
1560 double zDist = 999;
1561
1562 double xd = b->x - a->x;
1563 double yd = b->y - a->y;
1564 double zd = b->z - a->z;
1565
1566 if (xClipped) xDist = (xClip - a->x) / xd;
1567 if (yClipped) yDist = (yClip - a->y) / yd;
1568 if (zClipped) zDist = (zClip - a->z) / zd;
1569
1570 int face = 0;
1571 if (xDist < yDist && xDist < zDist)
1572 {
1573 if (xTile1 > xTile0) face = 4;
1574 else face = 5;
1575
1576 a->x = xClip;
1577 a->y += yd * xDist;
1578 a->z += zd * xDist;
1579 }
1580 else if (yDist < zDist)
1581 {
1582 if (yTile1 > yTile0) face = 0;
1583 else face = 1;
1584
1585 a->x += xd * yDist;
1586 a->y = yClip;
1587 a->z += zd * yDist;
1588 }
1589 else
1590 {
1591 if (zTile1 > zTile0) face = 2;
1592 else face = 3;
1593
1594 a->x += xd * zDist;
1595 a->y += yd * zDist;
1596 a->z = zClip;
1597 }
1598
1599 Vec3 *tPos = Vec3::newTemp(a->x, a->y, a->z);
1600 xTile0 = (int) (tPos->x = floor(a->x));
1601 if (face == 5)
1602 {
1603 xTile0--;
1604 tPos->x++;
1605 }
1606 yTile0 = (int) (tPos->y = floor(a->y));
1607 if (face == 1)
1608 {
1609 yTile0--;
1610 tPos->y++;
1611 }
1612 zTile0 = (int) (tPos->z = floor(a->z));
1613 if (face == 3)
1614 {
1615 zTile0--;
1616 tPos->z++;
1617 }
1618
1619 int t = getTile(xTile0, yTile0, zTile0);
1620 int data = getData(xTile0, yTile0, zTile0);
1621 Tile *tile = Tile::tiles[t];
1622 if (solidOnly && tile != NULL && tile->getAABB(this, xTile0, yTile0, zTile0) == NULL)
1623 {
1624 // No collision
1625
1626 }
1627 else if (t > 0 && tile->mayPick(data, liquid))
1628 {
1629 HitResult *r = tile->clip(this, xTile0, yTile0, zTile0, a, b);
1630 if (r != NULL) return r;
1631 }
1632 }
1633 return NULL;
1634}
1635
1636
1637void Level::playEntitySound(shared_ptr<Entity> entity, int iSound, float volume, float pitch)
1638{
1639 if(entity == NULL) return;
1640 AUTO_VAR(itEnd, listeners.end());
1641 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1642 {
1643 // 4J-PB - if the entity is a local player, don't play the sound
1644 if(entity->GetType() == eTYPE_SERVERPLAYER)
1645 {
1646 //app.DebugPrintf("ENTITY is serverplayer\n");
1647
1648 (*it)->playSound(iSound, entity->x, entity->y - entity->heightOffset, entity->z, volume, pitch);
1649 }
1650 else
1651 {
1652 (*it)->playSound(iSound, entity->x, entity->y - entity->heightOffset, entity->z, volume, pitch);
1653 }
1654 }
1655}
1656
1657void Level::playPlayerSound(shared_ptr<Player> entity, int iSound, float volume, float pitch)
1658{
1659 if (entity == NULL) return;
1660 AUTO_VAR(itEnd, listeners.end());
1661 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1662 {
1663 (*it)->playSoundExceptPlayer(entity, iSound, entity->x, entity->y - entity->heightOffset, entity->z, volume, pitch);
1664 }
1665}
1666
1667//void Level::playSound(double x, double y, double z, const wstring& name, float volume, float pitch)
1668void Level::playSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist)
1669{
1670 AUTO_VAR(itEnd, listeners.end());
1671 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1672 {
1673 (*it)->playSound(iSound, x, y, z, volume, pitch, fClipSoundDist);
1674 }
1675}
1676
1677void Level::playLocalSound(double x, double y, double z, int iSound, float volume, float pitch, bool distanceDelay, float fClipSoundDist)
1678{
1679}
1680
1681void Level::playStreamingMusic(const wstring& name, int x, int y, int z)
1682{
1683 AUTO_VAR(itEnd, listeners.end());
1684 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1685 {
1686 (*it)->playStreamingMusic(name, x, y, z);
1687 }
1688}
1689
1690
1691void Level::playMusic(double x, double y, double z, const wstring& string, float volume)
1692{
1693}
1694
1695// 4J removed -
1696/*
1697void Level::addParticle(const wstring& id, double x, double y, double z, double xd, double yd, double zd)
1698{
1699AUTO_VAR(itEnd, listeners.end());
1700for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1701(*it)->addParticle(id, x, y, z, xd, yd, zd);
1702}
1703*/
1704
1705// 4J-PB added
1706void Level::addParticle(ePARTICLE_TYPE id, double x, double y, double z, double xd, double yd, double zd)
1707{
1708 AUTO_VAR(itEnd, listeners.end());
1709 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1710 (*it)->addParticle(id, x, y, z, xd, yd, zd);
1711}
1712
1713bool Level::addGlobalEntity(shared_ptr<Entity> e)
1714{
1715 globalEntities.push_back(e);
1716 return true;
1717}
1718
1719#pragma optimize( "", off )
1720
1721bool Level::addEntity(shared_ptr<Entity> e)
1722{
1723 int xc = Mth::floor(e->x / 16);
1724 int zc = Mth::floor(e->z / 16);
1725
1726 if(e == NULL)
1727 {
1728 return false;
1729 }
1730
1731 bool forced = e->forcedLoading;
1732 if (e->instanceof(eTYPE_PLAYER))
1733 {
1734 forced = true;
1735 }
1736
1737 if (forced || hasChunk(xc, zc))
1738 {
1739 if (e->instanceof(eTYPE_PLAYER))
1740 {
1741 shared_ptr<Player> player = dynamic_pointer_cast<Player>(e);
1742
1743 // 4J Stu - Added so we don't continually add the player to the players list while they are dead
1744 if( find( players.begin(), players.end(), e ) == players.end() )
1745 {
1746 players.push_back(player);
1747 }
1748
1749 updateSleepingPlayerList();
1750 }
1751 MemSect(42);
1752 getChunk(xc, zc)->addEntity(e);
1753 MemSect(0);
1754 EnterCriticalSection(&m_entitiesCS);
1755 MemSect(43);
1756 entities.push_back(e);
1757 MemSect(0);
1758 LeaveCriticalSection(&m_entitiesCS);
1759 MemSect(44);
1760 entityAdded(e);
1761 MemSect(0);
1762 return true;
1763 }
1764 return false;
1765}
1766
1767#pragma optimize( "", on )
1768
1769void Level::entityAdded(shared_ptr<Entity> e)
1770{
1771 AUTO_VAR(itEnd, listeners.end());
1772 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1773 {
1774 (*it)->entityAdded(e);
1775 }
1776}
1777
1778
1779void Level::entityRemoved(shared_ptr<Entity> e)
1780{
1781 AUTO_VAR(itEnd, listeners.end());
1782 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1783 {
1784 (*it)->entityRemoved(e);
1785 }
1786}
1787
1788// 4J added
1789void Level::playerRemoved(shared_ptr<Entity> e)
1790{
1791 AUTO_VAR(itEnd, listeners.end());
1792 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
1793 {
1794 (*it)->playerRemoved(e);
1795 }
1796}
1797
1798void Level::removeEntity(shared_ptr<Entity> e)
1799{
1800 if (e->rider.lock() != NULL)
1801 {
1802 e->rider.lock()->ride(nullptr);
1803 }
1804 if (e->riding != NULL)
1805 {
1806 e->ride(nullptr);
1807 }
1808 e->remove();
1809 if (e->instanceof(eTYPE_PLAYER))
1810 {
1811 vector<shared_ptr<Player> >::iterator it = players.begin();
1812 vector<shared_ptr<Player> >::iterator itEnd = players.end();
1813 while( it != itEnd && *it != dynamic_pointer_cast<Player>(e) )
1814 it++;
1815
1816 if( it != itEnd )
1817 {
1818 players.erase( it );
1819 }
1820
1821 updateSleepingPlayerList();
1822 playerRemoved(e); // 4J added - this will let the entity tracker know that we have actually removed the player from the level's player list
1823 }
1824}
1825
1826
1827void Level::removeEntityImmediately(shared_ptr<Entity> e)
1828{
1829 e->remove();
1830
1831 if (e->instanceof(eTYPE_PLAYER))
1832 {
1833 vector<shared_ptr<Player> >::iterator it = players.begin();
1834 vector<shared_ptr<Player> >::iterator itEnd = players.end();
1835 while( it != itEnd && *it != dynamic_pointer_cast<Player>(e) )
1836 it++;
1837
1838 if( it != itEnd )
1839 {
1840 players.erase( it );
1841 }
1842
1843 updateSleepingPlayerList();
1844 playerRemoved(e); // 4J added - this will let the entity tracker know that we have actually removed the player from the level's player list
1845 }
1846
1847 int xc = e->xChunk;
1848 int zc = e->zChunk;
1849 if (e->inChunk && hasChunk(xc, zc))
1850 {
1851 getChunk(xc, zc)->removeEntity(e);
1852 }
1853
1854 EnterCriticalSection(&m_entitiesCS);
1855 vector<shared_ptr<Entity> >::iterator it = entities.begin();
1856 vector<shared_ptr<Entity> >::iterator endIt = entities.end();
1857 while( it != endIt && *it != e)
1858 it++;
1859
1860 if( it != endIt )
1861 {
1862 entities.erase( it );
1863 }
1864 LeaveCriticalSection(&m_entitiesCS);
1865 entityRemoved(e);
1866}
1867
1868
1869void Level::addListener(LevelListener *listener)
1870{
1871 listeners.push_back(listener);
1872}
1873
1874
1875void Level::removeListener(LevelListener *listener)
1876{
1877 vector<LevelListener *>::iterator it = listeners.begin();
1878 vector<LevelListener *>::iterator itEnd = listeners.end();
1879 while( it != itEnd && *it != listener )
1880 it++;
1881
1882 if( it != itEnd )
1883 listeners.erase( it );
1884}
1885
1886
1887// 4J - added noEntities and blockAtEdge parameter
1888AABBList *Level::getCubes(shared_ptr<Entity> source, AABB *box, bool noEntities/* = false*/, bool blockAtEdge/* = false*/)
1889{
1890 boxes.clear();
1891 int x0 = Mth::floor(box->x0);
1892 int x1 = Mth::floor(box->x1 + 1);
1893 int y0 = Mth::floor(box->y0);
1894 int y1 = Mth::floor(box->y1 + 1);
1895 int z0 = Mth::floor(box->z0);
1896 int z1 = Mth::floor(box->z1 + 1);
1897
1898 int maxxz = ( dimension->getXZSize() * 16 ) / 2;
1899 int minxz = -maxxz;
1900 for (int x = x0; x < x1; x++)
1901 for (int z = z0; z < z1; z++)
1902 {
1903 // 4J - If we are outside the map, return solid AABBs (rock is a bit of an arbitrary choice here, just need a correct AABB)
1904 if( blockAtEdge && ( ( x < minxz ) || ( x >= maxxz ) || ( z < minxz ) || ( z >= maxxz ) ) )
1905 {
1906 for (int y = y0 - 1; y < y1; y++)
1907 {
1908 Tile::stone->addAABBs(this, x, y, z, box, &boxes, source);
1909 }
1910 }
1911 else
1912 {
1913 if (hasChunkAt(x, 64, z))
1914 {
1915 for (int y = y0 - 1; y < y1; y++)
1916 {
1917 Tile *tile = Tile::tiles[getTile(x, y, z)];
1918 if (tile != NULL)
1919 {
1920 tile->addAABBs(this, x, y, z, box, &boxes, source);
1921 }
1922 }
1923 }
1924 }
1925 }
1926 // 4J - also stop player falling out of the bottom of the map if blockAtEdge is true. Again, rock is an arbitrary choice here
1927 // 4J Stu - Don't stop entities falling into the void while in The End (it has no bedrock)
1928 if( blockAtEdge && ( ( y0 - 1 ) < 0 ) && dimension->id != 1 )
1929 {
1930 for (int y = y0 - 1; y < 0; y++)
1931 {
1932 for (int x = x0; x < x1; x++)
1933 for (int z = z0; z < z1; z++)
1934 {
1935 Tile::stone->addAABBs(this, x, y, z, box, &boxes, source );
1936 }
1937 }
1938 }
1939 // 4J - final bounds check - limit vertical movement so we can't move above maxMovementHeight
1940 if( blockAtEdge && ( y1 > maxMovementHeight ) )
1941 {
1942 for (int y = maxMovementHeight; y < y1; y++)
1943 {
1944 for (int x = x0; x < x1; x++)
1945 for (int z = z0; z < z1; z++)
1946 {
1947 Tile::stone->addAABBs(this, x, y, z, box, &boxes, source );
1948 }
1949 }
1950 }
1951 // 4J - now add in collision for any blocks which have actually been removed, but haven't had their render data updated to reflect this yet. This is to stop the player
1952 // being able to move the view position inside a tile which is (visually) still there, and see out of the world. This is particularly a problem when moving upwards in
1953 // creative mode as the player can get very close to the edge of tiles whilst looking upwards and can therefore very quickly move inside one.
1954 Minecraft::GetInstance()->levelRenderer->destroyedTileManager->addAABBs( this, box, &boxes);
1955
1956 // 4J - added
1957 if( noEntities ) return &boxes;
1958
1959 double r = 0.25;
1960 vector<shared_ptr<Entity> > *ee = getEntities(source, box->grow(r, r, r));
1961 vector<shared_ptr<Entity> >::iterator itEnd = ee->end();
1962 for (AUTO_VAR(it, ee->begin()); it != itEnd; it++)
1963 {
1964 AABB *collideBox = (*it)->getCollideBox();
1965 if (collideBox != NULL && collideBox->intersects(box))
1966 {
1967 boxes.push_back(collideBox);
1968 }
1969
1970 collideBox = source->getCollideAgainstBox(*it);
1971 if (collideBox != NULL && collideBox->intersects(box))
1972 {
1973 boxes.push_back(collideBox);
1974 }
1975 }
1976
1977 return &boxes;
1978}
1979
1980// 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting the minecart in a tight corridor damages the player
1981AABBList *Level::getTileCubes(AABB *box, bool blockAtEdge/* = false */)
1982{
1983 return getCubes(nullptr, box, true, blockAtEdge);
1984 //boxes.clear();
1985 //int x0 = Mth::floor(box->x0);
1986 //int x1 = Mth::floor(box->x1 + 1);
1987 //int y0 = Mth::floor(box->y0);
1988 //int y1 = Mth::floor(box->y1 + 1);
1989 //int z0 = Mth::floor(box->z0);
1990 //int z1 = Mth::floor(box->z1 + 1);
1991
1992 //for (int x = x0; x < x1; x++)
1993 //{
1994 // for (int z = z0; z < z1; z++)
1995 // {
1996 // if (hasChunkAt(x, 64, z))
1997 // {
1998 // for (int y = y0 - 1; y < y1; y++)
1999 // {
2000 // Tile *tile = Tile::tiles[getTile(x, y, z)];
2001
2002 // if (tile != NULL)
2003 // {
2004 // tile->addAABBs(this, x, y, z, box, &boxes);
2005 // }
2006 // }
2007 // }
2008 // }
2009 //}
2010
2011 //return boxes;
2012}
2013
2014//4J - change brought forward from 1.8.2
2015int Level::getOldSkyDarken(float a)
2016{
2017 float td = getTimeOfDay(a);
2018
2019 float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.5f);
2020 if (br < 0.0f) br = 0.0f;
2021 if (br > 1.0f) br = 1.0f;
2022
2023 br = 1 - br;
2024
2025 br *= 1 - (getRainLevel(a) * 5 / 16.0f);
2026 br *= 1 - (getThunderLevel(a) * 5 / 16.0f);
2027 br = 1 - br;
2028 return ((int) (br * 11));
2029}
2030
2031//4J - change brought forward from 1.8.2
2032float Level::getSkyDarken(float a)
2033{
2034 float td = getTimeOfDay(a);
2035
2036 float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.2f);
2037 if (br < 0.0f) br = 0.0f;
2038 if (br > 1.0f) br = 1.0f;
2039
2040 br = 1.0f - br;
2041
2042 br *= 1.0f - (getRainLevel(a) * 5.0f / 16.0f);
2043 br *= 1.0f - (getThunderLevel(a) * 5.0f / 16.0f);
2044 // return ((int) (br * 13));
2045
2046 return br * 0.8f + 0.2f;
2047}
2048
2049
2050
2051Vec3 *Level::getSkyColor(shared_ptr<Entity> source, float a)
2052{
2053 float td = getTimeOfDay(a);
2054
2055 float br = Mth::cos(td * PI * 2) * 2 + 0.5f;
2056 if (br < 0.0f) br = 0.0f;
2057 if (br > 1.0f) br = 1.0f;
2058
2059 int xx = Mth::floor(source->x);
2060 int zz = Mth::floor(source->z);
2061 Biome *biome = getBiome(xx, zz);
2062 float temp = biome->getTemperature();
2063 int skyColor = biome->getSkyColor(temp);
2064
2065 float r = ((skyColor >> 16) & 0xff) / 255.0f;
2066 float g = ((skyColor >> 8) & 0xff) / 255.0f;
2067 float b = ((skyColor) & 0xff) / 255.0f;
2068 r *= br;
2069 g *= br;
2070 b *= br;
2071
2072 float rainLevel = getRainLevel(a);
2073 if (rainLevel > 0)
2074 {
2075 float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.6f;
2076
2077 float ba = 1 - rainLevel * 0.75f;
2078 r = r * ba + mid * (1 - ba);
2079 g = g * ba + mid * (1 - ba);
2080 b = b * ba + mid * (1 - ba);
2081 }
2082 float thunderLevel = getThunderLevel(a);
2083 if (thunderLevel > 0)
2084 {
2085 float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.2f;
2086
2087 float ba = 1 - thunderLevel * 0.75f;
2088 r = r * ba + mid * (1 - ba);
2089 g = g * ba + mid * (1 - ba);
2090 b = b * ba + mid * (1 - ba);
2091 }
2092
2093 if (skyFlashTime > 0)
2094 {
2095 float f = (skyFlashTime - a);
2096 if (f > 1) f = 1;
2097 f = f * 0.45f;
2098 r = r * (1 - f) + 0.8f * f;
2099 g = g * (1 - f) + 0.8f * f;
2100 b = b * (1 - f) + 1 * f;
2101 }
2102
2103 return Vec3::newTemp(r, g, b);
2104}
2105
2106
2107float Level::getTimeOfDay(float a)
2108{
2109 /*
2110 * 4J-PB removed line below - notch committed 1.6.6 with the incorrect
2111 * getTimeOfDay and changed it before releasing (without
2112 * re-committing)... that should be the only difference // jeb
2113 */
2114 /* if (this != NULL) return 0.5f; */
2115
2116 // 4J Added if so we can override timeOfDay without changing the time that affects ticking of things
2117 return dimension->getTimeOfDay(levelData->getDayTime(), a);;
2118}
2119
2120int Level::getMoonPhase()
2121{
2122 return dimension->getMoonPhase(levelData->getDayTime());
2123}
2124
2125float Level::getMoonBrightness()
2126{
2127 return Dimension::MOON_BRIGHTNESS_PER_PHASE[dimension->getMoonPhase(levelData->getDayTime())];
2128}
2129
2130float Level::getSunAngle(float a)
2131{
2132 float td = getTimeOfDay(a);
2133 return td * PI * 2;
2134}
2135
2136
2137Vec3 *Level::getCloudColor(float a)
2138{
2139 float td = getTimeOfDay(a);
2140
2141 float br = Mth::cos(td * PI * 2) * 2.0f + 0.5f;
2142 if (br < 0.0f) br = 0.0f;
2143 if (br > 1.0f) br = 1.0f;
2144
2145 int baseCloudColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_In_Cloud_Base_Colour );
2146
2147 float r = ((baseCloudColour >> 16) & 0xff) / 255.0f;
2148 float g = ((baseCloudColour >> 8) & 0xff) / 255.0f;
2149 float b = ((baseCloudColour) & 0xff) / 255.0f;
2150
2151 float rainLevel = getRainLevel(a);
2152 if (rainLevel > 0)
2153 {
2154 float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.6f;
2155
2156 float ba = 1 - rainLevel * 0.95f;
2157 r = r * ba + mid * (1 - ba);
2158 g = g * ba + mid * (1 - ba);
2159 b = b * ba + mid * (1 - ba);
2160 }
2161
2162 r *= br * 0.90f + 0.10f;
2163 g *= br * 0.90f + 0.10f;
2164 b *= br * 0.85f + 0.15f;
2165
2166 float thunderLevel = getThunderLevel(a);
2167 if (thunderLevel > 0)
2168 {
2169 float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.2f;
2170
2171 float ba = 1 - thunderLevel * 0.95f;
2172 r = r * ba + mid * (1 - ba);
2173 g = g * ba + mid * (1 - ba);
2174 b = b * ba + mid * (1 - ba);
2175 }
2176
2177 return Vec3::newTemp(r, g, b);
2178}
2179
2180
2181Vec3 *Level::getFogColor(float a)
2182{
2183 float td = getTimeOfDay(a);
2184 return dimension->getFogColor(td, a);
2185}
2186
2187
2188int Level::getTopRainBlock(int x, int z)
2189{
2190 // 4J - optimisation brought forward from 1.8.2 - used to do full calculation here but result is now cached in LevelChunk
2191 return getChunkAt(x, z)->getTopRainBlock(x & 15, z & 15);
2192}
2193
2194// 4J added
2195bool Level::biomeHasRain(int x, int z)
2196{
2197 return getChunkAt(x, z)->biomeHasRain(x & 15, z & 15);
2198}
2199
2200// 4J added
2201bool Level::biomeHasSnow(int x, int z)
2202{
2203 return getChunkAt(x, z)->biomeHasSnow(x & 15, z & 15);
2204}
2205
2206int Level::getTopSolidBlock(int x, int z)
2207{
2208 LevelChunk *levelChunk = getChunkAt(x, z);
2209
2210 int y = levelChunk->getHighestSectionPosition() + 15;
2211
2212 x &= 15;
2213 z &= 15;
2214
2215 while (y > 0)
2216 {
2217 int t = levelChunk->getTile(x, y, z);
2218 if (t == 0 || !(Tile::tiles[t]->material->blocksMotion()) || Tile::tiles[t]->material == Material::leaves)
2219 {
2220 y--;
2221 }
2222 else
2223 {
2224 return y + 1;
2225 }
2226 }
2227 return -1;
2228}
2229
2230
2231int Level::getLightDepth(int x, int z)
2232{
2233 return getChunkAt(x, z)->getHeightmap(x & 15, z & 15);
2234}
2235
2236
2237float Level::getStarBrightness(float a)
2238{
2239 float td = getTimeOfDay(a);
2240
2241 float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.25f);
2242 if (br < 0.0f) br = 0.0f;
2243 if (br > 1.0f) br = 1.0f;
2244
2245 return br * br * 0.5f;
2246}
2247
2248void Level::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay)
2249{
2250}
2251
2252void Level::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay, int priorityTilt)
2253{
2254}
2255
2256void Level::forceAddTileTick(int x, int y, int z, int tileId, int tickDelay, int prioTilt)
2257{
2258}
2259
2260void Level::tickEntities()
2261{
2262 vector<shared_ptr<Entity> >::iterator itGE = globalEntities.begin();
2263 while( itGE != globalEntities.end() )
2264 {
2265 shared_ptr<Entity> e = *itGE;
2266 e->tickCount++;
2267 e->tick();
2268 if (e->removed)
2269 {
2270 itGE = globalEntities.erase( itGE );
2271 }
2272 else
2273 {
2274 itGE++;
2275 }
2276 }
2277
2278 EnterCriticalSection(&m_entitiesCS);
2279
2280 for( AUTO_VAR(it, entities.begin()); it != entities.end(); )
2281 {
2282 bool found = false;
2283 for( AUTO_VAR(it2, entitiesToRemove.begin()); it2 != entitiesToRemove.end(); it2++ )
2284 {
2285 if( (*it) == (*it2) )
2286 {
2287 found = true;
2288 break;
2289 }
2290 }
2291 if( found )
2292 {
2293 it = entities.erase(it);
2294 }
2295 else
2296 {
2297 it++;
2298 }
2299 }
2300 LeaveCriticalSection(&m_entitiesCS);
2301
2302 AUTO_VAR(itETREnd, entitiesToRemove.end());
2303 for (AUTO_VAR(it, entitiesToRemove.begin()); it != itETREnd; it++)
2304 {
2305 shared_ptr<Entity> e = *it;//entitiesToRemove.at(j);
2306 int xc = e->xChunk;
2307 int zc = e->zChunk;
2308 if (e->inChunk && hasChunk(xc, zc))
2309 {
2310 getChunk(xc, zc)->removeEntity(e);
2311 }
2312 }
2313
2314 itETREnd = entitiesToRemove.end();
2315 for (AUTO_VAR(it, entitiesToRemove.begin()); it != itETREnd; it++)
2316 {
2317 entityRemoved(*it);
2318 }
2319 //
2320 entitiesToRemove.clear();
2321
2322 //for (int i = 0; i < entities.size(); i++)
2323
2324 /* 4J Jev, using an iterator causes problems here as
2325 * the vector is modified from inside this loop.
2326 */
2327 EnterCriticalSection(&m_entitiesCS);
2328
2329 for (unsigned int i = 0; i < entities.size(); )
2330 {
2331 shared_ptr<Entity> e = entities.at(i);
2332
2333 if (e->riding != NULL)
2334 {
2335 if (e->riding->removed || e->riding->rider.lock() != e)
2336 {
2337 e->riding->rider = weak_ptr<Entity>();
2338 e->riding = nullptr;
2339 }
2340 else
2341 {
2342 i++;
2343 continue;
2344 }
2345 }
2346
2347 if (!e->removed)
2348 {
2349#ifndef _FINAL_BUILD
2350 if ( !( app.DebugSettingsOn() && app.GetMobsDontTickEnabled() && e->instanceof(eTYPE_MOB) && !e->instanceof(eTYPE_PLAYER)) )
2351#endif
2352 {
2353 tick(e);
2354 }
2355 }
2356
2357 if (e->removed)
2358 {
2359 int xc = e->xChunk;
2360 int zc = e->zChunk;
2361 if (e->inChunk && hasChunk(xc, zc))
2362 {
2363 getChunk(xc, zc)->removeEntity(e);
2364 }
2365 //entities.remove(i--);
2366 //itE = entities.erase( itE );
2367
2368 // 4J Find the entity again before deleting, as things might have moved in the entity array eg
2369 // from the explosion created by tnt
2370 AUTO_VAR(it, find(entities.begin(), entities.end(), e));
2371 if( it != entities.end() )
2372 {
2373 entities.erase(it);
2374 }
2375
2376 entityRemoved(e);
2377 }
2378 else
2379 {
2380 i++;
2381 }
2382 }
2383 LeaveCriticalSection(&m_entitiesCS);
2384
2385 EnterCriticalSection(&m_tileEntityListCS);
2386
2387 updatingTileEntities = true;
2388 for (AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end();)
2389 {
2390 shared_ptr<TileEntity> te = *it;//tilevector<shared_ptr<Entity> >.at(i);
2391 if( !te->isRemoved() && te->hasLevel() )
2392 {
2393 if (hasChunkAt(te->x, te->y, te->z))
2394 {
2395#ifdef _LARGE_WORLDS
2396 LevelChunk *lc = getChunk(te->x >> 4, te->z >> 4);
2397 if(!isClientSide || !lc->isUnloaded())
2398#endif
2399 {
2400 te->tick();
2401 }
2402 }
2403 }
2404
2405 if( te->isRemoved() )
2406 {
2407 it = tileEntityList.erase(it);
2408 if (hasChunk(te->x >> 4, te->z >> 4))
2409 {
2410 LevelChunk *lc = getChunk(te->x >> 4, te->z >> 4);
2411 if (lc != NULL) lc->removeTileEntity(te->x & 15, te->y, te->z & 15);
2412 }
2413 }
2414 else
2415 {
2416 it++;
2417 }
2418 }
2419 updatingTileEntities = false;
2420
2421 // 4J-PB - Stuart - check this is correct here
2422
2423 if (!tileEntitiesToUnload.empty())
2424 {
2425 //tileEntityList.removeAll(tileEntitiesToUnload);
2426
2427 for( AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end(); )
2428 {
2429 bool found = false;
2430 for( AUTO_VAR(it2, tileEntitiesToUnload.begin()); it2 != tileEntitiesToUnload.end(); it2++ )
2431 {
2432 if( (*it) == (*it2) )
2433 {
2434 found = true;
2435 break;
2436 }
2437 }
2438 if( found )
2439 {
2440 if(isClientSide)
2441 {
2442 __debugbreak();
2443 }
2444 it = tileEntityList.erase(it);
2445 }
2446 else
2447 {
2448 it++;
2449 }
2450 }
2451 tileEntitiesToUnload.clear();
2452 }
2453
2454 if( !pendingTileEntities.empty() )
2455 {
2456 for( AUTO_VAR(it, pendingTileEntities.begin()); it != pendingTileEntities.end(); it++ )
2457 {
2458 shared_ptr<TileEntity> e = *it;
2459 if( !e->isRemoved() )
2460 {
2461 if( find(tileEntityList.begin(),tileEntityList.end(),e) == tileEntityList.end() )
2462 {
2463 tileEntityList.push_back(e);
2464 }
2465 if (hasChunk(e->x >> 4, e->z >> 4))
2466 {
2467 LevelChunk *lc = getChunk(e->x >> 4, e->z >> 4);
2468 if (lc != NULL) lc->setTileEntity(e->x & 15, e->y, e->z & 15, e);
2469 }
2470
2471 sendTileUpdated(e->x, e->y, e->z);
2472 }
2473 }
2474 pendingTileEntities.clear();
2475 }
2476 LeaveCriticalSection(&m_tileEntityListCS);
2477}
2478
2479void Level::addAllPendingTileEntities(vector< shared_ptr<TileEntity> >& entities)
2480{
2481 EnterCriticalSection(&m_tileEntityListCS);
2482 if( updatingTileEntities )
2483 {
2484 for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ )
2485 {
2486 pendingTileEntities.push_back(*it);
2487 }
2488 }
2489 else
2490 {
2491 for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ )
2492 {
2493 tileEntityList.push_back(*it);
2494 }
2495 }
2496 LeaveCriticalSection(&m_tileEntityListCS);
2497}
2498
2499void Level::tick(shared_ptr<Entity> e)
2500{
2501 tick(e, true);
2502}
2503
2504
2505void Level::tick(shared_ptr<Entity> e, bool actual)
2506{
2507 int xc = Mth::floor(e->x);
2508 int zc = Mth::floor(e->z);
2509 int r = 32;
2510#ifdef __PSVITA__
2511 // AP - make sure the dragon ticks all the time, even when there aren't any chunks.
2512 if (actual && e->GetType() != eTYPE_ENDERDRAGON && !hasChunksAt(xc - r, 0, zc - r, xc + r, 0, zc + r))
2513#else
2514 if (actual && !hasChunksAt(xc - r, 0, zc - r, xc + r, 0, zc + r))
2515#endif
2516 {
2517 return;
2518 }
2519
2520 e->xOld = e->x;
2521 e->yOld = e->y;
2522 e->zOld = e->z;
2523 e->yRotO = e->yRot;
2524 e->xRotO = e->xRot;
2525
2526#ifdef __PSVITA__
2527 // AP - make sure the dragon ticks all the time, even when there aren't any chunks.
2528 if (actual && (e->GetType() == eTYPE_ENDERDRAGON || e->inChunk) )
2529#else
2530 if (actual && e->inChunk )
2531#endif
2532 {
2533 e->tickCount++;
2534 if (e->riding != NULL)
2535 {
2536 e->rideTick();
2537 }
2538 else
2539 {
2540 e->tick();
2541 }
2542 }
2543
2544 // SANTITY!!
2545 if (Double::isNaN(e->x) || Double::isInfinite(e->x)) e->x = e->xOld;
2546 if (Double::isNaN(e->y) || Double::isInfinite(e->y)) e->y = e->yOld;
2547 if (Double::isNaN(e->z) || Double::isInfinite(e->z)) e->z = e->zOld;
2548 if (Double::isNaN(e->xRot) || Double::isInfinite(e->xRot)) e->xRot = e->xRotO;
2549 if (Double::isNaN(e->yRot) || Double::isInfinite(e->yRot)) e->yRot = e->yRotO;
2550
2551 int xcn = Mth::floor(e->x / 16);
2552 int ycn = Mth::floor(e->y / 16);
2553 int zcn = Mth::floor(e->z / 16);
2554
2555
2556
2557 if (!e->inChunk || (e->xChunk != xcn || e->yChunk != ycn || e->zChunk != zcn))
2558 {
2559 if (e->inChunk && hasChunk(e->xChunk, e->zChunk))
2560 {
2561 getChunk(e->xChunk, e->zChunk)->removeEntity(e, e->yChunk);
2562 }
2563
2564 if (hasChunk(xcn, zcn))
2565 {
2566
2567 e->inChunk = true;
2568 MemSect(39);
2569 getChunk(xcn, zcn)->addEntity(e);
2570 MemSect(0);
2571 }
2572 else
2573 {
2574 e->inChunk = false;
2575 // e.remove();
2576 }
2577 }
2578
2579 if (actual && e->inChunk)
2580 {
2581 if (e->rider.lock() != NULL)
2582 {
2583 if (e->rider.lock()->removed || e->rider.lock()->riding != e)
2584 {
2585 e->rider.lock()->riding = nullptr;
2586 e->rider = weak_ptr<Entity>();
2587 }
2588 else
2589 {
2590 tick(e->rider.lock());
2591 }
2592 }
2593 }
2594}
2595
2596
2597bool Level::isUnobstructed(AABB *aabb)
2598{
2599 return isUnobstructed(aabb, nullptr);
2600}
2601
2602bool Level::isUnobstructed(AABB *aabb, shared_ptr<Entity> ignore)
2603{
2604 vector<shared_ptr<Entity> > *ents = getEntities(nullptr, aabb);
2605 AUTO_VAR(itEnd, ents->end());
2606 for (AUTO_VAR(it, ents->begin()); it != itEnd; it++)
2607 {
2608 shared_ptr<Entity> e = *it;
2609 if (!e->removed && e->blocksBuilding && e != ignore) return false;
2610 }
2611 return true;
2612}
2613
2614
2615bool Level::containsAnyBlocks(AABB *box)
2616{
2617 int x0 = Mth::floor(box->x0);
2618 int x1 = Mth::floor(box->x1 + 1);
2619 int y0 = Mth::floor(box->y0);
2620 int y1 = Mth::floor(box->y1 + 1);
2621 int z0 = Mth::floor(box->z0);
2622 int z1 = Mth::floor(box->z1 + 1);
2623
2624 if (box->x0 < 0) x0--;
2625 if (box->y0 < 0) y0--;
2626 if (box->z0 < 0) z0--;
2627
2628 for (int x = x0; x < x1; x++)
2629 for (int y = y0; y < y1; y++)
2630 for (int z = z0; z < z1; z++)
2631 {
2632 Tile *tile = Tile::tiles[getTile(x, y, z)];
2633 if (tile != NULL)
2634 {
2635 return true;
2636 }
2637 }
2638 return false;
2639}
2640
2641
2642bool Level::containsAnyLiquid(AABB *box)
2643{
2644 int x0 = Mth::floor(box->x0);
2645 int x1 = Mth::floor(box->x1 + 1);
2646 int y0 = Mth::floor(box->y0);
2647 int y1 = Mth::floor(box->y1 + 1);
2648 int z0 = Mth::floor(box->z0);
2649 int z1 = Mth::floor(box->z1 + 1);
2650
2651 if (box->x0 < 0) x0--;
2652 if (box->y0 < 0) y0--;
2653 if (box->z0 < 0) z0--;
2654
2655 for (int x = x0; x < x1; x++)
2656 for (int y = y0; y < y1; y++)
2657 for (int z = z0; z < z1; z++)
2658 {
2659 Tile *tile = Tile::tiles[getTile(x, y, z)];
2660 if (tile != NULL && tile->material->isLiquid())
2661 {
2662 return true;
2663 }
2664 }
2665 return false;
2666}
2667
2668// 4J - added this to be used during mob spawning, and it returns true if there's any liquid in the bounding box, or might be because
2669// we don't have a loaded chunk that we'd need to determine whether it really did. The overall aim is to not load or create any chunk
2670// we haven't already got, and be cautious about placing the mob's.
2671bool Level::containsAnyLiquid_NoLoad(AABB *box)
2672{
2673 int x0 = Mth::floor(box->x0);
2674 int x1 = Mth::floor(box->x1 + 1);
2675 int y0 = Mth::floor(box->y0);
2676 int y1 = Mth::floor(box->y1 + 1);
2677 int z0 = Mth::floor(box->z0);
2678 int z1 = Mth::floor(box->z1 + 1);
2679
2680 if (box->x0 < 0) x0--;
2681 if (box->y0 < 0) y0--;
2682 if (box->z0 < 0) z0--;
2683
2684 for (int x = x0; x < x1; x++)
2685 for (int y = y0; y < y1; y++)
2686 for (int z = z0; z < z1; z++)
2687 {
2688 if( !hasChunkAt(x,y,z) ) return true; // If we don't have it, it might be liquid...
2689 Tile *tile = Tile::tiles[getTile(x, y, z)];
2690 if (tile != NULL && tile->material->isLiquid())
2691 {
2692 return true;
2693 }
2694 }
2695 return false;
2696}
2697
2698
2699bool Level::containsFireTile(AABB *box)
2700{
2701 int x0 = Mth::floor(box->x0);
2702 int x1 = Mth::floor(box->x1 + 1);
2703 int y0 = Mth::floor(box->y0);
2704 int y1 = Mth::floor(box->y1 + 1);
2705 int z0 = Mth::floor(box->z0);
2706 int z1 = Mth::floor(box->z1 + 1);
2707
2708 if (hasChunksAt(x0, y0, z0, x1, y1, z1))
2709 {
2710 for (int x = x0; x < x1; x++)
2711 for (int y = y0; y < y1; y++)
2712 for (int z = z0; z < z1; z++)
2713 {
2714 int t = getTile(x, y, z);
2715
2716 if (t == Tile::fire_Id || t == Tile::lava_Id || t == Tile::calmLava_Id) return true;
2717 }
2718 }
2719 return false;
2720}
2721
2722
2723bool Level::checkAndHandleWater(AABB *box, Material *material, shared_ptr<Entity> e)
2724{
2725 int x0 = Mth::floor(box->x0);
2726 int x1 = Mth::floor(box->x1 + 1);
2727
2728 int y0 = Mth::floor(box->y0);
2729 int y1 = Mth::floor(box->y1 + 1);
2730
2731 int z0 = Mth::floor(box->z0);
2732 int z1 = Mth::floor(box->z1 + 1);
2733
2734 if (!hasChunksAt(x0, y0, z0, x1, y1, z1))
2735 {
2736 return false;
2737 }
2738
2739 bool ok = false;
2740 Vec3 *current = Vec3::newTemp(0, 0, 0);
2741 for (int x = x0; x < x1; x++)
2742 {
2743 for (int y = y0; y < y1; y++)
2744 {
2745 for (int z = z0; z < z1; z++)
2746 {
2747 Tile *tile = Tile::tiles[getTile(x, y, z)];
2748 if (tile != NULL && tile->material == material)
2749 {
2750 double yt0 = y + 1 - LiquidTile::getHeight(getData(x, y, z));
2751 if (y1 >= yt0)
2752 {
2753 ok = true;
2754 tile->handleEntityInside(this, x, y, z, e, current);
2755 }
2756 }
2757 }
2758 }
2759 }
2760 if (current->length() > 0 && e->isPushedByWater())
2761 {
2762 current = current->normalize();
2763 double pow = 0.014;
2764 e->xd += current->x * pow;
2765 e->yd += current->y * pow;
2766 e->zd += current->z * pow;
2767 }
2768 return ok;
2769}
2770
2771
2772bool Level::containsMaterial(AABB *box, Material *material)
2773{
2774 int x0 = Mth::floor(box->x0);
2775 int x1 = Mth::floor(box->x1 + 1);
2776 int y0 = Mth::floor(box->y0);
2777 int y1 = Mth::floor(box->y1 + 1);
2778 int z0 = Mth::floor(box->z0);
2779 int z1 = Mth::floor(box->z1 + 1);
2780
2781 for (int x = x0; x < x1; x++)
2782 {
2783 for (int y = y0; y < y1; y++)
2784 {
2785 for (int z = z0; z < z1; z++)
2786 {
2787 Tile *tile = Tile::tiles[getTile(x, y, z)];
2788 if (tile != NULL && tile->material == material)
2789 {
2790 return true;
2791 }
2792 }
2793 }
2794 }
2795 return false;
2796}
2797
2798
2799bool Level::containsLiquid(AABB *box, Material *material)
2800{
2801 int x0 = Mth::floor(box->x0);
2802 int x1 = Mth::floor(box->x1 + 1);
2803 int y0 = Mth::floor(box->y0);
2804 int y1 = Mth::floor(box->y1 + 1);
2805 int z0 = Mth::floor(box->z0);
2806 int z1 = Mth::floor(box->z1 + 1);
2807
2808 for (int x = x0; x < x1; x++)
2809 {
2810 for (int y = y0; y < y1; y++)
2811 {
2812 for (int z = z0; z < z1; z++)
2813 {
2814 Tile *tile = Tile::tiles[getTile(x, y, z)];
2815 if (tile != NULL && tile->material == material)
2816 {
2817 int data = getData(x, y, z);
2818 double yh1 = y + 1;
2819 if (data < 8)
2820 {
2821 yh1 = y + 1 - data / 8.0;
2822 }
2823 if (yh1 >= box->y0)
2824 {
2825 return true;
2826 }
2827 }
2828 }
2829 }
2830 }
2831 return false;
2832}
2833
2834
2835shared_ptr<Explosion> Level::explode(shared_ptr<Entity> source, double x, double y, double z, float r, bool destroyBlocks)
2836{
2837 return explode(source, x, y, z, r, false, destroyBlocks);
2838}
2839
2840
2841shared_ptr<Explosion> Level::explode(shared_ptr<Entity> source, double x, double y, double z, float r, bool fire, bool destroyBlocks)
2842{
2843 shared_ptr<Explosion> explosion = shared_ptr<Explosion>( new Explosion(this, source, x, y, z, r) );
2844 explosion->fire = fire;
2845 explosion->destroyBlocks = destroyBlocks;
2846 explosion->explode();
2847 explosion->finalizeExplosion(true);
2848 return explosion;
2849}
2850
2851
2852float Level::getSeenPercent(Vec3 *center, AABB *bb)
2853{
2854 double xs = 1.0 / ((bb->x1 - bb->x0) * 2 + 1);
2855 double ys = 1.0 / ((bb->y1 - bb->y0) * 2 + 1);
2856 double zs = 1.0 / ((bb->z1 - bb->z0) * 2 + 1);
2857 int hits = 0;
2858 int count = 0;
2859 for (double xx = 0; xx <= 1; xx += xs) // 4J Stu - xx, yy and zz were floats, made them doubles to remove warnings
2860 for (double yy = 0; yy <= 1; yy += ys)
2861 for (double zz = 0; zz <= 1; zz += zs)
2862 {
2863 double x = bb->x0 + (bb->x1 - bb->x0) * xx;
2864 double y = bb->y0 + (bb->y1 - bb->y0) * yy;
2865 double z = bb->z0 + (bb->z1 - bb->z0) * zz;
2866 HitResult *res = clip(Vec3::newTemp(x, y, z), center);
2867 if ( res == NULL) hits++;
2868 delete res;
2869 count++;
2870 }
2871
2872 return hits / (float) count;
2873}
2874
2875
2876bool Level::extinguishFire(shared_ptr<Player> player, int x, int y, int z, int face)
2877{
2878 if (face == 0) y--;
2879 if (face == 1) y++;
2880 if (face == 2) z--;
2881 if (face == 3) z++;
2882 if (face == 4) x--;
2883 if (face == 5) x++;
2884
2885 if (getTile(x, y, z) == Tile::fire_Id)
2886 {
2887 levelEvent(player, LevelEvent::SOUND_FIZZ, x, y, z, 0);
2888 removeTile(x, y, z);
2889 return true;
2890 }
2891 return false;
2892}
2893
2894/*
2895shared_ptr<Entity> Level::findSubclassOf(Entity::Class *entityClass)
2896{
2897return shared_ptr<Entity>();
2898}
2899*/
2900
2901
2902wstring Level::gatherStats()
2903{
2904 wchar_t buf[64];
2905 EnterCriticalSection(&m_entitiesCS);
2906 swprintf(buf,64,L"All:%d",entities.size());
2907 LeaveCriticalSection(&m_entitiesCS);
2908 return wstring(buf);
2909}
2910
2911
2912wstring Level::gatherChunkSourceStats()
2913{
2914 return chunkSource->gatherStats();
2915}
2916
2917
2918shared_ptr<TileEntity> Level::getTileEntity(int x, int y, int z)
2919{
2920 if (y < minBuildHeight || y >= maxBuildHeight)
2921 {
2922 return nullptr;
2923 }
2924 shared_ptr<TileEntity> tileEntity = nullptr;
2925
2926 if (updatingTileEntities)
2927 {
2928 EnterCriticalSection(&m_tileEntityListCS);
2929 for (int i = 0; i < pendingTileEntities.size(); i++)
2930 {
2931 shared_ptr<TileEntity> e = pendingTileEntities.at(i);
2932 if (!e->isRemoved() && e->x == x && e->y == y && e->z == z)
2933 {
2934 tileEntity = e;
2935 break;
2936 }
2937 }
2938 LeaveCriticalSection(&m_tileEntityListCS);
2939 }
2940
2941 if (tileEntity == NULL)
2942 {
2943 LevelChunk *lc = getChunk(x >> 4, z >> 4);
2944 if (lc != NULL)
2945 {
2946 tileEntity = lc->getTileEntity(x & 15, y, z & 15);
2947 }
2948 }
2949
2950 if (tileEntity == NULL)
2951 {
2952 EnterCriticalSection(&m_tileEntityListCS);
2953 for( AUTO_VAR(it, pendingTileEntities.begin()); it != pendingTileEntities.end(); it++ )
2954 {
2955 shared_ptr<TileEntity> e = *it;
2956
2957 if (!e->isRemoved() && e->x == x && e->y == y && e->z == z)
2958 {
2959 tileEntity = e;
2960 break;
2961 }
2962 }
2963 LeaveCriticalSection(&m_tileEntityListCS);
2964 }
2965 return tileEntity;
2966}
2967
2968
2969void Level::setTileEntity(int x, int y, int z, shared_ptr<TileEntity> tileEntity)
2970{
2971 if (tileEntity != NULL && !tileEntity->isRemoved())
2972 {
2973 EnterCriticalSection(&m_tileEntityListCS);
2974 if (updatingTileEntities)
2975 {
2976 tileEntity->x = x;
2977 tileEntity->y = y;
2978 tileEntity->z = z;
2979
2980 // avoid adding duplicates
2981 for( AUTO_VAR(it, pendingTileEntities.begin()); it != pendingTileEntities.end();)
2982 {
2983 shared_ptr<TileEntity> next = *it;
2984 if (next->x == x && next->y == y && next->z == z)
2985 {
2986 next->setRemoved();
2987 it = pendingTileEntities.erase(it);
2988 }
2989 else
2990 {
2991 ++it;
2992 }
2993 }
2994
2995 pendingTileEntities.push_back(tileEntity);
2996 }
2997 else
2998 {
2999 tileEntityList.push_back(tileEntity);
3000
3001 LevelChunk *lc = getChunk(x >> 4, z >> 4);
3002 if (lc != NULL) lc->setTileEntity(x & 15, y, z & 15, tileEntity);
3003 }
3004 LeaveCriticalSection(&m_tileEntityListCS);
3005 }
3006}
3007
3008void Level::removeTileEntity(int x, int y, int z)
3009{
3010 EnterCriticalSection(&m_tileEntityListCS);
3011 shared_ptr<TileEntity> te = getTileEntity(x, y, z);
3012 if (te != NULL && updatingTileEntities)
3013 {
3014 te->setRemoved();
3015 AUTO_VAR(it, find(pendingTileEntities.begin(), pendingTileEntities.end(), te ));
3016 if( it != pendingTileEntities.end() )
3017 {
3018 pendingTileEntities.erase(it);
3019 }
3020 }
3021 else
3022 {
3023 if (te != NULL)
3024 {
3025 AUTO_VAR(it, find(pendingTileEntities.begin(), pendingTileEntities.end(), te ));
3026 if( it != pendingTileEntities.end() )
3027 {
3028 pendingTileEntities.erase(it);
3029 }
3030 AUTO_VAR(it2, find(tileEntityList.begin(), tileEntityList.end(), te));
3031 if( it2 != tileEntityList.end() )
3032 {
3033 tileEntityList.erase(it2);
3034 }
3035 }
3036 LevelChunk *lc = getChunk(x >> 4, z >> 4);
3037 if (lc != NULL) lc->removeTileEntity(x & 15, y, z & 15);
3038 }
3039 LeaveCriticalSection(&m_tileEntityListCS);
3040}
3041
3042void Level::markForRemoval(shared_ptr<TileEntity> entity)
3043{
3044 tileEntitiesToUnload.push_back(entity);
3045}
3046
3047bool Level::isSolidRenderTile(int x, int y, int z)
3048{
3049 Tile *tile = Tile::tiles[getTile(x, y, z)];
3050 if (tile == NULL) return false;
3051
3052 // 4J - addition here to make rendering big blocks of leaves more efficient. Normally leaves never consider themselves as solid, so
3053 // blocks of leaves will have all sides of each block completely visible. Changing to consider as solid if this block is surrounded by
3054 // other leaves (or solid things). This is paired with another change in Tile::getTexture which makes such solid tiles actually visibly solid (these
3055 // textures exist already for non-fancy graphics). Note: this tile-specific code is here rather than making some new virtual method in the tiles,
3056 // for the sake of efficiency - I don't imagine we'll be doing much more of this sort of thing
3057
3058 if( tile->id == Tile::leaves_Id )
3059 {
3060 int axo[6] = { 1,-1, 0, 0, 0, 0};
3061 int ayo[6] = { 0, 0, 1,-1, 0, 0};
3062 int azo[6] = { 0, 0, 0, 0, 1,-1};
3063 for( int i = 0; i < 6; i++ )
3064 {
3065 int t = getTile(x + axo[i], y + ayo[i] , z + azo[i]);
3066 if( ( t != Tile::leaves_Id ) && ( ( Tile::tiles[t] == NULL ) || !Tile::tiles[t]->isSolidRender() ) )
3067 {
3068 return false;
3069 }
3070 }
3071
3072 return true;
3073 }
3074 return tile->isSolidRender(!isClientSide);
3075}
3076
3077
3078bool Level::isSolidBlockingTile(int x, int y, int z)
3079{
3080 return Tile::isSolidBlockingTile(getTile(x, y, z));
3081}
3082
3083/**
3084* This method does the same as isSolidBlockingTile, except it will not
3085* check the tile if the coordinates is in an unloaded or empty chunk. This
3086* is to help vs the problem of "popping" torches in SMP.
3087*/
3088
3089bool Level::isSolidBlockingTileInLoadedChunk(int x, int y, int z, bool valueIfNotLoaded)
3090{
3091 if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE)
3092 {
3093 return valueIfNotLoaded;
3094 }
3095 LevelChunk *chunk = chunkSource->getChunk(x >> 4, z >> 4);
3096 if (chunk == NULL || chunk->isEmpty())
3097 {
3098 return valueIfNotLoaded;
3099 }
3100
3101 Tile *tile = Tile::tiles[getTile(x, y, z)];
3102 if (tile == NULL) return false;
3103 return tile->material->isSolidBlocking() && tile->isCubeShaped();
3104}
3105
3106bool Level::isFullAABBTile(int x, int y, int z)
3107{
3108 int tile = getTile(x, y, z);
3109 if (tile == 0 || Tile::tiles[tile] == NULL)
3110 {
3111 return false;
3112 }
3113 AABB *aabb = Tile::tiles[tile]->getAABB(this, x, y, z);
3114 return aabb != NULL && aabb->getSize() >= 1;
3115}
3116
3117bool Level::isTopSolidBlocking(int x, int y, int z)
3118{
3119 // Temporary workaround until tahgs per-face solidity is finished
3120 Tile *tile = Tile::tiles[getTile(x, y, z)];
3121 return isTopSolidBlocking(tile, getData(x, y, z));
3122}
3123
3124bool Level::isTopSolidBlocking(Tile *tile, int data)
3125{
3126 if (tile == NULL) return false;
3127
3128 if (tile->material->isSolidBlocking() && tile->isCubeShaped()) return true;
3129 if (dynamic_cast<StairTile *>(tile) != NULL)
3130 {
3131 return (data & StairTile::UPSIDEDOWN_BIT) == StairTile::UPSIDEDOWN_BIT;
3132 }
3133 if (dynamic_cast<HalfSlabTile *>(tile) != NULL)
3134 {
3135 return (data & HalfSlabTile::TOP_SLOT_BIT) == HalfSlabTile::TOP_SLOT_BIT;
3136 }
3137 if (dynamic_cast<HopperTile *>(tile) != NULL) return true;
3138 if (dynamic_cast<TopSnowTile *>(tile) != NULL) return (data & TopSnowTile::HEIGHT_MASK) == TopSnowTile::MAX_HEIGHT + 1;
3139 return false;
3140}
3141
3142void Level::updateSkyBrightness()
3143{
3144 int newDark = getOldSkyDarken(1);
3145 if (newDark != skyDarken)
3146 {
3147 skyDarken = newDark;
3148 }
3149}
3150
3151void Level::setSpawnSettings(bool spawnEnemies, bool spawnFriendlies)
3152{
3153 this->spawnEnemies = spawnEnemies;
3154 this->spawnFriendlies = spawnFriendlies;
3155}
3156
3157void Level::tick()
3158{
3159 PIXBeginNamedEvent(0,"Weather tick");
3160 tickWeather();
3161 PIXEndNamedEvent();
3162}
3163
3164void Level::prepareWeather()
3165{
3166 if (levelData->isRaining())
3167 {
3168 rainLevel = 1;
3169 if (levelData->isThundering())
3170 {
3171 thunderLevel = 1;
3172 }
3173 }
3174}
3175
3176
3177void Level::tickWeather()
3178{
3179 if (dimension->hasCeiling) return;
3180
3181#ifndef _FINAL_BUILD
3182 // debug setting added to disable weather
3183 if(app.DebugSettingsOn())
3184 {
3185 if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_DisableWeather))
3186 {
3187 levelData->setThundering(false);
3188 levelData->setThunderTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2);
3189 levelData->setRaining(false);
3190 levelData->setRainTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2);
3191 }
3192 }
3193#endif
3194
3195 int thunderTime = levelData->getThunderTime();
3196 if (thunderTime <= 0)
3197 {
3198 if (levelData->isThundering())
3199 {
3200 levelData->setThunderTime(random->nextInt(20 * 60 * 10) + 20 * 60 * 3);
3201 }
3202 else
3203 {
3204 levelData->setThunderTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2);
3205 }
3206 }
3207 else
3208 {
3209 thunderTime--;
3210 levelData->setThunderTime(thunderTime);
3211 if (thunderTime <= 0)
3212 {
3213 levelData->setThundering(!levelData->isThundering());
3214 }
3215 }
3216
3217 int rainTime = levelData->getRainTime();
3218 if (rainTime <= 0)
3219 {
3220 if (levelData->isRaining())
3221 {
3222 levelData->setRainTime(random->nextInt(TICKS_PER_DAY / 2) + TICKS_PER_DAY / 2);
3223 }
3224 else
3225 {
3226 levelData->setRainTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2);
3227 }
3228 }
3229 else
3230 {
3231 rainTime--;
3232 levelData->setRainTime(rainTime);
3233 if (rainTime <= 0)
3234 {
3235 levelData->setRaining(!levelData->isRaining());
3236 }
3237 /* if( !levelData->isRaining() )
3238 {
3239 levelData->setRaining(true);
3240 }*/
3241 }
3242
3243 oRainLevel = rainLevel;
3244 if (levelData->isRaining())
3245 {
3246 rainLevel += 0.01;
3247 }
3248 else
3249 {
3250 rainLevel -= 0.01;
3251 }
3252 if (rainLevel < 0) rainLevel = 0;
3253 if (rainLevel > 1) rainLevel = 1;
3254
3255 oThunderLevel = thunderLevel;
3256 if (levelData->isThundering())
3257 {
3258 thunderLevel += 0.01;
3259 }
3260 else
3261 {
3262 thunderLevel -= 0.01;
3263 }
3264 if (thunderLevel < 0) thunderLevel = 0;
3265 if (thunderLevel > 1) thunderLevel = 1;
3266}
3267
3268void Level::toggleDownfall()
3269{
3270 // this will trick the tickWeather method to toggle rain next tick
3271 levelData->setRainTime(1);
3272}
3273
3274void Level::buildAndPrepareChunksToPoll()
3275{
3276#if 0
3277 AUTO_VAR(itEnd, players.end());
3278 for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
3279 {
3280 shared_ptr<Player> player = *it;
3281 int xx = Mth::floor(player->x / 16);
3282 int zz = Mth::floor(player->z / 16);
3283
3284 int r = CHUNK_POLL_RANGE;
3285 for (int x = -r; x <= r; x++)
3286 for (int z = -r; z <= r; z++)
3287 {
3288 chunksToPoll.insert(ChunkPos(x + xx, z + zz));
3289 }
3290 }
3291#else
3292 // 4J - rewritten to add chunks interleaved by player, and to add them from the centre outwards. We're going to be
3293 // potentially adding less creatures than the original so that our count stays consistent with number of players added, so
3294 // we want to make sure as best we can that the ones we do add are near the active players
3295 int playerCount = (int)players.size();
3296 int *xx = new int[playerCount];
3297 int *zz = new int[playerCount];
3298 for (int i = 0; i < playerCount; i++)
3299 {
3300 shared_ptr<Player> player = players[i];
3301 xx[i] = Mth::floor(player->x / 16);
3302 zz[i] = Mth::floor(player->z / 16);
3303 chunksToPoll.insert(ChunkPos(xx[i], zz[i] ));
3304 }
3305
3306 for( int r = 1; r <= 9; r++ )
3307 {
3308 for( int l = 0; l < ( r * 2 ) ; l++ )
3309 {
3310 for( int i = 0; i < playerCount; i++ )
3311 {
3312 chunksToPoll.insert(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r ) ) );
3313 chunksToPoll.insert(ChunkPos( ( xx[i] + r ) , ( zz[i] - r ) + l ) );
3314 chunksToPoll.insert(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ) ) );
3315 chunksToPoll.insert(ChunkPos( ( xx[i] - r ) , ( zz[i] + r ) - l ) );
3316 }
3317 }
3318 }
3319 delete [] xx;
3320 delete [] zz;
3321#endif
3322
3323 if (delayUntilNextMoodSound > 0) delayUntilNextMoodSound--;
3324
3325 // 4J Stu - Added 1.2.3, but not sure if we want to do it
3326 //util.Timer.push("playerCheckLight");
3327 //// randomly check areas around the players
3328 //if (!players.isEmpty()) {
3329 // int select = random.nextInt(players.size());
3330 // Player player = players.get(select);
3331 // int px = Mth.floor(player.x) + random.nextInt(11) - 5;
3332 // int py = Mth.floor(player.y) + random.nextInt(11) - 5;
3333 // int pz = Mth.floor(player.z) + random.nextInt(11) - 5;
3334 // checkLight(px, py, pz);
3335 //}
3336 //util.Timer.pop();
3337}
3338
3339void Level::tickClientSideTiles(int xo, int zo, LevelChunk *lc)
3340{
3341 //lc->tick(); // 4J - brought this lighting update forward from 1.8.2
3342
3343 if (delayUntilNextMoodSound == 0 && !isClientSide)
3344 {
3345 randValue = randValue * 3 + addend;
3346 int val = (randValue >> 2);
3347 int x = (val & 15);
3348 int z = ((val >> 8) & 15);
3349 int y = ((val >> 16) & genDepthMinusOne);
3350
3351 int id = lc->getTile(x, y, z);
3352 x += xo;
3353 z += zo;
3354 if (id == 0 && this->getDaytimeRawBrightness(x, y, z) <= random->nextInt(8) && getBrightness(LightLayer::Sky, x, y, z) <= 0)
3355 {
3356 shared_ptr<Player> player = getNearestPlayer(x + 0.5, y + 0.5, z + 0.5, 8);
3357 if (player != NULL && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 2 * 2)
3358 {
3359 // 4J-PB - Fixed issue with cave audio event having 2 sounds at 192k
3360#ifdef _XBOX
3361 this->playSound(x + 0.5, y + 0.5, z + 0.5,eSoundType_AMBIENT_CAVE_CAVE2, 0.7f, 0.8f + random->nextFloat() * 0.2f);
3362#else
3363 this->playSound(x + 0.5, y + 0.5, z + 0.5,eSoundType_AMBIENT_CAVE_CAVE, 0.7f, 0.8f + random->nextFloat() * 0.2f);
3364#endif
3365 delayUntilNextMoodSound = random->nextInt(SharedConstants::TICKS_PER_SECOND * 60 * 10) + SharedConstants::TICKS_PER_SECOND * 60 * 5;
3366 }
3367 }
3368 }
3369
3370 // 4J Stu - Added 1.2.3, but do we need it?
3371 //lc->checkNextLight();
3372}
3373
3374void Level::tickTiles()
3375{
3376 buildAndPrepareChunksToPoll();
3377}
3378
3379bool Level::shouldFreezeIgnoreNeighbors(int x, int y, int z)
3380{
3381 return shouldFreeze(x, y, z, false);
3382}
3383
3384bool Level::shouldFreeze(int x, int y, int z)
3385{
3386 return shouldFreeze(x, y, z, true);
3387}
3388
3389bool Level::shouldFreeze(int x, int y, int z, bool checkNeighbors)
3390{
3391 Biome *biome = getBiome(x, z);
3392 float temp = biome->getTemperature();
3393 if (temp > 0.15f) return false;
3394
3395 if (y >= 0 && y < maxBuildHeight && getBrightness(LightLayer::Block, x, y, z) < 10)
3396 {
3397 int current = getTile(x, y, z);
3398 if ((current == Tile::calmWater_Id || current == Tile::water_Id) && getData(x, y, z) == 0)
3399 {
3400 if (!checkNeighbors) return true;
3401
3402 bool surroundedByWater = true;
3403 if (surroundedByWater && getMaterial(x - 1, y, z) != Material::water) surroundedByWater = false;
3404 if (surroundedByWater && getMaterial(x + 1, y, z) != Material::water) surroundedByWater = false;
3405 if (surroundedByWater && getMaterial(x, y, z - 1) != Material::water) surroundedByWater = false;
3406 if (surroundedByWater && getMaterial(x, y, z + 1) != Material::water) surroundedByWater = false;
3407 if (!surroundedByWater) return true;
3408 }
3409 }
3410 return false;
3411}
3412
3413bool Level::shouldSnow(int x, int y, int z)
3414{
3415 Biome *biome = getBiome(x, z);
3416 float temp = biome->getTemperature();
3417 if (temp > 0.15f) return false;
3418
3419
3420 if (y >= 0 && y < maxBuildHeight && getBrightness(LightLayer::Block, x, y, z) < 10)
3421 {
3422 int below = getTile(x, y - 1, z);
3423 int current = getTile(x, y, z);
3424 if (current == 0)
3425 {
3426 if (Tile::topSnow->mayPlace(this, x, y, z) && (below != 0 && below != Tile::ice_Id && Tile::tiles[below]->material->blocksMotion()))
3427 {
3428 return true;
3429 }
3430 }
3431 }
3432
3433 return false;
3434}
3435
3436void Level::checkLight(int x, int y, int z, bool force, bool rootOnlyEmissive) // 4J added force, rootOnlyEmissive parameters
3437{
3438 if (!dimension->hasCeiling) checkLight(LightLayer::Sky, x, y, z, force, false);
3439 checkLight(LightLayer::Block, x, y, z, force, rootOnlyEmissive);
3440}
3441
3442int Level::getExpectedLight(lightCache_t *cache, int x, int y, int z, LightLayer::variety layer, bool propagatedOnly)
3443{
3444 if (layer == LightLayer::Sky && canSeeSky(x, y, z)) return MAX_BRIGHTNESS;
3445 int id = getTile(x, y, z);
3446 int result = layer == LightLayer::Sky ? 0 : Tile::lightEmission[id];
3447 int block = Tile::lightBlock[id];
3448 if (block >= MAX_BRIGHTNESS && Tile::lightEmission[id] > 0) block = 1;
3449 if (block < 1) block = 1;
3450 if (block >= MAX_BRIGHTNESS)
3451 {
3452 return propagatedOnly ? 0 : getEmissionCached(cache, 0, x, y, z);
3453 }
3454
3455 if (result >= MAX_BRIGHTNESS - 1) return result;
3456
3457 for (int face = 0; face < 6; face++)
3458 {
3459 int xx = x + Facing::STEP_X[face];
3460 int yy = y + Facing::STEP_Y[face];
3461 int zz = z + Facing::STEP_Z[face];
3462 int brightness = getBrightnessCached(cache, layer, xx, yy, zz) - block;
3463
3464 if (brightness > result) result = brightness;
3465 if (result >= MAX_BRIGHTNESS - 1) return result;
3466 }
3467
3468 return result;
3469}
3470
3471// 4J - Made changes here so that lighting goes through a cache, if enabled for this thread
3472void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool force, bool rootOnlyEmissive)
3473{
3474 lightCache_t *cache = (lightCache_t *)TlsGetValue(tlsIdxLightCache);
3475 __uint64 cacheUse = 0;
3476
3477 if( force )
3478 {
3479 // 4J - special mode added so we can do lava lighting updates without having all neighbouring chunks loaded in
3480 if (!hasChunksAt(xc, yc, zc, 0)) return;
3481 }
3482 else
3483 {
3484 // 4J - this is normal java behaviour
3485 if (!hasChunksAt(xc, yc, zc, 17)) return;
3486 }
3487
3488#if 0
3489 /////////////////////////////////////////////////////////////////////////////////////////////
3490 // Get the frequency of the timer
3491 LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime1, qwDeltaTime2;
3492 float fElapsedTime1 = 0.0f;
3493 float fElapsedTime2 = 0.0f;
3494 QueryPerformanceFrequency( &qwTicksPerSec );
3495 float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
3496
3497 QueryPerformanceCounter( &qwTime );
3498 /////////////////////////////////////////////////////////////////////////////////////////////
3499#endif
3500
3501 EnterCriticalSection(&m_checkLightCS);
3502
3503 initCachePartial(cache, xc, yc, zc);
3504
3505 // If we're in cached mode, then use memory allocated after the cached data itself for the toCheck array, in an attempt to make both that & the other cached data sit on the CPU L2 cache better.
3506
3507 int *toCheck;
3508 if( cache == NULL )
3509 {
3510 toCheck = toCheckLevel;
3511 }
3512 else
3513 {
3514 toCheck = (int *)(cache + (16*16*16));
3515 }
3516
3517 int checkedPosition = 0;
3518 int toCheckCount = 0;
3519 //int darktcc = 0;
3520
3521
3522 // 4J - added
3523 int minXZ = - (dimension->getXZSize() * 16 ) / 2;
3524 int maxXZ = (dimension->getXZSize() * 16 ) / 2 - 1;
3525 if( ( xc > maxXZ ) || ( xc < minXZ ) || ( zc > maxXZ ) || ( zc < minXZ ) )
3526 {
3527 LeaveCriticalSection(&m_checkLightCS);
3528 return;
3529 }
3530
3531 // Lock 128K of cache (containing all the lighting cache + first 112K of toCheck array) on L2 to try and stop any cached data getting knocked out of L2 by other non-cached reads (or vice-versa)
3532 // if( cache ) XLockL2(XLOCKL2_INDEX_TITLE, cache, 128 * 1024, XLOCKL2_LOCK_SIZE_1_WAY, 0 );
3533
3534 {
3535 int centerCurrent = getBrightnessCached(cache, layer, xc, yc, zc);
3536 int centerExpected = getExpectedLight(cache, xc, yc, zc, layer, false);
3537
3538 if( centerExpected != centerCurrent && cache )
3539 {
3540 initCacheComplete(cache, xc, yc, zc);
3541 }
3542
3543 if (centerExpected > centerCurrent)
3544 {
3545 toCheck[toCheckCount++] = 32 | (32 << 6) | (32 << 12);
3546 }
3547 else if (centerExpected < centerCurrent)
3548 {
3549 // 4J - added tcn. This is the code that is run when checkLight has been called for a light source that has got darker / turned off.
3550 // In the original version, after zeroing tiles brightnesses that are deemed to come from this light source, all the zeroed tiles are then passed to the next
3551 // stage of the function to potentially have their brightnesses put back up again. We shouldn't need to consider All these tiles as starting points for this process, now just
3552 // considering the edge tiles (defined as a tile where we have a neighbour that is brightner than can be explained by the original light source we are turning off)
3553 int tcn = 0;
3554 if (layer == LightLayer::Block || true)
3555 {
3556 toCheck[toCheckCount++] = 32 | (32 << 6) | (32 << 12) | (centerCurrent << 18);
3557 while (checkedPosition < toCheckCount)
3558 {
3559 int p = toCheck[checkedPosition++];
3560 int x = ((p) & 63) - 32 + xc;
3561 int y = ((p >> 6) & 63) - 32 + yc;
3562 int z = ((p >> 12) & 63) - 32 + zc;
3563 int expected = ((p >> 18) & 15);
3564 int current = getBrightnessCached(cache, layer, x, y, z);
3565 if (current == expected)
3566 {
3567 setBrightnessCached(cache, &cacheUse, layer, x, y, z, 0);
3568 // cexp--; // 4J - removed, change from 1.2.3
3569 if (expected > 0)
3570 {
3571 int xd = Mth::abs(x - xc);
3572 int yd = Mth::abs(y - yc);
3573 int zd = Mth::abs(z - zc);
3574 if (xd + yd + zd < 17)
3575 {
3576 bool edge = false;
3577 for (int face = 0; face < 6; face++)
3578 {
3579 int xx = x + Facing::STEP_X[face];
3580 int yy = y + Facing::STEP_Y[face];
3581 int zz = z + Facing::STEP_Z[face];
3582
3583 // 4J - added - don't let this lighting creep out of the normal fixed world and into the infinite water chunks beyond
3584 if( ( xx > maxXZ ) || ( xx < minXZ ) || ( zz > maxXZ ) || ( zz < minXZ ) ) continue;
3585 if( ( yy < 0 ) || ( yy >= maxBuildHeight ) ) continue;
3586
3587 // 4J - some changes here brought forward from 1.2.3
3588 int block = max(1, getBlockingCached(cache, layer, NULL, xx, yy, zz) );
3589 current = getBrightnessCached(cache, layer, xx, yy, zz);
3590 if ((current == expected - block) && (toCheckCount < (32 * 32 * 32))) // 4J - 32 * 32 * 32 was toCheck.length
3591 {
3592 toCheck[toCheckCount++] = (xx - xc + 32) | ((yy - yc + 32) << 6) | ((zz - zc + 32) << 12) | ((expected - block) << 18);
3593 }
3594 else
3595 {
3596 // 4J - added - keep track of which tiles form the edge of the region we are zeroing
3597 if( current > ( expected - block ) )
3598 {
3599 edge = true;
3600 }
3601 }
3602 }
3603 // 4J - added - keep track of which tiles form the edge of the region we are zeroing - can store over the original elements in the array because tcn must be <= tcp
3604 if( edge == true )
3605 {
3606 toCheck[tcn++] = p;
3607 }
3608 }
3609 }
3610
3611 }
3612 }
3613 }
3614 checkedPosition = 0;
3615 // darktcc = tcc; ///////////////////////////////////////////////////
3616 toCheckCount = tcn; // 4J added - we've moved all the edge tiles to the start of the array, so only need to process these now. The original processes all tcc tiles again in the next section
3617 }
3618 }
3619
3620 while (checkedPosition < toCheckCount)
3621 {
3622 int p = toCheck[checkedPosition++];
3623 int x = ((p) & 63) - 32 + xc;
3624 int y = ((p >> 6) & 63) - 32 + yc;
3625 int z = ((p >> 12) & 63) - 32 + zc;
3626
3627 // If force is set, then this is being used to in a special mode to try and light lava tiles as chunks are being loaded in. In this case, we
3628 // don't want a lighting update to drag in any neighbouring chunks that aren't loaded yet.
3629 if( force )
3630 {
3631 if( !hasChunkAt(x,y,z) )
3632 {
3633 continue;
3634 }
3635 }
3636 int current = getBrightnessCached(cache, layer, x, y, z);
3637
3638 // If rootOnlyEmissive flag is set, then only consider the starting tile to be possibly emissive.
3639 bool propagatedOnly = false;
3640 if (layer == LightLayer::Block)
3641 {
3642 if( rootOnlyEmissive )
3643 {
3644 propagatedOnly = ( x != xc ) || ( y != yc ) || ( z != zc );
3645 }
3646 }
3647 int expected = getExpectedLight(cache, x, y, z, layer, propagatedOnly);
3648
3649 if (expected != current)
3650 {
3651 setBrightnessCached(cache, &cacheUse, layer, x, y, z, expected);
3652
3653 if (expected > current)
3654 {
3655 int xd = abs(x - xc);
3656 int yd = abs(y - yc);
3657 int zd = abs(z - zc);
3658 bool withinBounds = toCheckCount < (32 * 32 * 32) - 6; // 4J - 32 * 32 * 32 was toCheck.length
3659 if (xd + yd + zd < 17 && withinBounds)
3660 {
3661 // 4J - added extra checks here to stop lighting updates moving out of the actual fixed world and into the infinite water chunks
3662 if( ( x - 1 ) >= minXZ ) { if (getBrightnessCached(cache, layer, x - 1, y, z) < expected) toCheck[toCheckCount++] = (((x - 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
3663 if( ( x + 1 ) <= maxXZ ) { if (getBrightnessCached(cache, layer, x + 1, y, z) < expected) toCheck[toCheckCount++] = (((x + 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
3664 if( ( y - 1 ) >= 0 ) { if (getBrightnessCached(cache, layer, x, y - 1, z) < expected) toCheck[toCheckCount++] = (((x - xc) + 32)) + (((y - 1 - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
3665 if( ( y + 1 ) < maxBuildHeight ) { if (getBrightnessCached(cache, layer, x, y + 1, z) < expected) toCheck[toCheckCount++] = (((x - xc) + 32)) + (((y + 1 - yc) + 32) << 6) + (((z - zc) + 32) << 12); }
3666 if( ( z - 1 ) >= minXZ ) { if (getBrightnessCached(cache, layer, x, y, z - 1) < expected) toCheck[toCheckCount++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - 1 - zc) + 32) << 12); }
3667 if( ( z + 1 ) <= maxXZ ) { if (getBrightnessCached(cache, layer, x, y, z + 1) < expected) toCheck[toCheckCount++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z + 1 - zc) + 32) << 12); }
3668 }
3669 }
3670 }
3671 }
3672 // if( cache ) XUnlockL2(XLOCKL2_INDEX_TITLE);
3673#if 0
3674 QueryPerformanceCounter( &qwNewTime );
3675 qwDeltaTime1.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
3676 qwTime = qwNewTime;
3677#endif
3678
3679 flushCache(cache, cacheUse, layer);
3680#if 0
3681 /////////////////////////////////////////////////////////////////
3682 if( cache )
3683 {
3684 QueryPerformanceCounter( &qwNewTime );
3685 qwDeltaTime2.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
3686 fElapsedTime1 = fSecsPerTick * ((FLOAT)(qwDeltaTime1.QuadPart));
3687 fElapsedTime2 = fSecsPerTick * ((FLOAT)(qwDeltaTime2.QuadPart));
3688 if( ( darktcc > 0 ) | ( tcc > 0 ) )
3689 {
3690 printf("%d %d %d %f + %f = %f\n", darktcc, tcc, darktcc + tcc, fElapsedTime1 * 1000.0f, fElapsedTime2 * 1000.0f, ( fElapsedTime1 + fElapsedTime2 ) * 1000.0f);
3691 }
3692 }
3693 /////////////////////////////////////////////////////////////////
3694#endif
3695 LeaveCriticalSection(&m_checkLightCS);
3696
3697}
3698
3699
3700bool Level::tickPendingTicks(bool force)
3701{
3702 return false;
3703}
3704
3705vector<TickNextTickData> *Level::fetchTicksInChunk(LevelChunk *chunk, bool remove)
3706{
3707 return NULL;
3708}
3709
3710
3711vector<shared_ptr<Entity> > *Level::getEntities(shared_ptr<Entity> except, AABB *bb)
3712{
3713 return getEntities(except, bb, NULL);
3714}
3715
3716vector<shared_ptr<Entity> > *Level::getEntities(shared_ptr<Entity> except, AABB *bb, const EntitySelector *selector)
3717{
3718 MemSect(40);
3719 es.clear();
3720 int xc0 = Mth::floor((bb->x0 - 2) / 16);
3721 int xc1 = Mth::floor((bb->x1 + 2) / 16);
3722 int zc0 = Mth::floor((bb->z0 - 2) / 16);
3723 int zc1 = Mth::floor((bb->z1 + 2) / 16);
3724
3725#ifdef __PSVITA__
3726#ifdef _ENTITIES_RW_SECTION
3727 // AP - RW critical sections are expensive so enter it here so we only have to call it once instead of X times
3728 EnterCriticalRWSection(&LevelChunk::m_csEntities, false);
3729#else
3730 EnterCriticalSection(&LevelChunk::m_csEntities);
3731#endif
3732#endif
3733
3734 for (int xc = xc0; xc <= xc1; xc++)
3735 for (int zc = zc0; zc <= zc1; zc++)
3736 {
3737 if (hasChunk(xc, zc))
3738 {
3739 getChunk(xc, zc)->getEntities(except, bb, es, selector);
3740 }
3741 }
3742 MemSect(0);
3743
3744#ifdef __PSVITA__
3745#ifdef _ENTITIES_RW_SECTION
3746 LeaveCriticalRWSection(&LevelChunk::m_csEntities, false);
3747#else
3748 LeaveCriticalSection(&LevelChunk::m_csEntities);
3749#endif
3750#endif
3751
3752 return &es;
3753}
3754
3755vector<shared_ptr<Entity> > *Level::getEntitiesOfClass(const type_info& baseClass, AABB *bb)
3756{
3757 return getEntitiesOfClass(baseClass, bb, NULL);
3758}
3759
3760vector<shared_ptr<Entity> > *Level::getEntitiesOfClass(const type_info& baseClass, AABB *bb, const EntitySelector *selector)
3761{
3762 int xc0 = Mth::floor((bb->x0 - 2) / 16);
3763 int xc1 = Mth::floor((bb->x1 + 2) / 16);
3764 int zc0 = Mth::floor((bb->z0 - 2) / 16);
3765 int zc1 = Mth::floor((bb->z1 + 2) / 16);
3766 vector<shared_ptr<Entity> > *es = new vector<shared_ptr<Entity> >();
3767
3768#ifdef __PSVITA__
3769#ifdef _ENTITIES_RW_SECTION
3770 // AP - RW critical sections are expensive so enter it here so we only have to call it once instead of X times
3771 EnterCriticalRWSection(&LevelChunk::m_csEntities, false);
3772#else
3773 EnterCriticalSection(&LevelChunk::m_csEntities);
3774#endif
3775#endif
3776
3777 for (int xc = xc0; xc <= xc1; xc++)
3778 {
3779 for (int zc = zc0; zc <= zc1; zc++)
3780 {
3781 if (hasChunk(xc, zc))
3782 {
3783 getChunk(xc, zc)->getEntitiesOfClass(baseClass, bb, *es, selector);
3784 }
3785 }
3786 }
3787
3788#ifdef __PSVITA__
3789#ifdef _ENTITIES_RW_SECTION
3790 LeaveCriticalRWSection(&LevelChunk::m_csEntities, false);
3791#else
3792 LeaveCriticalSection(&LevelChunk::m_csEntities);
3793#endif
3794#endif
3795
3796 return es;
3797}
3798
3799shared_ptr<Entity> Level::getClosestEntityOfClass(const type_info& baseClass, AABB *bb, shared_ptr<Entity> source)
3800{
3801 vector<shared_ptr<Entity> > *entities = getEntitiesOfClass(baseClass, bb);
3802 shared_ptr<Entity> closest = nullptr;
3803 double closestDistSqr = Double::MAX_VALUE;
3804 //for (Entity entity : entities)
3805 for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it)
3806 {
3807 shared_ptr<Entity> entity = *it;
3808 if (entity == source) continue;
3809 double distSqr = source->distanceToSqr(entity);
3810 if (distSqr > closestDistSqr) continue;
3811 closest = entity;
3812 closestDistSqr = distSqr;
3813 }
3814 delete entities;
3815 return closest;
3816}
3817
3818vector<shared_ptr<Entity> > Level::getAllEntities()
3819{
3820 EnterCriticalSection(&m_entitiesCS);
3821 vector<shared_ptr<Entity> > retVec = entities;
3822 LeaveCriticalSection(&m_entitiesCS);
3823 return retVec;
3824}
3825
3826
3827void Level::tileEntityChanged(int x, int y, int z, shared_ptr<TileEntity> te)
3828{
3829 if (this->hasChunkAt(x, y, z))
3830 {
3831 getChunkAt(x, z)->markUnsaved();
3832 }
3833}
3834
3835#if 0
3836unsigned int Level::countInstanceOf(BaseObject::Class *clas)
3837{
3838 unsigned int count = 0;
3839 EnterCriticalSection(&m_entitiesCS);
3840 AUTO_VAR(itEnd, entities.end());
3841 for (AUTO_VAR(it, entities.begin()); it != itEnd; it++)
3842 {
3843 shared_ptr<Entity> e = *it;//entities.at(i);
3844 if (clas->isAssignableFrom(e->getClass())) count++;
3845 }
3846 LeaveCriticalSection(&m_entitiesCS);
3847
3848 return count;
3849}
3850#endif
3851
3852// 4J - added - more limited (but faster) version of above, used to count water animals, animals, monsters for the mob spawner
3853// singleType flag should be true if we are just trying to match eINSTANCEOF exactly, and false if it is a eINSTANCEOF from a group (eTYPE_WATERANIMAL, eTYPE_ANIMAL, eTYPE_MONSTER)
3854unsigned int Level::countInstanceOf(eINSTANCEOF clas, bool singleType, unsigned int *protectedCount/* = NULL*/, unsigned int *couldWanderCount/* = NULL*/)
3855{
3856 unsigned int count = 0;
3857 if( protectedCount ) *protectedCount = 0;
3858 if( couldWanderCount ) *couldWanderCount = 0;
3859 EnterCriticalSection(&m_entitiesCS);
3860 AUTO_VAR(itEnd, entities.end());
3861 for (AUTO_VAR(it, entities.begin()); it != itEnd; it++)
3862 {
3863 shared_ptr<Entity> e = *it;//entities.at(i);
3864 if( singleType )
3865 {
3866 if (e->GetType() == clas)
3867 {
3868 if ( protectedCount && e->isDespawnProtected() )
3869 {
3870 (*protectedCount)++;
3871 }
3872
3873 if ( couldWanderCount && e->couldWander() )
3874 {
3875 (*couldWanderCount)++;
3876 }
3877
3878 count++;
3879 }
3880 }
3881 else
3882 {
3883 if (e->instanceof(clas)) count++;
3884 }
3885 }
3886 LeaveCriticalSection(&m_entitiesCS);
3887
3888 return count;
3889}
3890
3891unsigned int Level::countInstanceOfInRange(eINSTANCEOF clas, bool singleType, int range, int x, int y, int z)
3892{
3893 unsigned int count = 0;
3894 EnterCriticalSection(&m_entitiesCS);
3895 AUTO_VAR(itEnd, entities.end());
3896 for (AUTO_VAR(it, entities.begin()); it != itEnd; it++)
3897 {
3898 shared_ptr<Entity> e = *it;//entities.at(i);
3899
3900 float sd = e->distanceTo(x,y,z);
3901 if (sd * sd > range * range)
3902 {
3903 continue;
3904 }
3905
3906 if( singleType )
3907 {
3908 if (e->GetType() == clas)
3909 {
3910 count++;
3911 }
3912 }
3913 else
3914 {
3915 if (e->instanceof(clas)) count++;
3916 }
3917 }
3918 LeaveCriticalSection(&m_entitiesCS);
3919
3920 return count;
3921}
3922
3923void Level::addEntities(vector<shared_ptr<Entity> > *list)
3924{
3925 //entities.addAll(list);
3926 EnterCriticalSection(&m_entitiesCS);
3927 entities.insert(entities.end(), list->begin(), list->end());
3928 AUTO_VAR(itEnd, list->end());
3929 bool deleteDragons = false;
3930 for (AUTO_VAR(it, list->begin()); it != itEnd; it++)
3931 {
3932 entityAdded(*it);
3933
3934 // 4J Stu - Special change to remove duplicate enderdragons that a previous bug might have produced
3935 if( (*it)->GetType() == eTYPE_ENDERDRAGON)
3936 {
3937 deleteDragons = true;
3938 }
3939 }
3940
3941 if(deleteDragons)
3942 {
3943 deleteDragons = false;
3944 for(AUTO_VAR(it, entities.begin()); it != entities.end(); ++it)
3945 {
3946 // 4J Stu - Special change to remove duplicate enderdragons that a previous bug might have produced
3947 if( (*it)->GetType() == eTYPE_ENDERDRAGON)
3948 {
3949 if(deleteDragons)
3950 {
3951 (*it)->remove();
3952 }
3953 else
3954 {
3955 deleteDragons = true;
3956 }
3957 }
3958 }
3959 }
3960 LeaveCriticalSection(&m_entitiesCS);
3961}
3962
3963
3964void Level::removeEntities(vector<shared_ptr<Entity> > *list)
3965{
3966 //entitiesToRemove.addAll(list);
3967 entitiesToRemove.insert(entitiesToRemove.end(), list->begin(), list->end());
3968}
3969
3970bool Level::mayPlace(int tileId, int x, int y, int z, bool ignoreEntities, int face, shared_ptr<Entity> ignoreEntity, shared_ptr<ItemInstance> item)
3971{
3972 int targetType = getTile(x, y, z);
3973 Tile *targetTile = Tile::tiles[targetType];
3974
3975 Tile *tile = Tile::tiles[tileId];
3976
3977 AABB *aabb = tile->getAABB(this, x, y, z);
3978 if (ignoreEntities) aabb = NULL;
3979 if (aabb != NULL && !isUnobstructed(aabb, ignoreEntity)) return false;
3980 if (targetTile != NULL &&
3981 (targetTile == Tile::water || targetTile == Tile::calmWater || targetTile == Tile::lava ||
3982 targetTile == Tile::calmLava || targetTile == Tile::fire || targetTile->material->isReplaceable()))
3983 {
3984 targetTile = NULL;
3985 }
3986 if (targetTile != NULL && targetTile->material == Material::decoration && tile == Tile::anvil) return true;
3987 if (tileId > 0 && targetTile == NULL)
3988 {
3989 if (tile->mayPlace(this, x, y, z, face, item))
3990 {
3991 return true;
3992 }
3993 }
3994 return false;
3995}
3996
3997
3998int Level::getSeaLevel()
3999{
4000 return seaLevel;
4001}
4002
4003
4004Path *Level::findPath(shared_ptr<Entity> from, shared_ptr<Entity> to, float maxDist, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat)
4005{
4006 int x = Mth::floor(from->x);
4007 int y = Mth::floor(from->y + 1);
4008 int z = Mth::floor(from->z);
4009
4010 int r = (int) (maxDist + 16);
4011 int x1 = x - r;
4012 int y1 = y - r;
4013 int z1 = z - r;
4014 int x2 = x + r;
4015 int y2 = y + r;
4016 int z2 = z + r;
4017 Region region = Region(this, x1, y1, z1, x2, y2, z2, 0);
4018 Path *path = (PathFinder(®ion, canPassDoors, canOpenDoors, avoidWater, canFloat)).findPath(from.get(), to.get(), maxDist);
4019 return path;
4020}
4021
4022
4023Path *Level::findPath(shared_ptr<Entity> from, int xBest, int yBest, int zBest, float maxDist, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat)
4024{
4025 int x = Mth::floor(from->x);
4026 int y = Mth::floor(from->y);
4027 int z = Mth::floor(from->z);
4028
4029 int r = (int) (maxDist + 8);
4030 int x1 = x - r;
4031 int y1 = y - r;
4032 int z1 = z - r;
4033 int x2 = x + r;
4034 int y2 = y + r;
4035 int z2 = z + r;
4036 Region region = Region(this, x1, y1, z1, x2, y2, z2, 0);
4037 Path *path = (PathFinder(®ion, canPassDoors, canOpenDoors, avoidWater, canFloat)).findPath(from.get(), xBest, yBest, zBest, maxDist);
4038 return path;
4039}
4040
4041
4042int Level::getDirectSignal(int x, int y, int z, int dir)
4043{
4044 int t = getTile(x, y, z);
4045 if (t == 0) return Redstone::SIGNAL_NONE;
4046 return Tile::tiles[t]->getDirectSignal(this, x, y, z, dir);
4047}
4048
4049int Level::getDirectSignalTo(int x, int y, int z)
4050{
4051 int result = Redstone::SIGNAL_NONE;
4052 result = max(result, getDirectSignal(x, y - 1, z, 0));
4053 if (result >= Redstone::SIGNAL_MAX) return result;
4054 result = max(result, getDirectSignal(x, y + 1, z, 1));
4055 if (result >= Redstone::SIGNAL_MAX) return result;
4056 result = max(result, getDirectSignal(x, y, z - 1, 2));
4057 if (result >= Redstone::SIGNAL_MAX) return result;
4058 result = max(result, getDirectSignal(x, y, z + 1, 3));
4059 if (result >= Redstone::SIGNAL_MAX) return result;
4060 result = max(result, getDirectSignal(x - 1, y, z, 4));
4061 if (result >= Redstone::SIGNAL_MAX) return result;
4062 result = max(result, getDirectSignal(x + 1, y, z, 5));
4063 if (result >= Redstone::SIGNAL_MAX) return result;
4064 return result;
4065}
4066
4067bool Level::hasSignal(int x, int y, int z, int dir)
4068{
4069 return getSignal(x, y, z, dir) > Redstone::SIGNAL_NONE;
4070}
4071
4072int Level::getSignal(int x, int y, int z, int dir)
4073{
4074 if (isSolidBlockingTile(x, y, z))
4075 {
4076 return getDirectSignalTo(x, y, z);
4077 }
4078 int t = getTile(x, y, z);
4079 if (t == 0) return Redstone::SIGNAL_NONE;
4080 return Tile::tiles[t]->getSignal(this, x, y, z, dir);
4081}
4082
4083bool Level::hasNeighborSignal(int x, int y, int z)
4084{
4085 if (getSignal(x, y - 1, z, 0) > 0) return true;
4086 if (getSignal(x, y + 1, z, 1) > 0) return true;
4087 if (getSignal(x, y, z - 1, 2) > 0) return true;
4088 if (getSignal(x, y, z + 1, 3) > 0) return true;
4089 if (getSignal(x - 1, y, z, 4) > 0) return true;
4090 if (getSignal(x + 1, y, z, 5) > 0) return true;
4091 return false;
4092}
4093
4094int Level::getBestNeighborSignal(int x, int y, int z)
4095{
4096 int best = Redstone::SIGNAL_NONE;
4097
4098 for (int i = 0; i < 6; i++)
4099 {
4100 int signal = getSignal(x + Facing::STEP_X[i], y + Facing::STEP_Y[i], z + Facing::STEP_Z[i], i);
4101
4102 if (signal >= Redstone::SIGNAL_MAX) return Redstone::SIGNAL_MAX;
4103 if (signal > best) best = signal;
4104 }
4105
4106 return best;
4107}
4108
4109// 4J Stu - Added maxYDist param
4110shared_ptr<Player> Level::getNearestPlayer(shared_ptr<Entity> source, double maxDist, double maxYDist /*= -1*/)
4111{
4112 return getNearestPlayer(source->x, source->y, source->z, maxDist, maxYDist);
4113}
4114
4115// 4J Stu - Added maxYDist param
4116shared_ptr<Player> Level::getNearestPlayer(double x, double y, double z, double maxDist, double maxYDist /*= -1*/)
4117{
4118 MemSect(21);
4119 double best = -1;
4120 shared_ptr<Player> result = nullptr;
4121 AUTO_VAR(itEnd, players.end());
4122 for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
4123 {
4124 shared_ptr<Player> p = *it;//players.at(i);
4125 double dist = p->distanceToSqr(x, y, z);
4126
4127 // Allow specifying shorter distances in the vertical
4128 if(maxYDist > 0 && abs(p->y - y) > maxYDist) continue;
4129
4130 // 4J Stu - Added check that this player is still alive
4131 if ((maxDist < 0 || dist < maxDist * maxDist) && (best == -1 || dist < best) && p->isAlive() )
4132 {
4133 best = dist;
4134 result = p;
4135 }
4136 }
4137 MemSect(0);
4138 return result;
4139}
4140
4141shared_ptr<Player> Level::getNearestPlayer(double x, double z, double maxDist)
4142{
4143 double best = -1;
4144 shared_ptr<Player> result = nullptr;
4145 AUTO_VAR(itEnd, players.end());
4146 for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
4147 {
4148 shared_ptr<Player> p = *it;
4149 double dist = p->distanceToSqr(x, p->y, z);
4150 if ((maxDist < 0 || dist < maxDist * maxDist) && (best == -1 || dist < best))
4151 {
4152 best = dist;
4153 result = p;
4154 }
4155 }
4156 return result;
4157}
4158
4159shared_ptr<Player> Level::getNearestAttackablePlayer(shared_ptr<Entity> source, double maxDist)
4160{
4161 return getNearestAttackablePlayer(source->x, source->y, source->z, maxDist);
4162}
4163
4164shared_ptr<Player> Level::getNearestAttackablePlayer(double x, double y, double z, double maxDist)
4165{
4166 double best = -1;
4167
4168 shared_ptr<Player> result = nullptr;
4169 AUTO_VAR(itEnd, players.end());
4170 for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
4171 {
4172 shared_ptr<Player> p = *it;
4173
4174 // 4J Stu - Added privilege check
4175 if (p->abilities.invulnerable || !p->isAlive() || p->hasInvisiblePrivilege() )
4176 {
4177 continue;
4178 }
4179
4180 double dist = p->distanceToSqr(x, y, z);
4181 double visibleDist = maxDist;
4182
4183 // decrease the max attackable distance if the target player
4184 // is sneaking or invisible
4185 if (p->isSneaking())
4186 {
4187 visibleDist *= .8f;
4188 }
4189 if (p->isInvisible())
4190 {
4191 float coverPercentage = p->getArmorCoverPercentage();
4192 if (coverPercentage < .1f)
4193 {
4194 coverPercentage = .1f;
4195 }
4196 visibleDist *= (.7f * coverPercentage);
4197 }
4198
4199 if ((visibleDist < 0 || dist < visibleDist * visibleDist) && (best == -1 || dist < best))
4200 {
4201 best = dist;
4202 result = p;
4203 }
4204 }
4205 return result;
4206}
4207
4208shared_ptr<Player> Level::getPlayerByName(const wstring& name)
4209{
4210 AUTO_VAR(itEnd, players.end());
4211 for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
4212 {
4213 if (name.compare( (*it)->getName()) == 0)
4214 {
4215 return *it; //players.at(i);
4216 }
4217 }
4218 return shared_ptr<Player>();
4219}
4220
4221shared_ptr<Player> Level::getPlayerByUUID(const wstring& name)
4222{
4223 AUTO_VAR(itEnd, players.end());
4224 for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
4225 {
4226 if (name.compare( (*it)->getUUID() ) == 0)
4227 {
4228 return *it; //players.at(i);
4229 }
4230 }
4231 return shared_ptr<Player>();
4232}
4233
4234// 4J Stu - Removed in 1.2.3 ?
4235byteArray Level::getBlocksAndData(int x, int y, int z, int xs, int ys, int zs, bool includeLighting/* = true*/)
4236{
4237 byteArray result( xs * ys * zs * 5 / 2 );
4238 int xc0 = x >> 4;
4239 int zc0 = z >> 4;
4240 int xc1 = (x + xs - 1) >> 4;
4241 int zc1 = (z + zs - 1) >> 4;
4242
4243 int p = 0;
4244
4245 int y0 = y;
4246 int y1 = y + ys;
4247 if (y0 < 0) y0 = 0;
4248 if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight;
4249 for (int xc = xc0; xc <= xc1; xc++)
4250 {
4251 int x0 = x - xc * 16;
4252 int x1 = x + xs - xc * 16;
4253 if (x0 < 0) x0 = 0;
4254 if (x1 > 16) x1 = 16;
4255 for (int zc = zc0; zc <= zc1; zc++)
4256 {
4257 int z0 = z - zc * 16;
4258 int z1 = z + zs - zc * 16;
4259 if (z0 < 0) z0 = 0;
4260 if (z1 > 16) z1 = 16;
4261 p = getChunk(xc, zc)->getBlocksAndData(&result, x0, y0, z0, x1, y1, z1, p, includeLighting);
4262 }
4263 }
4264
4265 return result;
4266}
4267
4268// 4J Stu - Removed in 1.2.3 ?
4269void Level::setBlocksAndData(int x, int y, int z, int xs, int ys, int zs, byteArray data, bool includeLighting/* = true*/)
4270{
4271 int xc0 = x >> 4;
4272 int zc0 = z >> 4;
4273 int xc1 = (x + xs - 1) >> 4;
4274 int zc1 = (z + zs - 1) >> 4;
4275
4276 int p = 0;
4277
4278 int y0 = y;
4279 int y1 = y + ys;
4280 if (y0 < 0) y0 = 0;
4281 if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight;
4282 for (int xc = xc0; xc <= xc1; xc++)
4283 {
4284 int x0 = x - xc * 16;
4285 int x1 = x + xs - xc * 16;
4286 if (x0 < 0) x0 = 0;
4287 if (x1 > 16) x1 = 16;
4288 for (int zc = zc0; zc <= zc1; zc++)
4289 {
4290 int z0 = z - zc * 16;
4291 int z1 = z + zs - zc * 16;
4292 if (z0 < 0) z0 = 0;
4293 if (z1 > 16) z1 = 16;
4294 LevelChunk *lc = getChunk(xc, zc);
4295 // 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us
4296 // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water.
4297 // This is quite expensive so only actually do it if we are hosting, online, and the update will actually
4298 // change something
4299 bool forceUnshare = false;
4300 if( g_NetworkManager.IsHost() && isClientSide )
4301 {
4302 forceUnshare = lc->testSetBlocksAndData(data, x0, y0, z0, x1, y1, z1, p);
4303 }
4304 if( forceUnshare )
4305 {
4306 int size = (x1 - x0 ) * ( y1 - y0 ) * ( z1 - z0 );
4307 PIXBeginNamedEvent(0,"Chunk data unsharing %d\n", size);
4308 lc->stopSharingTilesAndData();
4309 PIXEndNamedEvent();
4310 }
4311 if(p < data.length) p = lc->setBlocksAndData(data, x0, y0, z0, x1, y1, z1, p, includeLighting);
4312 setTilesDirty(xc * 16 + x0, y0, zc * 16 + z0, xc * 16 + x1, y1, zc * 16 + z1);
4313
4314 PIXBeginNamedEvent(0,"Chunk data sharing\n");
4315 if( g_NetworkManager.IsHost() && isClientSide )
4316 {
4317 lc->startSharingTilesAndData();
4318 }
4319 PIXEndNamedEvent();
4320 }
4321 }
4322}
4323
4324
4325void Level::disconnect(bool sendDisconnect /*= true*/)
4326{
4327}
4328
4329
4330void Level::checkSession()
4331{
4332 levelStorage->checkSession();
4333}
4334
4335
4336void Level::setGameTime(__int64 time)
4337{
4338 // 4J : WESTY : Added to track game time played by players for other awards.
4339 if (time != 0) // Ignore setting time to 0, done at level start and during tutorial.
4340 {
4341 // Determine step in time and ensure it is reasonable ( we only have an int to store the player stat).
4342 __int64 timeDiff = time - levelData->getGameTime();
4343
4344 if (timeDiff < 0)
4345 {
4346 timeDiff = 0;
4347 }
4348 else if (timeDiff > 100)
4349 {
4350 // Time differences of more than ~5 seconds are generally not real time passing so ignore (moving dimensions does this)
4351 app.DebugPrintf("Level::setTime: Massive time difference, ignoring for time passed stat (%lli)\n", timeDiff);
4352 timeDiff = 0;
4353 }
4354
4355 // Apply stat to each player.
4356 if ( timeDiff > 0 && levelData->getGameTime() != -1 )
4357 {
4358 AUTO_VAR(itEnd, players.end());
4359 for (vector<shared_ptr<Player> >::iterator it = players.begin(); it != itEnd; it++)
4360 {
4361 (*it)->awardStat( GenericStats::timePlayed(), GenericStats::param_time(timeDiff) );
4362 }
4363 }
4364 }
4365
4366 levelData->setGameTime(time);
4367}
4368
4369__int64 Level::getSeed()
4370{
4371 return levelData->getSeed();
4372}
4373
4374__int64 Level::getGameTime()
4375{
4376 return levelData->getGameTime();
4377}
4378
4379__int64 Level::getDayTime()
4380{
4381 return levelData->getDayTime();
4382}
4383
4384void Level::setDayTime(__int64 newTime)
4385{
4386 levelData->setDayTime(newTime);
4387}
4388
4389Pos *Level::getSharedSpawnPos()
4390{
4391 return new Pos(levelData->getXSpawn(), levelData->getYSpawn(), levelData->getZSpawn());
4392}
4393
4394void Level::setSpawnPos(int x, int y, int z)
4395{
4396 levelData->setSpawn(x, y, z);
4397}
4398
4399void Level::setSpawnPos(Pos *spawnPos)
4400{
4401 setSpawnPos(spawnPos->x, spawnPos->y, spawnPos->z);
4402}
4403
4404void Level::ensureAdded(shared_ptr<Entity> entity)
4405{
4406 int xc = Mth::floor(entity->x / 16);
4407 int zc = Mth::floor(entity->z / 16);
4408 int r = 2;
4409 for (int x = xc - r; x <= xc + r; x++)
4410 {
4411 for (int z = zc - r; z <= zc + r; z++)
4412 {
4413 getChunk(x, z);
4414 }
4415 }
4416
4417 //if (!entities.contains(entity))
4418 EnterCriticalSection(&m_entitiesCS);
4419 if( find(entities.begin(), entities.end(), entity) == entities.end() )
4420 {
4421 entities.push_back(entity);
4422 }
4423 LeaveCriticalSection(&m_entitiesCS);
4424}
4425
4426
4427bool Level::mayInteract(shared_ptr<Player> player, int xt, int yt, int zt, int content)
4428{
4429 return true;
4430}
4431
4432
4433void Level::broadcastEntityEvent(shared_ptr<Entity> e, byte event)
4434{
4435}
4436
4437ChunkSource *Level::getChunkSource()
4438{
4439 return chunkSource;
4440}
4441
4442
4443void Level::tileEvent(int x, int y, int z, int tile, int b0, int b1)
4444{
4445 if (tile > 0) Tile::tiles[tile]->triggerEvent(this, x, y, z, b0, b1);
4446}
4447
4448
4449LevelStorage *Level::getLevelStorage()
4450{
4451 return levelStorage.get();
4452}
4453
4454
4455LevelData *Level::getLevelData()
4456{
4457 return levelData;
4458}
4459
4460GameRules *Level::getGameRules()
4461{
4462 return levelData->getGameRules();
4463}
4464
4465void Level::updateSleepingPlayerList()
4466{
4467}
4468
4469float Level::getThunderLevel(float a)
4470{
4471 return (oThunderLevel + (thunderLevel - oThunderLevel) * a) * getRainLevel(a);
4472}
4473
4474
4475float Level::getRainLevel(float a)
4476{
4477 return oRainLevel + (rainLevel - oRainLevel) * a;
4478}
4479
4480
4481void Level::setRainLevel(float rainLevel)
4482{
4483 oRainLevel = rainLevel;
4484 this->rainLevel = rainLevel;
4485}
4486
4487
4488bool Level::isThundering()
4489{
4490 return getThunderLevel(1) > 0.9;
4491}
4492
4493
4494bool Level::isRaining()
4495{
4496 return getRainLevel(1) > 0.2;
4497}
4498
4499
4500bool Level::isRainingAt(int x, int y, int z)
4501{
4502 if (!isRaining()) return false;
4503 if (!canSeeSky(x, y, z)) return false;
4504 if (getTopRainBlock(x, z) > y) return false;
4505
4506 // 4J - changed to use new method of getting biomedata that caches results of rain & snow
4507 if (biomeHasSnow(x, z)) return false;
4508 return biomeHasRain(x, z);
4509}
4510
4511bool Level::isHumidAt(int x, int y, int z)
4512{
4513 Biome *biome = getBiome(x, z);
4514 return biome->isHumid();
4515}
4516
4517
4518void Level::setSavedData(const wstring& id, shared_ptr<SavedData> data)
4519{
4520 savedDataStorage->set(id, data);
4521}
4522
4523
4524shared_ptr<SavedData> Level::getSavedData(const type_info& clazz, const wstring& id)
4525{
4526 return savedDataStorage->get(clazz, id);
4527}
4528
4529
4530int Level::getFreeAuxValueFor(const wstring& id)
4531{
4532 return savedDataStorage->getFreeAuxValueFor(id);
4533}
4534
4535// 4J Added
4536int Level::getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale)
4537{
4538 return savedDataStorage->getAuxValueForMap(xuid, dimension, centreXC, centreZC, scale);
4539}
4540
4541void Level::globalLevelEvent(int type, int sourceX, int sourceY, int sourceZ, int data)
4542{
4543 AUTO_VAR(itEnd, listeners.end());
4544 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
4545 {
4546 (*it)->globalLevelEvent(type, sourceX, sourceY, sourceZ, data);
4547 }
4548}
4549
4550void Level::levelEvent(int type, int x, int y, int z, int data)
4551{
4552 levelEvent(nullptr, type, x, y, z, data);
4553}
4554
4555
4556void Level::levelEvent(shared_ptr<Player> source, int type, int x, int y, int z, int data)
4557{
4558 AUTO_VAR(itEnd, listeners.end());
4559 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
4560 {
4561 (*it)->levelEvent(source, type, x, y, z, data);
4562 }
4563}
4564
4565int Level::getMaxBuildHeight()
4566{
4567 return maxBuildHeight;
4568}
4569
4570int Level::getHeight()
4571{
4572 return dimension->hasCeiling ? genDepth : maxBuildHeight;
4573}
4574
4575Tickable *Level::makeSoundUpdater(shared_ptr<Minecart> minecart)
4576{
4577 return NULL;
4578}
4579
4580Random *Level::getRandomFor(int x, int z, int blend)
4581{
4582 __int64 seed = (x * 341873128712l + z * 132897987541l) + getLevelData()->getSeed() + blend;
4583 random->setSeed(seed);
4584 return random;
4585}
4586
4587TilePos *Level::findNearestMapFeature(const wstring& featureName, int x, int y, int z)
4588{
4589 return getChunkSource()->findNearestMapFeature(this, featureName, x, y, z);
4590}
4591
4592bool Level::isAllEmpty()
4593{
4594 return false;
4595}
4596
4597double Level::getHorizonHeight()
4598{
4599 if (levelData->getGenerator() == LevelType::lvl_flat)
4600 {
4601 return 0.0;
4602 }
4603 return 63.0;
4604}
4605
4606void Level::destroyTileProgress(int id, int x, int y, int z, int progress)
4607{
4608 AUTO_VAR(itEnd, listeners.end());
4609 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
4610 {
4611 (*it)->destroyTileProgress(id, x, y, z, progress);
4612 }
4613}
4614
4615void Level::createFireworks(double x, double y, double z, double xd, double yd, double zd, CompoundTag *infoTag)
4616{
4617
4618}
4619
4620Scoreboard *Level::getScoreboard()
4621{
4622 return scoreboard;
4623}
4624
4625void Level::updateNeighbourForOutputSignal(int x, int y, int z, int source)
4626{
4627 for (int dir = 0; dir < 4; dir++)
4628 {
4629 int xx = x + Direction::STEP_X[dir];
4630 int zz = z + Direction::STEP_Z[dir];
4631 int id = getTile(xx, y, zz);
4632 if (id == 0) continue;
4633 Tile *tile = Tile::tiles[id];
4634
4635 if (Tile::comparator_off->isSameDiode(id))
4636 {
4637 tile->neighborChanged(this, xx, y, zz, source);
4638 }
4639 else if (Tile::isSolidBlockingTile(id))
4640 {
4641 xx += Direction::STEP_X[dir];
4642 zz += Direction::STEP_Z[dir];
4643 id = getTile(xx, y, zz);
4644 tile = Tile::tiles[id];
4645
4646 if (Tile::comparator_off->isSameDiode(id))
4647 {
4648 tile->neighborChanged(this, xx, y, zz, source);
4649 }
4650 }
4651 }
4652}
4653
4654float Level::getDifficulty(double x, double y, double z)
4655{
4656 return getDifficulty(Mth::floor(x), Mth::floor(y), Mth::floor(z));
4657}
4658
4659/**
4660* Returns a difficulty scaled from 0 (easiest) to 1 (normal), may overflow
4661* to 1.5 (hardest) if allowed by player.
4662*/
4663float Level::getDifficulty(int x, int y, int z)
4664{
4665 float result = 0;
4666 bool isHard = difficulty == Difficulty::HARD;
4667
4668 if (hasChunkAt(x, y, z))
4669 {
4670 float moonBrightness = getMoonBrightness();
4671
4672 result += Mth::clamp(getChunkAt(x, z)->inhabitedTime / (TICKS_PER_DAY * 150.0f), 0.0f, 1.0f) * (isHard ? 1.0f : 0.75f);
4673 result += moonBrightness * 0.25f;
4674 }
4675
4676 if (difficulty < Difficulty::NORMAL)
4677 {
4678 result *= difficulty / 2.0f;
4679 }
4680
4681 return Mth::clamp(result, 0.0f, isHard ? 1.5f : 1.0f);;
4682}
4683
4684bool Level::useNewSeaLevel()
4685{
4686 return levelData->useNewSeaLevel();
4687}
4688
4689bool Level::getHasBeenInCreative()
4690{
4691 return levelData->getHasBeenInCreative();
4692}
4693
4694bool Level::isGenerateMapFeatures()
4695{
4696 return levelData->isGenerateMapFeatures();
4697}
4698
4699int Level::getSaveVersion()
4700{
4701 return getLevelStorage()->getSaveFile()->getSaveVersion();
4702}
4703
4704int Level::getOriginalSaveVersion()
4705{
4706 return getLevelStorage()->getSaveFile()->getOriginalSaveVersion();
4707}
4708
4709// 4J - determine if a chunk has been done the post-post-processing stage. This happens when *its* neighbours have each been post-processed, and does some final lighting that can
4710// only really be done when the post-processing has placed all possible tiles into this chunk.
4711bool Level::isChunkPostPostProcessed(int x, int z)
4712{
4713 if( !hasChunk(x, z) ) return false; // This will occur for non-loaded chunks, not for edge chunks
4714
4715 LevelChunk *lc = getChunk(x, z);
4716 if( lc->isEmpty() ) return true; // Since we've already eliminated non-loaded chunks, this should only occur for edge chunks. Consider those as fully processed
4717
4718 return (( lc->terrainPopulated & LevelChunk::sTerrainPostPostProcessed ) == LevelChunk::sTerrainPostPostProcessed);
4719}
4720
4721// 4J added - returns true if a chunk is fully, fully finalised - in that it can be sent to another machine. This is the case when all 8 neighbours of this chunk
4722// have not only been post-processed, but also had the post-post-processing done that they themselves can only do once Their 8 neighbours have been post-processed.
4723bool Level::isChunkFinalised(int x, int z)
4724{
4725 for( int xo = -1; xo <= 1; xo++ )
4726 for( int zo = -1; zo <= 1; zo++ )
4727 {
4728 if( !isChunkPostPostProcessed(x + xo, z + zo) ) return false;
4729 }
4730
4731 return true;
4732}
4733
4734int Level::getUnsavedChunkCount()
4735{
4736 return m_unsavedChunkCount;
4737}
4738
4739void Level::incrementUnsavedChunkCount()
4740{
4741 ++m_unsavedChunkCount;
4742}
4743
4744void Level::decrementUnsavedChunkCount()
4745{
4746 --m_unsavedChunkCount;
4747}
4748
4749bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
4750{
4751 int count = 0;
4752 int max = 0;
4753 if(spawnType == eSpawnType_Egg || spawnType == eSpawnType_Portal)
4754 {
4755 switch(type)
4756 {
4757 case eTYPE_VILLAGER:
4758 count = countInstanceOf( eTYPE_VILLAGER, true);
4759 max = MobCategory::MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG;
4760 break;
4761 case eTYPE_CHICKEN:
4762 count = countInstanceOf( eTYPE_CHICKEN, true);
4763 max = MobCategory::MAX_XBOX_CHICKENS_WITH_SPAWN_EGG;
4764 break;
4765 case eTYPE_WOLF:
4766 count = countInstanceOf( eTYPE_WOLF, true);
4767 max = MobCategory::MAX_XBOX_WOLVES_WITH_SPAWN_EGG;
4768 break;
4769 case eTYPE_MUSHROOMCOW:
4770 count = countInstanceOf( eTYPE_MUSHROOMCOW, true);
4771 max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG;
4772 break;
4773 case eTYPE_SQUID:
4774 count = countInstanceOf( eTYPE_SQUID, true);
4775 max = MobCategory::MAX_XBOX_SQUIDS_WITH_SPAWN_EGG;
4776 break;
4777 case eTYPE_SNOWMAN:
4778 count = countInstanceOf( eTYPE_SNOWMAN, true);
4779 max = MobCategory::MAX_XBOX_SNOWMEN;
4780 break;
4781 case eTYPE_VILLAGERGOLEM:
4782 count = countInstanceOf( eTYPE_VILLAGERGOLEM, true);
4783 max = MobCategory::MAX_XBOX_IRONGOLEM;
4784 break;
4785 case eTYPE_WITHERBOSS:
4786 count = countInstanceOf(eTYPE_WITHERBOSS, true) + countInstanceOf(eTYPE_ENDERDRAGON, true);
4787 max = MobCategory::MAX_CONSOLE_BOSS;
4788 break;
4789 default:
4790 if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
4791 {
4792 count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
4793 max = MobCategory::MAX_XBOX_ANIMALS_WITH_SPAWN_EGG;
4794 }
4795 // 4J: Use eTYPE_ENEMY instead of monster (slimes and ghasts aren't monsters)
4796 else if(Entity::instanceof(type, eTYPE_ENEMY))
4797 {
4798 count = countInstanceOf(eTYPE_ENEMY, false);
4799 max = MobCategory::MAX_XBOX_MONSTERS_WITH_SPAWN_EGG;
4800 }
4801 else if( (type & eTYPE_AMBIENT) == eTYPE_AMBIENT)
4802 {
4803 count = countInstanceOf( eTYPE_AMBIENT, false);
4804 max = MobCategory::MAX_AMBIENT_WITH_SPAWN_EGG;
4805 }
4806 // 4J: Added minecart and boats
4807 else if (Entity::instanceof(type, eTYPE_MINECART))
4808 {
4809 count = countInstanceOf(eTYPE_MINECART, false);
4810 max = Level::MAX_CONSOLE_MINECARTS;
4811 }
4812 else if (Entity::instanceof(type, eTYPE_BOAT))
4813 {
4814 count = countInstanceOf(eTYPE_BOAT, true);
4815 max = Level::MAX_XBOX_BOATS;
4816 }
4817 };
4818 }
4819 else if(spawnType == eSpawnType_Breed)
4820 {
4821 switch(type)
4822 {
4823 case eTYPE_VILLAGER:
4824 count = countInstanceOf( eTYPE_VILLAGER, true);
4825 max = MobCategory::MAX_VILLAGERS_WITH_BREEDING;
4826 break;
4827 case eTYPE_CHICKEN:
4828 count = countInstanceOf( eTYPE_CHICKEN, true);
4829 max = MobCategory::MAX_XBOX_CHICKENS_WITH_BREEDING;
4830 break;
4831 case eTYPE_WOLF:
4832 count = countInstanceOf( eTYPE_WOLF, true);
4833 max = MobCategory::MAX_XBOX_WOLVES_WITH_BREEDING;
4834 break;
4835 case eTYPE_MUSHROOMCOW:
4836 count = countInstanceOf( eTYPE_MUSHROOMCOW, true);
4837 max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING;
4838 break;
4839 default:
4840 if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
4841 {
4842 count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
4843 max = MobCategory::MAX_XBOX_ANIMALS_WITH_BREEDING;
4844 }
4845 else if( (type & eTYPE_MONSTER) == eTYPE_MONSTER)
4846 {
4847
4848 }
4849 break;
4850 }
4851 }
4852 // 4J: Interpret 0 as no limit
4853 return max == 0 || count < max;
4854}