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)