4jcraft/Minecraft.World/Level/Level.cpp

4357 lines
147 KiB
C++

#include "../Platform/stdafx.h"
#include "../Platform/System.h"
#include "../Util/BasicTypeContainers.h"
#include "../IO/Files/File.h"
#include "../Util/ProgressListener.h"
#include "../Headers/net.minecraft.h"
#include "../Headers/net.minecraft.world.h"
#include "../Headers/net.minecraft.world.entity.ai.village.h"
#include "../Headers/net.minecraft.world.entity.h"
#include "../Headers/net.minecraft.world.entity.global.h"
#include "../Headers/net.minecraft.world.entity.player.h"
#include "../Headers/net.minecraft.world.level.biome.h"
#include "../Headers/net.minecraft.world.level.chunk.h"
#include "../Headers/net.minecraft.world.level.dimension.h"
#include "../Headers/net.minecraft.world.level.tile.h"
#include "../Headers/net.minecraft.world.level.tile.entity.h"
#include "../Headers/net.minecraft.world.level.h"
#include "../Headers/net.minecraft.world.level.levelgen.h"
#include "../Headers/net.minecraft.world.level.storage.h"
#include "../Headers/net.minecraft.world.level.pathfinder.h"
#include "../Headers/net.minecraft.world.phys.h"
#include "ChunkPos.h"
#include "Explosion.h"
#include "Events/LevelListener.h"
#include "../Util/LightLayer.h"
#include "../Blocks/MobSpawner.h"
#include "Storage/Region.h"
#include "TickNextTickData.h"
#include "Level.h"
#include "../Util/ThreadName.h"
#include "../Util/WeighedRandom.h"
#include "../IO/Files/ConsoleSaveFile.h"
#include <xuiapp.h>
#include "../../Minecraft.Client/Minecraft.h"
#include "../../Minecraft.Client/Rendering/LevelRenderer.h"
#include "../Util/SoundTypes.h"
#include "Storage/SparseLightStorage.h"
#include "../../Minecraft.Client/Textures/Textures.h"
#include "../../Minecraft.Client/Textures/Packs/TexturePackRepository.h"
#include "../../Minecraft.Client/Textures/Packs/DLCTexturePack.h"
#include "../../Minecraft.Client/Platform/Common/DLC/DLCPack.h"
#include "../../Minecraft.Client/Platform/PS3/PS3Extras/ShutdownManager.h"
#include <cstdint>
namespace {
#if defined(_WIN32)
inline void* LevelTlsGetValue(Level::TlsKey key) { return TlsGetValue(key); }
inline void LevelTlsSetValue(Level::TlsKey key, void* value) {
TlsSetValue(key, value);
}
#else
pthread_key_t CreateLevelTlsKey() {
pthread_key_t key;
pthread_key_create(&key, NULL);
return key;
}
inline void* LevelTlsGetValue(pthread_key_t key) {
return pthread_getspecific(key);
}
inline void LevelTlsSetValue(pthread_key_t key, void* value) {
pthread_setspecific(key, value);
}
#endif
} // namespace
#if defined(_WIN32)
Level::TlsKey Level::tlsIdx = TlsAlloc();
Level::TlsKey Level::tlsIdxLightCache = TlsAlloc();
#else
Level::TlsKey Level::tlsIdx = CreateLevelTlsKey();
Level::TlsKey Level::tlsIdxLightCache = CreateLevelTlsKey();
#endif
// 4J : WESTY : Added for time played stats.
#include "../Headers/net.minecraft.stats.h"
// 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 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
// calling enableLightingCache, otherwise standard non-cached accesses are
// performed. General method for using caching if enabled on a thread is: (1)
// Call initCache, this invalidates any previous data in the cache (2) Use
// setBrightnessCached, getBrightnessCached, getEmissionCached,
// getBlockingCached methods to get and set data (3) Call flushCache, which
// writes through any dirty values in cache
#ifdef _LARGE_WORLDS
// Packing for cache entries in large worlds is as follows ( 64 bits per entry)
// Add the extra x and z data into the top 32 bits, to keep all the masks and
// code for everything else the same
// xxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzWEBLllllbbbbeeeexxxxxxyyyyzzzzzz
//
// xxxxxx - middle 6 bits of x position
// yyyy - top 4 bits of y position
// zzzzzz - middle 6 bits of z position
// eeee - light emission
// bbbb - light blocking
// llll - light level
// L - light value valid
// B - blocking value valid
// E - emission value valid
// W - lighting value requires write
// xxxxxxxxxxxxxxxx - top 16 bits of x position
// zzzzzzzzzzzzzzzz - top 16 bits of z position
#else
// Packing for cache entries is as follows ( 32 bits per entry)
// WEBLllllbbbbeeeexxxxxxyyyyzzzzzz
//
// xxxxxx - top 6 bits of x position
// yyyy - top 4 bits of y position
// zzzzzz - top 6 bits of z position
// eeee - light emission
// bbbb - light blocking
// llll - light level
// L - light value valid
// B - blocking value valid
// E - emission value valid
// W - lighting value requires write
#endif
void Level::enableLightingCache() {
// 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.
void* cache = (unsigned char*)XPhysicalAlloc(
256 * 1024, MAXULONG_PTR, 128 * 1024, PAGE_READWRITE | MEM_LARGE_PAGES);
LevelTlsSetValue(tlsIdxLightCache, cache);
}
void Level::destroyLightingCache() {
lightCache_t* cache =
static_cast<lightCache_t*>(LevelTlsGetValue(tlsIdxLightCache));
XPhysicalFree(cache);
}
void Level::initCache(lightCache_t* cache) {
cachewritten = false;
if (cache == NULL) return;
XMemSet128(cache, 0, 16 * 16 * 16 * sizeof(lightCache_t));
}
// Set a brightness value, going through the cache if enabled for this thread
void inline Level::setBrightnessCached(lightCache_t* cache, __uint64* cacheUse,
LightLayer::variety layer, int x, int y,
int z, int brightness) {
if (cache == NULL) {
setBrightness(layer, x, y, z, brightness, true);
return;
}
if (y & 0xffffff00) return; // Eliminate -ve ys and values > 255
int idx = ((x & 15) << 8) | ((y & 15) << 4) | (z & 15);
lightCache_t posbits =
((x & 0x3f0) << 6) | ((y & 0x0f0) << 2) | ((z & 0x3f0) >> 4);
#ifdef _LARGE_WORLDS
// Add in the higher bits for x and z
posbits |= ((((__uint64)x) & 0x3FFFC00L) << 38) |
((((__uint64)z) & 0x3FFFC00L) << 22);
#endif
lightCache_t cacheValue = cache[idx];
// If this cache entry doesn't refer to the same thing...
if ((cacheValue & POSITION_MASK) != posbits) {
/// and it has been written to...
if (cacheValue & LIGHTING_WRITEBACK) {
// Then we need to flush
int val = (cacheValue >> LIGHTING_SHIFT) & 15;
int xx = ((cacheValue >> 6) & 0x3f0) | (x & 15);
#ifdef _LARGE_WORLDS
xx |= ((cacheValue >> 38) & 0x3FFFC00);
xx = (xx << 6) >> 6; // sign extend
#else
xx = (xx << 22) >> 22; // sign extend
#endif
int yy = ((cacheValue >> 2) & 0x0f0) | (y & 15);
int zz = ((cacheValue << 4) & 0x3f0) | (z & 15);
#ifdef _LARGE_WORLDS
zz |= ((cacheValue >> 22) & 0x3FFFC00);
zz = (zz << 6) >> 6; // sign extend
#else
zz = (zz << 22) >> 22; // sign extend
#endif
setBrightness(layer, xx, yy, zz, val, true);
}
cacheValue = posbits;
}
// Just written to it, so value is valid & requires writing back
cacheValue &= ~(15 << LIGHTING_SHIFT);
cacheValue |= brightness << LIGHTING_SHIFT;
cacheValue |= (LIGHTING_WRITEBACK | LIGHTING_VALID);
// 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
(*cacheUse) |= ((1LL << (x & 15)) | (0x10000LL << (y & 15)) |
(0x100000000LL << (z & 15)));
cache[idx] = cacheValue;
}
// Get a brightness value, going through the cache if enabled for this thread
inline int Level::getBrightnessCached(lightCache_t* cache,
LightLayer::variety layer, int x, int y,
int z) {
if (cache == NULL) return getBrightness(layer, x, y, z);
if (y & 0xffffff00)
return getBrightness(
layer, x, y,
z); // Fall back on original method for out-of-bounds y
int idx = ((x & 15) << 8) | ((y & 15) << 4) | (z & 15);
lightCache_t posbits =
((x & 0x3f0) << 6) | ((y & 0x0f0) << 2) | ((z & 0x3f0) >> 4);
#ifdef _LARGE_WORLDS
// Add in the higher bits for x and z
posbits |= ((((__uint64)x) & 0x3FFFC00L) << 38) |
((((__uint64)z) & 0x3FFFC00L) << 22);
#endif
lightCache_t cacheValue = cache[idx];
if ((cacheValue & POSITION_MASK) != posbits) {
// Position differs - need to evict this cache entry
if (cacheValue & LIGHTING_WRITEBACK) {
// Then we need to flush
int val = (cacheValue >> LIGHTING_SHIFT) & 15;
int xx = ((cacheValue >> 6) & 0x3f0) | (x & 15);
#ifdef _LARGE_WORLDS
xx |= ((cacheValue >> 38) & 0x3FFFC00);
xx = (xx << 6) >> 6; // sign extend
#else
xx = (xx << 22) >> 22; // sign extend
#endif
int yy = ((cacheValue >> 2) & 0x0f0) | (y & 15);
int zz = ((cacheValue << 4) & 0x3f0) | (z & 15);
#ifdef _LARGE_WORLDS
zz |= ((cacheValue >> 22) & 0x3FFFC00);
zz = (zz << 6) >> 6; // sign extend
#else
zz = (zz << 22) >> 22; // sign extend
#endif
setBrightness(layer, xx, yy, zz, val, true);
}
cacheValue = posbits | LIGHTING_VALID;
int val = getBrightness(layer, x, y, z);
cacheValue |= val << LIGHTING_SHIFT;
} else {
// The position matches - will incurr a read miss if the lighting value
// isn't valid
if ((cacheValue & LIGHTING_VALID) == 0) {
int val = getBrightness(layer, x, y, z);
cacheValue |= val << LIGHTING_SHIFT;
cacheValue |= LIGHTING_VALID;
} else {
// All valid - just return value
return (cacheValue >> LIGHTING_SHIFT) & 15;
}
}
cache[idx] = cacheValue;
return (cacheValue >> LIGHTING_SHIFT) & 15;
}
// Get a block emission value, going through the cache if enabled for this
// thread
inline int Level::getEmissionCached(lightCache_t* cache, int ct, int x, int y,
int z) {
if (cache == NULL) return Tile::lightEmission[ct];
int idx = ((x & 15) << 8) | ((y & 15) << 4) | (z & 15);
lightCache_t posbits =
((x & 0x3f0) << 6) | ((y & 0x0f0) << 2) | ((z & 0x3f0) >> 4);
#ifdef _LARGE_WORLDS
// Add in the higher bits for x and z
posbits |= ((((__uint64)x) & 0x3FFFC00) << 38) |
((((__uint64)z) & 0x3FFFC00) << 22);
#endif
lightCache_t cacheValue = cache[idx];
if ((cacheValue & POSITION_MASK) != posbits) {
// Position differs - need to evict this cache entry
if (cacheValue & LIGHTING_WRITEBACK) {
// Then we need to flush
int val = (cacheValue >> LIGHTING_SHIFT) & 15;
int xx = ((cacheValue >> 6) & 0x3f0) | (x & 15);
#ifdef _LARGE_WORLDS
xx |= ((cacheValue >> 38) & 0x3FFFC00);
xx = (xx << 6) >> 6; // sign extend
#else
xx = (xx << 22) >> 22; // sign extend
#endif
int yy = ((cacheValue >> 2) & 0x0f0) | (y & 15);
int zz = ((cacheValue << 4) & 0x3f0) | (z & 15);
#ifdef _LARGE_WORLDS
zz |= ((cacheValue >> 22) & 0x3FFFC00);
zz = (zz << 6) >> 6; // sign extend
#else
zz = (zz << 22) >> 22; // sign extend
#endif
setBrightness(LightLayer::Block, xx, yy, zz, val, true);
}
// Update both emission & blocking values whilst we are here
cacheValue = posbits | EMISSION_VALID | BLOCKING_VALID;
int t = getTile(x, y, z);
cacheValue |= (Tile::lightEmission[t] & 15) << EMISSION_SHIFT;
cacheValue |= (Tile::lightBlock[t] & 15) << BLOCKING_SHIFT;
} else {
// The position matches - will incurr a read miss if the lighting value
// isn't valid
if ((cacheValue & EMISSION_VALID) == 0) {
// Update both emission & blocking values whilst we are here
cacheValue |= EMISSION_VALID | BLOCKING_VALID;
int t = getTile(x, y, z);
cacheValue |= (Tile::lightEmission[t] & 15) << EMISSION_SHIFT;
cacheValue |= (Tile::lightBlock[t] & 15) << BLOCKING_SHIFT;
} else {
// All valid - just return value
return (cacheValue >> EMISSION_SHIFT) & 15;
}
}
cache[idx] = cacheValue;
return (cacheValue >> EMISSION_SHIFT) & 15;
}
// Get a tile light blocking value, going through cache if enabled for this
// thread
inline int Level::getBlockingCached(lightCache_t* cache,
LightLayer::variety layer, int* ct, int x,
int y, int z) {
if (cache == NULL) {
int t = getTile(x, y, z);
if (ct) *ct = t;
return Tile::lightBlock[t];
}
int idx = ((x & 15) << 8) | ((y & 15) << 4) | (z & 15);
lightCache_t posbits =
((x & 0x3f0) << 6) | ((y & 0x0f0) << 2) | ((z & 0x3f0) >> 4);
#ifdef _LARGE_WORLDS
// Add in the higher bits for x and z
posbits |= ((((__uint64)x) & 0x3FFFC00L) << 38) |
((((__uint64)z) & 0x3FFFC00L) << 22);
#endif
lightCache_t cacheValue = cache[idx];
if ((cacheValue & POSITION_MASK) != posbits) {
// Position differs - need to evict this cache entry
if (cacheValue & LIGHTING_WRITEBACK) {
// Then we need to flush
int val = (cacheValue >> LIGHTING_SHIFT) & 15;
int xx = ((cacheValue >> 6) & 0x3f0) | (x & 15);
#ifdef _LARGE_WORLDS
xx |= ((cacheValue >> 38) & 0x3FFFC00);
xx = (xx << 6) >> 6; // sign extend
#else
xx = (xx << 22) >> 22; // sign extend
#endif
int yy = ((cacheValue >> 2) & 0x0f0) | (y & 15);
int zz = ((cacheValue << 4) & 0x3f0) | (z & 15);
#ifdef _LARGE_WORLDS
zz |= ((cacheValue >> 22) & 0x3FFFC00);
zz = (zz << 6) >> 6; // sign extend
#else
zz = (zz << 22) >> 22; // sign extend
#endif
setBrightness(layer, xx, yy, zz, val, true);
}
// Update both emission & blocking values whilst we are here
cacheValue = posbits | EMISSION_VALID | BLOCKING_VALID;
int t = getTile(x, y, z);
cacheValue |= (Tile::lightEmission[t] & 15) << EMISSION_SHIFT;
cacheValue |= (Tile::lightBlock[t] & 15) << BLOCKING_SHIFT;
} else {
// The position matches - will incurr a read miss if the lighting value
// isn't valid
if ((cacheValue & EMISSION_VALID) == 0) {
// Update both emission & blocking values whilst we are here
cacheValue |= EMISSION_VALID | BLOCKING_VALID;
int t = getTile(x, y, z);
cacheValue |= (Tile::lightEmission[t] & 15) << EMISSION_SHIFT;
cacheValue |= (Tile::lightBlock[t] & 15) << BLOCKING_SHIFT;
} else {
// All valid - just return value
return (cacheValue >> BLOCKING_SHIFT) & 15;
}
}
cache[idx] = cacheValue;
return (cacheValue >> BLOCKING_SHIFT) & 15;
}
// 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 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 (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 (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
void Level::flushCache(lightCache_t* cache, __uint64 cacheUse,
LightLayer::variety layer) {
// 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
if (cacheUse == 0) return;
if (cache) {
lightCache_t* pcache = cache;
for (int x = 0; x < 16; x++) {
if ((cacheUse & (1LL << x)) == 0) {
pcache += 16 * 16;
continue;
}
for (int y = 0; y < 16; y++) {
if ((cacheUse & (0x10000LL << y)) == 0) {
pcache += 16;
continue;
}
for (int z = 0; z < 16; z++) {
if ((cacheUse & (0x100000000LL << z)) == 0) {
pcache++;
continue;
}
lightCache_t cacheValue = *pcache++;
if (cacheValue & LIGHTING_WRITEBACK) {
int val = (cacheValue >> LIGHTING_SHIFT) & 15;
int xx = ((cacheValue >> 6) & 0x3f0) | (x & 15);
#ifdef _LARGE_WORLDS
xx |= ((cacheValue >> 38) & 0x3FFFC00);
xx = (xx << 6) >> 6; // sign extend
#else
xx = (xx << 22) >> 22; // sign extend
#endif
int yy = ((cacheValue >> 2) & 0x0f0) | (y & 15);
int zz = ((cacheValue << 4) & 0x3f0) | (z & 15);
#ifdef _LARGE_WORLDS
zz |= ((cacheValue >> 22) & 0x3FFFC00);
zz = (zz << 6) >> 6; // sign extend
#else
zz = (zz << 22) >> 22; // sign extend
#endif
setBrightness(layer, xx, yy, zz, val, true);
}
}
}
}
}
// For client side (which has the renderer attached) we haven't been
// updating with each individual update, but have been gathering them up.
// Let the renderer know now the region that has been updated.
if (isClientSide && cachewritten) {
setTilesDirty(cacheminx, cacheminy, cacheminz, cachemaxx, cachemaxy,
cachemaxz);
}
}
// 4J - added following 2 functions to move instaBuild flag from being a class
// member, to TLS
bool Level::getInstaTick() {
return reinterpret_cast<std::intptr_t>(LevelTlsGetValue(tlsIdx)) != 0;
}
void Level::setInstaTick(bool enable) {
void* value = 0;
if (enable) value = (void*)1;
LevelTlsSetValue(tlsIdx, value);
}
// 4J - added
bool Level::hasEntitiesToRemove() { return !entitiesToRemove.empty(); }
void Level::_init() {
cloudColor = 0xffffff;
skyDarken = 0;
randValue = (new Random())->nextInt();
addend = 1013904223;
oRainLevel = rainLevel = 0.0f;
oThunderLevel = thunderLevel = 0.0f;
lightningTime = 0;
lightningBoltTime = 0;
noNeighborUpdate = false;
difficulty = 0;
random = new Random();
isNew = false;
dimension = NULL;
chunkSource = NULL;
levelStorage = nullptr;
levelData = NULL;
isFindingSpawn = false;
savedDataStorage = NULL;
spawnEnemies = true;
spawnFriendlies = true;
delayUntilNextMoodSound = random->nextInt(20 * 60 * 10);
isClientSide = false;
InitializeCriticalSection(&m_entitiesCS);
InitializeCriticalSection(&m_tileEntityListCS);
m_timeOfDayOverride = -1;
updatingTileEntities = false;
villageSiege = new VillageSiege(this);
toCheckLevel = new int[32 * 32 * 32]; // 4J - brought forward from 1.8.2
InitializeCriticalSectionAndSpinCount(
&m_checkLightCS, 5120); // 4J - added for 1.8.2 lighting
// 4J Added
m_bDisableAddNewTileEntities = false;
m_iHighestY = -1000;
m_unsavedChunkCount = 0;
}
// 4J - brought forward from 1.8.2
Biome* Level::getBiome(int x, int z) {
if (hasChunkAt(x, 0, z)) {
LevelChunk* lc = getChunkAt(x, z);
if (lc != NULL) {
// 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
Biome* biome =
lc->getBiome(x & 0xf, z & 0xf, dimension->biomeSource);
if (biome) return biome;
}
}
return dimension->biomeSource->getBiome(x, z);
}
BiomeSource* Level::getBiomeSource() { return dimension->biomeSource; }
Level::Level(std::shared_ptr<LevelStorage> levelStorage,
const std::wstring& name, Dimension* dimension,
LevelSettings* levelSettings, bool doCreateChunkSource)
: seaLevel(constSeaLevel) {
_init();
this->levelStorage =
levelStorage; // std::shared_ptr<LevelStorage>(levelStorage);
this->dimension = dimension;
this->levelData = new LevelData(levelSettings, name);
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
this->savedDataStorage = new SavedDataStorage(levelStorage.get());
std::shared_ptr<Villages> savedVillages =
std::dynamic_pointer_cast<Villages>(
savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID));
if (savedVillages == NULL) {
villages = std::shared_ptr<Villages>(new Villages(this));
savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages);
} else {
villages = savedVillages;
villages->setLevel(this);
}
dimension->init(this);
chunkSource = NULL; // 4J - added flag so chunk source can be called from
// derived class instead
updateSkyBrightness();
prepareWeather();
}
Level::Level(Level* level, Dimension* dimension) : seaLevel(constSeaLevel) {
_init();
this->levelStorage = level->levelStorage;
this->levelData = new LevelData(level->levelData);
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
this->savedDataStorage = new SavedDataStorage(levelStorage.get());
std::shared_ptr<Villages> savedVillages =
std::dynamic_pointer_cast<Villages>(
savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID));
if (savedVillages == NULL) {
villages = std::shared_ptr<Villages>(new Villages(this));
savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages);
} else {
villages = savedVillages;
villages->setLevel(this);
}
this->dimension = dimension;
dimension->init(this);
chunkSource = NULL;
updateSkyBrightness();
prepareWeather();
}
Level::Level(std::shared_ptr<LevelStorage> levelStorage,
const std::wstring& levelName, LevelSettings* levelSettings)
: seaLevel(constSeaLevel) {
_init(levelStorage, levelName, levelSettings, NULL, true);
}
Level::Level(std::shared_ptr<LevelStorage> levelStorage,
const std::wstring& levelName, LevelSettings* levelSettings,
Dimension* fixedDimension, bool doCreateChunkSource)
: seaLevel(constSeaLevel) {
_init(levelStorage, levelName, levelSettings, fixedDimension,
doCreateChunkSource);
}
void Level::_init(std::shared_ptr<LevelStorage> levelStorage,
const std::wstring& levelName, LevelSettings* levelSettings,
Dimension* fixedDimension, bool doCreateChunkSource) {
_init();
this->levelStorage =
levelStorage; // std::shared_ptr<LevelStorage>(levelStorage);
this->savedDataStorage = new SavedDataStorage(levelStorage.get());
std::shared_ptr<Villages> savedVillages =
std::dynamic_pointer_cast<Villages>(
savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID));
if (savedVillages == NULL) {
villages = std::shared_ptr<Villages>(new Villages(this));
savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages);
} else {
villages = savedVillages;
villages->setLevel(this);
}
levelData = levelStorage->prepareLevel();
isNew = levelData == NULL;
if (fixedDimension != NULL) {
dimension = fixedDimension;
}
// 4J Remove TU9 as getDimensions was never accurate. This path was never
// used anyway as we always set fixedDimension
// else if (levelData != NULL && levelData->getDimension() != 0)
//{
// dimension = Dimension::getNew(levelData->getDimension());
//}
else {
dimension = Dimension::getNew(0);
}
if (levelData == NULL) {
levelData = new LevelData(levelSettings, levelName);
} else {
levelData->setLevelName(levelName);
}
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
((Dimension*)dimension)->init(this);
chunkSource = doCreateChunkSource
? createChunkSource()
: NULL; // 4J - added flag so chunk source can be called
// from derived class instead
// 4J Stu- Moved to derived classes
// if (!levelData->isInitialized())
//{
// initializeLevel(levelSettings);
// levelData->setInitialized(true);
//}
updateSkyBrightness();
prepareWeather();
}
Level::~Level() {
delete random;
delete dimension;
delete chunkSource;
delete levelData;
delete toCheckLevel;
if (!isClientSide) {
NotGateTile::removeLevelReferences(this); // 4J added
}
DeleteCriticalSection(&m_checkLightCS);
// 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
if (savedDataStorage != NULL) delete savedDataStorage;
DeleteCriticalSection(&m_entitiesCS);
DeleteCriticalSection(&m_tileEntityListCS);
// 4J Stu - At least one of the listeners is something we cannot delete, the
// LevelRenderer
/*
for(int i = 0; i < listeners.size(); i++)
delete listeners[i];
*/
}
void Level::initializeLevel(LevelSettings* settings) {
levelData->setInitialized(true);
}
void Level::validateSpawn() { setSpawnPos(8, 64, 8); }
int Level::getTopTile(int x, int z) {
// 4J added - was breaking spawning as not finding ground in superflat
// worlds
if (levelData->getGenerator() == LevelType::lvl_flat) {
return Tile::grass_Id;
}
int y = seaLevel;
while (!isEmptyTile(x, y + 1, z)) {
y++;
}
return getTile(x, y, z);
}
int Level::getTile(int x, int y, int z) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return 0;
}
if (y < minBuildHeight) return 0;
if (y >= maxBuildHeight) return 0;
return getChunk(x >> 4, z >> 4)->getTile(x & 15, y, z & 15);
}
int Level::getTileLightBlock(int x, int y, int z) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return 0;
}
if (y < minBuildHeight) return 0;
if (y >= maxBuildHeight) return 0;
return getChunk(x >> 4, z >> 4)->getTileLightBlock(x & 15, y, z & 15);
}
bool Level::isEmptyTile(int x, int y, int z) { return getTile(x, y, z) == 0; }
bool Level::isEntityTile(int x, int y, int z) {
int t = getTile(x, y, z);
if (Tile::tiles[t] != NULL && Tile::tiles[t]->isEntityTile()) {
return true;
}
return false;
}
int Level::getTileRenderShape(int x, int y, int z) {
int t = getTile(x, y, z);
if (Tile::tiles[t] != NULL) {
return Tile::tiles[t]->getRenderShape();
}
return Tile::SHAPE_INVISIBLE;
}
bool Level::hasChunkAt(int x, int y, int z) {
if (y < minBuildHeight || y >= maxBuildHeight) return false;
return hasChunk(x >> 4, z >> 4);
}
// 4J added
bool Level::reallyHasChunkAt(int x, int y, int z) {
if (y < minBuildHeight || y >= maxBuildHeight) return false;
return reallyHasChunk(x >> 4, z >> 4);
}
bool Level::hasChunksAt(int x, int y, int z, int r) {
return hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r);
}
// 4J added
bool Level::reallyHasChunksAt(int x, int y, int z, int r) {
return reallyHasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r);
}
bool Level::hasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1) {
if (y1 < minBuildHeight || y0 >= maxBuildHeight) return false;
x0 >>= 4;
z0 >>= 4;
x1 >>= 4;
z1 >>= 4;
for (int x = x0; x <= x1; x++)
for (int z = z0; z <= z1; z++)
if (!hasChunk(x, z)) return false;
return true;
}
// 4J added
bool Level::reallyHasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1) {
x0 >>= 4;
z0 >>= 4;
x1 >>= 4;
z1 >>= 4;
for (int x = x0; x <= x1; x++)
for (int z = z0; z <= z1; z++)
if (!reallyHasChunk(x, z)) return false;
return true;
}
bool Level::hasChunk(int x, int z) { return this->chunkSource->hasChunk(x, z); }
// 4J added
bool Level::reallyHasChunk(int x, int z) {
return this->chunkSource->reallyHasChunk(x, z);
}
LevelChunk* Level::getChunkAt(int x, int z) { return getChunk(x >> 4, z >> 4); }
LevelChunk* Level::getChunk(int x, int z) {
return this->chunkSource->getChunk(x, z);
}
bool Level::setTileAndDataNoUpdate(int x, int y, int z, int tile, int data) {
return setTileAndDataNoUpdate(x, y, z, tile, data, true);
}
bool Level::setTileAndDataNoUpdate(int x, int y, int z, int tile, int data,
bool informClients) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return false;
}
if (y < 0) return false;
if (y >= maxBuildHeight) return false;
LevelChunk* c = getChunk(x >> 4, z >> 4);
// 4J - changes for lighting brought forward from 1.8.2
bool result;
#ifndef _CONTENT_PACKAGE
int old = c->getTile(x & 15, y, z & 15);
int olddata = c->getData(x & 15, y, z & 15);
#endif
result = c->setTileAndData(x & 15, y, z & 15, tile, data);
#ifndef _CONTENT_PACKAGE
PIXBeginNamedEvent(0, "Checking light %d %d %d", x, y, z);
PIXBeginNamedEvent(0, "was %d, %d now %d, %d", old, olddata, tile, data);
#endif
this->checkLight(x, y, z);
PIXEndNamedEvent();
PIXEndNamedEvent();
if (informClients && result && (isClientSide || c->seenByPlayer))
sendTileUpdated(x, y, z);
return result;
}
bool Level::setTileNoUpdate(int x, int y, int z, int tile) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return false;
}
if (y < 0) return false;
if (y >= maxBuildHeight) return false;
LevelChunk* c = getChunk(x >> 4, z >> 4);
// 4J - changes for lighting brought forward from 1.8.2
bool result = c->setTile(x & 15, y, z & 15, tile);
this->checkLight(x, y, z);
if (result && (isClientSide || c->seenByPlayer)) sendTileUpdated(x, y, z);
return result;
}
bool Level::setTileNoUpdateNoLightCheck(int x, int y, int z, int tile) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return false;
}
if (y < 0) return false;
if (y >= maxBuildHeight) return false;
LevelChunk* c = getChunk(x >> 4, z >> 4);
// 4J - changes for lighting brought forward from 1.8.2
bool result = c->setTile(x & 15, y, z & 15, tile);
return result;
}
Material* Level::getMaterial(int x, int y, int z) {
int t = getTile(x, y, z);
if (t == 0) return Material::air;
return Tile::tiles[t]->material;
}
int Level::getData(int x, int y, int z) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return 0;
}
if (y < 0) return 0;
if (y >= maxBuildHeight) return 0;
LevelChunk* c = getChunk(x >> 4, z >> 4);
x &= 15;
z &= 15;
return c->getData(x, y, z);
}
void Level::setData(int x, int y, int z, int data,
bool forceUpdate /*=false*/) // 4J added forceUpdate
{
if (setDataNoUpdate(x, y, z, data) || forceUpdate) {
tileUpdated(x, y, z, getTile(x, y, z));
}
}
bool Level::setDataNoUpdate(int x, int y, int z, int data) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return false;
}
if (y < 0) return false;
if (y >= maxBuildHeight) return false;
LevelChunk* c = getChunk(x >> 4, z >> 4);
int cx = x & 15;
int cz = z & 15;
// 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 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
// 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
unsigned char importantMask =
Tile::_sendTileData[c->getTile(cx, y, cz) & Tile::TILE_NUM_MASK];
bool sendTileData = importantMask != 0;
bool maskedBitsChanged;
bool result =
c->setData(cx, y, cz, data, importantMask, &maskedBitsChanged);
if (result && (isClientSide ||
(c->seenByPlayer && sendTileData && maskedBitsChanged)))
sendTileUpdated(x, y, z);
return result;
}
bool Level::setTile(int x, int y, int z, int tile) {
if (setTileNoUpdate(x, y, z, tile)) {
tileUpdated(x, y, z, tile);
return true;
}
return false;
}
bool Level::setTileAndData(int x, int y, int z, int tile, int data) {
if (setTileAndDataNoUpdate(x, y, z, tile, data)) {
tileUpdated(x, y, z, tile);
return true;
}
return false;
}
void Level::sendTileUpdated(int x, int y, int z) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->tileChanged(x, y, z);
}
}
void Level::tileUpdated(int x, int y, int z, int tile) {
this->updateNeighborsAt(x, y, z, tile);
}
void Level::lightColumnChanged(int x, int z, int y0, int y1) {
PIXBeginNamedEvent(0, "LightColumnChanged (%d,%d) %d to %d", x, z, y0, y1);
if (y0 > y1) {
int tmp = y1;
y1 = y0;
y0 = tmp;
}
if (!dimension->hasCeiling) {
PIXBeginNamedEvent(0, "Checking lights");
for (int y = y0; y <= y1; y++) {
PIXBeginNamedEvent(0, "Checking light %d", y);
checkLight(LightLayer::Sky, x, y, z);
PIXEndNamedEvent();
}
PIXEndNamedEvent();
}
PIXBeginNamedEvent(0, "Setting tiles dirty");
setTilesDirty(x, y0, z, x, y1, z);
PIXEndNamedEvent();
PIXEndNamedEvent();
}
void Level::setTileDirty(int x, int y, int z) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->setTilesDirty(x, y, z, x, y, z, this);
}
}
void Level::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->setTilesDirty(x0, y0, z0, x1, y1, z1, this);
}
}
void Level::swap(int x1, int y1, int z1, int x2, int y2, int z2) {
int t1 = getTile(x1, y1, z1);
int d1 = getData(x1, y1, z1);
int t2 = getTile(x2, y2, z2);
int d2 = getData(x2, y2, z2);
setTileAndDataNoUpdate(x1, y1, z1, t2, d2);
setTileAndDataNoUpdate(x2, y2, z2, t1, d1);
updateNeighborsAt(x1, y1, z1, t2);
updateNeighborsAt(x2, y2, z2, t1);
}
void Level::updateNeighborsAt(int x, int y, int z, int tile) {
neighborChanged(x - 1, y, z, tile);
neighborChanged(x + 1, y, z, tile);
neighborChanged(x, y - 1, z, tile);
neighborChanged(x, y + 1, z, tile);
neighborChanged(x, y, z - 1, tile);
neighborChanged(x, y, z + 1, tile);
}
void Level::neighborChanged(int x, int y, int z, int type) {
if (noNeighborUpdate || isClientSide) return;
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL) tile->neighborChanged(this, x, y, z, type);
}
bool Level::canSeeSky(int x, int y, int z) {
return getChunk(x >> 4, z >> 4)->isSkyLit(x & 15, y, z & 15);
}
int Level::getDaytimeRawBrightness(int x, int y, int z) {
if (y < 0) return 0;
if (y >= maxBuildHeight) y = maxBuildHeight - 1;
return getChunk(x >> 4, z >> 4)->getRawBrightness(x & 15, y, z & 15, 0);
}
int Level::getRawBrightness(int x, int y, int z) {
return getRawBrightness(x, y, z, true);
}
int Level::getRawBrightness(int x, int y, int z, bool propagate) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return MAX_BRIGHTNESS;
}
if (propagate) {
int id = getTile(x, y, z);
switch (id) {
case Tile::stoneSlabHalf_Id:
case Tile::woodSlabHalf_Id:
case Tile::farmland_Id:
case Tile::stairs_stone_Id:
case Tile::stairs_wood_Id: {
int br = getRawBrightness(x, y + 1, z, false);
int br1 = getRawBrightness(x + 1, y, z, false);
int br2 = getRawBrightness(x - 1, y, z, false);
int br3 = getRawBrightness(x, y, z + 1, false);
int br4 = getRawBrightness(x, y, z - 1, false);
if (br1 > br) br = br1;
if (br2 > br) br = br2;
if (br3 > br) br = br3;
if (br4 > br) br = br4;
return br;
} break;
}
}
if (y < 0) return 0;
if (y >= maxBuildHeight) y = maxBuildHeight - 1;
LevelChunk* c = getChunk(x >> 4, z >> 4);
x &= 15;
z &= 15;
return c->getRawBrightness(x, y, z, skyDarken);
}
bool Level::isSkyLit(int x, int y, int z) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return false;
}
if (dimension->hasCeiling) return false;
if (y < 0) return false;
if (y >= maxBuildHeight) return true;
if (!hasChunk(x >> 4, z >> 4)) return false;
LevelChunk* c = getChunk(x >> 4, z >> 4);
x &= 15;
z &= 15;
return c->isSkyLit(x, y, z);
}
int Level::getHeightmap(int x, int z) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return 0;
}
if (!hasChunk(x >> 4, z >> 4)) return 0;
LevelChunk* c = getChunk(x >> 4, z >> 4);
return c->getHeightmap(x & 15, z & 15);
}
void Level::updateLightIfOtherThan(LightLayer::variety layer, int x, int y,
int z, int expected) {
if (dimension->hasCeiling && layer == LightLayer::Sky) return;
if (!hasChunkAt(x, y, z)) return;
if (layer == LightLayer::Sky) {
if (isSkyLit(x, y, z)) expected = 15;
} else if (layer == LightLayer::Block) {
int t = getTile(x, y, z);
if (Tile::lightEmission[t] > expected)
expected = Tile::lightEmission[t];
}
if (getBrightness(layer, x, y, z) != expected) {
setBrightness(layer, x, y, z, expected);
}
}
// 4J - update brought forward from 1.8.2
int Level::getBrightnessPropagate(LightLayer::variety layer, int x, int y,
int z, int tileId) {
if (dimension->hasCeiling && layer == LightLayer::Sky) return 0;
if (y < 0) y = 0;
if (y >= maxBuildHeight && layer == LightLayer::Sky) {
// 4J Stu - The java LightLayer was an enum class type with a member
// "surrounding" which is what we were returning here. Surrounding has
// the same value as the enum value in our C++ code, so just cast it to
// an int
return (int)layer;
}
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
// 4J Stu - The java LightLayer was an enum class type with a member
// "surrounding" which is what we were returning here. Surrounding has
// the same value as the enum value in our C++ code, so just cast it to
// an int
return (int)layer;
}
int xc = x >> 4;
int zc = z >> 4;
if (!hasChunk(xc, zc)) return (int)layer;
{
int id = tileId > -1 ? tileId : getTile(x, y, z);
if (Tile::propagate[id]) {
int br = getBrightness(layer, x, y + 1, z);
int br1 = getBrightness(layer, x + 1, y, z);
int br2 = getBrightness(layer, x - 1, y, z);
int br3 = getBrightness(layer, x, y, z + 1);
int br4 = getBrightness(layer, x, y, z - 1);
if (br1 > br) br = br1;
if (br2 > br) br = br2;
if (br3 > br) br = br3;
if (br4 > br) br = br4;
return br;
}
}
LevelChunk* c = getChunk(xc, zc);
return c->getBrightness(layer, x & 15, y, z & 15);
}
int Level::getBrightness(LightLayer::variety layer, int x, int y, int z) {
// 4J - optimised. Not doing checks on x/z that are no longer necessary, and
// directly checking the cache within the
// ServerChunkCache/MultiplayerChunkCache rather than going through wrappers
// & virtual functions.
int xc = x >> 4;
int zc = z >> 4;
int ix = xc + (chunkSourceXZSize / 2);
int iz = zc + (chunkSourceXZSize / 2);
if ((ix < 0) || (ix >= chunkSourceXZSize)) return 0;
if ((iz < 0) || (iz >= chunkSourceXZSize)) return 0;
int idx = ix * chunkSourceXZSize + iz;
LevelChunk* c = chunkSourceCache[idx];
if (c == NULL) return (int)layer;
if (y < 0) y = 0;
if (y >= maxBuildHeight) y = maxBuildHeight - 1;
return c->getBrightness(layer, x & 15, y, z & 15);
}
// 4J added as optimisation - if all the neighbouring brightesses are going to
// be in the one chunk, just get the level chunk once
void Level::getNeighbourBrightnesses(int* brightnesses,
LightLayer::variety layer, int x, int y,
int z) {
if ((((x & 15) == 0) || ((x & 15) == 15)) ||
(((z & 15) == 0) || ((z & 15) == 15)) || ((y <= 0) || (y >= 127))) {
// We're spanning more than one chunk, just fall back on original java
// method here
brightnesses[0] = getBrightness(layer, x - 1, y, z);
brightnesses[1] = getBrightness(layer, x + 1, y, z);
brightnesses[2] = getBrightness(layer, x, y - 1, z);
brightnesses[3] = getBrightness(layer, x, y + 1, z);
brightnesses[4] = getBrightness(layer, x, y, z - 1);
brightnesses[5] = getBrightness(layer, x, y, z + 1);
} else {
// All in one chunk - just get the chunk once, and do a single call to
// get the results
int xc = x >> 4;
int zc = z >> 4;
int ix = xc + (chunkSourceXZSize / 2);
int iz = zc + (chunkSourceXZSize / 2);
// 4J Stu - The java LightLayer was an enum class type with a member
// "surrounding" which is what we were returning here. Surrounding has
// the same value as the enum value in our C++ code, so just cast it to
// an int
if (((ix < 0) || (ix >= chunkSourceXZSize)) ||
((iz < 0) || (iz >= chunkSourceXZSize))) {
for (int i = 0; i < 6; i++) {
brightnesses[i] = (int)layer;
}
return;
}
int idx = ix * chunkSourceXZSize + iz;
LevelChunk* c = chunkSourceCache[idx];
// 4J Stu - The java LightLayer was an enum class type with a member
// "surrounding" which is what we were returning here. Surrounding has
// the same value as the enum value in our C++ code, so just cast it to
// an int
if (c == NULL) {
for (int i = 0; i < 6; i++) {
brightnesses[i] = (int)layer;
}
return;
}
// Single call to the levelchunk too to avoid overhead of virtual fn
// calls
c->getNeighbourBrightnesses(brightnesses, layer, x & 15, y, z & 15);
}
}
void Level::setBrightness(
LightLayer::variety layer, int x, int y, int z, int brightness,
bool noUpdateOnClient /*=false*/) // 4J added noUpdateOnClient
{
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return;
}
if (y < 0) return;
if (y >= maxBuildHeight) return;
if (!hasChunk(x >> 4, z >> 4)) return;
LevelChunk* c = getChunk(x >> 4, z >> 4);
c->setBrightness(layer, x & 15, y, z & 15, brightness);
// 4J added
if (isClientSide && noUpdateOnClient) {
if (cachewritten) {
if (x < cacheminx) cacheminx = x;
if (x > cachemaxx) cachemaxx = x;
if (y < cacheminy) cacheminy = y;
if (y > cachemaxy) cachemaxy = y;
if (z < cacheminz) cacheminz = z;
if (z > cachemaxz) cachemaxz = z;
} else {
cachewritten = true;
cacheminx = x;
cachemaxx = x;
cacheminy = y;
cachemaxy = y;
cacheminz = z;
cachemaxz = z;
}
} else {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->tileLightChanged(x, y, z);
}
}
}
void Level::setTileBrightnessChanged(int x, int y, int z) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->tileLightChanged(x, y, z);
}
}
int Level::getLightColor(int x, int y, int z, int emitt, int tileId /*=-1*/) {
int s = getBrightnessPropagate(LightLayer::Sky, x, y, z, tileId);
int b = getBrightnessPropagate(LightLayer::Block, x, y, z, tileId);
if (b < emitt) b = emitt;
return s << 20 | b << 4;
}
float Level::getBrightness(int x, int y, int z, int emitt) {
int n = getRawBrightness(x, y, z);
if (n < emitt) n = emitt;
return dimension->brightnessRamp[n];
}
float Level::getBrightness(int x, int y, int z) {
return dimension->brightnessRamp[getRawBrightness(x, y, z)];
}
bool Level::isDay() { return this->skyDarken < 4; }
HitResult* Level::clip(Vec3* a, Vec3* b) { return clip(a, b, false, false); }
HitResult* Level::clip(Vec3* a, Vec3* b, bool liquid) {
return clip(a, b, liquid, false);
}
HitResult* Level::clip(Vec3* a, Vec3* b, bool liquid, bool solidOnly) {
if (Double::isNaN(a->x) || Double::isNaN(a->y) || Double::isNaN(a->z))
return NULL;
if (Double::isNaN(b->x) || Double::isNaN(b->y) || Double::isNaN(b->z))
return NULL;
int xTile1 = Mth::floor(b->x);
int yTile1 = Mth::floor(b->y);
int zTile1 = Mth::floor(b->z);
int xTile0 = Mth::floor(a->x);
int yTile0 = Mth::floor(a->y);
int zTile0 = Mth::floor(a->z);
{
int t = getTile(xTile0, yTile0, zTile0);
int data = getData(xTile0, yTile0, zTile0);
Tile* tile = Tile::tiles[t];
if (solidOnly && tile != NULL &&
tile->getAABB(this, xTile0, yTile0, zTile0) == NULL) {
// No collision
} else if (t > 0 && tile->mayPick(data, liquid)) {
HitResult* r = tile->clip(this, xTile0, yTile0, zTile0, a, b);
if (r != NULL) return r;
}
}
int maxIterations = 200;
while (maxIterations-- >= 0) {
if (Double::isNaN(a->x) || Double::isNaN(a->y) || Double::isNaN(a->z))
return NULL;
if (xTile0 == xTile1 && yTile0 == yTile1 && zTile0 == zTile1)
return NULL;
bool xClipped = true;
bool yClipped = true;
bool zClipped = true;
double xClip = 999;
double yClip = 999;
double zClip = 999;
if (xTile1 > xTile0)
xClip = xTile0 + 1.000;
else if (xTile1 < xTile0)
xClip = xTile0 + 0.000;
else
xClipped = false;
if (yTile1 > yTile0)
yClip = yTile0 + 1.000;
else if (yTile1 < yTile0)
yClip = yTile0 + 0.000;
else
yClipped = false;
if (zTile1 > zTile0)
zClip = zTile0 + 1.000;
else if (zTile1 < zTile0)
zClip = zTile0 + 0.000;
else
zClipped = false;
double xDist = 999;
double yDist = 999;
double zDist = 999;
double xd = b->x - a->x;
double yd = b->y - a->y;
double zd = b->z - a->z;
if (xClipped) xDist = (xClip - a->x) / xd;
if (yClipped) yDist = (yClip - a->y) / yd;
if (zClipped) zDist = (zClip - a->z) / zd;
int face = 0;
if (xDist < yDist && xDist < zDist) {
if (xTile1 > xTile0)
face = 4;
else
face = 5;
a->x = xClip;
a->y += yd * xDist;
a->z += zd * xDist;
} else if (yDist < zDist) {
if (yTile1 > yTile0)
face = 0;
else
face = 1;
a->x += xd * yDist;
a->y = yClip;
a->z += zd * yDist;
} else {
if (zTile1 > zTile0)
face = 2;
else
face = 3;
a->x += xd * zDist;
a->y += yd * zDist;
a->z = zClip;
}
Vec3* tPos = Vec3::newTemp(a->x, a->y, a->z);
xTile0 = (int)(tPos->x = floor(a->x));
if (face == 5) {
xTile0--;
tPos->x++;
}
yTile0 = (int)(tPos->y = floor(a->y));
if (face == 1) {
yTile0--;
tPos->y++;
}
zTile0 = (int)(tPos->z = floor(a->z));
if (face == 3) {
zTile0--;
tPos->z++;
}
int t = getTile(xTile0, yTile0, zTile0);
int data = getData(xTile0, yTile0, zTile0);
Tile* tile = Tile::tiles[t];
if (solidOnly && tile != NULL &&
tile->getAABB(this, xTile0, yTile0, zTile0) == NULL) {
// No collision
} else if (t > 0 && tile->mayPick(data, liquid)) {
HitResult* r = tile->clip(this, xTile0, yTile0, zTile0, a, b);
if (r != NULL) return r;
}
}
return NULL;
}
void Level::playSound(std::shared_ptr<Entity> entity, int iSound, float volume,
float pitch) {
if (entity == NULL) return;
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
// 4J-PB - if the entity is a local player, don't play the sound
if (entity->GetType() == eTYPE_SERVERPLAYER) {
// app.DebugPrintf("ENTITY is serverplayer\n");
(*it)->playSound(entity, iSound, entity->x,
entity->y - entity->heightOffset, entity->z,
volume, pitch);
} else {
(*it)->playSound(iSound, entity->x,
entity->y - entity->heightOffset, entity->z,
volume, pitch);
}
}
}
// void Level::playSound(double x, double y, double z, const std::wstring& name,
// float volume, float pitch)
void Level::playSound(double x, double y, double z, int iSound, float volume,
float pitch, float fClipSoundDist) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->playSound(iSound, x, y, z, volume, pitch, fClipSoundDist);
}
}
void Level::playLocalSound(double x, double y, double z, int iSound,
float volume, float pitch, float fClipSoundDist) {}
void Level::playStreamingMusic(const std::wstring& name, int x, int y, int z) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->playStreamingMusic(name, x, y, z);
}
}
void Level::playMusic(double x, double y, double z, const std::wstring& string,
float volume) {}
// 4J removed -
/*
void Level::addParticle(const std::wstring& id, double x, double y, double z,
double xd, double yd, double zd)
{
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
(*it)->addParticle(id, x, y, z, xd, yd, zd);
}
*/
// 4J-PB added
void Level::addParticle(ePARTICLE_TYPE id, double x, double y, double z,
double xd, double yd, double zd) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++)
(*it)->addParticle(id, x, y, z, xd, yd, zd);
}
bool Level::addGlobalEntity(std::shared_ptr<Entity> e) {
globalEntities.push_back(e);
return true;
}
#pragma optimize("", off)
bool Level::addEntity(std::shared_ptr<Entity> e) {
int xc = Mth::floor(e->x / 16);
int zc = Mth::floor(e->z / 16);
if (e == NULL) {
return false;
}
bool forced = false;
if (std::dynamic_pointer_cast<Player>(e) != NULL) {
forced = true;
}
if (forced || hasChunk(xc, zc)) {
if (std::dynamic_pointer_cast<Player>(e) != NULL) {
std::shared_ptr<Player> player =
std::dynamic_pointer_cast<Player>(e);
// 4J Stu - Added so we don't continually add the player to the
// players list while they are dead
if (find(players.begin(), players.end(), e) == players.end()) {
players.push_back(player);
}
updateSleepingPlayerList();
}
MemSect(42);
getChunk(xc, zc)->addEntity(e);
MemSect(0);
EnterCriticalSection(&m_entitiesCS);
MemSect(43);
entities.push_back(e);
MemSect(0);
LeaveCriticalSection(&m_entitiesCS);
MemSect(44);
entityAdded(e);
MemSect(0);
return true;
}
return false;
}
#pragma optimize("", on)
void Level::entityAdded(std::shared_ptr<Entity> e) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->entityAdded(e);
}
}
void Level::entityRemoved(std::shared_ptr<Entity> e) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->entityRemoved(e);
}
}
// 4J added
void Level::playerRemoved(std::shared_ptr<Entity> e) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->playerRemoved(e);
}
}
void Level::removeEntity(std::shared_ptr<Entity> e) {
if (e->rider.lock() != NULL) {
e->rider.lock()->ride(nullptr);
}
if (e->riding != NULL) {
e->ride(nullptr);
}
e->remove();
if (std::dynamic_pointer_cast<Player>(e) != NULL) {
std::vector<std::shared_ptr<Player> >::iterator it = players.begin();
std::vector<std::shared_ptr<Player> >::iterator itEnd = players.end();
while (it != itEnd && *it != std::dynamic_pointer_cast<Player>(e)) it++;
if (it != itEnd) {
players.erase(it);
}
updateSleepingPlayerList();
playerRemoved(e); // 4J added - this will let the entity tracker know
// that we have actually removed the player from the
// level's player list
}
}
void Level::removeEntityImmediately(std::shared_ptr<Entity> e) {
e->remove();
if (std::dynamic_pointer_cast<Player>(e) != NULL) {
std::vector<std::shared_ptr<Player> >::iterator it = players.begin();
std::vector<std::shared_ptr<Player> >::iterator itEnd = players.end();
while (it != itEnd && *it != std::dynamic_pointer_cast<Player>(e)) it++;
if (it != itEnd) {
players.erase(it);
}
updateSleepingPlayerList();
playerRemoved(e); // 4J added - this will let the entity tracker know
// that we have actually removed the player from the
// level's player list
}
int xc = e->xChunk;
int zc = e->zChunk;
if (e->inChunk && hasChunk(xc, zc)) {
getChunk(xc, zc)->removeEntity(e);
}
EnterCriticalSection(&m_entitiesCS);
std::vector<std::shared_ptr<Entity> >::iterator it = entities.begin();
std::vector<std::shared_ptr<Entity> >::iterator endIt = entities.end();
while (it != endIt && *it != e) it++;
if (it != endIt) {
entities.erase(it);
}
LeaveCriticalSection(&m_entitiesCS);
entityRemoved(e);
}
void Level::addListener(LevelListener* listener) {
listeners.push_back(listener);
}
void Level::removeListener(LevelListener* listener) {
std::vector<LevelListener*>::iterator it = listeners.begin();
std::vector<LevelListener*>::iterator itEnd = listeners.end();
while (it != itEnd && *it != listener) it++;
if (it != itEnd) listeners.erase(it);
}
// 4J - added noEntities and blockAtEdge parameter
AABBList* Level::getCubes(std::shared_ptr<Entity> source, AABB* box,
bool noEntities, bool blockAtEdge) {
boxes.clear();
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
int maxxz = (dimension->getXZSize() * 16) / 2;
int minxz = -maxxz;
for (int x = x0; x < x1; x++)
for (int z = z0; z < z1; z++) {
// 4J - If we are outside the map, return solid AABBs (rock is a bit
// of an arbitrary choice here, just need a correct AABB)
if (blockAtEdge &&
((x < minxz) || (x >= maxxz) || (z < minxz) || (z >= maxxz))) {
for (int y = y0 - 1; y < y1; y++) {
Tile::rock->addAABBs(this, x, y, z, box, &boxes, source);
}
} else {
if (hasChunkAt(x, 64, z)) {
for (int y = y0 - 1; y < y1; y++) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL) {
tile->addAABBs(this, x, y, z, box, &boxes, source);
}
}
}
}
}
// 4J - also stop player falling out of the bottom of the map if blockAtEdge
// is true. Again, rock is an arbitrary choice here 4J Stu - Don't stop
// entities falling into the void while in The End (it has no bedrock)
if (blockAtEdge && ((y0 - 1) < 0) && dimension->id != 1) {
for (int y = y0 - 1; y < 0; y++) {
for (int x = x0; x < x1; x++)
for (int z = z0; z < z1; z++) {
Tile::rock->addAABBs(this, x, y, z, box, &boxes, source);
}
}
}
// 4J - final bounds check - limit vertical movement so we can't move above
// maxMovementHeight
if (blockAtEdge && (y1 > maxMovementHeight)) {
for (int y = maxMovementHeight; y < y1; y++) {
for (int x = x0; x < x1; x++)
for (int z = z0; z < z1; z++) {
Tile::rock->addAABBs(this, x, y, z, box, &boxes, source);
}
}
}
// 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 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 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.
Minecraft::GetInstance()->levelRenderer->destroyedTileManager->addAABBs(
this, box, &boxes);
// 4J - added
if (noEntities) return &boxes;
double r = 0.25;
std::vector<std::shared_ptr<Entity> >* ee =
getEntities(source, box->grow(r, r, r));
std::vector<std::shared_ptr<Entity> >::iterator itEnd = ee->end();
for (AUTO_VAR(it, ee->begin()); it != itEnd; it++) {
AABB* collideBox = (*it)->getCollideBox();
if (collideBox != NULL && collideBox->intersects(box)) {
boxes.push_back(collideBox);
}
collideBox = source->getCollideAgainstBox(*it);
if (collideBox != NULL && collideBox->intersects(box)) {
boxes.push_back(collideBox);
}
}
return &boxes;
}
// 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting
// the minecart in a tight corridor damages the player
AABBList* Level::getTileCubes(AABB* box, bool blockAtEdge) {
return getCubes(nullptr, box, true, blockAtEdge);
// boxes.clear();
// int x0 = Mth::floor(box->x0);
// int x1 = Mth::floor(box->x1 + 1);
// int y0 = Mth::floor(box->y0);
// int y1 = Mth::floor(box->y1 + 1);
// int z0 = Mth::floor(box->z0);
// int z1 = Mth::floor(box->z1 + 1);
// for (int x = x0; x < x1; x++)
//{
// for (int z = z0; z < z1; z++)
// {
// if (hasChunkAt(x, 64, z))
// {
// for (int y = y0 - 1; y < y1; y++)
// {
// Tile *tile = Tile::tiles[getTile(x, y, z)];
// if (tile != NULL)
// {
// tile->addAABBs(this, x, y, z, box,
//&boxes);
// }
// }
// }
// }
//}
// return boxes;
}
// 4J - change brought forward from 1.8.2
int Level::getOldSkyDarken(float a) {
float td = getTimeOfDay(a);
float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.5f);
if (br < 0.0f) br = 0.0f;
if (br > 1.0f) br = 1.0f;
br = 1 - br;
br *= 1 - (getRainLevel(a) * 5 / 16.0f);
br *= 1 - (getThunderLevel(a) * 5 / 16.0f);
br = 1 - br;
return ((int)(br * 11));
}
// 4J - change brought forward from 1.8.2
float Level::getSkyDarken(float a) {
float td = getTimeOfDay(a);
float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.2f);
if (br < 0.0f) br = 0.0f;
if (br > 1.0f) br = 1.0f;
br = 1.0f - br;
br *= 1.0f - (getRainLevel(a) * 5.0f / 16.0f);
br *= 1.0f - (getThunderLevel(a) * 5.0f / 16.0f);
// return ((int) (br * 13));
return br * 0.8f + 0.2f;
}
Vec3* Level::getSkyColor(std::shared_ptr<Entity> source, float a) {
float td = getTimeOfDay(a);
float br = Mth::cos(td * PI * 2) * 2 + 0.5f;
if (br < 0.0f) br = 0.0f;
if (br > 1.0f) br = 1.0f;
int xx = Mth::floor(source->x);
int zz = Mth::floor(source->z);
Biome* biome = getBiome(xx, zz);
float temp = biome->getTemperature();
int skyColor = biome->getSkyColor(temp);
float r = ((skyColor >> 16) & 0xff) / 255.0f;
float g = ((skyColor >> 8) & 0xff) / 255.0f;
float b = ((skyColor) & 0xff) / 255.0f;
r *= br;
g *= br;
b *= br;
float rainLevel = getRainLevel(a);
if (rainLevel > 0) {
float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.6f;
float ba = 1 - rainLevel * 0.75f;
r = r * ba + mid * (1 - ba);
g = g * ba + mid * (1 - ba);
b = b * ba + mid * (1 - ba);
}
float thunderLevel = getThunderLevel(a);
if (thunderLevel > 0) {
float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.2f;
float ba = 1 - thunderLevel * 0.75f;
r = r * ba + mid * (1 - ba);
g = g * ba + mid * (1 - ba);
b = b * ba + mid * (1 - ba);
}
if (lightningBoltTime > 0) {
float f = (lightningBoltTime - a);
if (f > 1) f = 1;
f = f * 0.45f;
r = r * (1 - f) + 0.8f * f;
g = g * (1 - f) + 0.8f * f;
b = b * (1 - f) + 1 * f;
}
return Vec3::newTemp(r, g, b);
}
float Level::getTimeOfDay(float a) {
/*
* 4J-PB removed line below - notch committed 1.6.6 with the incorrect
* getTimeOfDay and changed it before releasing (without
* re-committing)... that should be the only difference // jeb
*/
/* if (this != NULL) return 0.5f; */
// 4J Added if so we can override timeOfDay without changing the time that
// affects ticking of things
if (m_timeOfDayOverride >= 0) {
return dimension->getTimeOfDay(m_timeOfDayOverride, a);
} else {
return dimension->getTimeOfDay(levelData->getTime(), a);
;
}
}
int Level::getMoonPhase(float a) {
return dimension->getMoonPhase(levelData->getTime(), a);
}
float Level::getSunAngle(float a) {
float td = getTimeOfDay(a);
return td * PI * 2;
}
Vec3* Level::getCloudColor(float a) {
float td = getTimeOfDay(a);
float br = Mth::cos(td * PI * 2) * 2.0f + 0.5f;
if (br < 0.0f) br = 0.0f;
if (br > 1.0f) br = 1.0f;
int baseCloudColour = Minecraft::GetInstance()->getColourTable()->getColor(
eMinecraftColour_In_Cloud_Base_Colour);
float r = ((baseCloudColour >> 16) & 0xff) / 255.0f;
float g = ((baseCloudColour >> 8) & 0xff) / 255.0f;
float b = ((baseCloudColour) & 0xff) / 255.0f;
float rainLevel = getRainLevel(a);
if (rainLevel > 0) {
float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.6f;
float ba = 1 - rainLevel * 0.95f;
r = r * ba + mid * (1 - ba);
g = g * ba + mid * (1 - ba);
b = b * ba + mid * (1 - ba);
}
r *= br * 0.90f + 0.10f;
g *= br * 0.90f + 0.10f;
b *= br * 0.85f + 0.15f;
float thunderLevel = getThunderLevel(a);
if (thunderLevel > 0) {
float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.2f;
float ba = 1 - thunderLevel * 0.95f;
r = r * ba + mid * (1 - ba);
g = g * ba + mid * (1 - ba);
b = b * ba + mid * (1 - ba);
}
return Vec3::newTemp(r, g, b);
}
Vec3* Level::getFogColor(float a) {
float td = getTimeOfDay(a);
return dimension->getFogColor(td, a);
}
int Level::getTopRainBlock(int x, int z) {
// 4J - optimisation brought forward from 1.8.2 - used to do full
// calculation here but result is now cached in LevelChunk
return getChunkAt(x, z)->getTopRainBlock(x & 15, z & 15);
}
// 4J added
bool Level::biomeHasRain(int x, int z) {
return getChunkAt(x, z)->biomeHasRain(x & 15, z & 15);
}
// 4J added
bool Level::biomeHasSnow(int x, int z) {
return getChunkAt(x, z)->biomeHasSnow(x & 15, z & 15);
}
int Level::getTopSolidBlock(int x, int z) {
LevelChunk* levelChunk = getChunkAt(x, z);
int y = levelChunk->getHighestSectionPosition() + 15;
x &= 15;
z &= 15;
while (y > 0) {
int t = levelChunk->getTile(x, y, z);
if (t == 0 || !(Tile::tiles[t]->material->blocksMotion()) ||
Tile::tiles[t]->material == Material::leaves) {
y--;
} else {
return y + 1;
}
}
return -1;
}
int Level::getLightDepth(int x, int z) {
return getChunkAt(x, z)->getHeightmap(x & 15, z & 15);
}
float Level::getStarBrightness(float a) {
float td = getTimeOfDay(a);
float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.25f);
if (br < 0.0f) br = 0.0f;
if (br > 1.0f) br = 1.0f;
return br * br * 0.5f;
}
void Level::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay) {}
void Level::forceAddTileTick(int x, int y, int z, int tileId, int tickDelay) {}
void Level::tickEntities() {
// for (int i = 0; i < globalEntities.size(); i++)
std::vector<std::shared_ptr<Entity> >::iterator itGE =
globalEntities.begin();
while (itGE != globalEntities.end()) {
std::shared_ptr<Entity> e = *itGE; // globalEntities.at(i);
e->tick();
if (e->removed) {
// globalEntities.remove(i--);
itGE = globalEntities.erase(itGE);
} else {
itGE++;
}
}
// entities.removeAll(entitiesToRemove);
EnterCriticalSection(&m_entitiesCS);
for (AUTO_VAR(it, entities.begin()); it != entities.end();) {
bool found = false;
for (AUTO_VAR(it2, entitiesToRemove.begin());
it2 != entitiesToRemove.end(); it2++) {
if ((*it) == (*it2)) {
found = true;
break;
}
}
if (found) {
it = entities.erase(it);
} else {
it++;
}
}
LeaveCriticalSection(&m_entitiesCS);
AUTO_VAR(itETREnd, entitiesToRemove.end());
for (AUTO_VAR(it, entitiesToRemove.begin()); it != itETREnd; it++) {
std::shared_ptr<Entity> e = *it; // entitiesToRemove.at(j);
int xc = e->xChunk;
int zc = e->zChunk;
if (e->inChunk && hasChunk(xc, zc)) {
getChunk(xc, zc)->removeEntity(e);
}
}
itETREnd = entitiesToRemove.end();
for (AUTO_VAR(it, entitiesToRemove.begin()); it != itETREnd; it++) {
entityRemoved(*it);
}
//
entitiesToRemove.clear();
// for (int i = 0; i < entities.size(); i++)
/* 4J Jev, using an iterator causes problems here as
* the vector is modified from inside this loop.
*/
EnterCriticalSection(&m_entitiesCS);
for (unsigned int i = 0; i < entities.size();) {
std::shared_ptr<Entity> e = entities.at(i);
if (e->riding != NULL) {
if (e->riding->removed || e->riding->rider.lock() != e) {
e->riding->rider = std::weak_ptr<Entity>();
e->riding = nullptr;
} else {
i++;
continue;
}
}
if (!e->removed) {
#ifndef _FINAL_BUILD
if (!(app.DebugSettingsOn() && app.GetMobsDontTickEnabled() &&
(std::dynamic_pointer_cast<Mob>(e) != NULL) &&
(std::dynamic_pointer_cast<Player>(e) == NULL)))
#endif
{
tick(e);
}
}
if (e->removed) {
int xc = e->xChunk;
int zc = e->zChunk;
if (e->inChunk && hasChunk(xc, zc)) {
getChunk(xc, zc)->removeEntity(e);
}
// entities.remove(i--);
// itE = entities.erase( itE );
// 4J Find the entity again before deleting, as things might have
// moved in the entity array eg from the explosion created by tnt
AUTO_VAR(it, find(entities.begin(), entities.end(), e));
if (it != entities.end()) {
entities.erase(it);
}
entityRemoved(e);
} else {
i++;
}
}
LeaveCriticalSection(&m_entitiesCS);
EnterCriticalSection(&m_tileEntityListCS);
updatingTileEntities = true;
for (AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end();) {
std::shared_ptr<TileEntity> te =
*it; // tilestd::vector<std::shared_ptr<Entity> >.at(i);
if (!te->isRemoved() && te->hasLevel()) {
if (hasChunkAt(te->x, te->y, te->z)) {
#ifdef _LARGE_WORLDS
LevelChunk* lc = getChunk(te->x >> 4, te->z >> 4);
if (!isClientSide || !lc->isUnloaded())
#endif
{
te->tick();
}
}
}
if (te->isRemoved()) {
it = tileEntityList.erase(it);
if (hasChunk(te->x >> 4, te->z >> 4)) {
LevelChunk* lc = getChunk(te->x >> 4, te->z >> 4);
if (lc != NULL)
lc->removeTileEntity(te->x & 15, te->y, te->z & 15);
}
} else {
it++;
}
}
updatingTileEntities = false;
// 4J-PB - Stuart - check this is correct here
if (!tileEntitiesToUnload.empty()) {
// tileEntityList.removeAll(tileEntitiesToUnload);
for (AUTO_VAR(it, tileEntityList.begin());
it != tileEntityList.end();) {
bool found = false;
for (AUTO_VAR(it2, tileEntitiesToUnload.begin());
it2 != tileEntitiesToUnload.end(); it2++) {
if ((*it) == (*it2)) {
found = true;
break;
}
}
if (found) {
if (isClientSide) {
__debugbreak();
}
it = tileEntityList.erase(it);
} else {
it++;
}
}
tileEntitiesToUnload.clear();
}
if (!pendingTileEntities.empty()) {
for (AUTO_VAR(it, pendingTileEntities.begin());
it != pendingTileEntities.end(); it++) {
std::shared_ptr<TileEntity> e = *it;
if (!e->isRemoved()) {
if (find(tileEntityList.begin(), tileEntityList.end(), e) ==
tileEntityList.end()) {
tileEntityList.push_back(e);
}
if (hasChunk(e->x >> 4, e->z >> 4)) {
LevelChunk* lc = getChunk(e->x >> 4, e->z >> 4);
if (lc != NULL)
lc->setTileEntity(e->x & 15, e->y, e->z & 15, e);
}
sendTileUpdated(e->x, e->y, e->z);
}
}
pendingTileEntities.clear();
}
LeaveCriticalSection(&m_tileEntityListCS);
}
void Level::addAllPendingTileEntities(
std::vector<std::shared_ptr<TileEntity> >& entities) {
EnterCriticalSection(&m_tileEntityListCS);
if (updatingTileEntities) {
for (AUTO_VAR(it, entities.begin()); it != entities.end(); it++) {
pendingTileEntities.push_back(*it);
}
} else {
for (AUTO_VAR(it, entities.begin()); it != entities.end(); it++) {
tileEntityList.push_back(*it);
}
}
LeaveCriticalSection(&m_tileEntityListCS);
}
void Level::tick(std::shared_ptr<Entity> e) { tick(e, true); }
void Level::tick(std::shared_ptr<Entity> e, bool actual) {
int xc = Mth::floor(e->x);
int zc = Mth::floor(e->z);
int r = 32;
#ifdef __PSVITA__
// AP - make sure the dragon ticks all the time, even when there aren't any
// chunks.
if (actual && e->GetType() != eTYPE_ENDERDRAGON &&
!hasChunksAt(xc - r, 0, zc - r, xc + r, 0, zc + r))
#else
if (actual && !hasChunksAt(xc - r, 0, zc - r, xc + r, 0, zc + r))
#endif
{
return;
}
e->xOld = e->x;
e->yOld = e->y;
e->zOld = e->z;
e->yRotO = e->yRot;
e->xRotO = e->xRot;
#ifdef __PSVITA__
// AP - make sure the dragon ticks all the time, even when there aren't any
// chunks.
if (actual && (e->GetType() == eTYPE_ENDERDRAGON || e->inChunk))
#else
if (actual && e->inChunk)
#endif
{
if (e->riding != NULL) {
e->rideTick();
} else {
e->tick();
}
}
// SANTITY!!
if (Double::isNaN(e->x) || Double::isInfinite(e->x)) e->x = e->xOld;
if (Double::isNaN(e->y) || Double::isInfinite(e->y)) e->y = e->yOld;
if (Double::isNaN(e->z) || Double::isInfinite(e->z)) e->z = e->zOld;
if (Double::isNaN(e->xRot) || Double::isInfinite(e->xRot))
e->xRot = e->xRotO;
if (Double::isNaN(e->yRot) || Double::isInfinite(e->yRot))
e->yRot = e->yRotO;
int xcn = Mth::floor(e->x / 16);
int ycn = Mth::floor(e->y / 16);
int zcn = Mth::floor(e->z / 16);
if (!e->inChunk ||
(e->xChunk != xcn || e->yChunk != ycn || e->zChunk != zcn)) {
if (e->inChunk && hasChunk(e->xChunk, e->zChunk)) {
getChunk(e->xChunk, e->zChunk)->removeEntity(e, e->yChunk);
}
if (hasChunk(xcn, zcn)) {
e->inChunk = true;
MemSect(39);
getChunk(xcn, zcn)->addEntity(e);
MemSect(0);
} else {
e->inChunk = false;
// e.remove();
}
}
if (actual && e->inChunk) {
if (e->rider.lock() != NULL) {
if (e->rider.lock()->removed || e->rider.lock()->riding != e) {
e->rider.lock()->riding = nullptr;
e->rider = std::weak_ptr<Entity>();
} else {
tick(e->rider.lock());
}
}
}
}
bool Level::isUnobstructed(AABB* aabb) { return isUnobstructed(aabb, nullptr); }
bool Level::isUnobstructed(AABB* aabb, std::shared_ptr<Entity> ignore) {
std::vector<std::shared_ptr<Entity> >* ents = getEntities(nullptr, aabb);
AUTO_VAR(itEnd, ents->end());
for (AUTO_VAR(it, ents->begin()); it != itEnd; it++) {
std::shared_ptr<Entity> e = *it;
if (!e->removed && e->blocksBuilding && e != ignore) return false;
}
return true;
}
bool Level::containsAnyBlocks(AABB* box) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
if (box->x0 < 0) x0--;
if (box->y0 < 0) y0--;
if (box->z0 < 0) z0--;
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL) {
return true;
}
}
return false;
}
bool Level::containsAnyLiquid(AABB* box) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
if (box->x0 < 0) x0--;
if (box->y0 < 0) y0--;
if (box->z0 < 0) z0--;
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL && tile->material->isLiquid()) {
return true;
}
}
return false;
}
// 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 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 we haven't already got, and be
// cautious about placing the mob's.
bool Level::containsAnyLiquid_NoLoad(AABB* box) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
if (box->x0 < 0) x0--;
if (box->y0 < 0) y0--;
if (box->z0 < 0) z0--;
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
if (!hasChunkAt(x, y, z))
return true; // If we don't have it, it might be liquid...
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL && tile->material->isLiquid()) {
return true;
}
}
return false;
}
bool Level::containsFireTile(AABB* box) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
if (hasChunksAt(x0, y0, z0, x1, y1, z1)) {
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
int t = getTile(x, y, z);
if (t == Tile::fire_Id || t == Tile::lava_Id ||
t == Tile::calmLava_Id)
return true;
}
}
return false;
}
bool Level::checkAndHandleWater(AABB* box, Material* material,
std::shared_ptr<Entity> e) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
if (!hasChunksAt(x0, y0, z0, x1, y1, z1)) {
return false;
}
bool ok = false;
Vec3* current = Vec3::newTemp(0, 0, 0);
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL && tile->material == material) {
double yt0 =
y + 1 - LiquidTile::getHeight(getData(x, y, z));
if (y1 >= yt0) {
ok = true;
tile->handleEntityInside(this, x, y, z, e, current);
}
}
}
if (current->length() > 0) {
current = current->normalize();
double pow = 0.014;
e->xd += current->x * pow;
e->yd += current->y * pow;
e->zd += current->z * pow;
}
return ok;
}
bool Level::containsMaterial(AABB* box, Material* material) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL && tile->material == material) {
return true;
}
}
return false;
}
bool Level::containsLiquid(AABB* box, Material* material) {
int x0 = Mth::floor(box->x0);
int x1 = Mth::floor(box->x1 + 1);
int y0 = Mth::floor(box->y0);
int y1 = Mth::floor(box->y1 + 1);
int z0 = Mth::floor(box->z0);
int z1 = Mth::floor(box->z1 + 1);
for (int x = x0; x < x1; x++)
for (int y = y0; y < y1; y++)
for (int z = z0; z < z1; z++) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile != NULL && tile->material == material) {
int data = getData(x, y, z);
double yh1 = y + 1;
if (data < 8) {
yh1 = y + 1 - data / 8.0;
}
if (yh1 >= box->y0) {
return true;
}
}
}
return false;
}
std::shared_ptr<Explosion> Level::explode(std::shared_ptr<Entity> source,
double x, double y, double z, float r,
bool destroyBlocks) {
return explode(source, x, y, z, r, false, destroyBlocks);
}
std::shared_ptr<Explosion> Level::explode(std::shared_ptr<Entity> source,
double x, double y, double z, float r,
bool fire, bool destroyBlocks) {
std::shared_ptr<Explosion> explosion =
std::shared_ptr<Explosion>(new Explosion(this, source, x, y, z, r));
explosion->fire = fire;
explosion->destroyBlocks = destroyBlocks;
explosion->explode();
explosion->finalizeExplosion(true);
return explosion;
}
float Level::getSeenPercent(Vec3* center, AABB* bb) {
double xs = 1.0 / ((bb->x1 - bb->x0) * 2 + 1);
double ys = 1.0 / ((bb->y1 - bb->y0) * 2 + 1);
double zs = 1.0 / ((bb->z1 - bb->z0) * 2 + 1);
int hits = 0;
int count = 0;
for (double xx = 0; xx <= 1;
xx += xs) // 4J Stu - xx, yy and zz were floats, made them doubles to
// remove warnings
for (double yy = 0; yy <= 1; yy += ys)
for (double zz = 0; zz <= 1; zz += zs) {
double x = bb->x0 + (bb->x1 - bb->x0) * xx;
double y = bb->y0 + (bb->y1 - bb->y0) * yy;
double z = bb->z0 + (bb->z1 - bb->z0) * zz;
HitResult* res = clip(Vec3::newTemp(x, y, z), center);
if (res == NULL) hits++;
delete res;
count++;
}
return hits / (float)count;
}
bool Level::extinguishFire(std::shared_ptr<Player> player, int x, int y, int z,
int face) {
if (face == 0) y--;
if (face == 1) y++;
if (face == 2) z--;
if (face == 3) z++;
if (face == 4) x--;
if (face == 5) x++;
if (getTile(x, y, z) == Tile::fire_Id) {
levelEvent(player, LevelEvent::SOUND_FIZZ, x, y, z, 0);
setTile(x, y, z, 0);
return true;
}
return false;
}
/*
std::shared_ptr<Entity> Level::findSubclassOf(Entity::Class *entityClass)
{
return std::shared_ptr<Entity>();
}
*/
std::wstring Level::gatherStats() {
wchar_t buf[64];
EnterCriticalSection(&m_entitiesCS);
swprintf(buf, 64, L"All:%d", this->entities.size());
LeaveCriticalSection(&m_entitiesCS);
return std::wstring(buf);
}
std::wstring Level::gatherChunkSourceStats() {
return chunkSource->gatherStats();
}
std::shared_ptr<TileEntity> Level::getTileEntity(int x, int y, int z) {
if (y >= Level::maxBuildHeight) {
return nullptr;
}
LevelChunk* lc = getChunk(x >> 4, z >> 4);
if (lc != NULL) return lc->getTileEntity(x & 15, y, z & 15);
if (lc != NULL) {
std::shared_ptr<TileEntity> tileEntity =
lc->getTileEntity(x & 15, y, z & 15);
if (tileEntity == NULL) {
EnterCriticalSection(&m_tileEntityListCS);
for (AUTO_VAR(it, pendingTileEntities.begin());
it != pendingTileEntities.end(); it++) {
std::shared_ptr<TileEntity> e = *it;
if (!e->isRemoved() && e->x == x && e->y == y && e->z == z) {
tileEntity = e;
break;
}
}
LeaveCriticalSection(&m_tileEntityListCS);
}
return tileEntity;
}
return nullptr;
}
void Level::setTileEntity(int x, int y, int z,
std::shared_ptr<TileEntity> tileEntity) {
if (tileEntity != NULL && !tileEntity->isRemoved()) {
EnterCriticalSection(&m_tileEntityListCS);
if (updatingTileEntities) {
tileEntity->x = x;
tileEntity->y = y;
tileEntity->z = z;
pendingTileEntities.push_back(tileEntity);
} else {
tileEntityList.push_back(tileEntity);
LevelChunk* lc = getChunk(x >> 4, z >> 4);
if (lc != NULL) lc->setTileEntity(x & 15, y, z & 15, tileEntity);
}
LeaveCriticalSection(&m_tileEntityListCS);
}
}
void Level::removeTileEntity(int x, int y, int z) {
EnterCriticalSection(&m_tileEntityListCS);
std::shared_ptr<TileEntity> te = getTileEntity(x, y, z);
if (te != NULL && updatingTileEntities) {
te->setRemoved();
AUTO_VAR(it, find(pendingTileEntities.begin(),
pendingTileEntities.end(), te));
if (it != pendingTileEntities.end()) {
pendingTileEntities.erase(it);
}
} else {
if (te != NULL) {
AUTO_VAR(it, find(pendingTileEntities.begin(),
pendingTileEntities.end(), te));
if (it != pendingTileEntities.end()) {
pendingTileEntities.erase(it);
}
AUTO_VAR(it2,
find(tileEntityList.begin(), tileEntityList.end(), te));
if (it2 != tileEntityList.end()) {
tileEntityList.erase(it2);
}
}
LevelChunk* lc = getChunk(x >> 4, z >> 4);
if (lc != NULL) lc->removeTileEntity(x & 15, y, z & 15);
}
LeaveCriticalSection(&m_tileEntityListCS);
}
void Level::markForRemoval(std::shared_ptr<TileEntity> entity) {
tileEntitiesToUnload.push_back(entity);
}
bool Level::isSolidRenderTile(int x, int y, int z) {
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile == NULL) return false;
// 4J - addition here to make rendering big blocks of leaves more efficient.
// Normally leaves never consider themselves as solid, so blocks of leaves
// will have all sides of each block completely visible. Changing to
// consider as solid if this block is surrounded by other leaves (or solid
// things). This is paired with another change in Tile::getTexture which
// makes such solid tiles actually visibly solid (these textures exist
// already for non-fancy graphics). Note: this tile-specific code is here
// rather than making some new virtual method in the tiles, for the sake of
// efficiency - I don't imagine we'll be doing much more of this sort of
// thing
if (tile->id == Tile::leaves_Id) {
int axo[6] = {1, -1, 0, 0, 0, 0};
int ayo[6] = {0, 0, 1, -1, 0, 0};
int azo[6] = {0, 0, 0, 0, 1, -1};
for (int i = 0; i < 6; i++) {
int t = getTile(x + axo[i], y + ayo[i], z + azo[i]);
if ((t != Tile::leaves_Id) && ((Tile::tiles[t] == NULL) ||
!Tile::tiles[t]->isSolidRender())) {
return false;
}
}
return true;
}
return tile->isSolidRender(!isClientSide);
}
bool Level::isSolidBlockingTile(int x, int y, int z) {
return Tile::isSolidBlockingTile(getTile(x, y, z));
}
/**
* This method does the same as isSolidBlockingTile, except it will not
* check the tile if the coordinates is in an unloaded or empty chunk. This
* is to help vs the problem of "popping" torches in SMP.
*/
bool Level::isSolidBlockingTileInLoadedChunk(int x, int y, int z,
bool valueIfNotLoaded) {
if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE ||
z >= MAX_LEVEL_SIZE) {
return valueIfNotLoaded;
}
LevelChunk* chunk = chunkSource->getChunk(x >> 4, z >> 4);
if (chunk == NULL || chunk->isEmpty()) {
return valueIfNotLoaded;
}
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile == NULL) return false;
return tile->material->isSolidBlocking() && tile->isCubeShaped();
}
// 4J - brought forward from 1.3.2
bool Level::isTopSolidBlocking(int x, int y, int z) {
// Temporary workaround until tahgs per-face solidity is finished
Tile* tile = Tile::tiles[getTile(x, y, z)];
if (tile == NULL) return false;
if (tile->material->isSolidBlocking() && tile->isCubeShaped()) return true;
if (dynamic_cast<StairTile*>(tile) != NULL) {
return (getData(x, y, z) & StairTile::UPSIDEDOWN_BIT) ==
StairTile::UPSIDEDOWN_BIT;
}
if (dynamic_cast<HalfSlabTile*>(tile) != NULL) {
return (getData(x, y, z) & HalfSlabTile::TOP_SLOT_BIT) ==
HalfSlabTile::TOP_SLOT_BIT;
}
if (dynamic_cast<TopSnowTile*>(tile) != NULL)
return (getData(x, y, z) & TopSnowTile::HEIGHT_MASK) ==
TopSnowTile::MAX_HEIGHT + 1;
return false;
}
void Level::updateSkyBrightness() {
int newDark = this->getOldSkyDarken(1);
if (newDark != skyDarken) {
skyDarken = newDark;
}
}
void Level::setSpawnSettings(bool spawnEnemies, bool spawnFriendlies) {
this->spawnEnemies = spawnEnemies;
this->spawnFriendlies = spawnFriendlies;
}
void Level::tick() {
PIXBeginNamedEvent(0, "Weather tick");
tickWeather();
PIXEndNamedEvent();
}
void Level::prepareWeather() {
if (levelData->isRaining()) {
this->rainLevel = 1;
if (levelData->isThundering()) {
this->thunderLevel = 1;
}
}
}
void Level::tickWeather() {
if (dimension->hasCeiling) return;
#ifndef _FINAL_BUILD
// debug setting added to disable weather
if (app.DebugSettingsOn()) {
if (app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()) &
(1L << eDebugSetting_DisableWeather)) {
levelData->setThundering(false);
levelData->setThunderTime(random->nextInt(TICKS_PER_DAY * 7) +
TICKS_PER_DAY / 2);
levelData->setRaining(false);
levelData->setRainTime(random->nextInt(TICKS_PER_DAY * 7) +
TICKS_PER_DAY / 2);
}
}
#endif
if (lightningTime > 0) {
lightningTime--;
}
int thunderTime = levelData->getThunderTime();
if (thunderTime <= 0) {
if (levelData->isThundering()) {
levelData->setThunderTime(random->nextInt(20 * 60 * 10) +
20 * 60 * 3);
} else {
levelData->setThunderTime(random->nextInt(TICKS_PER_DAY * 7) +
TICKS_PER_DAY / 2);
}
} else {
thunderTime--;
levelData->setThunderTime(thunderTime);
if (thunderTime <= 0) {
levelData->setThundering(!levelData->isThundering());
}
}
int rainTime = levelData->getRainTime();
if (rainTime <= 0) {
if (levelData->isRaining()) {
levelData->setRainTime(random->nextInt(TICKS_PER_DAY / 2) +
TICKS_PER_DAY / 2);
} else {
levelData->setRainTime(random->nextInt(TICKS_PER_DAY * 7) +
TICKS_PER_DAY / 2);
}
} else {
rainTime--;
levelData->setRainTime(rainTime);
if (rainTime <= 0) {
levelData->setRaining(!levelData->isRaining());
}
/* if( !levelData->isRaining() )
{
levelData->setRaining(true);
}*/
}
oRainLevel = rainLevel;
if (levelData->isRaining()) {
rainLevel += 0.01;
} else {
rainLevel -= 0.01;
}
if (rainLevel < 0) rainLevel = 0;
if (rainLevel > 1) rainLevel = 1;
oThunderLevel = thunderLevel;
if (levelData->isThundering()) {
thunderLevel += 0.01;
} else {
thunderLevel -= 0.01;
}
if (thunderLevel < 0) thunderLevel = 0;
if (thunderLevel > 1) thunderLevel = 1;
}
void Level::toggleDownfall() {
// this will trick the tickWeather method to toggle rain next tick
levelData->setRainTime(1);
}
void Level::buildAndPrepareChunksToPoll() {
#if 0
AUTO_VAR(itEnd, players.end());
for (AUTO_VAR(it, players.begin()); it != itEnd; it++)
{
std::shared_ptr<Player> player = *it;
int xx = Mth::floor(player->x / 16);
int zz = Mth::floor(player->z / 16);
int r = CHUNK_POLL_RANGE;
for (int x = -r; x <= r; x++)
for (int z = -r; z <= r; z++)
{
chunksToPoll.insert(ChunkPos(x + xx, z + zz));
}
}
#else
// 4J - rewritten to add chunks interleaved by player, and to add them from
// the centre outwards. We're going to be potentially adding less creatures
// than the original so that our count stays consistent with number of
// players added, so we want to make sure as best we can that the ones we do
// add are near the active players
int playerCount = (int)players.size();
int* xx = new int[playerCount];
int* zz = new int[playerCount];
for (int i = 0; i < playerCount; i++) {
std::shared_ptr<Player> player = players[i];
xx[i] = Mth::floor(player->x / 16);
zz[i] = Mth::floor(player->z / 16);
chunksToPoll.insert(ChunkPos(xx[i], zz[i]));
}
for (int r = 1; r <= 9; r++) {
for (int l = 0; l < (r * 2); l++) {
for (int i = 0; i < playerCount; i++) {
chunksToPoll.insert(ChunkPos((xx[i] - r) + l, (zz[i] - r)));
chunksToPoll.insert(ChunkPos((xx[i] + r), (zz[i] - r) + l));
chunksToPoll.insert(ChunkPos((xx[i] + r) - l, (zz[i] + r)));
chunksToPoll.insert(ChunkPos((xx[i] - r), (zz[i] + r) - l));
}
}
}
delete[] xx;
delete[] zz;
#endif
if (delayUntilNextMoodSound > 0) delayUntilNextMoodSound--;
// 4J Stu - Added 1.2.3, but not sure if we want to do it
// util.Timer.push("playerCheckLight");
//// randomly check areas around the players
// if (!players.isEmpty()) {
// int select = random.nextInt(players.size());
// Player player = players.get(select);
// int px = Mth.floor(player.x) + random.nextInt(11) - 5;
// int py = Mth.floor(player.y) + random.nextInt(11) - 5;
// int pz = Mth.floor(player.z) + random.nextInt(11) - 5;
// checkLight(px, py, pz);
// }
// util.Timer.pop();
}
void Level::tickClientSideTiles(int xo, int zo, LevelChunk* lc) {
// lc->tick(); // 4J - brought this lighting update forward from 1.8.2
if (delayUntilNextMoodSound == 0) {
randValue = (unsigned)randValue * 3 + (unsigned)addend;
int val = (randValue >> 2);
int x = (val & 15);
int z = ((val >> 8) & 15);
int y = ((val >> 16) & genDepthMinusOne);
int id = lc->getTile(x, y, z);
x += xo;
z += zo;
if (id == 0 &&
this->getDaytimeRawBrightness(x, y, z) <= random->nextInt(8) &&
getBrightness(LightLayer::Sky, x, y, z) <= 0) {
std::shared_ptr<Player> player =
getNearestPlayer(x + 0.5, y + 0.5, z + 0.5, 8);
if (player != NULL &&
player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 2 * 2) {
// 4J-PB - Fixed issue with cave audio event having 2 sounds at
// 192k
#ifdef _XBOX
this->playSound(x + 0.5, y + 0.5, z + 0.5,
eSoundType_AMBIENT_CAVE_CAVE2, 0.7f,
0.8f + random->nextFloat() * 0.2f);
#else
this->playSound(x + 0.5, y + 0.5, z + 0.5,
eSoundType_AMBIENT_CAVE_CAVE, 0.7f,
0.8f + random->nextFloat() * 0.2f);
#endif
delayUntilNextMoodSound =
random->nextInt(20 * 60 * 10) + 20 * 60 * 5;
}
}
}
// 4J Stu - Added 1.2.3, but do we need it?
// lc->checkNextLight();
}
void Level::tickTiles() { buildAndPrepareChunksToPoll(); }
bool Level::shouldFreezeIgnoreNeighbors(int x, int y, int z) {
return shouldFreeze(x, y, z, false);
}
bool Level::shouldFreeze(int x, int y, int z) {
return shouldFreeze(x, y, z, true);
}
bool Level::shouldFreeze(int x, int y, int z, bool checkNeighbors) {
Biome* biome = getBiome(x, z);
float temp = biome->getTemperature();
if (temp > 0.15f) return false;
if (y >= 0 && y < maxBuildHeight &&
getBrightness(LightLayer::Block, x, y, z) < 10) {
int current = getTile(x, y, z);
if ((current == Tile::calmWater_Id || current == Tile::water_Id) &&
getData(x, y, z) == 0) {
if (!checkNeighbors) return true;
bool surroundedByWater = true;
if (surroundedByWater &&
getMaterial(x - 1, y, z) != Material::water)
surroundedByWater = false;
if (surroundedByWater &&
getMaterial(x + 1, y, z) != Material::water)
surroundedByWater = false;
if (surroundedByWater &&
getMaterial(x, y, z - 1) != Material::water)
surroundedByWater = false;
if (surroundedByWater &&
getMaterial(x, y, z + 1) != Material::water)
surroundedByWater = false;
if (!surroundedByWater) return true;
}
}
return false;
}
bool Level::shouldSnow(int x, int y, int z) {
Biome* biome = getBiome(x, z);
float temp = biome->getTemperature();
if (temp > 0.15f) return false;
if (y >= 0 && y < maxBuildHeight &&
getBrightness(LightLayer::Block, x, y, z) < 10) {
int below = getTile(x, y - 1, z);
int current = getTile(x, y, z);
if (current == 0) {
if (Tile::topSnow->mayPlace(this, x, y, z) &&
(below != 0 && below != Tile::ice_Id &&
Tile::tiles[below]->material->blocksMotion())) {
return true;
}
}
}
return false;
}
void Level::checkLight(
int x, int y, int z, bool force,
bool rootOnlyEmissive) // 4J added force, rootOnlyEmissive parameters
{
if (!dimension->hasCeiling)
checkLight(LightLayer::Sky, x, y, z, force, false);
checkLight(LightLayer::Block, x, y, z, force, rootOnlyEmissive);
}
int Level::getExpectedSkyColor(lightCache_t* cache, int oc, int x, int y, int z,
int ct, int block) {
int expected = 0;
if (block == 255) return 0; // 4J added as optimisation
if (canSeeSky(x, y, z)) {
expected = 15;
} else {
if (block == 0) block = 1;
// 4J - changed this to attempt to get all 6 brightnesses of neighbours
// in a single call, as an optimisation
int b[6];
b[0] = getBrightnessCached(cache, LightLayer::Sky, x - 1, y, z);
b[1] = getBrightnessCached(cache, LightLayer::Sky, x + 1, y, z);
b[2] = getBrightnessCached(cache, LightLayer::Sky, x, y - 1, z);
b[3] = getBrightnessCached(cache, LightLayer::Sky, x, y + 1, z);
b[4] = getBrightnessCached(cache, LightLayer::Sky, x, y, z - 1);
b[5] = getBrightnessCached(cache, LightLayer::Sky, x, y, z + 1);
for (int i = 0; i < 6; i++) {
if ((b[i] - block) > expected) expected = b[i] - block;
}
}
return expected;
}
int Level::getExpectedBlockColor(lightCache_t* cache, int oc, int x, int y,
int z, int ct, int block,
bool propagatedOnly) {
int expected = propagatedOnly ? 0 : getEmissionCached(cache, ct, x, y, z);
if (block >= 15) return expected; // 4J added as optimisation
// 4J - changed this to attempt to get all 6 brightnesses of neighbours in a
// single call, as an optimisation
int b[6];
b[0] = getBrightnessCached(cache, LightLayer::Block, x - 1, y, z);
b[1] = getBrightnessCached(cache, LightLayer::Block, x + 1, y, z);
b[2] = getBrightnessCached(cache, LightLayer::Block, x, y - 1, z);
b[3] = getBrightnessCached(cache, LightLayer::Block, x, y + 1, z);
b[4] = getBrightnessCached(cache, LightLayer::Block, x, y, z - 1);
b[5] = getBrightnessCached(cache, LightLayer::Block, x, y, z + 1);
for (int i = 0; i < 6; i++) {
if ((b[i] - block) > expected) expected = b[i] - block;
}
return expected;
}
inline int GetIndex(int x, int y, int z) {
return ((x & 15) << 8) | ((y & 15) << 4) | (z & 15);
}
// 4J - Made changes here so that lighting goes through a cache, if enabled for
// this thread
void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc,
bool force, bool rootOnlyEmissive) {
lightCache_t* cache =
static_cast<lightCache_t*>(LevelTlsGetValue(tlsIdxLightCache));
__uint64 cacheUse = 0;
if (force) {
// 4J - special mode added so we can do lava lighting updates without
// having all neighbouring chunks loaded in
if (!hasChunksAt(xc, yc, zc, 0)) return;
} else {
// 4J - this is normal java behaviour
if (!hasChunksAt(xc, yc, zc, 17)) return;
}
#if 0
/////////////////////////////////////////////////////////////////////////////////////////////
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime1, qwDeltaTime2;
float fElapsedTime1 = 0.0f;
float fElapsedTime2 = 0.0f;
QueryPerformanceFrequency( &qwTicksPerSec );
float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
QueryPerformanceCounter( &qwTime );
/////////////////////////////////////////////////////////////////////////////////////////////
#endif
EnterCriticalSection(&m_checkLightCS);
#ifdef __PSVITA__
// AP - only clear the one array element required to check if something has
// changed
cachewritten = false;
if (cache != NULL) {
int idx;
if (!(yc & 0xffffff00)) {
idx = GetIndex(xc, yc, zc);
cache[idx] = 0;
idx = GetIndex(xc - 1, yc, zc);
cache[idx] = 0;
idx = GetIndex(xc + 1, yc, zc);
cache[idx] = 0;
idx = GetIndex(xc, yc, zc - 1);
cache[idx] = 0;
idx = GetIndex(xc, yc, zc + 1);
cache[idx] = 0;
}
if (!((yc - 1) & 0xffffff00)) {
idx = GetIndex(xc, yc - 1, zc);
cache[idx] = 0;
}
if (!((yc + 1) & 0xffffff00)) {
idx = GetIndex(xc, yc + 1, zc);
cache[idx] = 0;
}
}
#else
initCache(cache);
#endif
// 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.
int* toCheck;
if (cache == NULL) {
toCheck = toCheckLevel;
} else {
toCheck = (int*)(cache + (16 * 16 * 16));
}
int tcp = 0;
int tcc = 0;
// int darktcc = 0;
// 4J - added
int minXZ = -(dimension->getXZSize() * 16) / 2;
int maxXZ = (dimension->getXZSize() * 16) / 2 - 1;
if ((xc > maxXZ) || (xc < minXZ) || (zc > maxXZ) || (zc < minXZ)) {
LeaveCriticalSection(&m_checkLightCS);
return;
}
// 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)
// if( cache ) XLockL2(XLOCKL2_INDEX_TITLE, cache, 128 * 1024,
//XLOCKL2_LOCK_SIZE_1_WAY, 0 );
{
int cc = getBrightnessCached(cache, layer, xc, yc, zc);
int ex = 0;
{
int ct = 0;
int block = getBlockingCached(cache, layer, &ct, xc, yc, zc);
if (block == 0) block = 1;
int expected = 0;
if (layer == LightLayer::Sky) {
expected =
getExpectedSkyColor(cache, cc, xc, yc, zc, ct, block);
} else {
expected = getExpectedBlockColor(cache, cc, xc, yc, zc, ct,
block, false);
}
ex = expected;
}
#ifdef __PSVITA__
// AP - we only need to memset the entire array if we discover something
// has changed
if (ex != cc && cache) {
lightCache_t old[7];
if (!(yc & 0xffffff00)) {
old[0] = cache[GetIndex(xc, yc, zc)];
old[1] = cache[GetIndex(xc - 1, yc, zc)];
old[2] = cache[GetIndex(xc + 1, yc, zc)];
old[5] = cache[GetIndex(xc, yc, zc - 1)];
old[6] = cache[GetIndex(xc, yc, zc + 1)];
}
if (!((yc - 1) & 0xffffff00)) {
old[3] = cache[GetIndex(xc, yc - 1, zc)];
}
if (!((yc + 1) & 0xffffff00)) {
old[4] = cache[GetIndex(xc, yc + 1, zc)];
}
XMemSet128(cache, 0, 16 * 16 * 16 * sizeof(lightCache_t));
if (!(yc & 0xffffff00)) {
cache[GetIndex(xc, yc, zc)] = old[0];
cache[GetIndex(xc - 1, yc, zc)] = old[1];
cache[GetIndex(xc + 1, yc, zc)] = old[2];
cache[GetIndex(xc, yc, zc - 1)] = old[5];
cache[GetIndex(xc, yc, zc + 1)] = old[6];
}
if (!((yc - 1) & 0xffffff00)) {
cache[GetIndex(xc, yc - 1, zc)] = old[3];
}
if (!((yc + 1) & 0xffffff00)) {
cache[GetIndex(xc, yc + 1, zc)] = old[4];
}
}
#endif
if (ex > cc) {
toCheck[tcc++] = ((32)) + ((32) << 6) + ((32) << 12);
} else if (ex < cc) {
// 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.
// 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 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 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)
int tcn = 0;
if (layer == LightLayer::Block || true) {
toCheck[tcc++] =
((32)) + ((32) << 6) + ((32) << 12) + (cc << 18);
while (tcp < tcc) {
int p = toCheck[tcp++];
int x = ((p) & 63) - 32 + xc;
int y = ((p >> 6) & 63) - 32 + yc;
int z = ((p >> 12) & 63) - 32 + zc;
int cexp = ((p >> 18) & 15);
int o = getBrightnessCached(cache, layer, x, y, z);
if (o == cexp) {
setBrightnessCached(cache, &cacheUse, layer, x, y, z,
0);
// cexp--; // 4J - removed, change
// from 1.2.3
if (cexp > 0) {
int xd = x - xc;
int yd = y - yc;
int zd = z - zc;
if (xd < 0) xd = -xd;
if (yd < 0) yd = -yd;
if (zd < 0) zd = -zd;
if (xd + yd + zd < 17) {
bool edge = false;
for (int j = 0; j < 6; j++) {
int flip = j % 2 * 2 - 1;
int xx = x + ((j / 2) % 3 / 2) * flip;
int yy = y + ((j / 2 + 1) % 3 / 2) * flip;
int zz = z + ((j / 2 + 2) % 3 / 2) * flip;
// 4J - added - don't let this lighting
// creep out of the normal fixed world and
// into the infinite water chunks beyond
if ((xx > maxXZ) || (xx < minXZ) ||
(zz > maxXZ) || (zz < minXZ))
continue;
if ((yy < 0) || (yy >= maxBuildHeight))
continue;
o = getBrightnessCached(cache, layer, xx,
yy, zz);
// 4J - some changes here brought forward
// from 1.2.3
int block = getBlockingCached(
cache, layer, NULL, xx, yy, zz);
if (block == 0) block = 1;
if ((o == cexp - block) &&
(tcc <
(32 * 32 * 32))) // 4J - 32 * 32 * 32
// was toCheck.length
{
toCheck[tcc++] =
(((xx - xc) + 32)) +
(((yy - yc) + 32) << 6) +
(((zz - zc) + 32) << 12) +
((cexp - block) << 18);
} else {
// 4J - added - keep track of which
// tiles form the edge of the region we
// are zeroing
if (o > (cexp - block)) {
edge = true;
}
}
}
// 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
if (edge == true) {
toCheck[tcn++] = p;
}
}
}
}
}
}
tcp = 0;
// darktcc = tcc;
/////////////////////////////////////////////////////
tcc = 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
}
}
while (tcp < tcc) {
int p = toCheck[tcp++];
int x = ((p) & 63) - 32 + xc;
int y = ((p >> 6) & 63) - 32 + yc;
int z = ((p >> 12) & 63) - 32 + zc;
// 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
// don't want a lighting update to drag in any neighbouring chunks that
// aren't loaded yet.
if (force) {
if (!hasChunkAt(x, y, z)) {
continue;
}
}
int c = getBrightnessCached(cache, layer, x, y, z);
int ct = 0;
int block = getBlockingCached(cache, layer, &ct, x, y, z);
if (block == 0) block = 1;
int expected = 0;
if (layer == LightLayer::Sky) {
expected = getExpectedSkyColor(cache, c, x, y, z, ct, block);
} else {
// If rootOnlyEmissive flag is set, then only consider the starting
// tile to be possibly emissive.
bool propagatedOnly = false;
if (rootOnlyEmissive) {
propagatedOnly = (x != xc) || (y != yc) || (z != zc);
}
expected = getExpectedBlockColor(cache, c, x, y, z, ct, block,
propagatedOnly);
}
if (expected != c) {
setBrightnessCached(cache, &cacheUse, layer, x, y, z, expected);
if (expected > c) {
int xd = x - xc;
int yd = y - yc;
int zd = z - zc;
if (xd < 0) xd = -xd;
if (yd < 0) yd = -yd;
if (zd < 0) zd = -zd;
if (xd + yd + zd < 17 &&
tcc < (32 * 32 * 32) -
6) // 4J - 32 * 32 * 32 was toCheck.length
{
// 4J - added extra checks here to stop lighting updates
// moving out of the actual fixed world and into the
// infinite water chunks
if ((x - 1) >= minXZ) {
if (getBrightnessCached(cache, layer, x - 1, y, z) <
expected)
toCheck[tcc++] = (((x - 1 - xc) + 32)) +
(((y - yc) + 32) << 6) +
(((z - zc) + 32) << 12);
}
if ((x + 1) <= maxXZ) {
if (getBrightnessCached(cache, layer, x + 1, y, z) <
expected)
toCheck[tcc++] = (((x + 1 - xc) + 32)) +
(((y - yc) + 32) << 6) +
(((z - zc) + 32) << 12);
}
if ((y - 1) >= 0) {
if (getBrightnessCached(cache, layer, x, y - 1, z) <
expected)
toCheck[tcc++] = (((x - xc) + 32)) +
(((y - 1 - yc) + 32) << 6) +
(((z - zc) + 32) << 12);
}
if ((y + 1) < maxBuildHeight) {
if (getBrightnessCached(cache, layer, x, y + 1, z) <
expected)
toCheck[tcc++] = (((x - xc) + 32)) +
(((y + 1 - yc) + 32) << 6) +
(((z - zc) + 32) << 12);
}
if ((z - 1) >= minXZ) {
if (getBrightnessCached(cache, layer, x, y, z - 1) <
expected)
toCheck[tcc++] = (((x - xc) + 32)) +
(((y - yc) + 32) << 6) +
(((z - 1 - zc) + 32) << 12);
}
if ((z + 1) <= maxXZ) {
if (getBrightnessCached(cache, layer, x, y, z + 1) <
expected)
toCheck[tcc++] = (((x - xc) + 32)) +
(((y - yc) + 32) << 6) +
(((z + 1 - zc) + 32) << 12);
}
}
}
}
}
// if( cache ) XUnlockL2(XLOCKL2_INDEX_TITLE);
#if 0
QueryPerformanceCounter( &qwNewTime );
qwDeltaTime1.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
qwTime = qwNewTime;
#endif
flushCache(cache, cacheUse, layer);
#if 0
/////////////////////////////////////////////////////////////////
if( cache )
{
QueryPerformanceCounter( &qwNewTime );
qwDeltaTime2.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
fElapsedTime1 = fSecsPerTick * ((FLOAT)(qwDeltaTime1.QuadPart));
fElapsedTime2 = fSecsPerTick * ((FLOAT)(qwDeltaTime2.QuadPart));
if( ( darktcc > 0 ) | ( tcc > 0 ) )
{
printf("%d %d %d %f + %f = %f\n", darktcc, tcc, darktcc + tcc, fElapsedTime1 * 1000.0f, fElapsedTime2 * 1000.0f, ( fElapsedTime1 + fElapsedTime2 ) * 1000.0f);
}
}
/////////////////////////////////////////////////////////////////
#endif
LeaveCriticalSection(&m_checkLightCS);
}
bool Level::tickPendingTicks(bool force) { return false; }
std::vector<TickNextTickData>* Level::fetchTicksInChunk(LevelChunk* chunk,
bool remove) {
return NULL;
}
std::vector<std::shared_ptr<Entity> >* Level::getEntities(
std::shared_ptr<Entity> except, AABB* bb) {
MemSect(40);
es.clear();
int xc0 = Mth::floor((bb->x0 - 2) / 16);
int xc1 = Mth::floor((bb->x1 + 2) / 16);
int zc0 = Mth::floor((bb->z0 - 2) / 16);
int zc1 = Mth::floor((bb->z1 + 2) / 16);
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
// AP - RW critical sections are expensive so enter it here so we only have
// to call it once instead of X times
EnterCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
EnterCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
for (int xc = xc0; xc <= xc1; xc++)
for (int zc = zc0; zc <= zc1; zc++) {
if (hasChunk(xc, zc)) {
getChunk(xc, zc)->getEntities(except, bb, es);
}
}
MemSect(0);
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
LeaveCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
LeaveCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
return &es;
}
std::vector<std::shared_ptr<Entity> >* Level::getEntitiesOfClass(
const std::type_info& baseClass, AABB* bb) {
int xc0 = Mth::floor((bb->x0 - 2) / 16);
int xc1 = Mth::floor((bb->x1 + 2) / 16);
int zc0 = Mth::floor((bb->z0 - 2) / 16);
int zc1 = Mth::floor((bb->z1 + 2) / 16);
std::vector<std::shared_ptr<Entity> >* es =
new std::vector<std::shared_ptr<Entity> >();
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
// AP - RW critical sections are expensive so enter it here so we only have
// to call it once instead of X times
EnterCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
EnterCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
for (int xc = xc0; xc <= xc1; xc++)
for (int zc = zc0; zc <= zc1; zc++) {
if (hasChunk(xc, zc)) {
getChunk(xc, zc)->getEntitiesOfClass(baseClass, bb, *es);
}
}
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
LeaveCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
LeaveCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
return es;
}
std::shared_ptr<Entity> Level::getClosestEntityOfClass(
const std::type_info& baseClass, AABB* bb, std::shared_ptr<Entity> source) {
std::vector<std::shared_ptr<Entity> >* entities =
getEntitiesOfClass(baseClass, bb);
std::shared_ptr<Entity> closest = nullptr;
double closestDistSqr = Double::MAX_VALUE;
// for (Entity entity : entities)
for (AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) {
std::shared_ptr<Entity> entity = *it;
if (entity == source) continue;
double distSqr = source->distanceToSqr(entity);
if (distSqr > closestDistSqr) continue;
closest = entity;
closestDistSqr = distSqr;
}
delete entities;
return closest;
}
std::vector<std::shared_ptr<Entity> > Level::getAllEntities() {
EnterCriticalSection(&m_entitiesCS);
std::vector<std::shared_ptr<Entity> > retVec = entities;
LeaveCriticalSection(&m_entitiesCS);
return retVec;
}
void Level::tileEntityChanged(int x, int y, int z,
std::shared_ptr<TileEntity> te) {
if (this->hasChunkAt(x, y, z)) {
getChunkAt(x, z)->markUnsaved();
}
}
#if 0
unsigned int Level::countInstanceOf(BaseObject::Class *clas)
{
unsigned int count = 0;
EnterCriticalSection(&m_entitiesCS);
AUTO_VAR(itEnd, entities.end());
for (AUTO_VAR(it, entities.begin()); it != itEnd; it++)
{
std::shared_ptr<Entity> e = *it;//entities.at(i);
if (clas->isAssignableFrom(e->getClass())) count++;
}
LeaveCriticalSection(&m_entitiesCS);
return count;
}
#endif
// 4J - added - more limited (but faster) version of above, used to count water
// animals, animals, monsters for the mob spawner 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)
unsigned int Level::countInstanceOf(
eINSTANCEOF clas, bool singleType, unsigned int* protectedCount /* = NULL*/,
unsigned int* couldWanderCount /* = NULL*/) {
unsigned int count = 0;
if (protectedCount) *protectedCount = 0;
if (couldWanderCount) *couldWanderCount = 0;
EnterCriticalSection(&m_entitiesCS);
AUTO_VAR(itEnd, entities.end());
for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) {
std::shared_ptr<Entity> e = *it; // entities.at(i);
if (singleType) {
if (e->GetType() == clas) {
if (protectedCount && e->isDespawnProtected()) {
(*protectedCount)++;
}
if (couldWanderCount && e->couldWander()) {
(*couldWanderCount)++;
}
count++;
}
} else {
if (e->GetType() & clas) count++;
}
}
LeaveCriticalSection(&m_entitiesCS);
return count;
}
unsigned int Level::countInstanceOfInRange(eINSTANCEOF clas, bool singleType,
int range, int x, int y, int z) {
unsigned int count = 0;
EnterCriticalSection(&m_entitiesCS);
AUTO_VAR(itEnd, entities.end());
for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) {
std::shared_ptr<Entity> e = *it; // entities.at(i);
float sd = e->distanceTo(x, y, z);
if (sd * sd > range * range) {
continue;
}
if (singleType) {
if (e->GetType() == clas) {
count++;
}
} else {
if (e->GetType() & clas) {
count++;
}
}
}
LeaveCriticalSection(&m_entitiesCS);
return count;
}
void Level::addEntities(std::vector<std::shared_ptr<Entity> >* list) {
// entities.addAll(list);
EnterCriticalSection(&m_entitiesCS);
entities.insert(entities.end(), list->begin(), list->end());
AUTO_VAR(itEnd, list->end());
bool deleteDragons = false;
for (AUTO_VAR(it, list->begin()); it != itEnd; it++) {
entityAdded(*it);
// 4J Stu - Special change to remove duplicate enderdragons that a
// previous bug might have produced
if ((*it)->GetType() == eTYPE_ENDERDRAGON) {
deleteDragons = true;
}
}
if (deleteDragons) {
deleteDragons = false;
for (AUTO_VAR(it, entities.begin()); it != entities.end(); ++it) {
// 4J Stu - Special change to remove duplicate enderdragons that a
// previous bug might have produced
if ((*it)->GetType() == eTYPE_ENDERDRAGON) {
if (deleteDragons) {
(*it)->remove();
} else {
deleteDragons = true;
}
}
}
}
LeaveCriticalSection(&m_entitiesCS);
}
void Level::removeEntities(std::vector<std::shared_ptr<Entity> >* list) {
// entitiesToRemove.addAll(list);
entitiesToRemove.insert(entitiesToRemove.end(), list->begin(), list->end());
}
bool Level::mayPlace(int tileId, int x, int y, int z, bool ignoreEntities,
int face, std::shared_ptr<Entity> ignoreEntity) {
int targetType = getTile(x, y, z);
Tile* targetTile = Tile::tiles[targetType];
Tile* tile = Tile::tiles[tileId];
AABB* aabb = tile->getAABB(this, x, y, z);
if (ignoreEntities) aabb = NULL;
if (aabb != NULL && !isUnobstructed(aabb, ignoreEntity)) return false;
if (targetTile != NULL &&
(targetTile == Tile::water || targetTile == Tile::calmWater ||
targetTile == Tile::lava || targetTile == Tile::calmLava ||
targetTile == Tile::fire || targetTile->material->isReplaceable()))
targetTile = NULL;
if (targetTile != NULL && targetTile->material == Material::decoration &&
tile == Tile::anvil)
return true;
if (tileId > 0 && targetTile == NULL) {
if (tile->mayPlace(this, x, y, z, face)) {
return true;
}
}
return false;
}
int Level::getSeaLevel() { return seaLevel; }
Path* Level::findPath(std::shared_ptr<Entity> from, std::shared_ptr<Entity> to,
float maxDist, bool canPassDoors, bool canOpenDoors,
bool avoidWater, bool canFloat) {
int x = Mth::floor(from->x);
int y = Mth::floor(from->y + 1);
int z = Mth::floor(from->z);
int r = (int)(maxDist + 16);
int x1 = x - r;
int y1 = y - r;
int z1 = z - r;
int x2 = x + r;
int y2 = y + r;
int z2 = z + r;
Region region = Region(this, x1, y1, z1, x2, y2, z2);
Path* path =
(PathFinder(&region, canPassDoors, canOpenDoors, avoidWater, canFloat))
.findPath(from.get(), to.get(), maxDist);
return path;
}
Path* Level::findPath(std::shared_ptr<Entity> from, int xBest, int yBest,
int zBest, float maxDist, bool canPassDoors,
bool canOpenDoors, bool avoidWater, bool canFloat) {
int x = Mth::floor(from->x);
int y = Mth::floor(from->y);
int z = Mth::floor(from->z);
int r = (int)(maxDist + 8);
int x1 = x - r;
int y1 = y - r;
int z1 = z - r;
int x2 = x + r;
int y2 = y + r;
int z2 = z + r;
Region region = Region(this, x1, y1, z1, x2, y2, z2);
Path* path =
(PathFinder(&region, canPassDoors, canOpenDoors, avoidWater, canFloat))
.findPath(from.get(), xBest, yBest, zBest, maxDist);
return path;
}
bool Level::getDirectSignal(int x, int y, int z, int dir) {
int t = getTile(x, y, z);
if (t == 0) return false;
return Tile::tiles[t]->getDirectSignal(this, x, y, z, dir);
}
bool Level::hasDirectSignal(int x, int y, int z) {
if (getDirectSignal(x, y - 1, z, 0)) return true;
if (getDirectSignal(x, y + 1, z, 1)) return true;
if (getDirectSignal(x, y, z - 1, 2)) return true;
if (getDirectSignal(x, y, z + 1, 3)) return true;
if (getDirectSignal(x - 1, y, z, 4)) return true;
if (getDirectSignal(x + 1, y, z, 5)) return true;
return false;
}
bool Level::getSignal(int x, int y, int z, int dir) {
if (isSolidBlockingTile(x, y, z)) {
return hasDirectSignal(x, y, z);
}
int t = getTile(x, y, z);
if (t == 0) return false;
return Tile::tiles[t]->getSignal(this, x, y, z, dir);
}
bool Level::hasNeighborSignal(int x, int y, int z) {
if (getSignal(x, y - 1, z, 0)) return true;
if (getSignal(x, y + 1, z, 1)) return true;
if (getSignal(x, y, z - 1, 2)) return true;
if (getSignal(x, y, z + 1, 3)) return true;
if (getSignal(x - 1, y, z, 4)) return true;
if (getSignal(x + 1, y, z, 5)) return true;
return false;
}
// 4J Stu - Added maxYDist param
std::shared_ptr<Player> Level::getNearestPlayer(std::shared_ptr<Entity> source,
double maxDist,
double maxYDist /*= -1*/) {
return getNearestPlayer(source->x, source->y, source->z, maxDist, maxYDist);
}
// 4J Stu - Added maxYDist param
std::shared_ptr<Player> Level::getNearestPlayer(double x, double y, double z,
double maxDist,
double maxYDist /*= -1*/) {
MemSect(21);
double best = -1;
std::shared_ptr<Player> result = nullptr;
AUTO_VAR(itEnd, players.end());
for (AUTO_VAR(it, players.begin()); it != itEnd; it++) {
std::shared_ptr<Player> p = *it; // players.at(i);
double dist = p->distanceToSqr(x, y, z);
// Allow specifying shorter distances in the vertical
if (maxYDist > 0 && abs(p->y - y) > maxYDist) continue;
// 4J Stu - Added check that this player is still alive
if ((maxDist < 0 || dist < maxDist * maxDist) &&
(best == -1 || dist < best) && p->isAlive()) {
best = dist;
result = p;
}
}
MemSect(0);
return result;
}
std::shared_ptr<Player> Level::getNearestPlayer(double x, double z,
double maxDist) {
double best = -1;
std::shared_ptr<Player> result = nullptr;
AUTO_VAR(itEnd, players.end());
for (AUTO_VAR(it, players.begin()); it != itEnd; it++) {
std::shared_ptr<Player> p = *it;
double dist = p->distanceToSqr(x, p->y, z);
if ((maxDist < 0 || dist < maxDist * maxDist) &&
(best == -1 || dist < best)) {
best = dist;
result = p;
}
}
return result;
}
std::shared_ptr<Player> Level::getNearestAttackablePlayer(
std::shared_ptr<Entity> source, double maxDist) {
return getNearestAttackablePlayer(source->x, source->y, source->z, maxDist);
}
std::shared_ptr<Player> Level::getNearestAttackablePlayer(double x, double y,
double z,
double maxDist) {
double best = -1;
std::shared_ptr<Player> result = nullptr;
AUTO_VAR(itEnd, players.end());
for (AUTO_VAR(it, players.begin()); it != itEnd; it++) {
std::shared_ptr<Player> p = *it;
if (p->abilities.invulnerable) {
continue;
}
double dist = p->distanceToSqr(x, y, z);
double visibleDist = maxDist;
// decrease the max attackable distance if the target player
// is sneaking or invisible
if (p->isSneaking()) {
visibleDist *= .8f;
}
if (p->isInvisible()) {
float coverPercentage = p->getArmorCoverPercentage();
if (coverPercentage < .1f) {
coverPercentage = .1f;
}
visibleDist *= (.7f * coverPercentage);
}
// 4J Stu - Added check that this player is still alive and privilege
// check
if ((visibleDist < 0 || dist < visibleDist * visibleDist) &&
(best == -1 || dist < best) && p->isAlive() &&
!p->hasInvisiblePrivilege()) {
best = dist;
result = p;
}
}
return result;
}
std::shared_ptr<Player> Level::getPlayerByName(const std::wstring& name) {
AUTO_VAR(itEnd, players.end());
for (AUTO_VAR(it, players.begin()); it != itEnd; it++) {
if (name.compare((*it)->name) == 0) {
return *it; // players.at(i);
}
}
return std::shared_ptr<Player>();
}
std::shared_ptr<Player> Level::getPlayerByUUID(const std::wstring& name) {
AUTO_VAR(itEnd, players.end());
for (AUTO_VAR(it, players.begin()); it != itEnd; it++) {
if (name.compare((*it)->getUUID()) == 0) {
return *it; // players.at(i);
}
}
return std::shared_ptr<Player>();
}
// 4J Stu - Removed in 1.2.3 ?
byteArray Level::getBlocksAndData(int x, int y, int z, int xs, int ys, int zs,
bool includeLighting /* = true*/) {
byteArray result(xs * ys * zs * 5 / 2);
int xc0 = x >> 4;
int zc0 = z >> 4;
int xc1 = (x + xs - 1) >> 4;
int zc1 = (z + zs - 1) >> 4;
int p = 0;
int y0 = y;
int y1 = y + ys;
if (y0 < 0) y0 = 0;
if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight;
for (int xc = xc0; xc <= xc1; xc++) {
int x0 = x - xc * 16;
int x1 = x + xs - xc * 16;
if (x0 < 0) x0 = 0;
if (x1 > 16) x1 = 16;
for (int zc = zc0; zc <= zc1; zc++) {
int z0 = z - zc * 16;
int z1 = z + zs - zc * 16;
if (z0 < 0) z0 = 0;
if (z1 > 16) z1 = 16;
p = getChunk(xc, zc)->getBlocksAndData(&result, x0, y0, z0, x1, y1,
z1, p, includeLighting);
}
}
return result;
}
// 4J Stu - Removed in 1.2.3 ?
void Level::setBlocksAndData(int x, int y, int z, int xs, int ys, int zs,
byteArray data, bool includeLighting /* = true*/) {
int xc0 = x >> 4;
int zc0 = z >> 4;
int xc1 = (x + xs - 1) >> 4;
int zc1 = (z + zs - 1) >> 4;
int p = 0;
int y0 = y;
int y1 = y + ys;
if (y0 < 0) y0 = 0;
if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight;
for (int xc = xc0; xc <= xc1; xc++) {
int x0 = x - xc * 16;
int x1 = x + xs - xc * 16;
if (x0 < 0) x0 = 0;
if (x1 > 16) x1 = 16;
for (int zc = zc0; zc <= zc1; zc++) {
int z0 = z - zc * 16;
int z1 = z + zs - zc * 16;
if (z0 < 0) z0 = 0;
if (z1 > 16) z1 = 16;
LevelChunk* lc = getChunk(xc, zc);
// 4J Stu - Unshare before we make any changes incase the server is
// already another step ahead of us Fix for #7904 - Gameplay:
// Players can dupe torches by throwing them repeatedly into water.
// This is quite expensive so only actually do it if we are hosting,
// online, and the update will actually change something
bool forceUnshare = false;
if (g_NetworkManager.IsHost() && isClientSide) {
forceUnshare =
lc->testSetBlocksAndData(data, x0, y0, z0, x1, y1, z1, p);
}
if (forceUnshare) {
int size = (x1 - x0) * (y1 - y0) * (z1 - z0);
PIXBeginNamedEvent(0, "Chunk data unsharing %d\n", size);
lc->stopSharingTilesAndData();
PIXEndNamedEvent();
}
if (p < data.length)
p = lc->setBlocksAndData(data, x0, y0, z0, x1, y1, z1, p,
includeLighting);
setTilesDirty(xc * 16 + x0, y0, zc * 16 + z0, xc * 16 + x1, y1,
zc * 16 + z1);
PIXBeginNamedEvent(0, "Chunk data sharing\n");
if (g_NetworkManager.IsHost() && isClientSide) {
lc->startSharingTilesAndData();
}
PIXEndNamedEvent();
}
}
}
void Level::disconnect(bool sendDisconnect /*= true*/) {}
void Level::checkSession() { levelStorage->checkSession(); }
void Level::setTime(__int64 time) {
// 4J : WESTY : Added to track game time played by players for other awards.
if (time != 0) // Ignore setting time to 0, done at level start and during
// tutorial.
{
// Determine step in time and ensure it is reasonable ( we only have an
// int to store the player stat).
__int64 timeDiff = time - levelData->getTime();
// debug setting added to keep it at day time
#ifndef _FINAL_BUILD
if (app.DebugSettingsOn()) {
if (app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad()) &
(1L << eDebugSetting_FreezeTime)) {
timeDiff = 0;
time = levelData->getTime();
}
}
#endif
if (timeDiff < 0) {
timeDiff = 0;
} else if (timeDiff > 100) {
// Time differences of more than ~5 seconds are generally not real
// time passing so ignore (moving dimensions does this)
app.DebugPrintf(
"Level::setTime: Massive time difference, ignoring for time "
"passed stat (%lli)\n",
timeDiff);
timeDiff = 0;
}
// Apply stat to each player.
if (timeDiff > 0 && levelData->getTime() != -1) {
AUTO_VAR(itEnd, players.end());
for (std::vector<std::shared_ptr<Player> >::iterator it =
players.begin();
it != itEnd; it++) {
(*it)->awardStat(GenericStats::timePlayed(),
GenericStats::param_time(timeDiff));
}
}
}
this->levelData->setTime(time);
}
void Level::setOverrideTimeOfDay(__int64 time) { m_timeOfDayOverride = time; }
__int64 Level::getSeed() { return levelData->getSeed(); }
__int64 Level::getTime() { return levelData->getTime(); }
Pos* Level::getSharedSpawnPos() {
return new Pos(levelData->getXSpawn(), levelData->getYSpawn(),
levelData->getZSpawn());
}
void Level::setSpawnPos(int x, int y, int z) { levelData->setSpawn(x, y, z); }
void Level::setSpawnPos(Pos* spawnPos) {
setSpawnPos(spawnPos->x, spawnPos->y, spawnPos->z);
}
void Level::ensureAdded(std::shared_ptr<Entity> entity) {
int xc = Mth::floor(entity->x / 16);
int zc = Mth::floor(entity->z / 16);
int r = 2;
for (int x = xc - r; x <= xc + r; x++) {
for (int z = zc - r; z <= zc + r; z++) {
this->getChunk(x, z);
}
}
// if (!entities.contains(entity))
EnterCriticalSection(&m_entitiesCS);
if (find(entities.begin(), entities.end(), entity) == entities.end()) {
entities.push_back(entity);
}
LeaveCriticalSection(&m_entitiesCS);
}
bool Level::mayInteract(std::shared_ptr<Player> player, int xt, int yt, int zt,
int content) {
return true;
}
void Level::broadcastEntityEvent(std::shared_ptr<Entity> e, uint8_t event) {}
ChunkSource* Level::getChunkSource() { return chunkSource; }
void Level::tileEvent(int x, int y, int z, int tile, int b0, int b1) {
if (tile > 0) Tile::tiles[tile]->triggerEvent(this, x, y, z, b0, b1);
}
LevelStorage* Level::getLevelStorage() { return levelStorage.get(); }
LevelData* Level::getLevelData() { return levelData; }
void Level::updateSleepingPlayerList() {}
float Level::getThunderLevel(float a) {
return (oThunderLevel + (thunderLevel - oThunderLevel) * a) *
getRainLevel(a);
}
float Level::getRainLevel(float a) {
return oRainLevel + (rainLevel - oRainLevel) * a;
}
void Level::setRainLevel(float rainLevel) {
this->oRainLevel = rainLevel;
this->rainLevel = rainLevel;
}
bool Level::isThundering() { return getThunderLevel(1) > 0.9; }
bool Level::isRaining() { return getRainLevel(1) > 0.2; }
bool Level::isRainingAt(int x, int y, int z) {
if (!isRaining()) return false;
if (!canSeeSky(x, y, z)) return false;
if (getTopRainBlock(x, z) > y) return false;
// 4J - changed to use new method of getting biomedata that caches results
// of rain & snow
if (biomeHasSnow(x, z)) return false;
return biomeHasRain(x, z);
}
bool Level::isHumidAt(int x, int y, int z) {
Biome* biome = getBiome(x, z);
return biome->isHumid();
}
void Level::setSavedData(const std::wstring& id,
std::shared_ptr<SavedData> data) {
savedDataStorage->set(id, data);
}
std::shared_ptr<SavedData> Level::getSavedData(const std::type_info& clazz,
const std::wstring& id) {
return savedDataStorage->get(clazz, id);
}
int Level::getFreeAuxValueFor(const std::wstring& id) {
return savedDataStorage->getFreeAuxValueFor(id);
}
// 4J Added
int Level::getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC,
int centreZC, int scale) {
return savedDataStorage->getAuxValueForMap(xuid, dimension, centreXC,
centreZC, scale);
}
// void Level::globalLevelEvent(int type, int sourceX, int sourceY, int sourceZ,
// int data)
// {
// auto itEnd = listeners.end();
// for (auto it = listeners.begin(); it != itEnd; it++)
// {
// (*it)->globalLevelEvent(type, sourceX, sourceY, sourceZ, data);
// }
// }
void Level::levelEvent(int type, int x, int y, int z, int data) {
levelEvent(nullptr, type, x, y, z, data);
}
void Level::levelEvent(std::shared_ptr<Player> source, int type, int x, int y,
int z, int data) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->levelEvent(source, type, x, y, z, data);
}
}
int Level::getMaxBuildHeight() { return maxBuildHeight; }
int Level::getHeight() {
return dimension->hasCeiling ? genDepth : maxBuildHeight;
}
Random* Level::getRandomFor(int x, int z, int blend) {
__int64 seed = (x * 341873128712l + z * 132897987541l) +
getLevelData()->getSeed() + blend;
random->setSeed(seed);
return random;
}
bool Level::updateLights() { return false; }
TilePos* Level::findNearestMapFeature(const std::wstring& featureName, int x,
int y, int z) {
return getChunkSource()->findNearestMapFeature(this, featureName, x, y, z);
}
bool Level::isAllEmpty() { return false; }
double Level::getHorizonHeight() {
if (levelData->getGenerator() == LevelType::lvl_flat) {
return 0.0;
}
return 63.0;
}
void Level::destroyTileProgress(int id, int x, int y, int z, int progress) {
AUTO_VAR(itEnd, listeners.end());
for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) {
(*it)->destroyTileProgress(id, x, y, z, progress);
}
}
bool Level::useNewSeaLevel() { return levelData->useNewSeaLevel(); }
bool Level::getHasBeenInCreative() { return levelData->getHasBeenInCreative(); }
bool Level::isGenerateMapFeatures() {
return levelData->isGenerateMapFeatures();
}
int Level::getSaveVersion() {
return getLevelStorage()->getSaveFile()->getSaveVersion();
}
int Level::getOriginalSaveVersion() {
return getLevelStorage()->getSaveFile()->getOriginalSaveVersion();
}
// 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 only really be done when the post-processing has
// placed all possible tiles into this chunk.
bool Level::isChunkPostPostProcessed(int x, int z) {
if (!hasChunk(x, z))
return false; // This will occur for non-loaded chunks, not for edge
// chunks
LevelChunk* lc = getChunk(x, z);
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
return ((lc->terrainPopulated & LevelChunk::sTerrainPostPostProcessed) ==
LevelChunk::sTerrainPostPostProcessed);
}
// 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 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.
bool Level::isChunkFinalised(int x, int z) {
for (int xo = -1; xo <= 1; xo++)
for (int zo = -1; zo <= 1; zo++) {
if (!isChunkPostPostProcessed(x + xo, z + zo)) return false;
}
return true;
}
int Level::getUnsavedChunkCount() { return m_unsavedChunkCount; }
void Level::incrementUnsavedChunkCount() { ++m_unsavedChunkCount; }
void Level::decrementUnsavedChunkCount() { --m_unsavedChunkCount; }
bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType) {
int count = 0;
int max = 0;
if (spawnType == eSpawnType_Egg) {
switch (type) {
case eTYPE_VILLAGER:
count = countInstanceOf(eTYPE_VILLAGER, true);
max = MobCategory::MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG;
break;
case eTYPE_CHICKEN:
count = countInstanceOf(eTYPE_CHICKEN, true);
max = MobCategory::MAX_XBOX_CHICKENS_WITH_SPAWN_EGG;
break;
case eTYPE_WOLF:
count = countInstanceOf(eTYPE_WOLF, true);
max = MobCategory::MAX_XBOX_WOLVES_WITH_SPAWN_EGG;
break;
case eTYPE_MUSHROOMCOW:
count = countInstanceOf(eTYPE_MUSHROOMCOW, true);
max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG;
break;
case eTYPE_SQUID:
count = countInstanceOf(eTYPE_SQUID, true);
max = MobCategory::MAX_XBOX_SQUIDS_WITH_SPAWN_EGG;
break;
case eTYPE_SNOWMAN:
count = countInstanceOf(eTYPE_SNOWMAN, true);
max = MobCategory::MAX_XBOX_SNOWMEN;
break;
case eTYPE_VILLAGERGOLEM:
count = countInstanceOf(eTYPE_VILLAGERGOLEM, true);
max = MobCategory::MAX_XBOX_IRONGOLEM;
break;
default:
if ((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) ==
eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) {
count =
countInstanceOf(eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
max = MobCategory::MAX_XBOX_ANIMALS_WITH_SPAWN_EGG;
} else if ((type & eTYPE_MONSTER) == eTYPE_MONSTER) {
count = countInstanceOf(eTYPE_MONSTER, false);
max = MobCategory::MAX_XBOX_MONSTERS_WITH_SPAWN_EGG;
}
};
} else if (spawnType == eSpawnType_Breed) {
switch (type) {
case eTYPE_VILLAGER:
count = countInstanceOf(eTYPE_VILLAGER, true);
max = MobCategory::MAX_VILLAGERS_WITH_BREEDING;
break;
case eTYPE_CHICKEN:
count = countInstanceOf(eTYPE_CHICKEN, true);
max = MobCategory::MAX_XBOX_CHICKENS_WITH_BREEDING;
break;
case eTYPE_WOLF:
count = countInstanceOf(eTYPE_WOLF, true);
max = MobCategory::MAX_XBOX_WOLVES_WITH_BREEDING;
break;
case eTYPE_MUSHROOMCOW:
count = countInstanceOf(eTYPE_MUSHROOMCOW, true);
max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING;
break;
default:
if ((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) ==
eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) {
count =
countInstanceOf(eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
max = MobCategory::MAX_XBOX_ANIMALS_WITH_BREEDING;
} else if ((type & eTYPE_MONSTER) == eTYPE_MONSTER) {
}
break;
}
}
return count < max;
}