Added 3 Fog Choices (Pocket) (Java) (Unknown that was unused) Restored Java Beta Sky Color code that was unused that depends on biome temperature (choosable using Java fog) Tile Shadows and Entity Shadows that appear beneath them have been restored and fixed from the unused code, toggleable by turning fancy graphics on or off Entities will now render flames on themselves when on fire, including the player Added option to use Java Style Item Count text and position in tweaks - fileshredder
2252 lines
61 KiB
C++
Executable File
2252 lines
61 KiB
C++
Executable File
#include "Level.h"
|
|
#include "LevelListener.h"
|
|
#include "tile/entity/TileEntity.h"
|
|
#include "../entity/player/Player.h"
|
|
#include "../entity/EntityEvent.h"
|
|
|
|
#include "biome/BiomeSource.h"
|
|
#include "chunk/storage/ChunkStorage.h"
|
|
#include "chunk/ChunkCache.h"
|
|
#include "storage/LevelStorage.h"
|
|
#include "Region.h"
|
|
#include "Explosion.h"
|
|
#include "LevelConstants.h"
|
|
#include "pathfinder/PathFinder.h"
|
|
|
|
#include "../Facing.h"
|
|
#include "../../util/PerfTimer.h"
|
|
|
|
#include "tile/LiquidTile.h"
|
|
|
|
#include "biome/Biome.h"
|
|
#include "MobSpawner.h"
|
|
#include "../../network/packet/SetEntityDataPacket.h"
|
|
#include "../../network/RakNetInstance.h"
|
|
#include "../../network/packet/EntityEventPacket.h"
|
|
#include "../../network/packet/SetTimePacket.h"
|
|
#include "../entity/monster/Zombie.h"
|
|
#include "../inventory/BaseContainerMenu.h"
|
|
#include "../Difficulty.h"
|
|
#include "../../network/packet/ExplodePacket.h"
|
|
|
|
Level::Level(LevelStorage* levelStorage, const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension /* = NULL */)
|
|
: levelStorage(levelStorage),
|
|
isClientSide(false),
|
|
isFindingSpawn(false),
|
|
noNeighborUpdate(false),
|
|
skyDarken(0),
|
|
instaTick(false),
|
|
random(1),
|
|
_isNew(false),
|
|
_chunkSource(NULL),
|
|
_randValue(42184323),
|
|
_addend(1013904223),
|
|
_maxRecurse(0),
|
|
_updateLights(true),
|
|
_pathFinder(NULL),
|
|
_spawnFriendlies(true),
|
|
_spawnEnemies(true),
|
|
_lastSavedPlayerTime(0), // @attn: @time: clock starts on 0 now, change if not
|
|
raknetInstance(0),
|
|
updatingTileEntities(false),
|
|
allPlayersAreSleeping(false),
|
|
_nightMode(false)
|
|
{
|
|
_init(levelName, settings, generatorVersion, fixedDimension);
|
|
}
|
|
|
|
Level::~Level() {
|
|
LOGI("Erasing chunk source\n");
|
|
delete _chunkSource;
|
|
LOGI("Erasing dimension\n");
|
|
delete dimension;
|
|
delete _pathFinder;
|
|
|
|
std::set<Entity*> all;
|
|
all.insert(entities.begin(), entities.end());
|
|
all.insert(players.begin(), players.end());
|
|
PendingList::iterator it = _pendingPlayerRemovals.begin();
|
|
while (it != _pendingPlayerRemovals.end()) {
|
|
all.insert(it->e);
|
|
++it;
|
|
}
|
|
for (std::set<Entity*>::iterator it = all.begin(); it != all.end(); ++it)
|
|
delete *it;
|
|
|
|
std::set<TileEntity*> allTileEntities;
|
|
allTileEntities.insert(tileEntities.begin(), tileEntities.end());
|
|
allTileEntities.insert(pendingTileEntities.begin(), pendingTileEntities.end());
|
|
for (std::set<TileEntity*>::iterator it = allTileEntities.begin(); it != allTileEntities.end(); ++it)
|
|
delete *it;
|
|
}
|
|
|
|
void Level::_init(const std::string& levelName, const LevelSettings& settings, int generatorVersion, Dimension* fixedDimension) {
|
|
|
|
isGeneratingTerrain = false;
|
|
|
|
LevelData* preparedData = levelStorage->prepareLevel(this);
|
|
_isNew = (preparedData == NULL);
|
|
|
|
if (preparedData == NULL) {
|
|
levelData = LevelData(settings, levelName, generatorVersion);
|
|
} else {
|
|
levelData = *preparedData;
|
|
levelData.setLevelName(levelName);
|
|
}
|
|
|
|
if (fixedDimension != NULL) {
|
|
dimension = fixedDimension;
|
|
} else {
|
|
dimension = DimensionFactory::createDefaultDimension( &levelData );
|
|
}
|
|
|
|
dimension->init(this);
|
|
_chunkSource = createChunkSource();
|
|
|
|
_pathFinder = new PathFinder();
|
|
_nightMode = false;
|
|
updateSkyBrightness();
|
|
}
|
|
|
|
/*protected*/
|
|
ChunkSource* Level::createChunkSource() {
|
|
if (!levelStorage) {
|
|
printf("no level data, calling dimension->createRandomLevelSource\n");
|
|
return dimension->createRandomLevelSource(); //@note
|
|
}
|
|
|
|
ChunkStorage* chunkStorage = levelStorage->createChunkStorage(dimension);
|
|
return new ChunkCache(this, chunkStorage, dimension->createRandomLevelSource());
|
|
}
|
|
|
|
/*public*/
|
|
bool Level::checkAndHandleWater(const AABB& box, const Material* material, 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(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) {
|
|
float yt0 = y + 1 - LiquidTile::getHeight(getData(x, y, z));
|
|
if (y1 >= yt0) {
|
|
ok = true;
|
|
tile->handleEntityInside(this, x, y, z, e, current);
|
|
}
|
|
}
|
|
}
|
|
float len = current.length();
|
|
if (len > 0) {
|
|
const float pow = 0.004f / len;
|
|
e->xd += current.x * pow;
|
|
e->yd += current.y * pow;
|
|
e->zd += current.z * pow;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/*public*/
|
|
Player* Level::getNearestPlayer(Entity* source, float maxDist) {
|
|
return getNearestPlayer(source->x, source->y, source->z, maxDist);
|
|
}
|
|
|
|
/*public*/
|
|
Player* Level::getNearestPlayer(float x, float y, float z, float maxDist) {
|
|
float maxDistSqr = maxDist * maxDist;
|
|
float best = -1;
|
|
Player* result = NULL;
|
|
for (unsigned int i = 0; i < players.size(); i++) {
|
|
Player* p = players[i];
|
|
if (p->removed) continue;
|
|
float dist = p->distanceToSqr(x, y, z);
|
|
if ((maxDist < 0 || dist < maxDistSqr) && (best == -1 || dist < best)) {
|
|
best = dist;
|
|
result = p;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*public*/
|
|
void Level::tick() {
|
|
if (!isClientSide && levelData.getSpawnMobs()) {
|
|
static int _mobSpawnTick = 0;
|
|
const int MobSpawnInterval = 2;
|
|
if (++_mobSpawnTick >= MobSpawnInterval) {
|
|
_mobSpawnTick = 0;
|
|
TIMER_PUSH("mobSpawner");
|
|
MobSpawner::tick(this, _spawnEnemies && difficulty > Difficulty::PEACEFUL,
|
|
_spawnFriendlies && (levelData.getTime() % 400) < MobSpawnInterval);
|
|
TIMER_POP();
|
|
}
|
|
}
|
|
|
|
TIMER_PUSH("chunkSource");
|
|
_chunkSource->tick();
|
|
|
|
updateSkyDarken();
|
|
if(_nightMode) {
|
|
long curTime = levelData.getTime();
|
|
if(curTime % TICKS_PER_DAY != MIDDLE_OF_NIGHT_TIME) {
|
|
if(curTime % TICKS_PER_DAY < MIDDLE_OF_NIGHT_TIME && (curTime + 20) % TICKS_PER_DAY > MIDDLE_OF_NIGHT_TIME) {
|
|
curTime = MIDDLE_OF_NIGHT_TIME;
|
|
SetTimePacket packet(curTime);
|
|
raknetInstance->send(packet);
|
|
}
|
|
else {
|
|
curTime += 20;
|
|
if(curTime % 20 == 0) {
|
|
SetTimePacket packet(curTime);
|
|
raknetInstance->send(packet);
|
|
}
|
|
}
|
|
setTime(curTime%TICKS_PER_DAY);
|
|
}
|
|
}
|
|
else {
|
|
long time = levelData.getTime() + 1;
|
|
//if (time % (saveInterval) == 0) {
|
|
// save(false, NULL);
|
|
//}
|
|
levelData.setTime(time);
|
|
if ((time & 255) == 0) {
|
|
SetTimePacket packet(time);
|
|
raknetInstance->send(packet);
|
|
}
|
|
}
|
|
TIMER_POP_PUSH("tickPending");
|
|
tickPendingTicks(false);
|
|
|
|
TIMER_POP_PUSH("tickTiles");
|
|
tickTiles();
|
|
|
|
TIMER_POP_PUSH("sendEntityData");
|
|
for (unsigned int i = 0; i < entities.size(); ++i) {
|
|
Entity* e = entities[i];
|
|
SynchedEntityData* data = e->getEntityData();
|
|
if (data && data->isDirty()) {
|
|
SetEntityDataPacket packet(e->entityId, *data);
|
|
raknetInstance->send(packet);
|
|
}
|
|
}
|
|
|
|
//if (!_pendingEntityData.empty()) {
|
|
// for (EntityMap::iterator it = _pendingEntityData.begin(); it != _pendingEntityData.end(); ++it) {
|
|
// SetEntityDataPacket packet(it->first, it->second->getEntityData());
|
|
// raknetInstance->send(packet);
|
|
// }
|
|
// _pendingEntityData.clear();
|
|
//}
|
|
TIMER_POP();
|
|
}
|
|
|
|
/*protected*/
|
|
void Level::tickTiles() {
|
|
_chunksToPoll.clear();
|
|
|
|
static const int pollChunkOffsets[] = {
|
|
-1,-4, 0,-4, 1,-4, -2,-3, -1,-3, 0,-3, 1,-3, 2,-3, -3,-2,
|
|
-2,-2, -1,-2, 0,-2, 1,-2, 2,-2, 3,-2, -4,-1, -3,-1, -2,-1,
|
|
-1,-1, 0,-1, 1,-1, 2,-1, 3,-1, 4,-1, -4,0, -3,0, -2,0, -1,0,
|
|
0,0, 1,0, 2,0, 3,0, 4,0, -4,1, -3,1, -2,1, -1,1, 0,1, 1,1,
|
|
2,1, 3,1, 4,1, -3,2, -2,2, -1,2, 0,2, 1,2, 2,2, 3,2, -2,3,
|
|
-1,3, 0,3, 1,3, 2,3, -1,4, 0,4, 1,4
|
|
};
|
|
const int pollChunkOffsetsSize = sizeof(pollChunkOffsets) / sizeof(int);
|
|
|
|
TIMER_PUSH("buildList");
|
|
//static Stopwatch w;
|
|
//w.start();
|
|
for (size_t i = 0; i < players.size(); i++) {
|
|
Player* player = players[i];
|
|
int xx = Mth::floor(player->x / 16);
|
|
int zz = Mth::floor(player->z / 16);
|
|
|
|
for (int i = 0; i < pollChunkOffsetsSize; i += 2) {
|
|
const int xp = xx + pollChunkOffsets[i];
|
|
const int zp = zz + pollChunkOffsets[i+1];
|
|
if (xp >= 0 && xp < CHUNK_CACHE_WIDTH &&
|
|
zp >= 0 && zp < CHUNK_CACHE_WIDTH)
|
|
_chunksToPoll.insert(ChunkPos(xp, zp));
|
|
}
|
|
}
|
|
TIMER_POP();
|
|
|
|
//if (delayUntilNextMoodSound > 0) delayUntilNextMoodSound--;
|
|
TIMER_PUSH("loop");
|
|
for (ChunkPosSet::iterator it = _chunksToPoll.begin(); it != _chunksToPoll.end(); ++it) {
|
|
const ChunkPos& cp = *it;
|
|
int xo = cp.x * 16;
|
|
int zo = cp.z * 16;
|
|
// LevelSource region = new Region(this, xo, 0, zo, xo + 16, 128, zo + 16);
|
|
TIMER_PUSH("getChunk");
|
|
LevelChunk* lc = this->getChunk(cp.x, cp.z);
|
|
TIMER_POP_PUSH("tickChunk");
|
|
//lc->tick();
|
|
|
|
//if (delayUntilNextMoodSound == 0) {
|
|
// randValue = randValue * 3 + addend;
|
|
// int val = (randValue >> 2);
|
|
// int x = (val & 15);
|
|
// int z = ((val >> 8) & 15);
|
|
// int y = ((val >> 16) & 127);
|
|
|
|
// int id = lc->getTile(x, y, z);
|
|
// x += xo;
|
|
// z += zo;
|
|
// if (id == 0 && getRawBrightness(x, y, z) <= random.nextInt(8) && getBrightness(LightLayer::Sky, x, y, z) <= 0) {
|
|
// 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) {
|
|
// //this.playSound(x + 0.5, y + 0.5, z + 0.5, "ambient.cave.cave", 0.7f, 0.8f + random.nextFloat() * 0.2f);
|
|
// delayUntilNextMoodSound = random.nextInt(20 * 60 * 10) + 20 * 60 * 5;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
TIMER_POP_PUSH("tickTiles");
|
|
for (int i = 0; i < 20; i++) { //@todo: CHUNK_TILE_TICK_COUNT
|
|
_randValue = _randValue * 3 + _addend;
|
|
int val = (_randValue >> 2);
|
|
int x = (val & 15);
|
|
int z = ((val >> 8) & 15);
|
|
int y = ((val >> 16) & 127);
|
|
|
|
int id = lc->getTile(x, y, z);
|
|
if (Tile::shouldTick[id]) {
|
|
Tile::tiles[id]->tick(this, x + xo, y, z + zo, &random);
|
|
}
|
|
}
|
|
TIMER_POP();
|
|
}
|
|
TIMER_POP();
|
|
//w.stop();
|
|
//w.printEvery(30, "ticktiles");
|
|
}
|
|
|
|
bool Level::tickPendingTicks(bool force) {
|
|
int count = _tickNextTickSet.size();
|
|
if (count > MAX_TICK_TILES_PER_TICK) count = MAX_TICK_TILES_PER_TICK;
|
|
for (int i = 0; i < count; i++) {
|
|
TickDataSet::iterator td = _tickNextTickSet.begin();
|
|
if (!force && td->delay > levelData.getTime()) {
|
|
break;
|
|
}
|
|
|
|
int r = 8;
|
|
if (hasChunksAt(td->x - r, td->y - r, td->z - r, td->x + r, td->y + r, td->z + r)) {
|
|
int id = getTile(td->x, td->y, td->z);
|
|
if (id == td->tileId && id > 0) {
|
|
Tile::tiles[id]->tick(this, td->x, td->y, td->z, &random);
|
|
}
|
|
}
|
|
_tickNextTickSet.erase(td);
|
|
}
|
|
return _tickNextTickSet.size() != 0;
|
|
}
|
|
|
|
/*public*/
|
|
void Level::loadPlayer(Player* player, bool doAddPlayer /*= true*/) {
|
|
if (player) {
|
|
CompoundTag* loadedPlayerTag = levelData.getLoadedPlayerTag();
|
|
if (loadedPlayerTag != NULL) {
|
|
player->load(loadedPlayerTag);
|
|
levelData.setLoadedPlayerTag(NULL);
|
|
} else {
|
|
levelData.setLoadedPlayerTo(player);
|
|
}
|
|
if (doAddPlayer)
|
|
addEntity(player);
|
|
}
|
|
//} catch (Exception e) {
|
|
// e.printStackTrace();
|
|
//}
|
|
}
|
|
|
|
bool Level::findPath(Path* path, Entity* from, Entity* to, float maxDist, bool canOpenDoors, bool avoidWater) {
|
|
TIMER_PUSH("pathfind");
|
|
int x = Mth::floor(from->x);
|
|
int y = Mth::floor(from->y);
|
|
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;
|
|
//LOGI("trying to move! 1: %ld\n", levelData.getTime());
|
|
Region region(this, x1, y1, z1, x2, y2, z2);
|
|
_pathFinder->setLevelSource(®ion);
|
|
_pathFinder->canOpenDoors = canOpenDoors;
|
|
_pathFinder->avoidWater = avoidWater;
|
|
_pathFinder->findPath(path, from, to, maxDist);
|
|
TIMER_POP();
|
|
return true;
|
|
}
|
|
|
|
bool Level::findPath(Path* path, Entity* from, int xBest, int yBest, int zBest, float maxDist, bool canOpenDoors, bool avoidWater) {
|
|
TIMER_PUSH("pathfind");
|
|
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(this, x1, y1, z1, x2, y2, z2);
|
|
//LOGI("trying to move! 2: %ld\n", levelData.getTime());
|
|
_pathFinder->setLevelSource(®ion);
|
|
_pathFinder->canOpenDoors = canOpenDoors;
|
|
_pathFinder->avoidWater = avoidWater;
|
|
_pathFinder->findPath(path, from, xBest, yBest, zBest, maxDist);
|
|
TIMER_POP();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets the initial spawn, created this method so we could do a special
|
|
* location for the demo version.
|
|
*/
|
|
/*protected*/
|
|
void Level::setInitialSpawn() {
|
|
isFindingSpawn = true;
|
|
int xSpawn = CHUNK_CACHE_WIDTH * CHUNK_WIDTH / 2; // (Level.MAX_LEVEL_SIZE - 100) * 0;
|
|
int ySpawn = 64;
|
|
int zSpawn = CHUNK_CACHE_WIDTH * CHUNK_DEPTH / 2; // (Level.MAX_LEVEL_SIZE - 100) * 0;
|
|
while (!dimension->isValidSpawn(xSpawn, zSpawn)) {
|
|
xSpawn += random.nextInt(32) - random.nextInt(32);
|
|
zSpawn += random.nextInt(32) - random.nextInt(32);
|
|
|
|
if (xSpawn < 4) xSpawn += 32;
|
|
if (xSpawn >= LEVEL_WIDTH-4) xSpawn -= 32;
|
|
if (zSpawn < 4) zSpawn += 32;
|
|
if (zSpawn >= LEVEL_DEPTH-4) zSpawn -= 32;
|
|
}
|
|
levelData.setSpawn(xSpawn, ySpawn, zSpawn);
|
|
isFindingSpawn = false;
|
|
}
|
|
|
|
/*public*/
|
|
void Level::validateSpawn() {
|
|
if (levelData.getYSpawn() <= 0) {
|
|
levelData.setYSpawn(64);
|
|
}
|
|
int xSpawn = levelData.getXSpawn();
|
|
int zSpawn = levelData.getZSpawn();
|
|
while (getTopTile(xSpawn, zSpawn) == 0 || getTopTile(xSpawn, zSpawn) == Tile::invisible_bedrock->id) {
|
|
xSpawn += random.nextInt(8) - random.nextInt(8);
|
|
zSpawn += random.nextInt(8) - random.nextInt(8);
|
|
|
|
if (xSpawn < 4) xSpawn += 8;
|
|
if (xSpawn >= LEVEL_WIDTH-4) xSpawn -= 8;
|
|
if (zSpawn < 4) zSpawn += 8;
|
|
if (zSpawn >= LEVEL_DEPTH-4) zSpawn -= 8;
|
|
}
|
|
levelData.setXSpawn(xSpawn);
|
|
levelData.setZSpawn(zSpawn);
|
|
}
|
|
|
|
int Level::getTopTile(int x, int z) {
|
|
int y = 63;
|
|
while (!isEmptyTile(x, y + 1, z)) {
|
|
y++;
|
|
}
|
|
return getTile(x, y, z);
|
|
}
|
|
|
|
int Level::getTopTileY(int x, int z) {
|
|
int y = 63;
|
|
while (!isEmptyTile(x, y + 1, z)) {
|
|
y++;
|
|
}
|
|
return y;
|
|
}
|
|
//
|
|
// void clearLoadedPlayerData() {
|
|
// }
|
|
//
|
|
// void save(bool force, ProgressListener progressListener) {
|
|
// if (!chunkSource.shouldSave()) return;
|
|
//
|
|
// if (progressListener != NULL) progressListener.progressStartNoAbort("Saving level");
|
|
// saveLevelData();
|
|
//
|
|
// if (progressListener != NULL) progressListener.progressStage("Saving chunks");
|
|
// chunkSource.save(force, progressListener);
|
|
// }
|
|
//
|
|
|
|
//void Level::saveAllChunks() {
|
|
// _chunkSource->saveAll();
|
|
//}
|
|
|
|
void Level::saveLevelData() {
|
|
levelStorage->saveLevelData(levelData, &players);
|
|
}
|
|
|
|
// bool pauseSave(int step) {
|
|
// if (!chunkSource.shouldSave()) return true;
|
|
// if (step == 0) saveLevelData();
|
|
// return chunkSource.save(false, NULL);
|
|
// }
|
|
|
|
//void Level::savePlayerData() {
|
|
// levelStorage->savePlayerData(&levelData, players);
|
|
//}
|
|
|
|
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 < 0) return 0;
|
|
if (y >= DEPTH) return 0;
|
|
//if (z == 128) {
|
|
// int a = 0;
|
|
//}
|
|
//LOGI("%d ", z);
|
|
return getChunk(x >> 4, z >> 4)->getTile(x & 15, y, z & 15);
|
|
}
|
|
|
|
bool Level::isEmptyTile(int x, int y, int z) {
|
|
return getTile(x, y, z) == 0;
|
|
}
|
|
|
|
bool Level::hasChunkAt(int x, int y, int z) {
|
|
if (y < 0 || y >= Level::DEPTH) return false;
|
|
return hasChunk(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);
|
|
}
|
|
|
|
bool Level::hasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1) {
|
|
if (y1 < 0 || y0 >= Level::DEPTH) 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;
|
|
}
|
|
|
|
bool Level::hasChunk(int x, int z) {
|
|
return _chunkSource->hasChunk(x, z);
|
|
}
|
|
|
|
LevelChunk* Level::getChunkAt(int x, int z) {
|
|
return getChunk(x >> 4, z >> 4);
|
|
}
|
|
|
|
LevelChunk* Level::getChunk(int x, int z) {
|
|
return _chunkSource->getChunk(x, z);
|
|
}
|
|
|
|
bool Level::setTileAndDataNoUpdate(int x, int y, int z, int tile, 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 >= DEPTH) return false;
|
|
LevelChunk* c = getChunk(x >> 4, z >> 4);
|
|
return c->setTileAndData(x & 15, y, z & 15, tile, data);
|
|
}
|
|
|
|
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 >= DEPTH) return false;
|
|
LevelChunk* c = getChunk(x >> 4, z >> 4);
|
|
return c->setTile(x & 15, y, z & 15, tile);
|
|
}
|
|
|
|
const 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 >= DEPTH) 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) {
|
|
//// @newway
|
|
//if (setDataNoUpdate(x, y, z, data)) {
|
|
// int t = getTile(x, y, z);
|
|
// if (Tile::sendTileData[t]) {
|
|
// tileUpdated(x, y, z, t);
|
|
// } else {
|
|
// updateNeighborsAt(x, y, z, t);
|
|
// }
|
|
//}
|
|
|
|
// @oldway
|
|
if (setDataNoUpdate(x, y, z, data)) {
|
|
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 >= DEPTH) return false;
|
|
LevelChunk* c = getChunk(x >> 4, z >> 4);
|
|
x &= 15;
|
|
z &= 15;
|
|
|
|
//return c->setData(x, y, z, data);
|
|
|
|
if (c->getData(x, y, z) != data) {
|
|
c->setData(x, y, z, data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Level::setTile(int x, int y, int z, int tile) {
|
|
if (setTileNoUpdate(x, y, z, tile)) {
|
|
//printf("TILE UPDATED %d, %d, %d\n", x, y, z);
|
|
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) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->tileChanged(x, y, z);
|
|
}
|
|
}
|
|
|
|
/*protected*/
|
|
void Level::tileUpdated(int x, int y, int z, int tile) {
|
|
sendTileUpdated(x, y, z);
|
|
this->updateNeighborsAt(x, y, z, tile);
|
|
}
|
|
|
|
void Level::lightColumnChanged(int x, int z, int y0, int y1) {
|
|
if (y0 > y1) {
|
|
int tmp = y1;
|
|
y1 = y0;
|
|
y0 = tmp;
|
|
}
|
|
setTilesDirty(x, y0, z, x, y1, z);
|
|
}
|
|
|
|
void Level::setTileDirty(int x, int y, int z) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->setTilesDirty(x, y, z, x, y, z);
|
|
}
|
|
}
|
|
|
|
void Level::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->setTilesDirty(x0, y0, z0, x1, y1, z1);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/*private*/ 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::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);
|
|
if (id == Tile::stoneSlabHalf->id || id == Tile::farmland->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;
|
|
}
|
|
}
|
|
|
|
if (y < 0) return 0;
|
|
if (y >= DEPTH) {
|
|
int br = MAX_BRIGHTNESS - skyDarken;
|
|
if (br < 0) br = 0;
|
|
return br;
|
|
}
|
|
|
|
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 (y < 0) return false;
|
|
if (y >= DEPTH) 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);
|
|
}
|
|
|
|
BiomeSource* Level::getBiomeSource() {
|
|
return dimension->biomeSource;
|
|
}
|
|
Biome* Level::getBiome( int x, int z ) {
|
|
return dimension->biomeSource->getBiome(x, z);
|
|
}
|
|
|
|
void Level::updateLightIfOtherThan(const LightLayer& 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) {
|
|
updateLight(layer, x, y, z, x, y, z);
|
|
}
|
|
}
|
|
|
|
int Level::getBrightness(const LightLayer& layer, int x, int y, int z) {
|
|
if (y < 0 || y >= DEPTH/* || x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z > MAX_LEVEL_SIZE*/) {
|
|
return layer.surrounding;
|
|
}
|
|
int xc = x >> 4;
|
|
int zc = z >> 4;
|
|
if (!hasChunk(xc, zc)) return 0;
|
|
LevelChunk* c = getChunk(xc, zc);
|
|
return c->getBrightness(layer, x & 15, y, z & 15);
|
|
}
|
|
|
|
void Level::setBrightness(const LightLayer& layer, int x, int y, int z, int brightness) {
|
|
//if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z > MAX_LEVEL_SIZE) {
|
|
// return;
|
|
//}
|
|
if (y < 0) return;
|
|
if (y >= DEPTH) return;
|
|
if (!hasChunk(x >> 4, z >> 4)) return;
|
|
LevelChunk* c = getChunk(x >> 4, z >> 4);
|
|
c->setBrightness(layer, x & 15, y, z & 15, brightness);
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->tileBrightnessChanged(x, y, z);
|
|
}
|
|
}
|
|
|
|
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(const Vec3& a, const Vec3& b) {
|
|
// return clip(a, b, false);
|
|
//}
|
|
|
|
HitResult Level::clip(const Vec3& A, const Vec3& b, bool liquid /*= false*/, bool solidOnly /*= false*/) {
|
|
static Stopwatch sw;
|
|
//sw.printEvery(1000, "clip");
|
|
SwStartStopper w(sw);
|
|
|
|
if (A.x != A.x || A.y != A.y || A.z != A.z) return HitResult(); //@kludge This is actually a way to do it
|
|
if (b.x != b.x || b.y != b.y || b.z != b.z) return HitResult();
|
|
|
|
Vec3 a(A);
|
|
|
|
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 maxIterations = 200;
|
|
while (maxIterations-- >= 0) {
|
|
if (a.x != a.x || a.y != a.y || a.z != a.z)
|
|
return HitResult();
|
|
if (xTile0 == xTile1 && yTile0 == yTile1 && zTile0 == zTile1)
|
|
return HitResult();
|
|
|
|
float xClip = 999;
|
|
float yClip = 999;
|
|
float zClip = 999;
|
|
|
|
if (xTile1 > xTile0) xClip = xTile0 + 1.000f;
|
|
if (xTile1 < xTile0) xClip = xTile0 + 0.000f;
|
|
|
|
if (yTile1 > yTile0) yClip = yTile0 + 1.000f;
|
|
if (yTile1 < yTile0) yClip = yTile0 + 0.000f;
|
|
|
|
if (zTile1 > zTile0) zClip = zTile0 + 1.000f;
|
|
if (zTile1 < zTile0) zClip = zTile0 + 0.000f;
|
|
|
|
float xDist = 999;
|
|
float yDist = 999;
|
|
float zDist = 999;
|
|
|
|
float xd = b.x - a.x;
|
|
float yd = b.y - a.y;
|
|
float zd = b.z - a.z;
|
|
|
|
if (xClip != 999) xDist = (xClip - a.x) / xd;
|
|
if (yClip != 999) yDist = (yClip - a.y) / yd;
|
|
if (zClip != 999) 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(a.x, a.y, a.z);
|
|
xTile0 = (int) (tPos.x = (float)Mth::floor(a.x));
|
|
if (face == 5) {
|
|
xTile0--;
|
|
tPos.x++;
|
|
}
|
|
yTile0 = (int) (tPos.y = (float)Mth::floor(a.y));
|
|
if (face == 1) {
|
|
yTile0--;
|
|
tPos.y++;
|
|
}
|
|
zTile0 = (int) (tPos.z = (float)Mth::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)) {
|
|
if(xTile0 >= 0 && zTile0 >= 0 && xTile0 < LEVEL_WIDTH && zTile0 < LEVEL_WIDTH) {
|
|
HitResult r = tile->clip(this, xTile0, yTile0, zTile0, a, b);
|
|
if (r.isHit()) return r;
|
|
}
|
|
}
|
|
}
|
|
return HitResult();
|
|
}
|
|
|
|
void Level::playSound(Entity* entity, const std::string& name, float volume, float pitch) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->playSound(name, entity->x, entity->y - entity->heightOffset, entity->z, volume, pitch);
|
|
}
|
|
}
|
|
|
|
void Level::playSound(float x, float y, float z, const std::string& name, float volume, float pitch) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->playSound(name, x, y, z, volume, pitch);
|
|
}
|
|
}
|
|
|
|
void Level::levelEvent(Player* source, int type, int x, int y, int z, int data) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->levelEvent(source, type, x, y, z, data);
|
|
}
|
|
}
|
|
|
|
void Level::tileEvent(int x, int y, int z, int b0, int b1) {
|
|
int t = getTile(x, y, z);
|
|
if (t > 0) Tile::tiles[t]->triggerEvent(this, x, y, z, b0, b1);
|
|
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->tileEvent(x, y, z, b0, b1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// void playStreamingMusic(String name, int x, int y, int z) {
|
|
// for (unsigned int i = 0; i < listeners.size(); i++) {
|
|
// listeners.get(i).playStreamingMusic(name, x, y, z);
|
|
// }
|
|
// }
|
|
//
|
|
// void playMusic(float x, float y, float z, String string, float volume) {
|
|
// }
|
|
//
|
|
|
|
void Level::addParticle(const std::string& id, float x, float y, float z, float xd, float yd, float zd, int data /* = 0 */) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++)
|
|
_listeners[i]->addParticle(id, x, y, z, xd, yd, zd, data);
|
|
}
|
|
|
|
void Level::addParticle(ParticleType::Id id, float x, float y, float z, float xd, float yd, float zd, int data /* = 0 */) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++)
|
|
_listeners[i]->addParticle(id, x, y, z, xd, yd, zd, data);
|
|
}
|
|
|
|
bool Level::addEntity(Entity* e) {
|
|
Entity* prev = getEntity(e->entityId);
|
|
if (prev)
|
|
removeEntity(prev);
|
|
|
|
int xc = Mth::floor(e->x / 16.f);
|
|
int zc = Mth::floor(e->z / 16.f);
|
|
|
|
//bool forced = false;
|
|
//if (e->isPlayer()) {
|
|
// forced = true;
|
|
//}
|
|
bool forced = true;
|
|
|
|
if (forced/* || hasChunk(xc, zc)*/) {
|
|
if (e->isPlayer() && std::find(players.begin(), players.end(), e) == players.end()) {
|
|
players.push_back( (Player*) e );
|
|
}
|
|
getChunk(xc, zc)->addEntity(e);
|
|
entities.push_back(e);
|
|
entityIdLookup[e->entityId] = e;
|
|
entityAdded(e);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Entity* Level::getEntity(int entityId)
|
|
{
|
|
// TODO: Lookup map
|
|
/*for (unsigned int i = 0; i < entities.size(); i++)
|
|
{
|
|
if (entities[i]->entityId == entityId)
|
|
{
|
|
return entities[i];
|
|
}
|
|
}
|
|
return NULL;*/
|
|
EntityMap::const_iterator cit = entityIdLookup.find(entityId);
|
|
return (cit != entityIdLookup.end())? cit->second : NULL;
|
|
}
|
|
|
|
/*protected*/
|
|
void Level::entityAdded(Entity* e) {
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->entityAdded(e);
|
|
}
|
|
}
|
|
|
|
/*protected*/
|
|
void Level::entityRemoved(Entity* e) {
|
|
for (unsigned int j = 0; j < _listeners.size(); j++) {
|
|
_listeners[j]->entityRemoved(e);
|
|
}
|
|
}
|
|
|
|
Mob* Level::getMob(int entityId)
|
|
{
|
|
Entity* entity = getEntity(entityId);
|
|
return (entity && entity->isMob())? (Mob*)entity : NULL;
|
|
}
|
|
|
|
void Level::removeEntity(Entity* e) {
|
|
e->remove();
|
|
if (e->isPlayer() && e->reallyRemoveIfPlayer) {
|
|
Util::remove(players, (Player*) e);
|
|
}
|
|
}
|
|
|
|
void Level::tileEntityChanged(int x, int y, int z, TileEntity* te) {
|
|
if (this->hasChunkAt(x, y, z)) {
|
|
getChunkAt(x, z)->markUnsaved();
|
|
}
|
|
for (unsigned int i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->tileEntityChanged(x, y, z, te);
|
|
}
|
|
}
|
|
|
|
|
|
//void Level::removeEntityImmediately(Entity* e) {
|
|
// e->remove();
|
|
//
|
|
// if (e->isPlayer()) {
|
|
// Util::remove(players, (Player*)e);
|
|
// }
|
|
//
|
|
// int xc = e->xChunk;
|
|
// int zc = e->zChunk;
|
|
// if (e->inChunk && hasChunk(xc, zc)) {
|
|
// getChunk(xc, zc)->removeEntity(e);
|
|
// }
|
|
//
|
|
// Util::remove(entities, e);
|
|
//}
|
|
|
|
Biome::MobSpawnerData Level::getRandomMobSpawnAt(const MobCategory& mobCategory, int x, int y, int z) {
|
|
Biome::MobList mobList = _chunkSource->getMobsAt(mobCategory, x, y, z);
|
|
if (mobList.empty()) return Biome::MobSpawnerData();
|
|
|
|
Biome::MobSpawnerData* data = (Biome::MobSpawnerData*) WeighedRandom::getRandomItem(&random, mobList);
|
|
if (!data)
|
|
return Biome::MobSpawnerData();
|
|
return *data;
|
|
}
|
|
|
|
void Level::addListener(LevelListener* listener) {
|
|
_listeners.push_back(listener);
|
|
}
|
|
|
|
void Level::removeListener(LevelListener* listener) {
|
|
ListenerList::iterator it = std::find(_listeners.begin(), _listeners.end(), listener);
|
|
_listeners.erase(it);
|
|
}
|
|
|
|
std::vector<AABB>& Level::getCubes(const Entity* source, const AABB& box_) { //@attn: check the AABB* new/delete stuff
|
|
boxes.clear();
|
|
const AABB* box = &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);
|
|
|
|
for (int x = x0; x < x1; x++)
|
|
for (int z = z0; z < z1; z++) {
|
|
if (hasChunkAt(x, Level::DEPTH / 2, 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);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//int breakPoint = 0;
|
|
}
|
|
}
|
|
/*
|
|
float r = 0.25;
|
|
List<Entity> ee = getEntities(source, box.grow(r, r, r));
|
|
for (int i = 0; i < ee.size(); i++) {
|
|
AABB collideBox = ee.get(i).getCollideBox();
|
|
if (collideBox != NULL && collideBox.intersects(box)) {
|
|
boxes.add(collideBox);
|
|
}
|
|
|
|
collideBox = source.getCollideAgainstBox(ee.get(i));
|
|
if (collideBox != NULL && collideBox.intersects(box)) {
|
|
boxes.add(collideBox);
|
|
}
|
|
}
|
|
*/
|
|
|
|
return boxes;
|
|
}
|
|
|
|
int Level::getSkyDarken(float a) {
|
|
float td = getTimeOfDay(a);
|
|
|
|
float br = 1 - (Mth::cos(td * Mth::PI * 2) * 2 + 0.5f);
|
|
if (br < 0.0f) br = 0.0f;
|
|
if (br > 0.80f) br = 0.80f; //@note; was 1.0f
|
|
return ((int) (br * 11));
|
|
}
|
|
|
|
Vec3 Level::getSkyColor(Entity* source, float a) {
|
|
float td = getTimeOfDay(a);
|
|
|
|
float br = Mth::cos(td * Mth::PI * 2) * 2 + 0.5f;
|
|
if (br < 0.0f) br = 0.0f;
|
|
if (br > 0.75f) br = 0.75f; //@note; was 1.0f
|
|
|
|
int xx = Mth::floor(source->x);
|
|
int zz = Mth::floor(source->z);
|
|
// float temp = 0.5;//(float) getBiomeSource().getTemperature(xx, zz); // unused in normal pe too, just hardcoded temp values for some reason.
|
|
float temp = (float) getBiomeSource()->getTemperature(xx, zz);
|
|
int skyColor;
|
|
if (dimension->FogType == 1){ // 1 is java styled fog which should use temperatures!
|
|
skyColor = getBiomeSource()->getBiome(xx, zz)->getSkyColor(temp);
|
|
}
|
|
else { // otherwise default to the normal pocket edition skycolor
|
|
|
|
skyColor = 0x3070ff;
|
|
}
|
|
|
|
// int skyColor = 0x3070ff;//getBiomeSource().getBiome(xx, zz).getSkyColor(temp); // This is the vanilla way, add it as OPTION_SKY - shredder
|
|
|
|
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;
|
|
|
|
return Vec3(r, g, b);
|
|
}
|
|
|
|
Vec3 Level::getCloudColor( float a ) {
|
|
float td = getTimeOfDay(a);
|
|
|
|
float br = Mth::cos(td * Mth::PI * 2) * 2.0f + 0.5f;
|
|
if (br < 0.0f) br = 0.0f;
|
|
if (br > 1.0f) br = 1.0f;
|
|
long cloudColor = 0xffffff;
|
|
float r = ((cloudColor >> 16) & 0xff) / 255.0f;
|
|
float g = ((cloudColor >> 8) & 0xff) / 255.0f;
|
|
float b = ((cloudColor) & 0xff) / 255.0f;
|
|
|
|
float rainLevel = 0;//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 = 0; //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(r, g, b);
|
|
}
|
|
|
|
float Level::getTimeOfDay(float a) {
|
|
return dimension->getTimeOfDay(levelData.getTime(), a);
|
|
//int time = 20 * levelData.getTime();
|
|
//float out = dimension->getTimeOfDay(time, a);
|
|
//LOGI("getTime: %d, dayTime: %f\n", time, out);
|
|
//return out;
|
|
}
|
|
|
|
float Level::getSunAngle(float a) {
|
|
float td = getTimeOfDay(a);
|
|
return td * Mth::PI * 2;
|
|
}
|
|
|
|
//Vec3 Level::getCloudColor(float a) {
|
|
// float td = getTimeOfDay(a);
|
|
//
|
|
// float br = Mth::cos(td * Mth::PI * 2) * 2.0f + 0.5f;
|
|
// if (br < 0.f) br = 0;
|
|
// if (br > 1.f) br = 1;
|
|
//
|
|
// float r = ((cloudColor >> 16) & 0xff) / 255.0f;
|
|
// float g = ((cloudColor >> 8) & 0xff) / 255.0f;
|
|
// float b = ((cloudColor) & 0xff) / 255.0f;
|
|
//
|
|
// r *= br * 0.90f + 0.10f;
|
|
// g *= br * 0.90f + 0.10f;
|
|
// b *= br * 0.85f + 0.15f;
|
|
//
|
|
// return Vec3(r, g, b);
|
|
//}
|
|
|
|
Vec3 Level::getFogColor(float a) {
|
|
float td = getTimeOfDay(a);
|
|
return dimension->getFogColor(td, a);
|
|
}
|
|
|
|
int Level::getTopSolidBlock(int x, int z) {
|
|
LevelChunk* levelChunk = getChunkAt(x, z);
|
|
|
|
int y = Level::DEPTH - 1;
|
|
|
|
while (getMaterial(x, y, z)->blocksMotion() && y > 0) {
|
|
y--;
|
|
}
|
|
|
|
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->isLiquid())*/
|
|
|| !(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 * Mth::PI * 2) * 2 + 0.75f);
|
|
if (br < 0.f) br = 0;
|
|
if (br > 1.f) br = 1;
|
|
|
|
return br * br * 0.5f;
|
|
}
|
|
|
|
void Level::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay) {
|
|
TickNextTickData td(x, y, z, tileId);
|
|
int r = 8;
|
|
if (instaTick) {
|
|
if (hasChunksAt(td.x - r, td.y - r, td.z - r, td.x + r, td.y + r, td.z + r)) {
|
|
int id = getTile(td.x, td.y, td.z);
|
|
if (id == td.tileId && id > 0) {
|
|
Tile::tiles[id]->tick(this, td.x, td.y, td.z, &random);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r)) {
|
|
if (tileId > 0) {
|
|
td.setDelay(tickDelay + levelData.getTime());
|
|
}
|
|
if (_tickNextTickSet.find(td) == _tickNextTickSet.end()) {
|
|
_tickNextTickSet.insert(td);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Level::tickEntities() {
|
|
TIMER_PUSH("entities");
|
|
|
|
TIMER_PUSH("remove");
|
|
//Util::removeAll<Entity*>(entities, _entitiesToRemove);
|
|
// for (int j = 0; j < (int)_entitiesToRemove.size(); j++) {
|
|
// Entity* e = _entitiesToRemove[j];
|
|
// int xc = e->xChunk;
|
|
// int zc = e->zChunk;
|
|
// if (e->inChunk && hasChunk(xc, zc)) {
|
|
// getChunk(xc, zc)->removeEntity(e);
|
|
// }
|
|
// }
|
|
// for (int j = 0; j < (int)_entitiesToRemove.size(); j++) {
|
|
// entityRemoved(_entitiesToRemove[j]);
|
|
// //LOGI("a1 &e@delt: %p", _entitiesToRemove[j]);
|
|
// delete _entitiesToRemove[j];
|
|
// //LOGI("a2");
|
|
// }
|
|
// _entitiesToRemove.clear();
|
|
|
|
EntityList pendingRemovedEntities;
|
|
std::vector<Zombie*> zombies;
|
|
|
|
TIMER_POP_PUSH("regular");
|
|
for (unsigned int i = 0; i < entities.size(); i++) {
|
|
Entity* e = entities[i];
|
|
|
|
if (!e->removed) {
|
|
tick(e);
|
|
if (e->getEntityTypeId() == MobTypes::Zombie) {
|
|
zombies.push_back((Zombie*)e);
|
|
((Zombie*)e)->setUseNewAi(false); // @note: this is set under
|
|
}
|
|
}
|
|
|
|
TIMER_PUSH("remove");
|
|
if (e->removed && (!e->isPlayer() || e->reallyRemoveIfPlayer)) {
|
|
int xc = e->xChunk;
|
|
int zc = e->zChunk;
|
|
if (e->inChunk && hasChunk(xc, zc)) {
|
|
getChunk(xc, zc)->removeEntity(e);
|
|
}
|
|
entityIdLookup.erase(e->entityId);
|
|
entities.erase(entities.begin() + (i--));
|
|
entityRemoved(e);
|
|
pendingRemovedEntities.push_back(e);
|
|
}
|
|
TIMER_POP();
|
|
}
|
|
|
|
TIMER_POP_PUSH("remove");
|
|
for (unsigned int i = 0; i < pendingRemovedEntities.size(); ++i) {
|
|
Entity* e = pendingRemovedEntities[i];
|
|
//if (!e->isPlayer()) // @todo: remove fast as heck
|
|
if (e->isPlayer())
|
|
_pendingPlayerRemovals.push_back( PRInfo(e, 16) );
|
|
else
|
|
delete e;
|
|
|
|
} pendingRemovedEntities.clear();
|
|
|
|
// Yes, I know this is bad, but it's extremely few players
|
|
// to remove anyway.
|
|
for (int i = (int)_pendingPlayerRemovals.size()-1; i >= 0; --i) {
|
|
PRInfo& pr = _pendingPlayerRemovals[i];
|
|
if (--pr.ticks <= 0) {
|
|
LOGI("deleting: %p\n", pr.e);
|
|
delete pr.e;
|
|
_pendingPlayerRemovals.erase(_pendingPlayerRemovals.begin() + i);
|
|
}
|
|
}
|
|
|
|
//setZombieAi(zombies);
|
|
|
|
TIMER_POP_PUSH("tileEntities");
|
|
updatingTileEntities = true;
|
|
//LOGI("num tile entities %d\n", tileEntities.size());
|
|
for (unsigned int i = 0; i < tileEntities.size(); ++i) {
|
|
TileEntity* te = tileEntities[i];
|
|
if (!te->isRemoved() && te->level != NULL) {
|
|
if (hasChunkAt(te->x, te->y, te->z)) {
|
|
te->tick();
|
|
}
|
|
}
|
|
|
|
if (/*te->isFinished() || */te->isRemoved()) {
|
|
|
|
for (int j = 0; j < 10; ++j)
|
|
LOGI("REmoved tile-entity @ %d, %d, %d\n", te->x, te->y, te->z);
|
|
|
|
tileEntities.erase(tileEntities.begin() + (i--));
|
|
|
|
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);
|
|
}
|
|
}
|
|
delete te;
|
|
}
|
|
}
|
|
updatingTileEntities = false;
|
|
|
|
TIMER_POP_PUSH("pendingTileEntities");
|
|
if (!pendingTileEntities.empty()) {
|
|
for (unsigned int i = 0; i < pendingTileEntities.size(); ++i) {
|
|
TileEntity* e = pendingTileEntities[i];
|
|
if (!e->isRemoved()) {
|
|
bool found = false;
|
|
for (unsigned int j = 0; j < tileEntities.size(); ++j) {
|
|
if (tileEntities[j] == e) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
int xx = e->x, yy = e->y, zz = e->z;
|
|
|
|
LevelChunk* lc = getChunk(xx >> 4, zz >> 4);
|
|
bool has = lc && lc->hasTileEntityAt(e);
|
|
if (!found) {
|
|
if (!has) tileEntities.push_back(e);
|
|
else {
|
|
delete e;
|
|
e = NULL;
|
|
}
|
|
}
|
|
|
|
if (e && lc) lc->setTileEntity(xx & 15, yy, zz & 15, e);
|
|
sendTileUpdated(xx, yy, zz);
|
|
}
|
|
}
|
|
pendingTileEntities.clear();
|
|
}
|
|
|
|
|
|
TIMER_POP();
|
|
TIMER_POP();
|
|
}
|
|
|
|
class DistanceEntitySorter {
|
|
Vec3 c;
|
|
public:
|
|
DistanceEntitySorter(float x, float y, float z) : c(x, y, z) {}
|
|
bool operator() (const Entity* c0, const Entity* c1) {
|
|
const float d0 = c.distanceToSqr(c0->x, c0->y, c0->z);
|
|
const float d1 = c.distanceToSqr(c1->x, c1->y, c1->z);
|
|
return d0 < d1;
|
|
}
|
|
};
|
|
|
|
void Level::setZombieAi(std::vector<Zombie*>& zombies) {
|
|
unsigned int size = zombies.size();
|
|
const int NumSmartZombiesPerPlayer = 0;
|
|
if (size <= NumSmartZombiesPerPlayer) {
|
|
for (unsigned int i = 0; i < size; ++i)
|
|
zombies[i]->setUseNewAi(true);
|
|
return;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < players.size(); ++i) {
|
|
Player* p = players[i];
|
|
DistanceEntitySorter sorter(p->x, p->y, p->z);
|
|
std::nth_element(zombies.begin(), zombies.begin() + NumSmartZombiesPerPlayer, zombies.end(), sorter);
|
|
for (int j = 0; j < NumSmartZombiesPerPlayer; ++j)
|
|
if (zombies[j]->distanceToSqr(p) < 32*32)
|
|
zombies[j]->setUseNewAi(true);
|
|
}
|
|
}
|
|
|
|
void Level::tick(Entity* e) {
|
|
tick(e, true);
|
|
}
|
|
|
|
void Level::tick(Entity* e, bool actual) {
|
|
int xc = Mth::floor(e->x);
|
|
int zc = Mth::floor(e->z);
|
|
int r = 32;
|
|
if (actual && !hasChunksAt(xc - r, 0, zc - r, xc + r, 128, zc + r)) {
|
|
return;
|
|
}
|
|
|
|
e->xOld = e->x;
|
|
e->yOld = e->y;
|
|
e->zOld = e->z;
|
|
e->yRotO = e->yRot;
|
|
e->xRotO = e->xRot;
|
|
|
|
if (actual && e->inChunk) {
|
|
e->tick();
|
|
}
|
|
|
|
TIMER_PUSH("chunkCheck");
|
|
// SANITY!!
|
|
if (e->x != e->x) e->x = e->xOld; // @note: checking for NaN, not sure about Infinite
|
|
if (e->y != e->y) e->y = e->yOld;
|
|
if (e->z != e->z) e->z = e->zOld;
|
|
if (e->xRot != e->xRot) e->xRot = e->xRotO;
|
|
if (e->yRot != e->yRot) e->yRot = e->yRotO;
|
|
|
|
int xcn = Mth::floor(e->x / 16.0f);
|
|
int ycn = Mth::floor(e->y / 16.0f);
|
|
int zcn = Mth::floor(e->z / 16.0f);
|
|
|
|
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;
|
|
getChunk(xcn, zcn)->addEntity(e);
|
|
} else {
|
|
e->inChunk = false;
|
|
}
|
|
}
|
|
TIMER_POP();
|
|
|
|
// Save player info every n:th second
|
|
const float now = getTimeS();
|
|
if (now - _lastSavedPlayerTime >= 30) {
|
|
saveLevelData();
|
|
_lastSavedPlayerTime = now;
|
|
}
|
|
}
|
|
|
|
bool Level::isUnobstructed(const AABB& aabb) {
|
|
EntityList& entities = getEntities(NULL, aabb);
|
|
for (unsigned int i = 0; i < entities.size(); i++) {
|
|
Entity* e = entities[i];
|
|
if (!e->removed && e->blocksBuilding) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Level::containsAnyLiquid(const 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;
|
|
}
|
|
|
|
bool Level::containsFireTile(const 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*)(Tile::fire))->id
|
|
|| t == Tile::lava->id
|
|
|| t == Tile::calmLava->id) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Level::containsMaterial(const AABB& box, const 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(const AABB& box, const 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);
|
|
float yh1 = (float)(y + 1);
|
|
if (data < 8) {
|
|
yh1 = (float)y + 1.0f - (float)data / 8.0f;
|
|
}
|
|
if (yh1 >= box.y0) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* in java, this returns an Explosion */
|
|
void Level::explode(Entity* source, float x, float y, float z, float r) {
|
|
explode(source, x, y, z, r, false);
|
|
}
|
|
|
|
/* in java, this returns an Explosion */
|
|
void Level::explode(Entity* source, float x, float y, float z, float r, bool fire) {
|
|
if (!isClientSide) {
|
|
Explosion explosion(this, source, x, y, z, r);
|
|
explosion.fire = fire;
|
|
explosion.explode();
|
|
explosion.finalizeExplosion();
|
|
ExplodePacket packet(x, y, z, r, explosion.toBlow);
|
|
raknetInstance->send(packet);
|
|
}
|
|
}
|
|
|
|
float Level::getSeenPercent(const Vec3& center, const AABB& bb) {
|
|
float xs = 1.0f / ((bb.x1 - bb.x0) * 2 + 1);
|
|
float ys = 1.0f / ((bb.y1 - bb.y0) * 2 + 1);
|
|
float zs = 1.0f / ((bb.z1 - bb.z0) * 2 + 1);
|
|
int hits = 0;
|
|
int count = 0;
|
|
for (float xx = 0; xx <= 1; xx += xs)
|
|
for (float yy = 0; yy <= 1; yy += ys)
|
|
for (float zz = 0; zz <= 1; zz += zs) {
|
|
float x = bb.x0 + (bb.x1 - bb.x0) * xx;
|
|
float y = bb.y0 + (bb.y1 - bb.y0) * yy;
|
|
float z = bb.z0 + (bb.z1 - bb.z0) * zz;
|
|
if (!clip(Vec3(x, y, z), center).isHit()) hits++;
|
|
count++;
|
|
}
|
|
|
|
return hits / (float) count;
|
|
}
|
|
|
|
bool Level::isSolidBlockingTile(int x, int y, int z)
|
|
{
|
|
Tile* tile = Tile::tiles[getTile(x, y, z)];
|
|
if (tile == NULL) return false;
|
|
return tile->material->isSolidBlocking() && tile->isCubeShaped();
|
|
}
|
|
|
|
bool Level::isSolidRenderTile(int x, int y, int z) {
|
|
Tile* tile = Tile::tiles[getTile(x, y, z)];
|
|
if (tile == NULL) return false;
|
|
return tile->isSolidRender();
|
|
}
|
|
|
|
void Level::extinguishFire(int x, int y, int z, int face) {
|
|
switch (face) {
|
|
case Facing::DOWN : y--; break;
|
|
case Facing::UP : y++; break;
|
|
case Facing::NORTH: z--; break;
|
|
case Facing::SOUTH: z++; break;
|
|
case Facing::WEST : x--; break;
|
|
case Facing::EAST : x++; break;
|
|
}
|
|
|
|
if (getTile(x, y, z) == ((Tile*)Tile::fire)->id) {
|
|
//playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.fizz", 0.5f, 2.6f + (random.nextFloat() - random.nextFloat()) * 0.8f);
|
|
setTile(x, y, z, 0);
|
|
}
|
|
}
|
|
// String gatherStats() {
|
|
// return "All: " + this.entities.size();
|
|
// }
|
|
//
|
|
// String gatherChunkSourceStats() {
|
|
// return chunkSource.gatherStats();
|
|
// }
|
|
//
|
|
TileEntity* Level::getTileEntity(int x, int y, int z) {
|
|
LevelChunk* lc = getChunk(x >> 4, z >> 4);
|
|
if (!lc) return NULL;
|
|
|
|
if (TileEntity* tileEntity = lc->getTileEntity(x & 15, y, z & 15))
|
|
return tileEntity;
|
|
|
|
for (unsigned int i = 0; i < pendingTileEntities.size(); ++i) {
|
|
TileEntity* e = pendingTileEntities[i];
|
|
if (!e->isRemoved() && e->x == x && e->y == y && e->z == z)
|
|
return e;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void Level::setTileEntity(int x, int y, int z, TileEntity* tileEntity) {
|
|
if (!tileEntity || tileEntity->isRemoved())
|
|
return;
|
|
|
|
if (updatingTileEntities) {
|
|
tileEntity->x = x;
|
|
tileEntity->y = y;
|
|
tileEntity->z = z;
|
|
pendingTileEntities.push_back(tileEntity);
|
|
} else {
|
|
tileEntities.push_back(tileEntity);
|
|
|
|
LevelChunk* lc = getChunk(x >> 4, z >> 4);
|
|
if (lc != NULL) lc->setTileEntity(x & 15, y, z & 15, tileEntity);
|
|
}
|
|
}
|
|
|
|
void Level::removeTileEntity(int x, int y, int z) {
|
|
TileEntity* te = getTileEntity(x, y, z);
|
|
if (te && updatingTileEntities) {
|
|
te->setRemoved();
|
|
Util::remove(pendingTileEntities, te);
|
|
} else {
|
|
LevelChunk* lc = getChunk(x >> 4, z >> 4);
|
|
if (lc) lc->removeTileEntity(x & 15, y, z & 15);
|
|
|
|
if (te) {
|
|
Util::remove(pendingTileEntities, te);
|
|
Util::remove(tileEntities, te);
|
|
delete te;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// void forceSave(ProgressListener progressListener) {
|
|
// save(true, progressListener);
|
|
// }
|
|
//
|
|
|
|
int Level::getLightsToUpdate() {
|
|
return _lightUpdates.size();
|
|
}
|
|
|
|
bool Level::updateLights() {
|
|
if (_maxRecurse >= 50) {
|
|
return false;
|
|
}
|
|
//static int _MaxSize = 0;
|
|
_maxRecurse++;
|
|
//try {
|
|
int max = 500;
|
|
while ((int)_lightUpdates.size() > 0) {
|
|
if (--max <= 0)
|
|
{
|
|
_maxRecurse--;
|
|
return true;
|
|
}
|
|
LightUpdate l = _lightUpdates.back();
|
|
_lightUpdates.pop_back();
|
|
l.update(this);
|
|
//if ((int)_lightUpdates.size() > _MaxSize)
|
|
//{
|
|
// LOGI("MAX_updsize_light: %d (%d)\n", _lightUpdates.size(), _MaxSize);
|
|
// _MaxSize = _lightUpdates.size();
|
|
//}
|
|
}
|
|
_maxRecurse--;
|
|
return false;
|
|
//} finally {
|
|
//maxRecurse--;
|
|
//}
|
|
}
|
|
|
|
void Level::setUpdateLights(bool doUpdate) {
|
|
_updateLights = doUpdate;
|
|
}
|
|
|
|
void Level::updateLight(const LightLayer& layer, int x0, int y0, int z0, int x1, int y1, int z1) {
|
|
updateLight(layer, x0, y0, z0, x1, y1, z1, true);
|
|
}
|
|
|
|
static int maxLoop = 0;
|
|
|
|
void Level::updateLight(const LightLayer& layer, int x0, int y0, int z0, int x1, int y1, int z1, bool join) {
|
|
if ((dimension->hasCeiling && &layer == &LightLayer::Sky) || !_updateLights) return;
|
|
|
|
maxLoop++;
|
|
//if (x0 < -5 || z0 < -5) LOGI("x, z: %d, %d\n", x0, z0);
|
|
if (maxLoop == 50) {
|
|
maxLoop--;
|
|
return;
|
|
}
|
|
int xm = (x1 + x0) / 2;
|
|
int zm = (z1 + z0) / 2;
|
|
if (!hasChunkAt(xm, Level::DEPTH / 2, zm)) {
|
|
maxLoop--;
|
|
return;
|
|
}
|
|
if (getChunkAt(xm, zm)->isEmpty())
|
|
{
|
|
maxLoop--;
|
|
return;
|
|
}
|
|
int count = _lightUpdates.size();
|
|
if (join) {
|
|
int toCheck = 5;
|
|
if (toCheck > count) toCheck = count;
|
|
for (int i = 0; i < toCheck; i++) {
|
|
LightUpdate& last = _lightUpdates[_lightUpdates.size() - i - 1];
|
|
if (last.layer == &layer && last.expandToContain(x0, y0, z0, x1, y1, z1)) {
|
|
maxLoop--;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
_lightUpdates.push_back(LightUpdate(layer, x0, y0, z0, x1, y1, z1));
|
|
int max = 1000000;
|
|
if ((int)_lightUpdates.size() > max) {
|
|
LOGI("More than %d updates, aborting lighting updates\n", max);
|
|
_lightUpdates.clear();
|
|
}
|
|
maxLoop--;
|
|
}
|
|
//
|
|
// // int xxo, yyo, zzo;
|
|
//
|
|
bool Level::updateSkyBrightness() {
|
|
int newDark = this->getSkyDarken(1);
|
|
if (newDark != skyDarken) {
|
|
skyDarken = newDark;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Level::setSpawnSettings(bool spawnEnemies, bool spawnFriendlies) {
|
|
//this->spawnEnemies = spawnEnemies;
|
|
//this->spawnFriendlies = spawnFriendlies;
|
|
}
|
|
|
|
void Level::animateTick(int xt, int yt, int zt) {
|
|
int r = 16;
|
|
Random animateRandom;
|
|
|
|
for (int i = 0; i < 100; i++) {
|
|
int x = xt + random.nextInt(r) - random.nextInt(r);
|
|
int y = yt + random.nextInt(r) - random.nextInt(r);
|
|
int z = zt + random.nextInt(r) - random.nextInt(r);
|
|
int t = getTile(x, y, z);
|
|
if (t > 0) {
|
|
Tile::tiles[t]->animateTick(this, x, y, z, &animateRandom);
|
|
}
|
|
}
|
|
}
|
|
|
|
EntityList& Level::getEntities(Entity* except, const AABB& bb) {
|
|
_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);
|
|
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);
|
|
}
|
|
}
|
|
return _es;
|
|
}
|
|
|
|
// List<Entity> getEntitiesOfClass(Class<? extends Entity> 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);
|
|
// List<Entity> es = new ArrayList<Entity>();
|
|
// 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);
|
|
// }
|
|
// }
|
|
// return es;
|
|
// }
|
|
|
|
const EntityList& Level::getAllEntities() {
|
|
return entities;
|
|
}
|
|
|
|
// int countInstanceOf(Class<?> clas) {
|
|
// int count = 0;
|
|
// for (int i = 0; i < entities.size(); i++) {
|
|
// Entity e = entities.get(i);
|
|
// if (clas.isAssignableFrom(e.getClass())) count++;
|
|
// }
|
|
// return count;
|
|
// }
|
|
//
|
|
/*
|
|
void Level::addEntities(const EntityList& list) {
|
|
entities.insert(entities.end(), list.begin(), list.end());
|
|
for (int j = 0; j < (int)list.size(); j++) {
|
|
entityAdded(list[j]);
|
|
}
|
|
}
|
|
*/
|
|
|
|
//void Level::removeEntities(const EntityList& list) {
|
|
// _entitiesToRemove.insert(_entitiesToRemove.end(), list.begin(), list.end());
|
|
//}
|
|
|
|
void Level::prepare() {
|
|
while (_chunkSource->tick())
|
|
;
|
|
}
|
|
|
|
bool Level::mayPlace(int tileId, int x, int y, int z, bool ignoreEntities,unsigned char face) {
|
|
int targetType = getTile(x, y, z);
|
|
const 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)) return false;
|
|
if (targetTile == Tile::water || targetTile == Tile::calmWater || targetTile == Tile::lava || targetTile == Tile::calmLava || targetTile == (Tile*)(Tile::fire) || targetTile == Tile::topSnow) targetTile = NULL;
|
|
if (tileId > 0 && targetTile == NULL) {
|
|
if (tile->mayPlace(this, x, y, z, face)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int Level::getSeaLevel() {
|
|
return SEA_LEVEL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// void checkSession() {
|
|
// levelStorage.checkSession();
|
|
// }
|
|
//
|
|
void Level::setTime(long time) {
|
|
this->levelData.setTime(time);
|
|
}
|
|
|
|
long Level::getSeed() {
|
|
return levelData.getSeed();
|
|
}
|
|
|
|
long Level::getTime() {
|
|
return levelData.getTime();
|
|
}
|
|
|
|
Pos Level::getSharedSpawnPos() {
|
|
return Pos(levelData.getXSpawn(), levelData.getYSpawn(), levelData.getZSpawn());
|
|
}
|
|
|
|
void Level::setSpawnPos(Pos spawnPos) {
|
|
levelData.setSpawn(spawnPos.x, spawnPos.y, spawnPos.z);
|
|
}
|
|
|
|
/*
|
|
void Level::ensureAdded(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 (std::find(entities.begin(), entities.end(), entity) == entities.end()) {
|
|
entities.push_back(entity);
|
|
}
|
|
}
|
|
*/
|
|
|
|
bool Level::mayInteract(Player* player, int xt, int yt, int zt) {
|
|
return true;
|
|
}
|
|
|
|
void Level::broadcastEntityEvent(Entity* e, char eventId) {
|
|
if (isClientSide) return;
|
|
|
|
EntityEventPacket packet(e->entityId, eventId);
|
|
raknetInstance->send(packet);
|
|
}
|
|
|
|
/*
|
|
void Level::removeAllPendingEntityRemovals() {
|
|
//Util::removeAll(entities, _entitiesToRemove);
|
|
// //entities.removeAll(entitiesToRemove);
|
|
// for (int j = 0; j < (int)_entitiesToRemove.size(); j++) {
|
|
// Entity* e = _entitiesToRemove[j];
|
|
// int xc = e->xChunk;
|
|
// int zc = e->zChunk;
|
|
// if (e->inChunk && hasChunk(xc, zc)) {
|
|
// getChunk(xc, zc)->removeEntity(e);
|
|
// }
|
|
// }
|
|
|
|
// for (unsigned int j = 0; j < _entitiesToRemove.size(); j++) {
|
|
// entityRemoved(_entitiesToRemove[j]);
|
|
// }
|
|
// _entitiesToRemove.clear();
|
|
|
|
for (unsigned int i = 0; i < entities.size(); i++) {
|
|
Entity* e = entities[i];
|
|
|
|
if (e->removed) {
|
|
int xc = e->xChunk;
|
|
int zc = e->zChunk;
|
|
if (e->inChunk && hasChunk(xc, zc)) {
|
|
getChunk(xc, zc)->removeEntity(e);
|
|
}
|
|
entities.erase( entities.begin() + (i--) );
|
|
entityRemoved(e);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
ChunkSource* Level::getChunkSource() {
|
|
return _chunkSource;
|
|
}
|
|
|
|
// void tileEvent(int x, int y, int z, int b0, int b1) {
|
|
// int t = getTile(x, y, z);
|
|
// if (t > 0) Tile.tiles[t].triggerEvent(this, x, y, z, b0, b1);
|
|
// }
|
|
|
|
LevelStorage* Level::getLevelStorage() {
|
|
return levelStorage;
|
|
}
|
|
|
|
LevelData* Level::getLevelData() {
|
|
return &levelData;
|
|
}
|
|
|
|
void Level::takePicture( TripodCamera* cam, Entity* e )
|
|
{
|
|
for (unsigned int i = 0; i < _listeners.size(); ++i)
|
|
_listeners[i]->takePicture(cam, e);
|
|
}
|
|
|
|
int Level::getEntitiesOfType( int entityType, const AABB& bb, EntityList& list )
|
|
{
|
|
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);
|
|
int count = -(int)list.size();
|
|
for (int xc = xc0; xc <= xc1; xc++)
|
|
for (int zc = zc0; zc <= zc1; zc++) {
|
|
if (hasChunk(xc, zc)) {
|
|
getChunk(xc, zc)->getEntitiesOfType(entityType, bb, list);
|
|
}
|
|
}
|
|
return list.size() - count;
|
|
}
|
|
|
|
int Level::getEntitiesOfClass( int type, const AABB& bb, EntityList& list ) {
|
|
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);
|
|
int count = -(int)list.size();
|
|
for (int xc = xc0; xc <= xc1; xc++)
|
|
for (int zc = zc0; zc <= zc1; zc++) {
|
|
if (hasChunk(xc, zc)) {
|
|
getChunk(xc, zc)->getEntitiesOfClass(type, bb, list);
|
|
}
|
|
}
|
|
return list.size() - count;
|
|
}
|
|
|
|
int Level::countInstanceOfType( int typeId ) {
|
|
int n = 0;
|
|
for (unsigned int i = 0; i < entities.size(); ++i)
|
|
if (typeId == entities[i]->getEntityTypeId())
|
|
++n;
|
|
return n;
|
|
}
|
|
|
|
int Level::countInstanceOfBaseType( int baseTypeId ) {
|
|
if (baseTypeId < MobTypes::BaseEnemy || baseTypeId > MobTypes::BaseWaterCreature) {
|
|
LOGE("Bad typeId sent to Level::countInstanceOf. Note that only Base types (MobTypes::Base*) are supported for now.\n");
|
|
return -1;
|
|
}
|
|
|
|
int n = 0;
|
|
for (unsigned int i = 0; i < entities.size(); ++i)
|
|
if (baseTypeId == entities[i]->getCreatureBaseType())
|
|
++n;
|
|
return n;
|
|
}
|
|
|
|
void Level::dispatchEntityData( Entity* e )
|
|
{
|
|
if (isClientSide) return;
|
|
_pendingEntityData.insert(std::make_pair(e->entityId, e));
|
|
}
|
|
|
|
void Level::saveGame()
|
|
{
|
|
if (levelStorage) {
|
|
levelStorage->saveGame(this);
|
|
saveLevelData();
|
|
}
|
|
}
|
|
|
|
void Level::loadEntities()
|
|
{
|
|
if (levelStorage)
|
|
levelStorage->loadEntities(this, NULL);
|
|
}
|
|
|
|
void Level::updateSkyDarken()
|
|
{
|
|
if (updateSkyBrightness())
|
|
for (unsigned i = 0; i < _listeners.size(); i++) {
|
|
_listeners[i]->skyColorChanged();
|
|
}
|
|
}
|
|
|
|
void Level::removePlayer( Player* player )
|
|
{
|
|
for (unsigned int i = 0; i < players.size(); ++i)
|
|
if (players[i] == player) {
|
|
players.erase( players.begin() + i );
|
|
}
|
|
}
|
|
|
|
int Level::isNightMode() {
|
|
return _nightMode;
|
|
}
|
|
|
|
void Level::setNightMode( bool isNightMode ) {
|
|
_nightMode = isNightMode;
|
|
}
|
|
|
|
bool Level::inRange( int x, int y, int z ) {
|
|
return x >= 0 && x < LEVEL_WIDTH
|
|
&& y >= 0 && y < LEVEL_HEIGHT
|
|
&& z >= 0 && z < LEVEL_DEPTH;
|
|
}
|
|
|
|
//
|
|
// AdventureSettings
|
|
//
|
|
AdventureSettings::AdventureSettings()
|
|
: doTickTime(true),
|
|
noPvP(false),
|
|
noPvM(false),
|
|
noMvP(false),
|
|
immutableWorld(false),
|
|
showNameTags(true)
|
|
{
|
|
}
|