mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 16:30:15 +00:00
Add MCLQ water, TaxiPathNode transports, and vanilla M2 particles
- Parse MCLQ sub-chunks in vanilla ADTs for water rendering (WotLK uses MH2O) - Load TaxiPathNode.dbc for MO_TRANSPORT world-coordinate paths (vanilla boats) - Parse data[] from SMSG_GAMEOBJECT_QUERY_RESPONSE (taxiPathId for transports) - Support vanilla M2 particle emitters (504-byte struct, different from WotLK 476) - Add character preview texture diagnostic logging - Fix disconnect handling on character screen (show error only when no chars)
This commit is contained in:
parent
cbb3035313
commit
bf31da8c13
14 changed files with 556 additions and 55 deletions
|
|
@ -374,6 +374,17 @@ void ADTLoader::parseMCNK(const uint8_t* data, size_t size, int chunkIndex, ADTT
|
|||
uint32_t skip = (possibleMagic == MCAL) ? 8 : 0;
|
||||
parseMCAL(data + ofsAlpha + skip, sizeAlpha - skip, chunk);
|
||||
}
|
||||
|
||||
// Liquid (MCLQ) - vanilla/TBC per-chunk water (no MH2O in these expansions)
|
||||
// ofsLiquid at MCNK header offset 0x60, sizeLiquid at 0x64
|
||||
uint32_t ofsLiquid = readUInt32(data, 0x60);
|
||||
uint32_t sizeLiquid = readUInt32(data, 0x64);
|
||||
if (ofsLiquid > 0 && sizeLiquid > 8 && ofsLiquid + sizeLiquid <= size) {
|
||||
uint32_t possibleMagic = readUInt32(data, ofsLiquid);
|
||||
uint32_t skip = (possibleMagic == MCLQ) ? 8 : 0;
|
||||
parseMCLQ(data + ofsLiquid + skip, sizeLiquid - skip,
|
||||
chunkIndex, chunk.flags, terrain);
|
||||
}
|
||||
}
|
||||
|
||||
void ADTLoader::parseMCVT(const uint8_t* data, size_t size, MapChunk& chunk) {
|
||||
|
|
@ -453,6 +464,100 @@ void ADTLoader::parseMCAL(const uint8_t* data, size_t size, MapChunk& chunk) {
|
|||
std::memcpy(chunk.alphaMap.data(), data, size);
|
||||
}
|
||||
|
||||
void ADTLoader::parseMCLQ(const uint8_t* data, size_t size, int chunkIndex,
|
||||
uint32_t mcnkFlags, ADTTerrain& terrain) {
|
||||
// MCLQ: Vanilla/TBC per-chunk liquid data (inside MCNK)
|
||||
// Layout:
|
||||
// float minHeight, maxHeight (8 bytes)
|
||||
// SLiquidVertex[9*9] (81 * 8 = 648 bytes)
|
||||
// water: uint8 depth, flow0, flow1, filler, float height
|
||||
// magma: uint16 s, uint16 t, float height
|
||||
// uint8 tiles[8*8] (64 bytes)
|
||||
// Total minimum: 720 bytes
|
||||
|
||||
if (size < 720) {
|
||||
return; // Not enough data for a valid MCLQ
|
||||
}
|
||||
|
||||
float minHeight = readFloat(data, 0);
|
||||
float maxHeight = readFloat(data, 4);
|
||||
|
||||
// Determine liquid type from MCNK flags
|
||||
// 0x04 = has liquid (river/lake), 0x08 = ocean, 0x10 = magma, 0x20 = slime
|
||||
uint16_t liquidType = 0; // water
|
||||
if (mcnkFlags & 0x08) liquidType = 1; // ocean
|
||||
else if (mcnkFlags & 0x10) liquidType = 2; // magma
|
||||
else if (mcnkFlags & 0x20) liquidType = 3; // slime
|
||||
|
||||
// Read 9x9 height values (skip depth/flow bytes, just read the float height)
|
||||
const uint8_t* vertData = data + 8;
|
||||
std::vector<float> heights(81);
|
||||
for (int i = 0; i < 81; i++) {
|
||||
heights[i] = readFloat(vertData, i * 8 + 4); // float at offset 4 within each 8-byte vertex
|
||||
}
|
||||
|
||||
// Read 8x8 tile flags
|
||||
const uint8_t* tileData = data + 8 + 648;
|
||||
std::vector<uint8_t> tileMask(64);
|
||||
bool anyVisible = false;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint8_t tileFlag = tileData[i];
|
||||
// Bit 0x0F = liquid type, bit 0x40 = fatigue, bit 0x80 = hidden
|
||||
// A tile is visible if NOT hidden (0x80 not set) and type is non-zero or has base flag
|
||||
bool hidden = (tileFlag & 0x80) != 0;
|
||||
tileMask[i] = hidden ? 0 : 1;
|
||||
if (!hidden) anyVisible = true;
|
||||
}
|
||||
|
||||
if (!anyVisible) {
|
||||
return; // All tiles hidden, no visible water
|
||||
}
|
||||
|
||||
// Validate heights - if all heights are 0 or unreasonable, skip
|
||||
bool validHeights = false;
|
||||
for (float h : heights) {
|
||||
if (h != 0.0f && std::isfinite(h)) {
|
||||
validHeights = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If heights are all zero, use maxHeight as flat water level
|
||||
if (!validHeights) {
|
||||
for (float& h : heights) h = maxHeight;
|
||||
}
|
||||
|
||||
// Build a WaterLayer matching the MH2O format
|
||||
ADTTerrain::WaterLayer layer;
|
||||
layer.liquidType = liquidType;
|
||||
layer.flags = 0;
|
||||
layer.minHeight = minHeight;
|
||||
layer.maxHeight = maxHeight;
|
||||
layer.x = 0;
|
||||
layer.y = 0;
|
||||
layer.width = 8; // 8 tiles = 9 vertices per axis
|
||||
layer.height = 8;
|
||||
layer.heights = std::move(heights);
|
||||
layer.mask.resize(8); // 8 bytes = 64 bits for 8x8 tiles
|
||||
for (int row = 0; row < 8; row++) {
|
||||
uint8_t rowBits = 0;
|
||||
for (int col = 0; col < 8; col++) {
|
||||
if (tileMask[row * 8 + col]) {
|
||||
rowBits |= (1 << col);
|
||||
}
|
||||
}
|
||||
layer.mask[row] = rowBits;
|
||||
}
|
||||
|
||||
terrain.waterData[chunkIndex].layers.push_back(std::move(layer));
|
||||
|
||||
static int mclqLogCount = 0;
|
||||
if (mclqLogCount < 5) {
|
||||
LOG_INFO("MCLQ[", chunkIndex, "]: type=", liquidType,
|
||||
" height=[", minHeight, ",", maxHeight, "]");
|
||||
mclqLogCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void ADTLoader::parseMH2O(const uint8_t* data, size_t size, ADTTerrain& terrain) {
|
||||
// MH2O contains water/liquid data for all 256 map chunks
|
||||
// Structure: 256 SMLiquidChunk headers followed by instance data
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue