diff options
Diffstat (limited to 'Minecraft.World/MapItem.cpp')
| -rw-r--r-- | Minecraft.World/MapItem.cpp | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/Minecraft.World/MapItem.cpp b/Minecraft.World/MapItem.cpp new file mode 100644 index 00000000..abf4fb9a --- /dev/null +++ b/Minecraft.World/MapItem.cpp @@ -0,0 +1,347 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.saveddata.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "MapItem.h" +#include "net.minecraft.world.inventory.h" +#include "JavaMath.h" + +MapItem::MapItem(int id) : ComplexItem(id) +{ + this->setMaxStackSize(1); +} + +shared_ptr<MapItemSavedData> MapItem::getSavedData(short idNum, Level *level) +{ + std::wstring id = wstring( L"map_" ) + _toString(idNum); + shared_ptr<MapItemSavedData> mapItemSavedData = dynamic_pointer_cast<MapItemSavedData>(level->getSavedData(typeid(MapItemSavedData), id)); + + if (mapItemSavedData == NULL) + { + // 4J Stu - This call comes from ClientConnection, but i don't see why we should be trying to work out + // the id again when it's passed as a param. In any case that won't work with the new map setup + //int aux = level->getFreeAuxValueFor(L"map"); + int aux = idNum; + + id = wstring( L"map_" ) + _toString(aux); + mapItemSavedData = shared_ptr<MapItemSavedData>( new MapItemSavedData(id) ); + + level->setSavedData(id, (shared_ptr<SavedData> ) mapItemSavedData); + } + + return mapItemSavedData; +} + +shared_ptr<MapItemSavedData> MapItem::getSavedData(shared_ptr<ItemInstance> itemInstance, Level *level) +{ + MemSect(31); + std::wstring id = wstring( L"map_" ) + _toString(itemInstance->getAuxValue() ); + MemSect(0); + shared_ptr<MapItemSavedData> mapItemSavedData = dynamic_pointer_cast<MapItemSavedData>( level->getSavedData(typeid(MapItemSavedData), id ) ); + + bool newData = false; + if (mapItemSavedData == NULL) + { + // 4J Stu - I don't see why we should be trying to work out the id again when it's passed as a param. + // In any case that won't work with the new map setup + //itemInstance->setAuxValue(level->getFreeAuxValueFor(L"map")); + + id = wstring( L"map_" ) + _toString(itemInstance->getAuxValue() ); + mapItemSavedData = shared_ptr<MapItemSavedData>( new MapItemSavedData(id) ); + + newData = true; + } + + mapItemSavedData->scale = 3; +#ifndef _LARGE_WORLDS + // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map + mapItemSavedData->x = 0; + mapItemSavedData->z = 0; +#endif + + if( newData ) + { +#ifdef _LARGE_WORLDS + int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapItemSavedData->scale); + mapItemSavedData->x = Math::round((float) level->getLevelData()->getXSpawn() / scale) * scale; + mapItemSavedData->z = Math::round(level->getLevelData()->getZSpawn() / scale) * scale; +#endif + mapItemSavedData->dimension = (byte) level->dimension->id; + + mapItemSavedData->setDirty(); + + level->setSavedData(id, (shared_ptr<SavedData> ) mapItemSavedData); + } + + return mapItemSavedData; +} + +void MapItem::update(Level *level, shared_ptr<Entity> player, shared_ptr<MapItemSavedData> data) +{ + if (level->dimension->id != data->dimension) + { + // Wrong dimension, abort + return; + } + + int w = MapItem::IMAGE_WIDTH; + int h = MapItem::IMAGE_HEIGHT; + + int scale = 1 << data->scale; + + int xo = data->x; + int zo = data->z; + + int xp = Mth::floor(player->x - xo) / scale + w / 2; + int zp = Mth::floor(player->z - zo) / scale + h / 2; + + int rad = 128 / scale; + if (level->dimension->hasCeiling) + { + rad /= 2; + } + data->step++; + + for (int x = xp - rad + 1; x < xp + rad; x++) + { + if ((x & 15) != (data->step & 15)) continue; + + int yd0 = 255; + int yd1 = 0; + + double ho = 0; + for (int z = zp - rad - 1; z < zp + rad; z++) + { + if (x < 0 || z < -1 || x >= w || z >= h) continue; + + int xd = x - xp; + int zd = z - zp; + + bool ditherBlack = xd * xd + zd * zd > (rad - 2) * (rad - 2); + + int xx = (xo / scale + x - w / 2) * scale; + int zz = (zo / scale + z - h / 2) * scale; + + int r = 0; + int g = 0; + int b = 0; + + + int count[256]; + memset( count,0,sizeof(int)*256); + + LevelChunk *lc = level->getChunkAt(xx, zz); + if(lc->isEmpty()) continue; + int xso = ((xx)) & 15; + int zso = ((zz)) & 15; + int liquidDepth = 0; + + double hh = 0; + if (level->dimension->hasCeiling) + { + int ss = xx + zz * 231871; + ss = ss * ss * 31287121 + ss * 11; + if (((ss >> 20) & 1) == 0) count[Tile::dirt_Id] += 10; + else count[Tile::rock_Id] += 10; + hh = 100; + } + else + { + for (int xs = 0; xs < scale; xs++) + { + for (int zs = 0; zs < scale; zs++) + { + int yy = lc->getHeightmap(xs + xso, zs + zso) + 1; + int t = 0; + if (yy > 1) + { + bool ok = false; + do + { + ok = true; + t = lc->getTile(xs + xso, yy - 1, zs + zso); + if (t == 0) ok = false; + else if (yy > 0 && t > 0 && Tile::tiles[t]->material->color == MaterialColor::none) + { + ok = false; + } + + if (!ok) + { + yy--; + if (yy <= 0) break; + t = lc->getTile(xs + xso, yy - 1, zs + zso); + } + + } while (yy > 0 && !ok); + + if (yy > 0 && t != 0 && Tile::tiles[t]->material->isLiquid()) + { + int y = yy - 1; + int below = 0; + do + { + below = lc->getTile(xs + xso, y--, zs + zso); + liquidDepth++; + } while (y > 0 && below != 0 && Tile::tiles[below]->material->isLiquid()); + } + } + hh += yy / (double) (scale * scale); + + count[t]++; + } + } + } + liquidDepth /= scale * scale; + r /= scale * scale; + g /= scale * scale; + b /= scale * scale; + + int best = 0; + int tBest = 0; + for (int j = 0; j < 256; j++) + { + if (count[j] > best) + { + tBest = j; + best = count[j]; + } + } + + double diff = ((hh - ho) * 4 / (scale + 4)) + (((x + z) & 1) - 0.5) * 0.4; + int br = 1; + if (diff > +0.6) br = 2; + if (diff < -0.6) br = 0; + + int col = 0; + if (tBest > 0) + { + MaterialColor *mc = Tile::tiles[tBest]->material->color; + if (mc == MaterialColor::water) + { + diff = (liquidDepth * 0.1) + ((x + z) & 1) * 0.2; + br = 1; + if (diff < 0.5) br = 2; + if (diff > 0.9) br = 0; + } + col = mc->id; + } + + ho = hh; + + if (z < 0) continue; + if (xd * xd + zd * zd >= rad * rad) continue; + if (ditherBlack && ((x + z) & 1) == 0) + { + continue; + } + byte oldColor = data->colors[x + z * w]; + byte newColor = (byte) (col * 4 + br); + if (oldColor != newColor) + { + if (yd0 > z) yd0 = z; + if (yd1 < z) yd1 = z; + data->colors[x + z * w] = newColor; + } + } + if (yd0 <= yd1) + { + data->setDirty(x, yd0, yd1); + } + } +} + +void MapItem::inventoryTick(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Entity> owner, int slot, bool selected) +{ + if (level->isClientSide) return; + + shared_ptr<MapItemSavedData> data = getSavedData(itemInstance, level); + if (dynamic_pointer_cast<Player>(owner) != NULL) + { + shared_ptr<Player> player = dynamic_pointer_cast<Player>(owner); + + // 4J Stu - If the player has a map that belongs to another player, then merge the data over and change this map id to the owners id + int ownersAuxValue = level->getAuxValueForMap(player->getXuid(), data->dimension, data->x, data->z, data->scale); + if(ownersAuxValue != itemInstance->getAuxValue() ) + { + shared_ptr<MapItemSavedData> ownersData = getSavedData(ownersAuxValue,level); + + ownersData->x = data->x; + ownersData->z = data->z; + ownersData->scale = data->scale; + ownersData->dimension = data->dimension; + + itemInstance->setAuxValue( ownersAuxValue ); + ownersData->tickCarriedBy(player, itemInstance ); + ownersData->mergeInMapData(data); + player->inventoryMenu->broadcastChanges(); + + data = ownersData; + } + else + { + data->tickCarriedBy(player, itemInstance); + } + } + + if (selected) + { + update(level, owner, data); + } +} + +void MapItem::onCraftedBy(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Player> player) +{ + wchar_t buf[64]; + + int mapScale = 3; +#ifdef _LARGE_WORLDS + int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale); + int centreXC = (int) (Math::round(player->x / scale) * scale); + int centreZC = (int) (Math::round(player->z / scale) * scale); +#else + // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map + int centreXC = 0; + int centreZC = 0; +#endif + + itemInstance->setAuxValue(level->getAuxValueForMap(player->getXuid(), player->dimension, centreXC, centreZC, mapScale)); + + swprintf(buf,64,L"map_%d", itemInstance->getAuxValue()); + std::wstring id = wstring(buf); + + shared_ptr<MapItemSavedData> data = getSavedData(itemInstance->getAuxValue(), level); + // 4J Stu - We only have one map per player per dimension, so don't reset the one that they have + // when a new one is created + if( data == NULL ) + { + data = shared_ptr<MapItemSavedData>( new MapItemSavedData(id) ); + } + level->setSavedData(id, (shared_ptr<SavedData> ) data); + + data->scale = mapScale; + // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map + data->x = centreXC; + data->z = centreZC; + data->dimension = (byte) level->dimension->id; + data->setDirty(); +} + +shared_ptr<Packet> MapItem::getUpdatePacket(shared_ptr<ItemInstance> itemInstance, Level *level, shared_ptr<Player> player) +{ + charArray data = MapItem::getSavedData(itemInstance, level)->getUpdatePacket(itemInstance, level, player); + + if (data.data == NULL || data.length == 0) return nullptr; + + shared_ptr<Packet> retval = shared_ptr<Packet>(new ComplexItemDataPacket((short) Item::map->id, (short) itemInstance->getAuxValue(), data)); + delete data.data; + return retval; +} |
