diff --git a/resources/resources.qrc b/resources/resources.qrc index 1e53b8d4..99aa535c 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -56,5 +56,7 @@ ../src/noggit/rendering/glsl/square_frag.glsl ../src/noggit/rendering/glsl/cylinder_vert.glsl ../src/noggit/rendering/glsl/cylinder_frag.glsl + ../src/noggit/rendering/glsl/line_vert.glsl + ../src/noggit/rendering/glsl/line_frag.glsl diff --git a/src/noggit/ChunkWater.cpp b/src/noggit/ChunkWater.cpp index 46d2dd75..7f3b0bfa 100755 --- a/src/noggit/ChunkWater.cpp +++ b/src/noggit/ChunkWater.cpp @@ -22,6 +22,7 @@ void ChunkWater::from_mclq(std::vector& layers) { glm::vec3 pos(xbase, 0.0f, zbase); + if (!Render.has_value()) Render.emplace(); for (mclq& liquid : layers) { std::uint8_t mclq_liquid_type = 0; @@ -32,8 +33,8 @@ void ChunkWater::from_mclq(std::vector& layers) { mclq_tile const& tile = liquid.tiles[z * 8 + x]; - misc::bit_or(Render.fishable, x, z, tile.fishable); - misc::bit_or(Render.fatigue, x, z, tile.fatigue); + misc::bit_or(Render.value().fishable, x, z, tile.fishable); + misc::bit_or(Render.value().fatigue, x, z, tile.fatigue); if (!tile.dont_render) { @@ -79,8 +80,9 @@ void ChunkWater::fromFile(BlizzardArchive::ClientFile &f, size_t basePos) //render if (header.ofsRenderMask) { - f.seek(basePos + header.ofsRenderMask + sizeof(MH2O_Render)); - f.read(&Render, sizeof(MH2O_Render)); + Render.emplace(); + f.seek(basePos + header.ofsRenderMask); + f.read(&Render.value(), sizeof(MH2O_Render)); } for (std::size_t k = 0; k < header.nLayers; ++k) @@ -121,9 +123,17 @@ void ChunkWater::save(sExtendableArray& adt, int base_pos, int& header_pos, int& if (hasData(0)) { header.nLayers = _layers.size(); - header.ofsRenderMask = current_pos - base_pos; - adt.Insert(current_pos, sizeof(MH2O_Render), reinterpret_cast(&Render)); - current_pos += sizeof(MH2O_Render); + + if (Render.has_value()) + { + header.ofsRenderMask = current_pos - base_pos; + adt.Insert(current_pos, sizeof(MH2O_Render), reinterpret_cast(&Render.value())); + current_pos += sizeof(MH2O_Render); + } + else + { + header.ofsRenderMask = 0; + } header.ofsInformation = current_pos - base_pos; int info_pos = current_pos; diff --git a/src/noggit/ChunkWater.hpp b/src/noggit/ChunkWater.hpp index 24318ca8..0bb2faa1 100755 --- a/src/noggit/ChunkWater.hpp +++ b/src/noggit/ChunkWater.hpp @@ -7,6 +7,7 @@ #include #include +#include class sExtendableArray; class MapChunk; @@ -83,7 +84,7 @@ private: void copy_height_to_layer(liquid_layer& target, glm::vec3 const& pos, float radius); - MH2O_Render Render; + std::optional Render; std::vector _layers; MapChunk* _chunk; diff --git a/src/noggit/MapHeaders.h b/src/noggit/MapHeaders.h index 4d33d78d..33c76aad 100755 --- a/src/noggit/MapHeaders.h +++ b/src/noggit/MapHeaders.h @@ -6,7 +6,7 @@ union mcnk_flags { - uint32_t value; + uint32_t value = 0; struct { uint32_t has_mcsh : 1; @@ -112,42 +112,42 @@ struct ENTRY_MODF }; struct MapChunkHeader { - uint32_t flags; + uint32_t flags = 0; uint32_t ix; uint32_t iy; - uint32_t nLayers; - uint32_t nDoodadRefs; - uint32_t ofsHeight; - uint32_t ofsNormal; - uint32_t ofsLayer; - uint32_t ofsRefs; - uint32_t ofsAlpha; - uint32_t sizeAlpha; - uint32_t ofsShadow; - uint32_t sizeShadow; - uint32_t areaid; - uint32_t nMapObjRefs; - uint32_t holes; - std::uint16_t doodadMapping[8]; - std::uint8_t doodadStencil[8]; - uint32_t ofsSndEmitters; - uint32_t nSndEmitters; - uint32_t ofsLiquid; - uint32_t sizeLiquid; + uint32_t nLayers = 0; + uint32_t nDoodadRefs = 0; + uint32_t ofsHeight = 0; + uint32_t ofsNormal = 0; + uint32_t ofsLayer = 0; + uint32_t ofsRefs = 0; + uint32_t ofsAlpha = 0; + uint32_t sizeAlpha = 0; + uint32_t ofsShadow = 0; + uint32_t sizeShadow = 0; + uint32_t areaid = 0; + uint32_t nMapObjRefs = 0; + uint32_t holes = 0; + std::uint16_t doodadMapping[8]{ 0 }; + std::uint8_t doodadStencil[8]{ 0 }; + uint32_t ofsSndEmitters = 0; + uint32_t nSndEmitters = 0; + uint32_t ofsLiquid = 0; + uint32_t sizeLiquid = 0; float zpos; float xpos; float ypos; - uint32_t ofsMCCV; - uint32_t unused1; - uint32_t unused2; + uint32_t ofsMCCV = 0; + uint32_t unused1 = 0; + uint32_t unused2 = 0; }; struct MCCV { - uint32_t textureID; - uint32_t flags; - uint32_t ofsAlpha; - uint32_t effectID; + uint32_t textureID = 0; + uint32_t flags = 0; + uint32_t ofsAlpha = 0; + uint32_t effectID = 0; }; struct MCLYFlags @@ -166,9 +166,9 @@ struct MCLYFlags struct ENTRY_MCLY { - uint32_t textureID; - uint32_t flags; - uint32_t ofsAlpha; + uint32_t textureID = 0; + uint32_t flags = 0; + uint32_t ofsAlpha = 0; uint32_t effectID = 0xFFFF; // default value, see https://wowdev.wiki/ADT/v18#MCLY_sub-chunk }; diff --git a/src/noggit/MapTile.h b/src/noggit/MapTile.h index e372bf88..332b14d2 100755 --- a/src/noggit/MapTile.h +++ b/src/noggit/MapTile.h @@ -190,7 +190,7 @@ private: unsigned _chunk_update_flags; // MHDR: - int mFlags; + int mFlags = 0; bool mBigAlpha; // Data to be loaded and later unloaded. diff --git a/src/noggit/MapView.cpp b/src/noggit/MapView.cpp index c6d93d21..7ae78d1a 100755 --- a/src/noggit/MapView.cpp +++ b/src/noggit/MapView.cpp @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -1102,6 +1103,21 @@ void MapView::setupFileMenu() ADD_ACTION (file_menu, "Save current tile", "Ctrl+Shift+S", [this] { save(save_mode::current); emit saved();}); ADD_ACTION (file_menu, "Save changed tiles", QKeySequence::Save, [this] { save(save_mode::changed); emit saved(); }); ADD_ACTION (file_menu, "Save all tiles", "Ctrl+Shift+A", [this] { save(save_mode::all); emit saved(); }); + ADD_ACTION(file_menu, "Generate new WDL", "", [this] + { + QMessageBox prompt; + prompt.setIcon(QMessageBox::Warning); + prompt.setWindowFlags(Qt::WindowStaysOnTopHint); + prompt.setText(std::string("Warning!\nThis will attempt to load all tiles in the map to generate a new WDL." + "\nThis is likely to crash if there is any issue with any tile, it is recommended that you save your work first. Only use this if you really need a fresh WDL.").c_str()); + prompt.setInformativeText(std::string("Are you sure ?").c_str()); + prompt.setStandardButtons(QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No); + prompt.setDefaultButton(QMessageBox::No); + bool answer = prompt.exec() == QMessageBox::StandardButton::Yes; + if (answer) + _world->horizon.save_wdl(_world.get(), true); + } + ); ADD_ACTION ( file_menu , "Reload tile" @@ -1138,17 +1154,22 @@ void MapView::setupFileMenu() } ); - ADD_ACTION ( file_menu - , "Write coordinates to port.txt" - , Qt::Key_G - , [this] - { + ADD_ACTION(file_menu + , "Write coordinates to port.txt and copy to clipboard" + , Qt::Key_G + , [this] + { + std::stringstream port_command; + port_command << ".go XYZ " << (ZEROPOINT - _camera.position.z) << " " << (ZEROPOINT - _camera.position.x) << " " << _camera.position.y << " " << _world->getMapID(); std::ofstream f("ports.txt", std::ios_base::app); f << "Map: " << gAreaDB.getAreaName(_world->getAreaID (_camera.position)) << " on ADT " << std::floor(_camera.position.x / TILESIZE) << " " << std::floor(_camera.position.z / TILESIZE) << std::endl; - f << "Trinity:" << std::endl << ".go " << (ZEROPOINT - _camera.position.z) << " " << (ZEROPOINT - _camera.position.x) << " " << _camera.position.y << " " << _world->getMapID() << std::endl; - f << "ArcEmu:" << std::endl << ".worldport " << _world->getMapID() << " " << (ZEROPOINT - _camera.position.z) << " " << (ZEROPOINT - _camera.position.x) << " " << _camera.position.y << " " << std::endl << std::endl; + f << "Trinity/AC:" << std::endl << port_command.str() << std::endl; + // f << "ArcEmu:" << std::endl << ".worldport " << _world->getMapID() << " " << (ZEROPOINT - _camera.position.z) << " " << (ZEROPOINT - _camera.position.x) << " " << _camera.position.y << " " << std::endl << std::endl; f.close(); + QClipboard* clipboard = QGuiApplication::clipboard(); + clipboard->setText(port_command.str().c_str(), QClipboard::Clipboard); } + ); } @@ -5074,6 +5095,9 @@ void MapView::wheelEvent (QWheelEvent* event) void MapView::mouseReleaseEvent (QMouseEvent* event) { + makeCurrent(); + OpenGL::context::scoped_setter const _(::gl, context()); + switch (event->button()) { case Qt::LeftButton: @@ -5205,7 +5229,10 @@ void MapView::save(save_mode mode) case save_mode::current: _world->mapIndex.saveTile(TileIndex(_camera.position), _world.get()); break; case save_mode::changed: _world->mapIndex.saveChanged(_world.get()); break; case save_mode::all: _world->mapIndex.saveall(_world.get()); break; - } + } + // write wdl, we update wdl data prior in the mapIndex saving fucntions above + _world->horizon.save_wdl(_world.get()); + NOGGIT_ACTION_MGR->purge(); AsyncLoader::instance().reset_object_fail(); diff --git a/src/noggit/liquid_layer.cpp b/src/noggit/liquid_layer.cpp index c4c2bcf6..87868218 100755 --- a/src/noggit/liquid_layer.cpp +++ b/src/noggit/liquid_layer.cpp @@ -27,6 +27,9 @@ liquid_layer::liquid_layer(ChunkWater* chunk, glm::vec3 const& base, float heigh , pos(base) , _chunk(chunk) { + if (!gLiquidTypeDB.CheckIfIdExists(_liquid_id)) + _liquid_id = 1; + for (int z = 0; z < 9; ++z) { for (int x = 0; x < 9; ++x) @@ -54,6 +57,9 @@ liquid_layer::liquid_layer(ChunkWater* chunk, glm::vec3 const& base, mclq& liqui , pos(base) , _chunk(chunk) { + if (!gLiquidTypeDB.CheckIfIdExists(_liquid_id)) + _liquid_id = 1; + changeLiquidID(_liquid_id); for (int z = 0; z < 8; ++z) @@ -107,6 +113,10 @@ liquid_layer::liquid_layer(ChunkWater* chunk , pos(base) , _chunk(chunk) { + // check if liquid id is valid or some downported maps will crash + if (!gLiquidTypeDB.CheckIfIdExists(_liquid_id)) + _liquid_id = 1; + int offset = 0; for (int z = 0; z < info.height; ++z) { diff --git a/src/noggit/map_horizon.cpp b/src/noggit/map_horizon.cpp index 88e0d45c..c2ae1969 100755 --- a/src/noggit/map_horizon.cpp +++ b/src/noggit/map_horizon.cpp @@ -5,11 +5,13 @@ #include #include #include +#include #include #include #include #include +#include struct color { @@ -126,10 +128,37 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind } // todo: handle those too ? case 'MWMO': - case 'MWID': - case 'MODF': + { + { + char const* lCurPos = reinterpret_cast(wdl_file.getPointer()); + char const* lEnd = lCurPos + size; + + while (lCurPos < lEnd) + { + mWMOFilenames.push_back(BlizzardArchive::ClientData::normalizeFilenameInternal(std::string(lCurPos))); + lCurPos += strlen(lCurPos) + 1; + } + } wdl_file.seekRelative(size); break; + } + case 'MWID': + wdl_file.seekRelative(size); + break; + // TODO + case 'MODF': + { + wdl_file.seekRelative(size); + break; + // { + // ENTRY_MODF const* modf_ptr = reinterpret_cast(wdl_file.getPointer()); + // for (unsigned int i = 0; i < size / sizeof(ENTRY_MODF); ++i) + // { + // lWMOInstances.push_back(modf_ptr[i]); + // } + // } + // break; + } case 'MAOF': { assert(size == 64 * 64 * sizeof(uint32_t)); @@ -159,9 +188,18 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind //! \todo There also is MAHO giving holes into this heightmap. wdl_file.read(_tiles[y][x]->height_17, 17 * 17 * sizeof(int16_t)); wdl_file.read(_tiles[y][x]->height_16, 16 * 16 * sizeof(int16_t)); + + + wdl_file.read(&fourcc, 4); + if (fourcc == 'MAHO') + { + wdl_file.read(&size, 4); + assert(size == 0x20); + wdl_file.read(_tiles[y][x]->holes, 16 * sizeof(int16_t)); + } + } } - done = true; break; } @@ -174,40 +212,242 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind wdl_file.close(); - _qt_minimap = QImage (16 * 64, 16 * 64, QImage::Format_ARGB32); - _qt_minimap.fill (Qt::transparent); + set_minimap(index); +} - for (size_t y (0); y < 64; ++y) - { - for (size_t x (0); x < 64; ++x) +void map_horizon::set_minimap(const MapIndex* const index) +{ + _qt_minimap = QImage(16 * 64, 16 * 64, QImage::Format_ARGB32); + _qt_minimap.fill(Qt::transparent); + + for (size_t y(0); y < 64; ++y) { - if (_tiles[y][x]) - { - //! \todo There also is a second heightmap appended which has additional 16*16 pixels. - //! \todo There also is MAHO giving holes into this heightmap. + for (size_t x(0); x < 64; ++x) + { + if (_tiles[y][x]) + { + //! \todo There also is a second heightmap appended which has additional 16*16 pixels. + //! \todo There also is MAHO giving holes into this heightmap. - for (size_t j (0); j < 16; ++j) - { - for (size_t i (0); i < 16; ++i) - { - //! \todo R and B are inverted here - _qt_minimap.setPixel(x * 16 + i, y * 16 + j, color_for_height(_tiles[y][x]->height_17[j][i])); - } + for (size_t j(0); j < 16; ++j) + { + for (size_t i(0); i < 16; ++i) + { + //! \todo R and B are inverted here + _qt_minimap.setPixel(x * 16 + i, y * 16 + j, color_for_height(_tiles[y][x]->height_17[j][i])); + } + } + } + // the adt exist but there's no data in the wdl + else if (index->hasTile(TileIndex(x, y))) + { + for (size_t j(0); j < 16; ++j) + { + for (size_t i(0); i < 16; ++i) + { + _qt_minimap.setPixel(x * 16 + i, y * 16 + j, color(200, 100, 25)); + } + } + } } - } - // the adt exist but there's no data in the wdl - else if (index->hasTile(TileIndex(x, y))) - { - for (size_t j(0); j < 16; ++j) - { - for (size_t i(0); i < 16; ++i) - { - _qt_minimap.setPixel(x * 16 + i, y * 16 + j, color(200, 100, 25)); - } - } - } } - } +} + +Noggit::map_horizon_tile* map_horizon::get_horizon_tile(int y, int x) +{ + return _tiles[y][x].get(); +} + +int16_t map_horizon::getWdlheight(MapTile* tile, float x, float y) +{ + int cx = std::min(std::max(static_cast(x / CHUNKSIZE), 0), 15); + int cy = std::min(std::max(static_cast(y / CHUNKSIZE), 0), 15); + + x -= cx * CHUNKSIZE; + y -= cy * CHUNKSIZE; + + int row = static_cast(y / (UNITSIZE * 0.5f) + 0.5f); + int col = static_cast((x - UNITSIZE * 0.5f * (row % 2)) / UNITSIZE + 0.5f); + bool inner = (row % 2) == 1; + + if (row < 0 || col < 0 || row > 16 || col >(inner ? 8 : 9)) + return 0; + + // truncate and clamp the float value + auto chunk = tile->getChunk(cx, cy); + // float height = heights[cy * 16 + cx][17 * (row / 2) + (inner ? 9 : 0) + col]; + float height = chunk->getHeightmap()[17 * (row / 2) + (inner ? 9 : 0) + col].y; + return std::min(std::max(static_cast(height), static_cast(SHRT_MIN)), static_cast(SHRT_MAX)); +} + +void map_horizon::update_horizon_tile(MapTile* mTile) +{ + auto tile_index = mTile->index; + + // calculate the heightmap as a short array + float x, y; + for (int i = 0; i < 17; i++) + { + for (int j = 0; j < 17; j++) + { + // outer - correct + x = j * CHUNKSIZE; + y = i * CHUNKSIZE; + + if (!_tiles[tile_index.z][tile_index.x].get()) // tile has not been initialised + // continue; + { + _tiles[tile_index.z][tile_index.x] = std::make_unique(); + // do we need to use memcpy as well ? + } + // only works for initialised + _tiles[tile_index.z][tile_index.x].get()->height_17[i][j] = getWdlheight(mTile, x, y); + + // inner - close enough; correct values appear to use some form of averaging + if (i < 16 && j < 16) + _tiles[tile_index.z][tile_index.x].get()->height_16[i][j] = getWdlheight(mTile, x + CHUNKSIZE / 2.0f, y + CHUNKSIZE / 2.0f); + } + } + // Holes + for (int i = 0; i < 16; ++i) + { + std::bitset<16>wdlHoleMask(0); + + for (int j = 0; j < 16; ++j) + { + auto chunk = mTile->getChunk(j, i); + // the ordering seems to be : short array = Y axis, flags values = X axis and the values are for a whole chunk. + + std::bitset<16> holeBits(chunk->getHoleMask()); + + if (holeBits.count() == 16) // if all holes are set in a chunk + wdlHoleMask.set(j, true); + } + _tiles[tile_index.z][tile_index.x].get()->holes[i] = static_cast(wdlHoleMask.to_ulong()); + } +} + +void map_horizon::save_wdl(World* world, bool regenerate) +{ + world->wait_for_all_tile_updates(); + + std::stringstream filename; + filename << "World\\Maps\\" << world->basename << "\\" << world->basename << ".wdl"; + //Log << "Saving WDL \"" << filename << "\"." << std::endl; + + sExtendableArray wdlFile = sExtendableArray(); + int curPos = 0; + + // MVER + // { + wdlFile.Extend(8 + 0x4); + SetChunkHeader(wdlFile, curPos, 'MVER', 4); + + // MVER data + *(wdlFile.GetPointer(8)) = 18; // write version 18 + curPos += 8 + 0x4; + // } + + // MWMO + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MWMO', 0); + curPos += 8; + // } + + // MWID + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MWID', 0); + curPos += 8; + // } + + // TODO : MODF + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MODF', 0); + curPos += 8; + // } + + //uint32_t mare_offsets[64][64] = { 0 }; + // MAOF + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MAOF', 64 * 64 * 4); + curPos += 8; + wdlFile.Extend(64 * 64 * 4); + uint mareoffset = curPos + 64 * 64 * 4; + + for (int y = 0; y < 64; ++y) + { + for (int x = 0; x < 64; ++x) + { + TileIndex index(x, y); + + bool has_tile = world->mapIndex.hasTile(index); + // write offset in MAOF entry + *(wdlFile.GetPointer(curPos)) = has_tile ? mareoffset : 0; + + if (has_tile) + { + // MARE Header + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, mareoffset, 'MARE', (2 * (17 * 17)) + (2 * (16 * 16))); // outer heights+inner heights + mareoffset += 8; + + // this might be invalid if map had no WDL + Noggit::map_horizon_tile* horizon_tile = get_horizon_tile(y, x); + + // laod tile and extract WDL data + if (!horizon_tile || regenerate) + { + bool unload = !world->mapIndex.tileLoaded(index) && !world->mapIndex.tileAwaitingLoading(index); + MapTile* mTile = world->mapIndex.loadTile(index); + + if (mTile) + mTile->wait_until_loaded(); + + update_horizon_tile(mTile); + if (unload) + world->mapIndex.unloadTile(index); + + auto test = get_horizon_tile(y, x); + horizon_tile = get_horizon_tile(y, x); + } + if (!horizon_tile) + { + return; // failed to generate data somehow + LogError << "Failed to generate the WDL file." << std::endl; + } + + wdlFile.Insert(mareoffset, sizeof(Noggit::map_horizon_tile::height_17), reinterpret_cast(&horizon_tile->height_17)); + mareoffset += sizeof(Noggit::map_horizon_tile::height_17); + wdlFile.Insert(mareoffset, sizeof(Noggit::map_horizon_tile::height_16), reinterpret_cast(&horizon_tile->height_16)); + mareoffset += sizeof(Noggit::map_horizon_tile::height_16); + + // MAHO (maparea holes) MAHO was added in WOTLK ? + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, mareoffset, 'MAHO', (2 * 16)); // 1 hole mask for each chunk + mareoffset += 8; + wdlFile.Extend(32); + for (int i = 0; i < 16; ++i) + { + wdlFile.Insert(mareoffset, 2, (char*)&horizon_tile->holes[i]); + mareoffset += 2; + } + } + curPos += 4; + } + } + BlizzardArchive::ClientFile f(filename.str(), Noggit::Application::NoggitApplication::instance()->clientData(), + BlizzardArchive::ClientFile::NEW_FILE); + f.setBuffer(wdlFile.data); + f.save(); + f.close(); + + set_minimap(&world->mapIndex); } map_horizon::minimap::minimap(const map_horizon& horizon) diff --git a/src/noggit/map_horizon.h b/src/noggit/map_horizon.h index 64f10b6b..cdd558f8 100755 --- a/src/noggit/map_horizon.h +++ b/src/noggit/map_horizon.h @@ -15,6 +15,9 @@ #include class MapIndex; +class MapTile; +class MapView; +class World; namespace Noggit { @@ -23,6 +26,7 @@ struct map_horizon_tile { int16_t height_17[17][17]; int16_t height_16[16][16]; + int16_t holes[16]; }; struct map_horizon_batch @@ -76,11 +80,24 @@ public: map_horizon(const std::string& basename, const MapIndex * const index); + void set_minimap(const MapIndex* const index); + + Noggit::map_horizon_tile* get_horizon_tile(int y, int x); + QImage _qt_minimap; + void update_horizon_tile(MapTile* mTile); + + void save_wdl(World* world, bool regenerate = false); + private: + int16_t getWdlheight(MapTile* tile, float x, float y); + std::string _filename; + std::vector mWMOFilenames; + // std::vector lWMOInstances; + std::unique_ptr _tiles[64][64]; }; diff --git a/src/noggit/map_index.cpp b/src/noggit/map_index.cpp index 02e3fda3..f0034b4c 100755 --- a/src/noggit/map_index.cpp +++ b/src/noggit/map_index.cpp @@ -177,6 +177,7 @@ void MapIndex::saveall (World* world) for (MapTile* tile : loaded_tiles()) { + world->horizon.update_horizon_tile(tile); tile->saveTile(world); tile->changed = false; } @@ -462,6 +463,7 @@ void MapIndex::saveTile(const TileIndex& tile, World* world, bool save_unloaded) if (tileLoaded(tile)) { saveMaxUID(); + world->horizon.update_horizon_tile(mTiles[tile.z][tile.x].tile.get()); mTiles[tile.z][tile.x].tile->saveTile(world); } } @@ -516,6 +518,7 @@ void MapIndex::saveChanged (World* world, bool save_unloaded) { if (tile->changed.load()) { + world->horizon.update_horizon_tile(tile); tile->saveTile(world); tile->changed = false; } @@ -1205,3 +1208,111 @@ void MapIndex::set_basename(const std::string &pBasename) } } } + +void MapIndex::create_empty_wdl() +{ + // for new map creation, creates a new WDL with all heights as 0 + std::stringstream filename; + filename << "World\\Maps\\" << basename << "\\" << basename << ".wdl"; // mapIndex.basename ? + //Log << "Saving WDL \"" << filename << "\"." << std::endl; + + sExtendableArray wdlFile = sExtendableArray(); + int curPos = 0; + + // MVER + // { + wdlFile.Extend(8 + 0x4); + SetChunkHeader(wdlFile, curPos, 'MVER', 4); + + // MVER data + *(wdlFile.GetPointer(8)) = 18; // write version 18 + curPos += 8 + 0x4; + // } + + // MWMO + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MWMO', 0); + + curPos += 8; + // } + + // MWID + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MWID', 0); + + curPos += 8; + // } + + // MODF + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MODF', 0); + + curPos += 8; + // } + + uint32_t mare_offsets[4096] = { 0 }; // [64][64]; + // MAOF + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MAOF', 64 * 64 * 4); + curPos += 8; + + uint32_t mareoffset = curPos + 64 * 64 * 4; + + for (int y = 0; y < 64; ++y) + { + for (int x = 0; x < 64; ++x) + { + TileIndex index(x, y); + + bool has_tile = hasTile(index); + + // if (tile_exists) + if (has_tile) // TODO check if tile exists + { + // write offset in MAOF entry + wdlFile.Insert(curPos, 4, (char*)&mareoffset); + mare_offsets[y * 64 + x] = mareoffset; + mareoffset += 1138; // mare + maho + } + else + wdlFile.Extend(4); + curPos += 4; + + } + } + + for (auto offset : mare_offsets) + { + if (!offset) + continue; + + // MARE + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MARE', (2 * (17 * 17)) + (2 * (16 * 16))); // outer heights+inner heights + curPos += 8; + + // write inner and outer heights + wdlFile.Extend((2 * (17 * 17)) + (2 * (16 * 16))); + curPos += (2 * (17 * 17)) + (2 * (16 * 16)); + // } + + // MAHO (maparea holes) + // { + wdlFile.Extend(8); + SetChunkHeader(wdlFile, curPos, 'MAHO', 2 * 16); // 1 hole mask for each chunk + curPos += 8; + + wdlFile.Extend(32); + curPos += 32; + } + BlizzardArchive::ClientFile f(filename.str(), Noggit::Application::NoggitApplication::instance()->clientData(), + BlizzardArchive::ClientFile::NEW_FILE); + f.setBuffer(wdlFile.data); + f.save(); + f.close(); +} diff --git a/src/noggit/map_index.hpp b/src/noggit/map_index.hpp index d81978f4..ebcd2722 100755 --- a/src/noggit/map_index.hpp +++ b/src/noggit/map_index.hpp @@ -186,6 +186,8 @@ public: void set_basename(const std::string& pBasename); + void create_empty_wdl(); + void enterTile(const TileIndex& tile); MapTile *loadTile(const TileIndex& tile, bool reloading = false); diff --git a/src/noggit/rendering/Primitives.cpp b/src/noggit/rendering/Primitives.cpp index 5d882bc8..cda53226 100755 --- a/src/noggit/rendering/Primitives.cpp +++ b/src/noggit/rendering/Primitives.cpp @@ -408,7 +408,7 @@ void Square::setup_buffers() } - void Cylinder::draw(glm::mat4x4 const& mvp, glm::vec3 const& pos, const glm::vec4 color, float radius, int precision, World* world, int height) + /*void Cylinder::draw(glm::mat4x4 const& mvp, glm::vec3 const& pos, const glm::vec4 color, float radius, int precision, World* world, int height) { if (!_buffers_are_setup) { @@ -501,4 +501,156 @@ void Square::setup_buffers() } _buffers_are_setup = true; + }*/ + + void Line::initSpline() + { + draw(glm::mat4x4{}, + std::vector{ {}, {} }, + glm::vec4{}, + false); + } + + void Line::draw(glm::mat4x4 const& mvp + , std::vector const points + , glm::vec4 const& color + , bool spline + ) + { + if (points.size() < 2) + return; + + if (!spline || points.size() == 2) + { + setup_buffers(points); + } + else + { + initSpline(); + setup_buffers_interpolated(points); + } + + OpenGL::Scoped::use_program line_shader{ *_program.get() }; + + line_shader.uniform("model_view_projection", mvp); + line_shader.uniform("color", color); + + OpenGL::Scoped::vao_binder const _(_vao[0]); + gl.drawElements(GL_LINE_STRIP, _indices_vbo, _indice_count, GL_UNSIGNED_SHORT, nullptr); + } + + + void Line::setup_buffers(std::vector const points) + { + _vao.upload(); + _buffers.upload(); + + std::vector vertices = points; + std::vector indices; + + for (int i = 0; i < points.size(); ++i) + { + indices.push_back(i); + } + + setup_shader(vertices, indices); + } + + void Line::setup_buffers_interpolated(std::vector const points) + { + const float tension = 0.5f; + + std::vector tempPoints; + tempPoints.push_back(points[0]); + + for (auto const& p : points) + tempPoints.push_back(p); + + tempPoints.push_back(points[points.size() - 1]); + + std::vector vertices; + std::vector indices; + + for (int i = 1; i < tempPoints.size() - 2; i++) + { + auto s = tension * 2.f; + auto p0 = tempPoints[i - 1]; + auto p1 = tempPoints[i + 0]; + auto p2 = tempPoints[i + 1]; + auto p3 = tempPoints[i + 2]; + + glm::vec3 m1( + (p2.x - p0.x) / s, + (p2.y - p0.y) / s, + (p2.z - p0.z) / s + ); + + glm::vec3 m2( + (p3.x - p1.x) / s, + (p3.y - p1.y) / s, + (p3.z - p1.z) / s + ); + + vertices.push_back(interpolate(0, p1, p2, m1, m2)); + + for (float t = 0.01f; t < 1.f; t += 0.01f) + vertices.push_back(interpolate(t, p1, p2, m1, m2)); + + vertices.push_back(interpolate(1, p1, p2, m1, m2)); + } + + for (int i = 0; i < vertices.size(); ++i) + { + indices.push_back(i); + } + + setup_shader(vertices, indices); + } + + glm::vec3 Line::interpolate(float t, glm::vec3 p0, glm::vec3 p1, glm::vec3 m0, glm::vec3 m1) + { + auto c = 2 * t * t * t - 3 * t * t; + auto c0 = c + 1; + auto c1 = t * t * t - 2 * t * t + t; + auto c2 = -c; + auto c3 = t * t * t - t * t; + + return (c0 * p0 + c1 * m0 + c2 * p1 + c3 * m1); + } + + void Line::setup_shader(std::vector vertices, std::vector indices) + { + _indice_count = (int)indices.size(); + _program.reset(new OpenGL::program( + { + { GL_VERTEX_SHADER, OpenGL::shader::src_from_qrc("line_vs") }, + { GL_FRAGMENT_SHADER, OpenGL::shader::src_from_qrc("line_fs") } + } + )); + + gl.bufferData(_vertices_vbo, vertices, GL_STATIC_DRAW); + gl.bufferData(_indices_vbo, indices, GL_STATIC_DRAW); + + OpenGL::Scoped::index_buffer_manual_binder indices_binder(_indices_vbo); + OpenGL::Scoped::use_program shader(*_program.get()); + + { + OpenGL::Scoped::vao_binder const _(_vao[0]); + + OpenGL::Scoped::buffer_binder const vertices_binder(_vertices_vbo); + shader.attrib("position", 3, GL_FLOAT, GL_FALSE, 0, 0); + indices_binder.bind(); + } + + _buffers_are_setup = true; + } + + void Line::unload() + { + _vao.unload(); + _buffers.unload(); + _program.reset(); + + _buffers_are_setup = false; + } \ No newline at end of file diff --git a/src/noggit/rendering/Primitives.hpp b/src/noggit/rendering/Primitives.hpp index 9ce9bd82..b9b96c2a 100755 --- a/src/noggit/rendering/Primitives.hpp +++ b/src/noggit/rendering/Primitives.hpp @@ -136,7 +136,7 @@ namespace Noggit::Rendering::Primitives std::unique_ptr _program; }; - class Cylinder + /*class Cylinder { public: void draw(glm::mat4x4 const& mvp, glm::vec3 const& pos, const glm::vec4 color, float radius, int precision, World* world, int height = 10); @@ -147,6 +147,30 @@ namespace Noggit::Rendering::Primitives void setup_buffers(int precision, World* world, int height); int _indice_count = 0; + OpenGL::Scoped::deferred_upload_vertex_arrays<1> _vao; + OpenGL::Scoped::deferred_upload_buffers<2> _buffers; + GLuint const& _vertices_vbo = _buffers[0]; + GLuint const& _indices_vbo = _buffers[1]; + std::unique_ptr _program; + };*/ + + class Line + { + public: + void initSpline(); + void draw(glm::mat4x4 const& mvp, std::vector const points, glm::vec4 const& color, bool spline); + void unload(); + + private: + bool _buffers_are_setup = false; + void setup_buffers(std::vector const points); + + void setup_buffers_interpolated(std::vector const points); + glm::vec3 interpolate(float t, glm::vec3 p0, glm::vec3 p1, glm::vec3 m0, glm::vec3 m1); + + int _indice_count = 0; + + void setup_shader(std::vector vertices, std::vector indices); OpenGL::Scoped::deferred_upload_vertex_arrays<1> _vao; OpenGL::Scoped::deferred_upload_buffers<2> _buffers; GLuint const& _vertices_vbo = _buffers[0]; diff --git a/src/noggit/rendering/WorldRender.cpp b/src/noggit/rendering/WorldRender.cpp index 02c160ed..7a8f9e79 100755 --- a/src/noggit/rendering/WorldRender.cpp +++ b/src/noggit/rendering/WorldRender.cpp @@ -1189,7 +1189,7 @@ void WorldRender::unload() _cursor_render.unload(); _sphere_render.unload(); _square_render.unload(); - _cylinder_render.unload(); + _line_render.unload(); _horizon_render.reset(); _liquid_texture_manager.unload(); diff --git a/src/noggit/rendering/WorldRender.hpp b/src/noggit/rendering/WorldRender.hpp index 383f6707..9100e339 100755 --- a/src/noggit/rendering/WorldRender.hpp +++ b/src/noggit/rendering/WorldRender.hpp @@ -128,7 +128,7 @@ namespace Noggit::Rendering Noggit::CursorRender _cursor_render; Noggit::Rendering::Primitives::Sphere _sphere_render; Noggit::Rendering::Primitives::Square _square_render; - Noggit::Rendering::Primitives::Cylinder _cylinder_render; + Noggit::Rendering::Primitives::Line _line_render; // buffers OpenGL::Scoped::deferred_upload_buffers<8> _buffers; diff --git a/src/noggit/rendering/glsl/line_frag.glsl b/src/noggit/rendering/glsl/line_frag.glsl new file mode 100644 index 00000000..cde311a8 --- /dev/null +++ b/src/noggit/rendering/glsl/line_frag.glsl @@ -0,0 +1,10 @@ +#version 330 core + +uniform vec4 color; + +out vec4 out_color; + +void main() +{ + out_color = color; +} \ No newline at end of file diff --git a/src/noggit/rendering/glsl/line_vert.glsl b/src/noggit/rendering/glsl/line_vert.glsl new file mode 100644 index 00000000..6cf1462d --- /dev/null +++ b/src/noggit/rendering/glsl/line_vert.glsl @@ -0,0 +1,10 @@ +#version 330 core + +in vec4 position; + +uniform mat4 model_view_projection; + +void main() +{ + gl_Position = model_view_projection * position; +} \ No newline at end of file diff --git a/src/noggit/rendering/glsl/terrain_vert.glsl b/src/noggit/rendering/glsl/terrain_vert.glsl index c8ada267..9f8e6c36 100755 --- a/src/noggit/rendering/glsl/terrain_vert.glsl +++ b/src/noggit/rendering/glsl/terrain_vert.glsl @@ -1,8 +1,8 @@ // This file is part of Noggit3, licensed under GNU General Public License (version 3). #version 410 core -const double TILESIZE = 533.33333; -const double CHUNKSIZE = TILESIZE / 16.0; +const float TILESIZE = 533.33333; +const float CHUNKSIZE = 533.33333 / 16.0; in vec2 position; in vec2 texcoord; @@ -160,25 +160,25 @@ void main() instanceID = base_instance + (t_x * 16 + t_z); vec4 normal_pos = texelFetch(heightmap, ivec2(gl_VertexID, instanceID), 0); - dvec3 pos = dvec3(double(instances[instanceID].ChunkXZ_TileXZ.z * TILESIZE) - + double(instances[instanceID].ChunkXZ_TileXZ.x * CHUNKSIZE) - + double(position.x) - , double(normal_pos.a) - , double(instances[instanceID].ChunkXZ_TileXZ.w * TILESIZE) - + double(instances[instanceID].ChunkXZ_TileXZ.y * CHUNKSIZE) - + double(position.y) + vec3 pos = vec3(instances[instanceID].ChunkXZ_TileXZ.z * TILESIZE + + instances[instanceID].ChunkXZ_TileXZ.x * CHUNKSIZE + + position.x + , normal_pos.a + , instances[instanceID].ChunkXZ_TileXZ.w * TILESIZE + + instances[instanceID].ChunkXZ_TileXZ.y * CHUNKSIZE + + position.y ); bool is_hole = isHoleVertex(gl_VertexID, instances[instanceID].ChunkHoles_DrawImpass_TexLayerCount_CantPaint.r); float NaN = makeNaN(1); - dvec4 pos_after_holecheck = (is_hole ? dvec4(NaN, NaN, NaN, 1.0) : dvec4(pos, 1.0)); - gl_Position = projection * model_view * vec4(pos_after_holecheck); + vec4 pos_after_holecheck = (is_hole ? vec4(NaN, NaN, NaN, 1.0) : vec4(pos, 1.0)); + gl_Position = projection * model_view * pos_after_holecheck; vary_normal = normal_pos.rgb; triangle_normal = normal_pos.rgb; - vary_position = vec3(pos); + vary_position = pos; vary_mccv = texelFetch(mccv, ivec2(gl_VertexID, instanceID), 0).rgb; vary_t0_uv = texcoord + animUVOffset(instances[instanceID].ChunkTexDoAnim.r, diff --git a/src/noggit/ui/TexturePicker.cpp b/src/noggit/ui/TexturePicker.cpp index 886274a1..705b8aa5 100755 --- a/src/noggit/ui/TexturePicker.cpp +++ b/src/noggit/ui/TexturePicker.cpp @@ -117,6 +117,9 @@ namespace Noggit void texture_picker::updateSelection() { + if (!_chunk) + return; + for (size_t index = 0; index < _chunk->texture_set->num(); ++index) { _labels[index]->unselect(); diff --git a/src/noggit/ui/ZoneIDBrowser.cpp b/src/noggit/ui/ZoneIDBrowser.cpp index 68450a4e..afc8860a 100755 --- a/src/noggit/ui/ZoneIDBrowser.cpp +++ b/src/noggit/ui/ZoneIDBrowser.cpp @@ -531,6 +531,7 @@ namespace Noggit layout->addRow("Zone Intro Music:", _zone_intro_music_button); layout->addRow("Sound Ambience Day:", _sound_ambiance_day_button); layout->addRow("Sound Ambience Night:", _sound_ambiance_night_button); + layout->addRow(new QLabel("If only day or night is set but not the other, the other will be saved as the same sound.")); layout->addRow(AdvancedOptionsBox); layout->addRow(save_area_button); @@ -757,28 +758,44 @@ namespace Noggit // Just iterate the DBC to see if an entry with our settings already exists, if not create it. // The reasoning for not having a selector/picker with the existing pairs is that it has less freedom, is harder to use and it's ugly if they don't have a name. // This doesn't have the option to edit that entry for all its users though. - bool sound_ambiance_exists {false}; - for (DBCFile::Iterator i = gSoundAmbienceDB.begin(); i != gSoundAmbienceDB.end(); ++i) + + int soundambience_day = _sound_ambiance_day_button->property("id").toInt(); + int soundambience_night = _sound_ambiance_night_button->property("id").toInt(); + + if (soundambience_day && !soundambience_night) // if day is set but not night, set night to day + soundambience_night = soundambience_day; + else if (!soundambience_day && soundambience_night) // night to day + soundambience_night = soundambience_day; + + if (soundambience_day && soundambience_night) // check if both day and night are set { - int day_id = i->getInt(SoundAmbienceDB::SoundEntry_day); - int night_id = i->getInt(SoundAmbienceDB::SoundEntry_night); - if (day_id == _sound_ambiance_day_button->property("id").toInt() && night_id == _sound_ambiance_night_button->property("id").toInt()) + bool sound_ambiance_exists {false}; + for (DBCFile::Iterator i = gSoundAmbienceDB.begin(); i != gSoundAmbienceDB.end(); ++i) { - record.write(AreaDB::SoundAmbience, i->getInt(SoundAmbienceDB::ID)); - sound_ambiance_exists = true; - break; + int day_id = i->getInt(SoundAmbienceDB::SoundEntry_day); + int night_id = i->getInt(SoundAmbienceDB::SoundEntry_night); + if (day_id == soundambience_day && night_id == soundambience_night) + { + record.write(AreaDB::SoundAmbience, i->getInt(SoundAmbienceDB::ID)); + sound_ambiance_exists = true; + break; + } + } + if (!sound_ambiance_exists) + { + // create new sond entry record with the two ids + auto new_id = gSoundAmbienceDB.getEmptyRecordID(); + auto new_record = gSoundAmbienceDB.addRecord(new_id); + + new_record.write(SoundAmbienceDB::SoundEntry_day, soundambience_day); + new_record.write(SoundAmbienceDB::SoundEntry_night, soundambience_night); + gSoundAmbienceDB.save(); + record.write(AreaDB::SoundAmbience, new_id); } } - if (!sound_ambiance_exists) - { - // create new sond entry record - auto new_id = gSoundAmbienceDB.getEmptyRecordID(); - auto new_record = gSoundAmbienceDB.addRecord(new_id); + else + record.write(AreaDB::SoundAmbience, 0); // if night or day isn't set, set to 0 - new_record.write(SoundAmbienceDB::SoundEntry_day, _sound_ambiance_day_button->property("id").toInt()); - new_record.write(SoundAmbienceDB::SoundEntry_night, _sound_ambiance_night_button->property("id").toInt()); - gSoundAmbienceDB.save(); - } record.write(AreaDB::ZoneMusic, _zone_music_button->property("id").toInt()); record.write(AreaDB::ZoneIntroMusicTable, _zone_intro_music_button->property("id").toInt()); @@ -786,6 +803,13 @@ namespace Noggit record.write(AreaDB::ExplorationLevel, _exploration_level_spinbox->value()); _area_name->toRecord(record, AreaDB::Name); + // update name in the tree + auto parent = static_cast(this->parentWidget()); + auto item = parent->create_or_get_tree_widget_item(_area_id_label->text().toInt()); + std::stringstream ss; + std::string areaName = record.getLocalizedString(AreaDB::Name); + ss << _area_id_label->text().toInt() << "-" << areaName; + item->setText(0, QString(ss.str().c_str())); record.write(AreaDB::FactionGroup, _faction_group_combobox->currentIndex() * 2); record.write(AreaDB::MinElevation, static_cast(_min_elevation_spinbox->value())); @@ -793,6 +817,8 @@ namespace Noggit record.write(AreaDB::LightId, 0); // never used gAreaDB.save(); + + load_area(_area_id_label->text().toInt()); // reload ui, especially for night/day ambience } } diff --git a/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.cpp b/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.cpp index ac1057c2..b7643fff 100755 --- a/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.cpp +++ b/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.cpp @@ -111,8 +111,21 @@ MapCreationWizard::MapCreationWizard(std::shared_ptr pro _map_settings = new QGroupBox("Map settings", this); layout_right->addWidget(_map_settings); - auto map_settings_layout = new QFormLayout(_map_settings); - _map_settings->setLayout(map_settings_layout); + auto box_map_settings_layout = new QVBoxLayout(_map_settings); + _map_settings->setLayout(box_map_settings_layout); + + + _tabs = new QTabWidget(_map_settings); + + auto map_settings_widget(new QWidget(this)); + auto map_difficulty_widget(new QWidget(this)); + + _tabs->addTab(map_settings_widget, "Map Settings"); + _tabs->addTab(map_difficulty_widget, "Map Difficulty Settings"); + + box_map_settings_layout->addWidget(_tabs); + + auto map_settings_layout = new QFormLayout(map_settings_widget); _directory = new QLineEdit(_map_settings); map_settings_layout->addRow("Map directory:", _directory); @@ -206,6 +219,33 @@ MapCreationWizard::MapCreationWizard(std::shared_ptr pro _max_players->setMaximum(std::numeric_limits::max()); map_settings_layout->addRow("Max players:",_max_players); + // difficulty tab + auto difficulty_settings_layout = new QFormLayout(map_difficulty_widget); + _map_settings->setLayout(difficulty_settings_layout); + + _difficulty_type = new QComboBox(_map_settings); + + difficulty_settings_layout->addRow("Difficulty Index", _difficulty_type); + + _difficulty_req_message = new LocaleDBCEntry(_map_settings); + _difficulty_req_message->setDisabled(true); // disable them until they're actually saveable, only "display" it for now + difficulty_settings_layout->addRow("Requirement Message", _difficulty_req_message); + + _difficulty_raid_duration = new QSpinBox(_map_settings); + _difficulty_raid_duration->setDisabled(true); + _difficulty_raid_duration->setRange(0, 7); + difficulty_settings_layout->addRow("Instance Duration(days)", _difficulty_raid_duration); + + _difficulty_max_players = new QSpinBox(_map_settings); + _difficulty_max_players->setDisabled(true); + difficulty_settings_layout->addRow("Max Players", _difficulty_max_players); + + _difficulty_string = new QLineEdit(_map_settings); + _difficulty_string->setDisabled(true); + difficulty_settings_layout->addRow("Difficulty String", _difficulty_string); + + difficulty_settings_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + // Bottom row auto bottom_row_wgt = new QWidget(layout_right_holder); auto btn_row_layout = new QHBoxLayout(layout_right_holder); @@ -255,6 +295,10 @@ MapCreationWizard::MapCreationWizard(std::shared_ptr pro } ); + connect(_difficulty_type, qOverload(&QComboBox::currentIndexChanged), [this](int index) { + selectMapDifficulty(); + }); + // Selection QObject::connect @@ -317,6 +361,40 @@ MapCreationWizard::MapCreationWizard(std::shared_ptr pro } +std::string MapCreationWizard::getDifficultyString() +{ + if (_instance_type->itemData(_instance_type->currentIndex()).toInt() == 1 && _difficulty_max_players->value() == 5) // dungeon + { + if (_difficulty_type->currentIndex() == 0) + return "DUNGEON_DIFFICULTY_5PLAYER"; + else + return "DUNGEON_DIFFICULTY_5PLAYER_HEROIC"; + } + else if (_instance_type->itemData(_instance_type->currentIndex()).toInt() == 2) + { + switch (_difficulty_max_players->value()) + { + case 10: + if (_difficulty_type->currentIndex() == 0) + return "RAID_DIFFICULTY_10PLAYER"; + else + return "RAID_DIFFICULTY_10PLAYER_HEROIC"; + case 20: + if (_difficulty_type->currentIndex() == 0) + return "RAID_DIFFICULTY_20PLAYER"; + case 25: + // in BC 25men was difficulty 0, after the 10men mode in wrath it is difficulty 1 + if (_difficulty_type->currentIndex() == (0 || 1)) // maybe instead check if a difficulty 25 already exists + return "RAID_DIFFICULTY_25PLAYER"; + else + return "RAID_DIFFICULTY_25PLAYER_HEROIC"; + case 40: + return "RAID_DIFFICULTY_40PLAYER"; + } + } + return ""; +} + void MapCreationWizard::selectMap(int map_id) { _is_new_record = false; @@ -342,8 +420,29 @@ void MapCreationWizard::selectMap(int map_id) auto expansionId = record.Columns["ExpansionID"].Value; auto maxPlayers = record.Columns["MaxPlayers"].Value; auto timeOffset = record.Columns["TimeOffset"].Value; + auto raidOffset = record.Columns["RaidOffset"].Value; _world = new World(directoryName, map_id, Noggit::NoggitRenderContext::MAP_VIEW); + + // check if map has a wdl and prompt to create a new one + std::stringstream filename; + filename << "World\\Maps\\" << _world->basename << "\\" << _world->basename << ".wdl"; + if (!Application::NoggitApplication::instance()->clientData()->exists(filename.str())) + { + QMessageBox prompt; + prompt.setText(std::string("This map has no existing horizon data (.wdl file).").c_str()); + prompt.setInformativeText(std::string("Do you want to generate a new .wdl file ?").c_str()); + prompt.setStandardButtons(QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No); + prompt.setDefaultButton(QMessageBox::Yes); + bool answer = prompt.exec() == QMessageBox::StandardButton::Yes; + if (answer) + { + _world->horizon.save_wdl(_world, true); + _world->horizon.set_minimap(&_world->mapIndex); + // _world = new World(directoryName, map_id, Noggit::NoggitRenderContext::MAP_VIEW); // refresh minimap + } + } + _minimap_widget->world(_world); _directory->setText(QString::fromStdString(directoryName)); @@ -383,11 +482,60 @@ void MapCreationWizard::selectMap(int map_id) _time_of_day_override->setValue(std::atoi(timeOffset.c_str())); _expansion_id->setCurrentIndex(std::atoi(expansionId.c_str())); - //_raid_offset->setValue(record.getInt(64)); only ever used in 2 places? not sure what for + _raid_offset->setValue(std::atoi(raidOffset.c_str())); // only ever used in 2 places? not sure what for _max_players->setValue(std::atoi(maxPlayers.c_str())); _project->ClientDatabase->UnloadTable("Map"); + + auto difficulty_table = _project->ClientDatabase->LoadTable("MapDifficulty", readFileAsIMemStream); + + auto iterator = difficulty_table.Records(); + + QSignalBlocker const difficulty_type_blocker(_difficulty_type); + _difficulty_type->clear(); + + while (iterator.HasRecords()) + { + auto record = iterator.Next(); + + // auto difficulty_id = std::atoi(record.Columns["ID"].Value.c_str()); + auto record_id = record.RecordId; + auto diff_mapId = std::atoi(record.Columns["MapID"].Value.c_str()); + auto difficulty_type = std::atoi(record.Columns["Difficulty"].Value.c_str()); + if (diff_mapId == map_id) + { + std::string diff_text = "Difficulty " + record.Columns["Difficulty"].Value; + _difficulty_type->insertItem(difficulty_type, diff_text.c_str(), QVariant(record_id)); + } + } + _project->ClientDatabase->UnloadTable("MapDifficulty"); + _difficulty_type->setCurrentIndex(0); + selectMapDifficulty(); +} + +void MapCreationWizard::selectMapDifficulty() +{ + if (!_difficulty_type->count()) + return; + + auto selected_difficulty_id = _difficulty_type->itemData(_difficulty_type->currentIndex()).toInt(); + if (!selected_difficulty_id) + return; + + auto difficulty_table = _project->ClientDatabase->LoadTable("MapDifficulty", readFileAsIMemStream); + auto record = difficulty_table.Record(selected_difficulty_id); + + //_difficulty_type; + _difficulty_req_message->fill(record, "Message_lang"); + + auto raid_duration = std::atoi(record.Columns["RaidDuration"].Value.c_str()); + _difficulty_raid_duration->setValue(raid_duration / 60 / 60 / 24); // convert from seconds to days + + _difficulty_max_players->setValue(std::atoi(record.Columns["MaxPlayers"].Value.c_str())); + _difficulty_string->setText(record.Columns["Difficultystring"].Value.c_str()); + + _project->ClientDatabase->UnloadTable("MapDifficulty"); } void MapCreationWizard::wheelEvent(QWheelEvent* event) @@ -458,11 +606,15 @@ void MapCreationWizard::saveCurrentEntry() } // Save ADTs and WDT to disk + // _world->mapIndex.create_empty_wdl(); _world->mapIndex.setBigAlpha(_is_big_alpha->isChecked()); _world->setBasename(_directory->text().toStdString()); _world->mapIndex.set_sort_models_by_size_class(_sort_by_size_cat->isChecked()); _world->mapIndex.saveChanged(_world, true); - _world->mapIndex.save(); + _world->mapIndex.save(); // save wdt file + // create default wdl + if (_is_new_record) + _world->mapIndex.create_empty_wdl(); // Save Map.dbc record DBCFile::Record record = _is_new_record ? gMapDB.addRecord(_cur_map_id) : gMapDB.getByID(_cur_map_id); diff --git a/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.hpp b/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.hpp index 3637d0b2..37e0c85e 100755 --- a/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.hpp +++ b/src/noggit/ui/tools/MapCreationWizard/Ui/MapCreationWizard.hpp @@ -97,6 +97,8 @@ namespace Noggit int _selected_map; QGroupBox* _map_settings; + QTabWidget* _tabs; + // Map settings QLineEdit* _directory; @@ -124,6 +126,13 @@ namespace Noggit QSpinBox* _raid_offset; QSpinBox* _max_players; + // map difficulty settings + QComboBox* _difficulty_type; + LocaleDBCEntry* _difficulty_req_message; + QSpinBox* _difficulty_raid_duration; + QSpinBox* _difficulty_max_players; + QLineEdit* _difficulty_string; + World* _world = nullptr; bool _is_new_record = false; @@ -131,7 +140,10 @@ namespace Noggit QMetaObject::Connection _connection; + std::string getDifficultyString(); + void selectMap(int map_id); + void selectMapDifficulty(); void saveCurrentEntry(); void discardChanges(); diff --git a/src/noggit/ui/windows/changelog/Changelog.cpp b/src/noggit/ui/windows/changelog/Changelog.cpp index c3313040..cca57b44 100644 --- a/src/noggit/ui/windows/changelog/Changelog.cpp +++ b/src/noggit/ui/windows/changelog/Changelog.cpp @@ -10,7 +10,7 @@ namespace Noggit CChangelog::CChangelog(QWidget* parent) : QDialog(parent) { - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + /*setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui = new ::Ui::Changelog; ui->setupUi(this); @@ -46,7 +46,7 @@ namespace Noggit connect(ui->listWidget, &QListWidget::itemClicked, [&](QListWidgetItem *item) { OpenChangelog(item->data(1).toString()); - }); + });*/ } void CChangelog::SelectFirst() diff --git a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp index b273b8bb..ea64612c 100755 --- a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp +++ b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp @@ -30,17 +30,18 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application:: _ui->label_2->setStyleSheet("QLabel#title { font-size: 18px; padding: 0px; }"); _settings = new Noggit::Ui::settings(this); - _changelog = new Noggit::Ui::CChangelog(this); + //_changelog = new Noggit::Ui::CChangelog(this); _load_project_component = std::make_unique(); _ui->settings_button->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::Icons::cog)); _ui->settings_button->setIconSize(QSize(20,20)); - _ui->changelog_button->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::Icons::file)); - _ui->changelog_button->setIconSize(QSize(20, 20)); - _ui->changelog_button->setText(tr(" Changelog")); - _ui->changelog_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + _ui->changelog_button->hide(); + //_ui->changelog_button->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::Icons::file)); + //_ui->changelog_button->setIconSize(QSize(20, 20)); + //_ui->changelog_button->setText(tr(" Changelog")); + //_ui->changelog_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); Component::RecentProjectsComponent::buildRecentProjectsList(this); @@ -50,11 +51,11 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application:: } ); - QObject::connect(_ui->changelog_button, &QToolButton::clicked, [&]() + /*QObject::connect(_ui->changelog_button, &QToolButton::clicked, [&]() { _changelog->SelectFirst(); _changelog->show(); - }); + });*/ QObject::connect(_ui->button_create_new_project, &QPushButton::clicked, [=, this] { @@ -142,7 +143,7 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application:: ); // !disable-update && !force-changelog - if (!_noggit_application->GetCommand(0) && !_noggit_application->GetCommand(1)) + /*if (!_noggit_application->GetCommand(0) && !_noggit_application->GetCommand(1)) { _updater = new Noggit::Ui::CUpdater(this); @@ -151,13 +152,13 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application:: _updater->setModal(true); _updater->show(); }); - } + }*/ auto _set = new QSettings(this); - auto first_changelog = _set->value("first_changelog", false); + //auto first_changelog = _set->value("first_changelog", false); // force-changelog - if (_noggit_application->GetCommand(1) || !first_changelog.toBool()) + /*if (_noggit_application->GetCommand(1) || !first_changelog.toBool()) { _changelog->setModal(true); _changelog->show(); @@ -167,7 +168,7 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application:: _set->setValue("first_changelog", true); _set->sync(); } - } + }*/ } diff --git a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp index 8217f098..3c411646 100755 --- a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp +++ b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp @@ -48,8 +48,8 @@ namespace Noggit::Ui::Windows ::Ui::NoggitProjectSelectionWindow* _ui; Noggit::Application::NoggitApplication* _noggit_application; Noggit::Ui::settings* _settings; - Noggit::Ui::CUpdater* _updater; - Noggit::Ui::CChangelog* _changelog; + //Noggit::Ui::CUpdater* _updater; + //Noggit::Ui::CChangelog* _changelog; std::unique_ptr _project_selection_page; std::unique_ptr _load_project_component; diff --git a/src/noggit/ui/windows/updater/Updater.cpp b/src/noggit/ui/windows/updater/Updater.cpp index 1bffd89e..27467aa7 100644 --- a/src/noggit/ui/windows/updater/Updater.cpp +++ b/src/noggit/ui/windows/updater/Updater.cpp @@ -8,7 +8,7 @@ namespace Noggit CUpdater::CUpdater(QWidget* parent) : QDialog(parent) { - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + /*setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui = new ::Ui::Updater; ui->setupUi(this); @@ -36,7 +36,7 @@ namespace Noggit QNetworkReply* reply = (new QNetworkAccessManager)->get(request); connect(reply, SIGNAL(finished()), this, SLOT(GenerateOnlineMD5())); - GenerateLocalMD5(); + GenerateLocalMD5();*/ } QByteArray CUpdater::FileMD5(const QString& filename, QCryptographicHash::Algorithm algo)