#include "DimensionMap.h" #include #include "Constants.h" #include "Globals.h" #include "World.h" DimensionMap::DimensionMap(MapOptions* zOptions) : DrawableBackground(), zOptions(zOptions), originChunkCenter(0, 0), scrollOffset(0, 0), chunkCount(0), pixelsPerBlock(16), requestCount(0), drag(0), nextPlayersRequest(-1) { setStyle(Style::Visible | Style::Allowed); chunks = new Framework::RCTrie(); setMouseEvent(_ret1ME); requestNextChunk(); char msg[2]; msg[0] = 2; // subscribe to map changes msg[1] = 1; World::INSTANCE->zClient()->dimensionAPIRequest(msg, 2); LTDBFile iconsDat; iconsDat.setFile(new Text("data/images/gui_icons.ltdb")); iconsDat.readData(0); playerIcon = iconsDat.load(0, new Text("player.png")); } DimensionMap::~DimensionMap() { char msg[2]; msg[0] = 2; // unsubscribe from map changes msg[1] = 2; World::INSTANCE->zClient()->dimensionAPIRequest(msg, 2); chunks->release(); playerIcon->release(); } void DimensionMap::getAddrOf(Point cPos, char* addr) const { *(int*)addr = cPos.x; *((int*)addr + 1) = cPos.y; } void DimensionMap::getAddrOfWorld(Point wPos, char* addr) const { // needed because otherwise would (-8, -8) have the same // adress as (8, 8) if (wPos.x < 0) wPos.x -= CHUNK_SIZE; if (wPos.y < 0) wPos.y -= CHUNK_SIZE; wPos /= CHUNK_SIZE; getAddrOf(wPos, addr); } Framework::Point DimensionMap::getMinVisibleChunkCenter( Framework::Point& screenPos) const { screenPos = getSize() / 2 - scrollOffset; Point currentChunkCenter = originChunkCenter; while (screenPos.x + pixelsPerBlock * (CHUNK_SIZE / 2) >= 0) { screenPos.x -= pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.x -= CHUNK_SIZE; } while (screenPos.y + pixelsPerBlock * (CHUNK_SIZE / 2) >= 0) { screenPos.y -= pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.y -= CHUNK_SIZE; } while (screenPos.x + pixelsPerBlock * (CHUNK_SIZE / 2) < 0) { screenPos.x += pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.x += CHUNK_SIZE; } while (screenPos.y + pixelsPerBlock * (CHUNK_SIZE / 2) < 0) { screenPos.y += pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.y += CHUNK_SIZE; } return currentChunkCenter; } Framework::Point DimensionMap::getMaxVisibleChunkCenter( Framework::Point& screenPos) const { screenPos = getSize() / 2 - scrollOffset; Point currentChunkCenter = originChunkCenter; while (screenPos.x - pixelsPerBlock * (CHUNK_SIZE / 2) < getWidth()) { screenPos.x += pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.x += CHUNK_SIZE; } while (screenPos.y - pixelsPerBlock * (CHUNK_SIZE / 2) < getHeight()) { screenPos.y += pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.y += CHUNK_SIZE; } while (screenPos.x - pixelsPerBlock * (CHUNK_SIZE / 2) >= getWidth()) { screenPos.x -= pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.x -= CHUNK_SIZE; } while (screenPos.y - pixelsPerBlock * (CHUNK_SIZE / 2) >= getHeight()) { screenPos.y -= pixelsPerBlock * CHUNK_SIZE; currentChunkCenter.y -= CHUNK_SIZE; } return currentChunkCenter; } void DimensionMap::removeUnused() const { Point tmp; Framework::Point min = getMinVisibleChunkCenter(tmp) - Point(CHUNK_SIZE, CHUNK_SIZE) * 5; Framework::Point max = getMaxVisibleChunkCenter(tmp) + Point(CHUNK_SIZE, CHUNK_SIZE) * 5; char addr[8]; for (auto i = chunkList.begin(); i;) { if (i->getChunkCenter().x < min.x || i->getChunkCenter().y < min.y || i->getChunkCenter().x > max.x || i->getChunkCenter().y > max.y) { getAddrOfWorld(i->getChunkCenter(), addr); chunks->remove(addr, 8); i.remove(); } else { ++i; } } } void DimensionMap::updatePlayers(char* data) { int count = *(int*)data; data += 4; cs.lock(); players.clear(); // read player information from data buffer for (int i = 0; i < count; i++) { unsigned char nameLen = (unsigned char)*data; data++; char* name = new char[nameLen + 1]; memcpy(name, data, nameLen); name[nameLen] = 0; data += nameLen; MapPlayer player; player.name = name; delete[] name; player.position.x = *(float*)data; player.position.y = *(float*)(data + 4); player.position.z = *(float*)(data + 8); data += 12; players.add(player); } cs.unlock(); } void DimensionMap::requestNextChunk() { cs.lock(); if (requestCount >= 20) { cs.unlock(); return; } if (chunkCount == 0) { requestCount++; Vec3 playerPos = World::INSTANCE->getCurrentPlayerEntity()->getPos(); char msg[10]; msg[0] = 2; msg[1] = 0; *(int*)(msg + 2) = (int)playerPos.x; *(int*)(msg + 6) = (int)playerPos.y; World::INSTANCE->zClient()->dimensionAPIRequest(msg, 10); } else { while (requestCount < 20) { Point minScreenPos; Point minVisibleChunk = getMinVisibleChunkCenter(minScreenPos); Point maxScreenPos; Point maxVisibleChunk = getMaxVisibleChunkCenter(maxScreenPos); Point screenPos = minScreenPos; Point screenCenter = getSize() / 2; double minDist = -1; Point resultChunk(0, 0); char addr[8]; for (int x = minVisibleChunk.x; x <= maxVisibleChunk.x; x += CHUNK_SIZE) { for (int y = minVisibleChunk.y; y <= maxVisibleChunk.y; y += CHUNK_SIZE) { getAddrOfWorld({x, y}, addr); if (!chunks->z(addr, 8)) { if (minDist < 0 || (screenCenter - screenPos).getLengthSq() < minDist) { minDist = (screenCenter - screenPos).getLengthSq(); resultChunk = {x, y}; } } screenPos.y += pixelsPerBlock * CHUNK_SIZE; } screenPos.x += pixelsPerBlock * CHUNK_SIZE; screenPos.y = minScreenPos.y; } if (minDist >= 0) { requestCount++; char msg[10]; msg[0] = 2; msg[1] = 0; *(int*)(msg + 2) = (int)resultChunk.x; *(int*)(msg + 6) = (int)resultChunk.y; World::INSTANCE->zClient()->dimensionAPIRequest(msg, 10); getAddrOfWorld({resultChunk.x, resultChunk.y}, addr); chunks->set(addr, 8, new ChunkMap(resultChunk)); } else { break; } } } cs.unlock(); } void DimensionMap::addChunk(ChunkMap* chunk) { cs.lock(); if (chunkCount == 0) originChunkCenter = chunk->getChunkCenter(); char addr[8]; getAddrOfWorld(chunk->getChunkCenter(), addr); ChunkMap* old = chunks->z(addr, 8); if (old) chunkList.removeValue(old); chunks->set(addr, 8, chunk); chunkList.add(chunk); chunkCount++; requestCount--; removeUnused(); cs.unlock(); requestNextChunk(); } bool DimensionMap::tick(double time) { if (nextPlayersRequest < 0 && zOptions->isShowPlayers()) { nextPlayersRequest = 2; char msg[2]; msg[0] = 2; // request map players msg[1] = 3; if (World::INSTANCE) { World::INSTANCE->zClient()->dimensionAPIRequest(msg, 2); } } nextPlayersRequest -= time; if (lastSize != getSize()) { lastSize = getSize(); requestNextChunk(); } return DrawableBackground::tick(time); } void DimensionMap::render(Framework::Image& rObj) { DrawableBackground::render(rObj); if (!rObj.setDrawOptions(innenPosition, innenSize)) return; cs.lock(); if (zOptions->isFollowPlayer()) { Vec3 playerPos = World::INSTANCE->getCurrentPlayerEntity()->getPos(); scrollOffset = (Point((int)playerPos.x, (int)playerPos.y) - originChunkCenter) * pixelsPerBlock; requestNextChunk(); } Point minScreenPos; Point minVisibleChunk = getMinVisibleChunkCenter(minScreenPos); Point maxScreenPos; Point maxVisibleChunk = getMaxVisibleChunkCenter(maxScreenPos); char addr[8]; // render chunks Point screenPos = minScreenPos; for (int x = minVisibleChunk.x; x <= maxVisibleChunk.x; x += CHUNK_SIZE) { for (int y = minVisibleChunk.y; y <= maxVisibleChunk.y; y += CHUNK_SIZE) { getAddrOfWorld({x, y}, addr); ChunkMap* map = chunks->z(addr, 8); if (map) { if (zOptions->isUnderground()) { map->setMaxHeight( (int)(World::INSTANCE->getCurrentPlayerEntity() ->getPos() .z / 2)); } else { map->setMaxHeight(255); } Point topLeft(screenPos.x - (pixelsPerBlock * CHUNK_SIZE) / 2, screenPos.y - (pixelsPerBlock * CHUNK_SIZE) / 2); rObj.drawImageScaled(topLeft.x, topLeft.y, pixelsPerBlock * CHUNK_SIZE, pixelsPerBlock * CHUNK_SIZE, map->getRenderedImage()); } screenPos.y += pixelsPerBlock * CHUNK_SIZE; } screenPos.x += pixelsPerBlock * CHUNK_SIZE; screenPos.y = minScreenPos.y; } // render shadow and borders screenPos = minScreenPos; for (int x = minVisibleChunk.x; x <= maxVisibleChunk.x; x += CHUNK_SIZE) { for (int y = minVisibleChunk.y; y <= maxVisibleChunk.y; y += CHUNK_SIZE) { getAddrOfWorld({x, y}, addr); ChunkMap* map = chunks->z(addr, 8); if (map) { Point topLeft(screenPos.x - (pixelsPerBlock * CHUNK_SIZE) / 2, screenPos.y - (pixelsPerBlock * CHUNK_SIZE) / 2); getAddrOfWorld({x, y - CHUNK_SIZE}, addr); ChunkMap* tmp = chunks->z(addr, 8); unsigned char* heightMapTop = tmp ? tmp->getHeightMap() : 0; getAddrOfWorld({x + CHUNK_SIZE, y}, addr); tmp = chunks->z(addr, 8); unsigned char* heightMapRight = tmp ? tmp->getHeightMap() : 0; getAddrOfWorld({x, y + CHUNK_SIZE}, addr); tmp = chunks->z(addr, 8); unsigned char* heightMapBottom = tmp ? tmp->getHeightMap() : 0; getAddrOfWorld({x - CHUNK_SIZE, y}, addr); tmp = chunks->z(addr, 8); unsigned char* heightMapLeft = tmp ? tmp->getHeightMap() : 0; unsigned char* heightMap = map->getHeightMap(); for (int xx = 0; xx < CHUNK_SIZE; xx++) { for (int yy = 0; yy < CHUNK_SIZE; yy++) { bool shadowR = 0; bool shadowB = 0; if (xx < CHUNK_SIZE - 1) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMap[yy * CHUNK_SIZE + xx + 1]) { rObj.drawLineVAlpha((xx * pixelsPerBlock) + topLeft.x + pixelsPerBlock, (yy * pixelsPerBlock) + topLeft.y, pixelsPerBlock, 0x40000000); shadowR = 1; } } else if (heightMapRight) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMapRight[yy * CHUNK_SIZE]) { rObj.drawLineVAlpha((xx * pixelsPerBlock) + topLeft.x + pixelsPerBlock, (yy * pixelsPerBlock) + topLeft.y, pixelsPerBlock, 0x40000000); shadowR = 1; } } if (yy < CHUNK_SIZE - 1) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMap[(yy + 1) * CHUNK_SIZE + xx]) { rObj.drawLineHAlpha( (xx * pixelsPerBlock) + topLeft.x, (yy * pixelsPerBlock) + topLeft.y + pixelsPerBlock, pixelsPerBlock, 0x30000000); shadowB = 1; } } else if (heightMapBottom) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMapBottom[xx]) { rObj.drawLineHAlpha( (xx * pixelsPerBlock) + topLeft.x, (yy * pixelsPerBlock) + topLeft.y + pixelsPerBlock, pixelsPerBlock, 0x30000000); shadowB = 1; } } if (xx > 0) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMap[yy * CHUNK_SIZE + xx - 1]) { rObj.drawLineVAlpha( (xx * pixelsPerBlock) + topLeft.x, (yy * pixelsPerBlock) + topLeft.y, pixelsPerBlock - shadowB, 0x20FFFFFF); } } else if (heightMapLeft) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMapLeft[yy * CHUNK_SIZE + CHUNK_SIZE - 1]) { rObj.drawLineVAlpha( (xx * pixelsPerBlock) + topLeft.x, (yy * pixelsPerBlock) + topLeft.y, pixelsPerBlock - shadowB, 0x20FFFFFF); } } if (yy > 0) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMap[(yy - 1) * CHUNK_SIZE + xx]) { rObj.drawLineHAlpha( (xx * pixelsPerBlock) + topLeft.x, (yy * pixelsPerBlock) + topLeft.y, pixelsPerBlock - shadowR, 0x10FFFFFF); } } else if (heightMapTop) { if (heightMap[yy * CHUNK_SIZE + xx] > heightMapTop[(CHUNK_SIZE - 1) * CHUNK_SIZE + xx]) { rObj.drawLineHAlpha( (xx * pixelsPerBlock) + topLeft.x, (yy * pixelsPerBlock) + topLeft.y, pixelsPerBlock - shadowR, 0x10FFFFFF); } } } } if (zOptions->isShowChunkBorders()) { rObj.drawLineHAlpha(topLeft.x, topLeft.y, pixelsPerBlock * CHUNK_SIZE, 0x50FFFFFF); rObj.drawLineVAlpha(topLeft.x, topLeft.y, pixelsPerBlock * CHUNK_SIZE, 0x50FFFFFF); } } screenPos.y += pixelsPerBlock * CHUNK_SIZE; } screenPos.x += pixelsPerBlock * CHUNK_SIZE; screenPos.y = minScreenPos.y; } // render players if (zOptions->isShowPlayers()) { TextRenderer tm( dynamic_cast(uiFactory.initParam.font->getThis())); tm.setFontSize(12); for (const MapPlayer& player : players) { Point screenPos = getSize() / 2 - scrollOffset + (Point((int)player.position.x, (int)player.position.y) - originChunkCenter) * pixelsPerBlock + Point(pixelsPerBlock, pixelsPerBlock) / 2 - playerIcon->getSize() / 2; rObj.alphaImage(screenPos.x, screenPos.y, playerIcon->getWidth(), playerIcon->getHeight(), *playerIcon); int textWidth = tm.getTextWidth(player.name); int textheight = tm.getTextHeight(player.name); screenPos = screenPos + Point(playerIcon->getWidth(), 0) / 2 - Point(textWidth / 2, textheight + 2); rObj.alphaRegion( screenPos.x, screenPos.y, textWidth, textheight, 0x70000000); tm.renderText( screenPos.x, screenPos.y, player.name, rObj, 0xFFFFFFFF); } } cs.unlock(); rObj.releaseDrawOptions(); } void DimensionMap::doMouseEvent(Framework::MouseEvent& me, bool userRet) { if (me.id == ME_PLeft) { drag = 1; lastMouse = {me.mx, me.my}; } if (me.id == ME_RLeft || me.id == ME_Leaves) drag = 0; if (me.id == ME_Move && drag) { scrollOffset -= Point(me.mx, me.my) - lastMouse; lastMouse = Point(me.mx, me.my); rend = 1; requestNextChunk(); } if (me.id == ME_DScroll && pixelsPerBlock > 1) { scrollOffset = (scrollOffset / pixelsPerBlock) * (pixelsPerBlock - 1); pixelsPerBlock--; rend = 1; requestNextChunk(); } if (me.id == ME_UScroll) { scrollOffset = (scrollOffset / pixelsPerBlock) * (pixelsPerBlock + 1); pixelsPerBlock++; rend = 1; requestNextChunk(); } DrawableBackground::doMouseEvent(me, userRet); }