Code:
#include "World.h"
#include "Game.h"
#include "Chunk.h"
#include "Physics.h"
#include "ItemIDs.h"
#include "Point2.h"
#include "Graphics.h"
#include "Player.h"
#define SAFE_DELETE(x) if((x) != NULL) delete (x)
#define SAFE_DELETE_ARRAY(x) if((x) != NULL) delete [] (x)
#define SAFE_RELEASE(x) if((x) != NULL) (x)->Release()
Game* World::cGame = NULL;
World::World(unsigned int width, int oX, int oZ, unsigned int _terrainSpread,
unsigned int _terrainHarshness, std::string _worldName, int _seed) :
originX(oX), originZ(oZ), worldSize(width), terrainSpread(_terrainSpread),
terrainHarshness(_terrainHarshness), chunkUpdatesPerFrame(CHUNK_UPDATES_PER_FRAME),
chunkFS(_worldName), worldGenerator(_seed, _terrainHarshness, _terrainSpread)
{
Chunk::cGame = cGame;
Chunk::cWorld = this;
Chunk::cChunkFilesystem = &chunkFS;
if(worldSize % 2 == 0)
worldSize++; //ensure its odd
std::pair<int, Chunk*> c;
int key = 0;
//Load or generate chunks
for(int x = originX; x < (originX + (int)worldSize); ++x)
{
for(int z = originZ; z < (originZ + (int)worldSize); ++z)
{
key = x * worldSize + z;
c.first = key;
c.second = new Chunk(x, z);
chunkRenderList.insert(c);
if(!c.second->load())
worldGenerator.fillChunk(c.second);
}
}
//Set all chunks dirty so they are updated
for(std::unordered_map<int, Chunk*>::iterator it = chunkRenderList.begin(); it != chunkRenderList.end(); ++it)
{
it->second->setDirty(8);
}
}
World::~World()
{
for(std::unordered_map<int, Chunk*>::iterator it = chunkRenderList.begin(); it != chunkRenderList.end(); ++it)
{
it->second->save();
SAFE_DELETE(it->second);
}
}
bool World::isBlockAt(int x, int y, int z)
{
Chunk* pChunk = getBlockChunk(x, z);
if(pChunk == NULL)
return false;
Block* pBlock = NULL;
pBlock = pChunk->getBlock(x, y, z);
if(pBlock == NULL)
return false;
if(pBlock->id != 0)
return true;
return false;
}
void World::renderChunks()
{
cGame->cGraphics->setTexture("Textures.bmp");
cGame->cGraphics->getDevice()->SetFVF(FVF_CUSTOMVERTEX);
cGame->cGraphics->getDevice()->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
for(std::unordered_map<int, Chunk*>::iterator it = chunkRenderList.begin(); it != chunkRenderList.end(); ++it)
{
if(it->second != NULL)
it->second->render(cGame->cGraphics);
}
}
void World::removeBlock(int x, int y, int z)
{
if(isBlockAt(x, y, z))
{
Chunk* pChunk = getBlockChunk(x, z);
Chunk* pChunk2 = NULL;
if(pChunk == NULL)
return;
pChunk->removeBlock(x, y, z);
pChunk->setDirty(9);
if(isBlockAt(x - 1, y, z))
{
pChunk2 = getBlockChunk(x - 1, z);
if((pChunk2 != NULL) && (pChunk2 != pChunk))
pChunk2->setDirty(10);
}
if(isBlockAt(x + 1, y, z))
{
pChunk2 = getBlockChunk(x + 1, z);
if((pChunk2 != NULL) && (pChunk2 != pChunk))
pChunk2->setDirty(10);
}
if(isBlockAt(x, y, z - 1))
{
pChunk2 = getBlockChunk(x, z - 1);
if((pChunk2 != NULL) && (pChunk2 != pChunk))
pChunk2->setDirty(10);
}
if(isBlockAt(x, y, z + 1))
{
pChunk2 = getBlockChunk(x, z + 1);
if((pChunk2 != NULL) && (pChunk2 != pChunk))
pChunk2->setDirty(10);
}
}
}
bool World::addBlock(int x, int y, int z, int type)
{
if(!isBlockAt(x, y, z))
{
Chunk* pChunk = getBlockChunk(x, z);
if(pChunk == NULL)
return false;
pChunk->addBlock(x, y, z, type);
if(cGame->cPlayer->getPhysics()->playerIntersect())
{
pChunk->removeBlock(x, y, z);
return false;
}
pChunk->setDirty(10);
if(isBlockAt(x - 1, y, z))
{
pChunk = getBlockChunk(x - 1, z);
pChunk->setDirty(0);
}
if(isBlockAt(x + 1, y, z))
{
pChunk = getBlockChunk(x + 1, z);
pChunk->setDirty(0);
}
if(isBlockAt(x, y, z - 1))
{
pChunk = getBlockChunk(x, z - 1);
pChunk->setDirty(0);
}
if(isBlockAt(x, y, z + 1))
{
pChunk = getBlockChunk(x, z + 1);
pChunk->setDirty(0);
}
return true;
}
return false;
}
Chunk* World::getBlockChunk(int x, int z)
{
//Negative numbers round up...
x = (x < 0) ? (x - CHUNK_WIDTH + 1) : x;
z = (z < 0) ? (z - CHUNK_WIDTH + 1) : z;
//Divide by CHUNK_WIDTH to get chunk location
x /= CHUNK_WIDTH;
z /= CHUNK_WIDTH;
int key = x * (int)worldSize + z;
find_it = chunkRenderList.find(key);
return (find_it == chunkRenderList.end()) ? NULL : find_it->second;
}
void World::updateDirtyChunks()
{
Chunk* pChunk = NULL;
for(int i = 0; i < (int)chunkUpdatesPerFrame; ++i)
{
if(chunkDirtyList.empty())
break;
pChunk = chunkDirtyList.top().pChunk;
if(pChunk != NULL)
{
for(std::unordered_map<int, Chunk*>::iterator it = chunkRenderList.begin(); it != chunkRenderList.end(); ++it)
{
if(it->second == pChunk)
{
pChunk->refreshVertices();
break;
}
}
}
chunkDirtyList.pop();
}
}
unsigned int World::getVertexCount()
{
unsigned int vertexCount = 0;
for(std::unordered_map<int, Chunk*>::iterator it = chunkRenderList.begin(); it != chunkRenderList.end(); ++it)
{
vertexCount += it->second->getPrimitiveCount() * 3;
}
return vertexCount;
}
void World::update(int playerPosX, int playerPosZ)
{
updateDirtyChunks();
processChunkLoadList();
processChunkUnloadList();
updateLoadedChunks(playerPosX, playerPosZ);
}
Block* World::getBlock(int x, int y, int z)
{
Chunk* c = getBlockChunk(x, z);
return (c == NULL) ? NULL : c->getBlock(x, y, z);
}
void World::processChunkLoadList()
{
if(!chunkLoadList.empty())
{
Point2 chunkToLoad = chunkLoadList.front();
chunkLoadList.pop_front();
addChunkToRenderList(chunkToLoad.x, chunkToLoad.y);
}
}
void World::addChunkToRenderList(int chunkLocX, int chunkLocZ)
{
Chunk* newChunk = new Chunk(chunkLocX, chunkLocZ);
chunkRenderList.insert(std::pair<int, Chunk*>(chunkLocX * (int)worldSize + chunkLocZ, newChunk));
if(!newChunk->load())
worldGenerator.fillChunk(newChunk);
newChunk->setDirty(5);
Chunk* pChunk = getBlockChunk((chunkLocX - 1) * CHUNK_WIDTH, chunkLocZ * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
pChunk = getBlockChunk((chunkLocX + 1) * CHUNK_WIDTH, chunkLocZ * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
pChunk = getBlockChunk(chunkLocX * CHUNK_WIDTH, (chunkLocZ - 1) * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
pChunk = getBlockChunk(chunkLocX * CHUNK_WIDTH, (chunkLocZ + 1) * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
/*pChunk = getBlockChunk((chunkLocX + 1) * CHUNK_WIDTH, (chunkLocZ + 1) * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
pChunk = getBlockChunk((chunkLocX + 1) * CHUNK_WIDTH, (chunkLocZ - 1) * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
pChunk = getBlockChunk((chunkLocX - 1) * CHUNK_WIDTH, (chunkLocZ - 1) * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
pChunk = getBlockChunk((chunkLocX - 1) * CHUNK_WIDTH, (chunkLocZ + 1) * CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);*/
}
void World::addChunkToDirtyList(Chunk* pChunk, char priority)
{
ChunkUpdate dirtyChunk(pChunk, priority);
chunkDirtyList.push(dirtyChunk);
}
void World::updateLoadedChunks(int playerPosX, int playerPosZ)
{
int renderDistance = worldSize / 2;
Chunk* pChunk = NULL;
bool incIt = false;
originX = (playerPosX < 0) ? (playerPosX / CHUNK_WIDTH - 1) : playerPosX / CHUNK_WIDTH;
originZ = (playerPosZ < 0) ? (playerPosZ / CHUNK_WIDTH - 1) : playerPosZ / CHUNK_WIDTH;
originX -= renderDistance;
originZ -= renderDistance;
originX *= CHUNK_WIDTH;
originZ *= CHUNK_WIDTH;
int cPosX, cPosZ, pPosX, pPosZ;
for(std::unordered_map<int, Chunk*>::iterator it = chunkRenderList.begin(); it != chunkRenderList.end();)
{
cPosX = (it->second->getChunkPosition().x < 0) ? (it->second->getChunkPosition().x - CHUNK_WIDTH + 1) : it->second->getChunkPosition().x;
cPosZ = (it->second->getChunkPosition().y < 0) ? (it->second->getChunkPosition().y - CHUNK_WIDTH + 1) : it->second->getChunkPosition().y;
cPosX /= CHUNK_WIDTH;
cPosZ /= CHUNK_WIDTH;
pPosX = (playerPosX < 0) ? (playerPosX - CHUNK_WIDTH + 1) : playerPosX;
pPosZ = (playerPosZ < 0) ? (playerPosZ - CHUNK_WIDTH + 1) : playerPosZ;
pPosX /= CHUNK_WIDTH;
pPosZ /= CHUNK_WIDTH;
//Check if chunk is within render distance
if(cPosX > (pPosX + renderDistance))
{
//Dirty neighbouring chunk
pChunk = getBlockChunk(cPosX - 1, cPosZ);
if(pChunk != NULL)
pChunk->setDirty(0);
//Unload chunk
chunkUnloadList.push_front(it->second);
it = chunkRenderList.erase(it);
continue;
}
else
incIt = true;
if(cPosX < (pPosX - renderDistance))
{
//Dirty neighbouring chunk
pChunk = getBlockChunk(cPosX + CHUNK_WIDTH, cPosZ);
if(pChunk != NULL)
pChunk->setDirty(0);
//Unload chunk
chunkUnloadList.push_front(it->second);
it = chunkRenderList.erase(it);
continue;
}
else
incIt = true;
if(cPosZ > (pPosZ + renderDistance))
{
//Dirty neighbouring chunk
pChunk = getBlockChunk(cPosX, cPosZ - 1);
if(pChunk != NULL)
pChunk->setDirty(0);
//Unload chunk
chunkUnloadList.push_front(it->second);
it = chunkRenderList.erase(it);
continue;
}
else
incIt = true;
if(cPosZ < (pPosZ - renderDistance))
{
//Dirty neighbouring chunk
pChunk = getBlockChunk(cPosX, cPosZ + CHUNK_WIDTH);
if(pChunk != NULL)
pChunk->setDirty(0);
//Unload chunk
chunkUnloadList.push_front(it->second);
it = chunkRenderList.erase(it);
continue;
}
else
incIt = true;
if(incIt)
{
++it;
incIt = false;
}
}
for(int x = (originX / CHUNK_WIDTH); x < ((originX / CHUNK_WIDTH) + (int)worldSize); x++)
{
for(int z = (originZ / CHUNK_WIDTH); z < ((originZ / CHUNK_WIDTH) + (int)worldSize); z++)
{
if(getBlockChunk(x * CHUNK_WIDTH, z * CHUNK_WIDTH) == NULL)
{
Point2 p(x, z);
chunkLoadList.push_front(p);
}
}
}
}
int World::getTerrainHeight(int x, int z)
{
Chunk* pChunk = getBlockChunk(x, z);
for(int y = 0; y < CHUNK_HEIGHT; ++y)
{
if(pChunk->getBlock(x, y, z)->id == BlockID_Air)
return y - 1;
}
return CHUNK_HEIGHT;
}
void World::processChunkUnloadList()
{
if(!chunkUnloadList.empty())
{
Chunk* chunkToUnload = chunkUnloadList.front();
chunkUnloadList.pop_front();
if(chunkToUnload != NULL)
{
chunkToUnload->save();
delete chunkToUnload;
}
}
}
That's like 1/20th of the code. Which I built myself. Now STFU.