/** * 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]; }