188 lines
4.9 KiB
C
188 lines
4.9 KiB
C
/**
|
|
* Copyright (c) 2025 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "map.h"
|
|
#include "util/memory.h"
|
|
#include "assert/assert.h"
|
|
#include "asset/asset.h"
|
|
|
|
map_t MAP;
|
|
|
|
errorret_t mapInit() {
|
|
memoryZero(&MAP, sizeof(map_t));
|
|
|
|
// Init the default chunks. In future I'll probably make this based on where
|
|
// the player spawns in to save an initial mapSet.
|
|
chunkindex_t index = 0;
|
|
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
|
for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
|
for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
|
|
chunk_t *chunk = &MAP.chunks[index];
|
|
chunk->position.x = x;
|
|
chunk->position.y = y;
|
|
chunk->position.z = z;
|
|
MAP.chunkOrder[index] = chunk;
|
|
errorChain(mapChunkLoad(chunk));
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t mapPositionSet(const chunkpos_t newPos) {
|
|
const chunkpos_t curPos = MAP.chunkPosition;
|
|
if(chunkPositionIsEqual(curPos, newPos)) {
|
|
errorOk();
|
|
}
|
|
|
|
// Determine which chunks remain loaded
|
|
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
|
|
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
|
|
|
|
uint32_t remainingCount = 0;
|
|
uint32_t freedCount = 0;
|
|
|
|
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
|
// Will this chunk remain loaded?
|
|
chunk_t *chunk = &MAP.chunks[i];
|
|
if(
|
|
chunk->position.x >= newPos.x &&
|
|
chunk->position.x < newPos.x + MAP_CHUNK_WIDTH &&
|
|
|
|
chunk->position.y >= newPos.y &&
|
|
chunk->position.y < newPos.y + MAP_CHUNK_HEIGHT &&
|
|
|
|
chunk->position.z >= newPos.z &&
|
|
chunk->position.z < newPos.z + MAP_CHUNK_DEPTH
|
|
) {
|
|
// Stays loaded
|
|
chunksRemaining[remainingCount++] = i;
|
|
continue;
|
|
}
|
|
|
|
// Not remaining loaded
|
|
chunksFreed[freedCount++] = i;
|
|
}
|
|
|
|
// Unload the freed chunks
|
|
for(chunkindex_t i = 0; i < freedCount; i++) {
|
|
chunk_t *chunk = &MAP.chunks[chunksFreed[i]];
|
|
mapChunkUnload(chunk);
|
|
}
|
|
|
|
// This can probably be optimized later, for now we check each chunk and see
|
|
// if it needs loading or not, and update the chunk order
|
|
chunkindex_t orderIndex = 0;
|
|
for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) {
|
|
for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) {
|
|
for(chunkunit_t xOff = 0; xOff < MAP_CHUNK_WIDTH; xOff++) {
|
|
const chunkpos_t newChunkPos = {
|
|
newPos.x + xOff, newPos.y + yOff, newPos.z + zOff
|
|
};
|
|
|
|
// Is this chunk already loaded (was not unloaded earlier)?
|
|
chunkindex_t chunkIndex = -1;
|
|
for(chunkindex_t i = 0; i < remainingCount; i++) {
|
|
chunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
|
|
if(!chunkPositionIsEqual(chunk->position, newChunkPos)) continue;
|
|
chunkIndex = chunksRemaining[i];
|
|
break;
|
|
}
|
|
|
|
// Need to load this chunk
|
|
if(chunkIndex == -1) {
|
|
// Find a freed chunk to reuse
|
|
chunkIndex = chunksFreed[--freedCount];
|
|
chunk_t *chunk = &MAP.chunks[chunkIndex];
|
|
chunk->position = newChunkPos;
|
|
errorChain(mapChunkLoad(chunk));
|
|
}
|
|
|
|
MAP.chunkOrder[orderIndex++] = &MAP.chunks[chunkIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update map position
|
|
MAP.chunkPosition = newPos;
|
|
|
|
errorOk();
|
|
}
|
|
|
|
void mapUpdate() {
|
|
|
|
}
|
|
|
|
void mapDispose() {
|
|
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
|
mapChunkUnload(&MAP.chunks[i]);
|
|
}
|
|
}
|
|
|
|
void mapChunkUnload(chunk_t* chunk) {
|
|
for(uint8_t i = 0; i < chunk->meshCount; i++) {
|
|
meshDispose(&chunk->meshes[i]);
|
|
}
|
|
}
|
|
|
|
errorret_t mapChunkLoad(chunk_t* chunk) {
|
|
char_t buffer[64];
|
|
|
|
chunk->meshCount = 0;
|
|
|
|
snprintf(buffer, sizeof(buffer), "map/map/%d_%d_%d.dcf",
|
|
chunk->position.x,
|
|
chunk->position.y,
|
|
chunk->position.z
|
|
);
|
|
|
|
if(!assetFileExists(buffer)) {
|
|
memoryZero(chunk->tiles, sizeof(chunk->tiles));
|
|
errorOk();
|
|
}
|
|
|
|
errorChain(assetLoad(buffer, chunk));
|
|
errorOk();
|
|
}
|
|
|
|
chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {
|
|
chunkpos_t relPos = {
|
|
position.x - MAP.chunkPosition.x,
|
|
position.y - MAP.chunkPosition.y,
|
|
position.z - MAP.chunkPosition.z
|
|
};
|
|
|
|
if(
|
|
relPos.x < 0 || relPos.y < 0 || relPos.z < 0 ||
|
|
relPos.x >= MAP_CHUNK_WIDTH ||
|
|
relPos.y >= MAP_CHUNK_HEIGHT ||
|
|
relPos.z >= MAP_CHUNK_DEPTH
|
|
) {
|
|
return -1;
|
|
}
|
|
|
|
return chunkPosToIndex(&relPos);
|
|
}
|
|
|
|
chunk_t* mapGetChunk(const uint8_t index) {
|
|
if(index >= MAP_CHUNK_COUNT) return NULL;
|
|
return MAP.chunkOrder[index];
|
|
}
|
|
|
|
tile_t mapGetTile(const worldpos_t position) {
|
|
chunkpos_t chunkPos;
|
|
worldPosToChunkPos(&position, &chunkPos);
|
|
chunkindex_t chunkIndex = mapGetChunkIndexAt(chunkPos);
|
|
if(chunkIndex == -1) return TILE_SHAPE_NULL;
|
|
|
|
chunk_t *chunk = mapGetChunk(chunkIndex);
|
|
assertNotNull(chunk, "Chunk pointer cannot be NULL");
|
|
chunktileindex_t tileIndex = woprldPosToChunkTileIndex(&position);
|
|
return chunk->tiles[tileIndex];
|
|
} |