void CreateFilter() { NavTerrainFlag includeFlags = 0; NavTerrainFlag excludeFlags = 0; if (_sourceUnit.IsTypeId(TypeId.Unit)) { Creature creature = _sourceUnit.ToCreature(); if (creature.CanWalk()) { includeFlags |= NavTerrainFlag.Ground; } // creatures don't take environmental damage if (creature.CanSwim()) { includeFlags |= (NavTerrainFlag.Water | NavTerrainFlag.MagmaSlime); } } else { includeFlags = (NavTerrainFlag.Ground | NavTerrainFlag.Water | NavTerrainFlag.MagmaSlime); } _filter.setIncludeFlags((ushort)includeFlags); _filter.setExcludeFlags((ushort)excludeFlags); UpdateFilter(); }
void UpdateFilter() { // allow creatures to cheat and use different movement types if they are moved // forcefully into terrain they can't normally move in if (_sourceUnit.IsInWater() || _sourceUnit.IsUnderWater()) { NavTerrainFlag includedFlags = (NavTerrainFlag)_filter.getIncludeFlags(); includedFlags |= GetNavTerrain(_sourceUnit.GetPositionX(), _sourceUnit.GetPositionY(), _sourceUnit.GetPositionZ()); _filter.setIncludeFlags((ushort)includedFlags); } }
/**************************************************************************/ bool loadMap(uint mapID, uint tileX, uint tileY, MeshData meshData, Spot portion) { string mapFileName = $"maps/{mapID:D4}_{tileY:D2}_{tileX:D2}.map"; if (!File.Exists(mapFileName)) { int parentMapId = vmapManager.GetParentMapId(mapID); if (parentMapId != -1) { mapFileName = $"maps/{parentMapId:D4}_{tileY:D2}_{tileX:D2}.map"; } } if (!File.Exists(mapFileName)) { return(false); } using (BinaryReader reader = new BinaryReader(File.Open(mapFileName, FileMode.Open, FileAccess.Read, FileShare.Read))) { map_fileheader fheader = reader.Read <map_fileheader>(); if (fheader.versionMagic != SharedConst.MAP_VERSION_MAGIC) { Console.WriteLine($"{mapFileName} is the wrong version, please extract new .map files"); return(false); } bool haveTerrain = false; bool haveLiquid = false; reader.BaseStream.Seek(fheader.heightMapOffset, SeekOrigin.Begin); map_heightHeader hheader = reader.Read <map_heightHeader>(); if (hheader.fourcc == 1413957709) { haveTerrain = !Convert.ToBoolean(hheader.flags & (uint)MapHeightFlags.NoHeight); haveLiquid = fheader.liquidMapOffset != 0;// && !m_skipLiquid; } // no data in this map file if (!haveTerrain && !haveLiquid) { return(false); } // data used later byte[][][] holes = new byte[16][][]; for (var i = 0; i < 16; ++i) { holes[i] = new byte[16][]; for (var x = 0; x < 16; ++x) { holes[i][x] = new byte[8]; } } ushort[][] liquid_entry = new ushort[16][]; byte[][] liquid_flags = new byte[16][]; for (var i = 0; i < 16; ++i) { liquid_entry[i] = new ushort[16]; liquid_flags[i] = new byte[16]; } List <int> ltriangles = new List <int>(); List <int> ttriangles = new List <int>(); // terrain data if (haveTerrain) { float heightMultiplier; float[] V9 = new float[SharedConst.V9_SIZE_SQ]; float[] V8 = new float[SharedConst.V8_SIZE_SQ]; int expected = SharedConst.V9_SIZE_SQ + SharedConst.V8_SIZE_SQ; if (Convert.ToBoolean(hheader.flags & (uint)MapHeightFlags.AsInt8)) { byte[] v9 = new byte[SharedConst.V9_SIZE_SQ]; byte[] v8 = new byte[SharedConst.V8_SIZE_SQ]; int count = 0; count += reader.Read(v9, 0, SharedConst.V9_SIZE_SQ); count += reader.Read(v8, 0, SharedConst.V8_SIZE_SQ); if (count != expected) { Console.WriteLine($"TerrainBuilder.loadMap: Failed to read some data expected {expected}, read {count}"); } heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255; for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight; } for (int i = 0; i < SharedConst.V8_SIZE_SQ; ++i) { V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight; } } else if (Convert.ToBoolean(hheader.flags & (uint)MapHeightFlags.AsInt16)) { ushort[] v9 = new ushort[SharedConst.V9_SIZE_SQ]; ushort[] v8 = new ushort[SharedConst.V8_SIZE_SQ]; for (var i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { v9[i] = reader.ReadUInt16(); } for (var i = 0; i < SharedConst.V8_SIZE_SQ; ++i) { v8[i] = reader.ReadUInt16(); } heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535; for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight; } for (int i = 0; i < SharedConst.V8_SIZE_SQ; ++i) { V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight; } } else { for (var i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { V9[i] = reader.ReadSingle(); } for (var i = 0; i < SharedConst.V8_SIZE_SQ; ++i) { V8[i] = reader.ReadSingle(); } } // hole data if (fheader.holesSize != 0) { reader.BaseStream.Seek(fheader.holesOffset, SeekOrigin.Begin); int readCount = 0; for (var i = 0; i < 16; ++i) { for (var x = 0; x < 16; ++x) { for (var c = 0; c < 8; ++c) { if (readCount == fheader.holesSize) { break; } holes[i][x][c] = reader.ReadByte(); } } } } int count1 = meshData.solidVerts.Count / 3; float xoffset = ((float)tileX - 32) * SharedConst.GRID_SIZE; float yoffset = ((float)tileY - 32) * SharedConst.GRID_SIZE; float[] coord = new float[3]; for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { getHeightCoord(i, Grid.V9, xoffset, yoffset, ref coord, ref V9); meshData.solidVerts.Add(coord[0]); meshData.solidVerts.Add(coord[2]); meshData.solidVerts.Add(coord[1]); } for (int i = 0; i < SharedConst.V8_SIZE_SQ; ++i) { getHeightCoord(i, Grid.V8, xoffset, yoffset, ref coord, ref V8); meshData.solidVerts.Add(coord[0]); meshData.solidVerts.Add(coord[2]); meshData.solidVerts.Add(coord[1]); } int[] indices = { 0, 0, 0 }; int loopStart = 0, loopEnd = 0, loopInc = 0; getLoopVars(portion, ref loopStart, ref loopEnd, ref loopInc); for (int i = loopStart; i < loopEnd; i += loopInc) { for (Spot j = Spot.Top; j <= Spot.Bottom; j += 1) { getHeightTriangle(i, j, indices); ttriangles.Add(indices[2] + count1); ttriangles.Add(indices[1] + count1); ttriangles.Add(indices[0] + count1); } } } // liquid data if (haveLiquid) { reader.BaseStream.Seek(fheader.liquidMapOffset, SeekOrigin.Begin); map_liquidHeader lheader = reader.Read <map_liquidHeader>(); float[] liquid_map = null; if (!Convert.ToBoolean(lheader.flags & 0x0001)) { for (var i = 0; i < 16; ++i) { for (var x = 0; x < 16; ++x) { liquid_entry[i][x] = reader.ReadUInt16(); } } for (var i = 0; i < 16; ++i) { for (var x = 0; x < 16; ++x) { liquid_flags[i][x] = reader.ReadByte(); } } } else { for (var i = 0; i < 16; ++i) { for (var x = 0; x < 16; ++x) { liquid_entry[i][x] = lheader.liquidType; liquid_flags[i][x] = lheader.liquidFlags; } } } if (!Convert.ToBoolean(lheader.flags & 0x0002)) { int toRead = lheader.width * lheader.height; liquid_map = new float[toRead]; for (var i = 0; i < toRead; ++i) { liquid_map[i] = reader.ReadSingle(); } } int count = meshData.liquidVerts.Count / 3; float xoffset = (tileX - 32) * SharedConst.GRID_SIZE; float yoffset = (tileY - 32) * SharedConst.GRID_SIZE; float[] coord = new float[3]; int row, col; // generate coordinates if (!Convert.ToBoolean(lheader.flags & 0x0002)) { int j = 0; for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { row = i / SharedConst.V9_SIZE; col = i % SharedConst.V9_SIZE; if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height || col < lheader.offsetX || col >= lheader.offsetX + lheader.width) { // dummy vert using invalid height meshData.liquidVerts.Add((xoffset + col * SharedConst.GRID_PART_SIZE) * -1); meshData.liquidVerts.Add(SharedConst.INVALID_MAP_LIQ_HEIGHT); meshData.liquidVerts.Add((yoffset + row * SharedConst.GRID_PART_SIZE) * -1); continue; } getLiquidCoord(i, j, xoffset, yoffset, ref coord, ref liquid_map); meshData.liquidVerts.Add(coord[0]); meshData.liquidVerts.Add(coord[2]); meshData.liquidVerts.Add(coord[1]); j++; } } else { for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i) { row = i / SharedConst.V9_SIZE; col = i % SharedConst.V9_SIZE; meshData.liquidVerts.Add((xoffset + col * SharedConst.GRID_PART_SIZE) * -1); meshData.liquidVerts.Add(lheader.liquidLevel); meshData.liquidVerts.Add((yoffset + row * SharedConst.GRID_PART_SIZE) * -1); } } int[] indices = { 0, 0, 0 }; int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = Spot.Bottom - Spot.Top; getLoopVars(portion, ref loopStart, ref loopEnd, ref loopInc); // generate triangles for (int i = loopStart; i < loopEnd; i += loopInc) { for (Spot j = Spot.Top; j <= Spot.Bottom; j += triInc) { getHeightTriangle(i, j, indices, true); ltriangles.Add(indices[2] + count); ltriangles.Add(indices[1] + count); ltriangles.Add(indices[0] + count); } } } // now that we have gathered the data, we can figure out which parts to keep: // liquid above ground, ground above liquid int loopStart1 = 0, loopEnd1 = 0, loopInc1 = 0, tTriCount = 4; bool useTerrain, useLiquid; float[] lverts = meshData.liquidVerts.ToArray(); int[] ltris = ltriangles.ToArray(); float[] tverts = meshData.solidVerts.ToArray(); int[] ttris = ttriangles.ToArray(); if ((ltriangles.Count + ttriangles.Count) == 0) { return(false); } // make a copy of liquid vertices // used to pad right-bottom frame due to lost vertex data at extraction float[] lverts_copy = null; if (meshData.liquidVerts.Count != 0) { lverts_copy = new float[meshData.liquidVerts.Count]; Array.Copy(lverts, lverts_copy, meshData.liquidVerts.Count); } getLoopVars(portion, ref loopStart1, ref loopEnd1, ref loopInc1); for (int i = loopStart1; i < loopEnd1; i += loopInc1) { for (int j = 0; j < 2; ++j) { // default is true, will change to false if needed useTerrain = true; useLiquid = true; NavTerrainFlag navTerrain = 0; // if there is no liquid, don't use liquid if (meshData.liquidVerts.Count == 0 || ltriangles.Count == 0) { useLiquid = false; } else { byte liquidType = getLiquidType(i, liquid_flags); if (Convert.ToBoolean(liquidType & (byte)LiquidTypeMask.DarkWater)) { // players should not be here, so logically neither should creatures useTerrain = false; useLiquid = false; } else if ((liquidType & (byte)(LiquidTypeMask.Water | LiquidTypeMask.Ocean)) != 0) { liquidType = (byte)NavArea.Water; } else if (Convert.ToBoolean(liquidType & (byte)(LiquidTypeMask.Magma | LiquidTypeMask.Slime))) { liquidType = (byte)NavArea.MagmaSlime; } else { useLiquid = false; } } // if there is no terrain, don't use terrain if (ttriangles.Count == 0) { useTerrain = false; } // while extracting ADT data we are losing right-bottom vertices // this code adds fair approximation of lost data if (useLiquid) { float quadHeight = 0; uint validCount = 0; for (uint idx = 0; idx < 3; idx++) { float h = lverts_copy[ltris[idx] * 3 + 1]; if (h != SharedConst.INVALID_MAP_LIQ_HEIGHT && h < SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX) { quadHeight += h; validCount++; } } // update vertex height data if (validCount > 0 && validCount < 3) { quadHeight /= validCount; for (uint idx = 0; idx < 3; idx++) { float h = lverts[ltris[idx] * 3 + 1]; if (h == SharedConst.INVALID_MAP_LIQ_HEIGHT || h > SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX) { lverts[ltris[idx] * 3 + 1] = quadHeight; } } } // no valid vertexes - don't use this poly at all if (validCount == 0) { useLiquid = false; } } // if there is a hole here, don't use the terrain if (useTerrain && fheader.holesSize != 0) { useTerrain = !isHole(i, holes); } // we use only one terrain kind per quad - pick higher one if (useTerrain && useLiquid) { float minLLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX; float maxLLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT; for (uint x = 0; x < 3; x++) { float h = lverts[ltris[x] * 3 + 1]; if (minLLevel > h) { minLLevel = h; } if (maxLLevel < h) { maxLLevel = h; } } float maxTLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT; float minTLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX; for (uint x = 0; x < 6; x++) { float h = tverts[ttris[x] * 3 + 1]; if (maxTLevel < h) { maxTLevel = h; } if (minTLevel > h) { minTLevel = h; } } // terrain under the liquid? if (minLLevel > maxTLevel) { useTerrain = false; } //liquid under the terrain? if (minTLevel > maxLLevel) { useLiquid = false; } } // store the result if (useLiquid) { meshData.liquidType.Add((byte)navTerrain); for (int k = 0; k < 3; ++k) { meshData.liquidTris.Add(ltris[k]); } } if (useTerrain) { for (int k = 0; k < 3 * tTriCount / 2; ++k) { meshData.solidTris.Add(ttris[k]); } } ltris = ltris.Skip(3).ToArray(); ttris = ttris.Skip(3 * tTriCount / 2).ToArray(); } } } return(meshData.solidTris.Count != 0 || meshData.liquidTris.Count != 0); }