NavTerrain GetNavTerrain(float x, float y, float z) { LiquidData data; ZLiquidStatus liquidStatus = _sourceUnit.GetMap().getLiquidStatus(x, y, z, MapConst.MapAllLiquidTypes, out data); if (liquidStatus == ZLiquidStatus.NoWater) { return(NavTerrain.Ground); } switch (data.type_flags) { case MapConst.MapLiquidTypeWater: case MapConst.MapLiquidTypeOcean: return(NavTerrain.Water); case MapConst.MapLiquidTypeMagma: return(NavTerrain.Magma); case MapConst.MapLiquidTypeSlime: return(NavTerrain.Slime); default: return(NavTerrain.Ground); } }
NavTerrainFlag GetNavTerrain(float x, float y, float z) { LiquidData data; ZLiquidStatus liquidStatus = _sourceUnit.GetMap().getLiquidStatus(_sourceUnit.GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out data); if (liquidStatus == ZLiquidStatus.NoWater) { return(NavTerrainFlag.Ground); } data.type_flags &= ~MapConst.MapLiquidTypeDarkWater; switch (data.type_flags) { case MapConst.MapLiquidTypeWater: case MapConst.MapLiquidTypeOcean: return(NavTerrainFlag.Water); case MapConst.MapLiquidTypeMagma: case MapConst.MapLiquidTypeSlime: return(NavTerrainFlag.MagmaSlime); default: return(NavTerrainFlag.Ground); } }
void BuildPolyPath(Vector3 startPos, Vector3 endPos) { // *** getting start/end poly logic *** float distToStartPoly = 0; float distToEndPoly = 0; float[] startPoint = { startPos.Y, startPos.Z, startPos.X }; float[] endPoint = { endPos.Y, endPos.Z, endPos.X }; ulong startPoly = GetPolyByLocation(startPoint, ref distToStartPoly); ulong endPoly = GetPolyByLocation(endPoint, ref distToEndPoly); // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying and swimming exception ) // its up to caller how he will use this info if (startPoly == 0 || endPoly == 0) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); bool path = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanFly(); bool waterPath = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanSwim(); if (waterPath) { // Check both start and end points, if they're both in water, then we can *safely* let the creature move for (uint i = 0; i < _pathPoints.Length; ++i) { ZLiquidStatus status = _sourceUnit.GetMap().getLiquidStatus(_pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, MapConst.MapAllLiquidTypes); // One of the points is not in the water, cancel movement. if (status == ZLiquidStatus.NoWater) { waterPath = false; break; } } } pathType = (path || waterPath) ? (PathType.Normal | PathType.NotUsingPath) : PathType.NoPath; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . farFromPoly distToStartPoly={0:F3} distToEndPoly={1:F3}\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (_sourceUnit.IsTypeId(TypeId.Unit)) { Creature owner = _sourceUnit.ToCreature(); Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos; if (_sourceUnit.GetMap().IsUnderWater(p.X, p.Y, p.Z)) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . underWater case\n"); if (owner.CanSwim()) { buildShotrcut = true; } } else { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . flying case\n"); if (owner.CanFly()) { buildShotrcut = true; } } } if (buildShotrcut) { BuildShortcut(); pathType = (PathType.Normal | PathType.NotUsingPath); return; } else { float[] closestPoint = new float[3]; // we may want to use closestPointOnPolyBoundary instead bool posOverPoly = false; if (Detour.dtStatusSucceed(_navMeshQuery.closestPointOnPoly(endPoly, endPoint, closestPoint, ref posOverPoly))) { Detour.dtVcopy(endPoint, closestPoint); SetActualEndPosition(new Vector3(endPoint[2], endPoint[0], endPoint[1])); } pathType = PathType.Incomplete; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == endPoly)\n"); BuildShortcut(); _pathPolyRefs[0] = startPoly; _polyLength = 1; pathType = farFromPoly ? PathType.Incomplete : PathType.Normal; Log.outDebug(LogFilter.Maps, "BuildPolyPath . path type {0}\n", pathType); return; } // look for startPoly/endPoly in current path /// @todo we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint pathStartIndex = 0; uint pathEndIndex = 0; if (_polyLength != 0) { for (; pathStartIndex < _polyLength; ++pathStartIndex) { // here to carch few bugs if (_pathPolyRefs[pathStartIndex] == 0) { Log.outError(LogFilter.Maps, "Invalid poly ref in BuildPolyPath. _polyLength: {0}, pathStartIndex: {1}," + " startPos: {2}, endPos: {3}, mapid: {4}", _polyLength, pathStartIndex, startPos, endPos, _sourceUnit.GetMapId()); break; } if (_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } } for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex) { if (_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } } if (startPolyFound && endPolyFound) { Log.outDebug(LogFilter.Maps, "BuildPolyPath : (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out _polyLength = pathEndIndex - pathStartIndex + 1; Array.Copy(_pathPolyRefs, pathStartIndex, _pathPolyRefs, 0, _polyLength); } else if (startPolyFound && !endPolyFound) { Log.outDebug(LogFilter.Maps, "BuildPolyPath : (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready _polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length /// @todo play with the values here uint prefixPolyLength = (uint)(_polyLength * 0.8f + 0.5f); Array.Copy(_pathPolyRefs, pathStartIndex, _pathPolyRefs, 0, prefixPolyLength); ulong suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float[] suffixEndPoint = new float[3]; bool posOverPoly = false; if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, ref posOverPoly))) { // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that // try to recover by using prev polyref --prefixPolyLength; suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1]; if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, ref posOverPoly))) { // suffixStartPoly is still invalid, error state BuildShortcut(); pathType = PathType.NoPath; return; } } // generate suffix uint suffixPolyLength = 0; uint dtResult; if (_straightLine) { float hit = 0; float[] hitNormal = new float[3]; dtResult = _navMeshQuery.raycast( suffixStartPoly, suffixEndPoint, endPoint, _filter, ref hit, hitNormal, _pathPolyRefs, ref suffixPolyLength, 74 - (int)prefixPolyLength); // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != float.MaxValue) { // the ray hit something, return no path instead of the incomplete one pathType = PathType.NoPath; return; } } else { dtResult = _navMeshQuery.findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position _filter, // polygon search filter _pathPolyRefs, ref suffixPolyLength, 74 - (int)prefixPolyLength); } if (suffixPolyLength == 0 || Detour.dtStatusFailed(dtResult)) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString()); } Log.outDebug(LogFilter.Maps, "m_polyLength={0} prefixPolyLength={1} suffixPolyLength={2} \n", _polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap _polyLength = prefixPolyLength + suffixPolyLength - 1; } else { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (!startPolyFound && !endPolyFound)\n"); // either we have no path at all . first run // or something went really wrong . we aren't moving along the path to the target // just generate new path // free and invalidate old path data Clear(); uint dtResult; if (_straightLine) { float hit = 0; float[] hitNormal = new float[3]; dtResult = _navMeshQuery.raycast( startPoly, startPoint, endPoint, _filter, ref hit, hitNormal, _pathPolyRefs, ref _polyLength, 74); // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != float.MaxValue) { // the ray hit something, return no path instead of the incomplete one pathType = PathType.NoPath; return; } } else { dtResult = _navMeshQuery.findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position _filter, // polygon search filter _pathPolyRefs, // [out] path ref _polyLength, 74); // max number of polygons in output path } if (_polyLength == 0 || Detour.dtStatusFailed(dtResult)) { // only happens if we passed bad data to findPath(), or navmesh is messed up Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString()); BuildShortcut(); pathType = PathType.NoPath; return; } } // by now we know what type of path we can get if (_pathPolyRefs[_polyLength - 1] == endPoly && !pathType.HasAnyFlag(PathType.Incomplete)) { pathType = PathType.Normal; } else { pathType = PathType.Incomplete; } // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }
public override void UpdateUnderwaterState(Map m, float x, float y, float z) { LiquidData liquid_status; ZLiquidStatus res = m.getLiquidStatus(GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); if (res == 0) { m_MirrorTimerFlags &= ~(PlayerUnderwaterState.InWater | PlayerUnderwaterState.InLava | PlayerUnderwaterState.InSlime | PlayerUnderwaterState.InDarkWater); if (_lastLiquid != null && _lastLiquid.SpellID != 0) { RemoveAurasDueToSpell(_lastLiquid.SpellID); } _lastLiquid = null; return; } uint liqEntry = liquid_status.entry; if (liqEntry != 0) { LiquidTypeRecord liquid = CliDB.LiquidTypeStorage.LookupByKey(liqEntry); if (_lastLiquid != null && _lastLiquid.SpellID != 0 && _lastLiquid != liquid) { RemoveAurasDueToSpell(_lastLiquid.SpellID); } if (liquid != null && liquid.SpellID != 0) { if (res.HasAnyFlag(ZLiquidStatus.UnderWater | ZLiquidStatus.InWater)) { if (!HasAura(liquid.SpellID)) { CastSpell(this, liquid.SpellID, true); } } else { RemoveAurasDueToSpell(liquid.SpellID); } } _lastLiquid = liquid; } else if (_lastLiquid != null && _lastLiquid.SpellID != 0) { RemoveAurasDueToSpell(_lastLiquid.SpellID); _lastLiquid = null; } // All liquids type - check under water position if (liquid_status.type_flags.HasAnyFlag <uint>(MapConst.MapLiquidTypeWater | MapConst.MapLiquidTypeOcean | MapConst.MapLiquidTypeMagma | MapConst.MapLiquidTypeSlime)) { if (res.HasAnyFlag(ZLiquidStatus.UnderWater)) { m_MirrorTimerFlags |= PlayerUnderwaterState.InWater; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InWater; } } // Allow travel in dark water on taxi or transport if (liquid_status.type_flags.HasAnyFlag <uint>(MapConst.MapLiquidTypeDarkWater) && !IsInFlight() && GetTransport() == null) { m_MirrorTimerFlags |= PlayerUnderwaterState.InDarkWater; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InDarkWater; } // in lava check, anywhere in lava level if (liquid_status.type_flags.HasAnyFlag <uint>(MapConst.MapLiquidTypeMagma)) { if (res.HasAnyFlag(ZLiquidStatus.UnderWater | ZLiquidStatus.InWater | ZLiquidStatus.WaterWalk)) { m_MirrorTimerFlags |= PlayerUnderwaterState.InLava; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InLava; } } // in slime check, anywhere in slime level if (liquid_status.type_flags.HasAnyFlag <uint>(MapConst.MapLiquidTypeSlime)) { if (res.HasAnyFlag(ZLiquidStatus.UnderWater | ZLiquidStatus.InWater | ZLiquidStatus.WaterWalk)) { m_MirrorTimerFlags |= PlayerUnderwaterState.InSlime; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InSlime; } } }
public override void ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional <LiquidData> liquidData) { // process liquid auras using generic unit code base.ProcessTerrainStatusUpdate(status, liquidData); // player specific logic for mirror timers if (status != 0 && liquidData.HasValue) { // Breath bar state (under water in any liquid type) if (liquidData.Value.type_flags.HasAnyFlag(LiquidHeaderTypeFlags.AllLiquids)) { if (status.HasAnyFlag(ZLiquidStatus.UnderWater)) { m_MirrorTimerFlags |= PlayerUnderwaterState.InWater; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InWater; } } // Fatigue bar state (if not on flight path or transport) if (liquidData.Value.type_flags.HasAnyFlag(LiquidHeaderTypeFlags.DarkWater) && !IsInFlight() && !GetTransport()) { m_MirrorTimerFlags |= PlayerUnderwaterState.InDarkWater; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InDarkWater; } // Lava state (any contact) if (liquidData.Value.type_flags.HasAnyFlag(LiquidHeaderTypeFlags.Magma)) { if (status.HasAnyFlag(ZLiquidStatus.InContact)) { m_MirrorTimerFlags |= PlayerUnderwaterState.InLava; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InLava; } } // Slime state (any contact) if (liquidData.Value.type_flags.HasAnyFlag(LiquidHeaderTypeFlags.Slime)) { if (status.HasAnyFlag(ZLiquidStatus.InContact)) { m_MirrorTimerFlags |= PlayerUnderwaterState.InSlime; } else { m_MirrorTimerFlags &= ~PlayerUnderwaterState.InSlime; } } } else { m_MirrorTimerFlags &= ~(PlayerUnderwaterState.InWater | PlayerUnderwaterState.InLava | PlayerUnderwaterState.InSlime | PlayerUnderwaterState.InDarkWater); } }