bool TerrainInfo::CheckPathAccurate(float srcX, float srcY, float srcZ, float& dstX, float& dstY, float& dstZ, Unit* mover, bool onlyLOS ) const { float tstX = dstX; float tstY = dstY; float tstZ = dstZ; // check by standart way. may be not need path checking? if (!mover && CheckPath(srcX, srcY, srcZ, tstX, tstY, tstZ) && IsNextZcoordOK(tstX, tstY, tstZ, 5.0f)) { DEBUG_LOG("TerrainInfo::CheckPathAccurate vmaps hit! delta is %f %f %f",dstX - tstX,dstY - tstY,dstZ - tstZ); dstX = tstX; dstY = tstY; dstZ = tstZ + 0.1f; return true; } const float distance = sqrt((dstY - srcY)*(dstY - srcY) + (dstX - srcX)*(dstX - srcX)); const float DELTA = 0.5f; const uint8 numChecks = ceil(fabs(distance/DELTA)); const float DELTA_X = (dstX-srcX)/numChecks; const float DELTA_Y = (dstY-srcY)/numChecks; const float DELTA_Z = (dstZ-srcZ)/numChecks; float lastGoodX = srcX; float lastGoodY = srcY; float lastGoodZ = srcZ; uint32 errorsCount = 0; uint32 goodCount = 0; uint32 vmaperrorsCount = 0; std::set<GameObject*> inLOSGOList; bool bGOCheck = false; if (mover) { std::list<GameObject*> tempTargetGOList; MaNGOS::GameObjectInRangeCheck check(mover, tstX, tstY, tstZ, distance + 2.0f *mover->GetObjectBoundingRadius()); MaNGOS::GameObjectListSearcher<MaNGOS::GameObjectInRangeCheck> searcher(tempTargetGOList, check); Cell::VisitAllObjects(mover, searcher, 2*mover->GetObjectBoundingRadius()); if (!tempTargetGOList.empty()) { for(std::list<GameObject*>::iterator iter = tempTargetGOList.begin(); iter != tempTargetGOList.end(); ++iter) { GameObject* pGo = *iter; if (!pGo || !pGo->IsInWorld()) continue; // Not require check GO's, if his not in path // first fast check if (pGo->GetPositionX() > std::max(srcX, dstX) || pGo->GetPositionX() < std::min(srcX, dstX) || pGo->GetPositionY() > std::max(srcY, dstY) || pGo->GetPositionY() < std::min(srcY, dstY)) continue; // don't check very small and very large objects if (pGo->GetDeterminativeSize(true) < mover->GetObjectBoundingRadius() * 0.5f || pGo->GetDeterminativeSize(false) > mover->GetObjectBoundingRadius() * 100.0f) continue; // second check by angle float angle = mover->GetAngle(pGo) - mover->GetAngle(dstX, dstY); if (abs(sin(angle)) * pGo->GetExactDist2d(srcX, srcY) > pGo->GetObjectBoundingRadius() * 0.5f) continue; bool bLOSBreak = false; switch (pGo->GetGoType()) { case GAMEOBJECT_TYPE_TRAP: case GAMEOBJECT_TYPE_SPELL_FOCUS: case GAMEOBJECT_TYPE_MO_TRANSPORT: case GAMEOBJECT_TYPE_CAMERA: case GAMEOBJECT_TYPE_FISHINGNODE: case GAMEOBJECT_TYPE_SUMMONING_RITUAL: case GAMEOBJECT_TYPE_SPELLCASTER: case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_CAPTURE_POINT: break; case GAMEOBJECT_TYPE_DOOR: if (pGo->isSpawned() && pGo->GetGoState() == GO_STATE_READY) bLOSBreak = true; break; case GAMEOBJECT_TYPE_TRANSPORT: if (pGo->isSpawned() && pGo->GetGoState() == GO_STATE_ACTIVE) bLOSBreak = true; break; case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: if (!pGo->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED)) bLOSBreak = true; break; default: if (pGo->isSpawned()) bLOSBreak = true; break; } if (bLOSBreak) inLOSGOList.insert(pGo); } } if (!inLOSGOList.empty()) bGOCheck = true; } //Going foward until max distance for (uint8 i = 1; i < numChecks; ++i) { float prevX = srcX + (float(i-1)*DELTA_X); float prevY = srcY + (float(i-1)*DELTA_Y); float prevZ = GetHeight(prevX, prevY, srcZ + 5.0f); tstX = srcX + (float(i)*DELTA_X); tstY = srcY + (float(i)*DELTA_Y); tstZ = GetHeight(tstX, tstY, srcZ + 5.0f); MaNGOS::NormalizeMapCoord(tstX); MaNGOS::NormalizeMapCoord(tstY); if (tstZ <= INVALID_HEIGHT) break; tstZ += (0.5f + DELTA_Z); if (!CheckPath(prevX, prevY, prevZ, tstX, tstY, tstZ)) { ++vmaperrorsCount; ++errorsCount; goodCount = 0; } else if (!IsNextZcoordOK(tstX, tstY, tstZ, 5.0f)) { ++errorsCount; goodCount = 0; } else if (mover && bGOCheck) { bool bError = false; for(std::set<GameObject*>::const_iterator iter = inLOSGOList.begin(); iter != inLOSGOList.end(); ++iter) { if (!(*iter) || (*iter)->GetDistance2d(tstX, tstY) > (*iter)->GetObjectBoundingRadius()) continue; // DEBUG_LOG("TerrainInfo::CheckPathAccurate GO %s in LOS found, %f %f %f ",(*iter)->GetObjectGuid().GetString().c_str(),tstX,tstY,tstZ); bError = true; break; } if (bError) { ++errorsCount; goodCount = 0; } else ++goodCount; } else { ++goodCount; } // DEBUG_LOG("TerrainInfo::CheckPathAccurate test data %f %f %f good=%u, errors=%u vmap=%u",tstX,tstY,tstZ, goodCount, errorsCount, vmaperrorsCount); if (!errorsCount) { lastGoodX = prevX; lastGoodY = prevY; lastGoodZ = prevZ; } else if (onlyLOS) break; if (errorsCount && goodCount > 10) { --errorsCount; goodCount -= 10; } } if (errorsCount) { dstX = lastGoodX; dstY = lastGoodY; dstZ = GetHeight(lastGoodX, lastGoodY, lastGoodZ+2.0f) + 0.5f; } else { dstX = tstX; dstY = tstY; dstZ = GetHeight(tstX, tstY, tstZ+2.0f) + 0.5f; } return (errorsCount == 0); }
bool TerrainInfo::CheckPathAccurate(float srcX, float srcY, float srcZ, float& dstX, float& dstY, float& dstZ, Unit* mover ) const { float tstX = dstX; float tstY = dstY; float tstZ = dstZ; // check by standart way. may be not need path checking? if (!mover && CheckPath(srcX, srcY, srcZ, tstX, tstY, tstZ) && IsNextZcoordOK(tstX, tstY, tstZ, 5.0f)) { DEBUG_LOG("TerrainInfo::CheckPathAccurate vmaps hit! delta is %f %f %f",dstX - tstX,dstY - tstY,dstZ - tstZ); dstX = tstX; dstY = tstY; dstZ = tstZ + 0.1f; return true; } const float distance = sqrt((dstY - srcY)*(dstY - srcY) + (dstX - srcX)*(dstX - srcX)); const float DELTA = 0.5f; const uint8 numChecks = ceil(fabs(distance/DELTA)); const float DELTA_X = (dstX-srcX)/numChecks; const float DELTA_Y = (dstY-srcY)/numChecks; float lastGoodX = srcX; float lastGoodY = srcY; float lastGoodZ = srcZ; uint32 errorsCount = 0; uint32 goodCount = 0; uint32 vmaperrorsCount = 0; //Going foward until max distance for (uint8 i = 1; i < numChecks; ++i) { float prevX = srcX + (float(i-1)*DELTA_X); float prevY = srcY + (float(i-1)*DELTA_Y); float prevZ = GetHeight(prevX, prevY, srcZ+5.0f); tstX = srcX + (float(i)*DELTA_X); tstY = srcY + (float(i)*DELTA_Y); tstZ = GetHeight(tstX, tstY, srcZ+5.0f); MaNGOS::NormalizeMapCoord(tstX); MaNGOS::NormalizeMapCoord(tstY); if (tstZ <= INVALID_HEIGHT) break; tstZ += 0.5f; if (!CheckPath(prevX, prevY, prevZ, tstX, tstY, tstZ)) { ++vmaperrorsCount; ++errorsCount; goodCount = 0; } else if (!IsNextZcoordOK(tstX, tstY, tstZ, 5.0f)) { ++errorsCount; goodCount = 0; } else if (mover) { std::list<GameObject*> tempTargetGOList; MaNGOS::GameObjectInRangeCheck check(mover, tstX, tstY, tstZ, 2*mover->GetObjectBoundingRadius()); MaNGOS::GameObjectListSearcher<MaNGOS::GameObjectInRangeCheck> searcher(tempTargetGOList, check); Cell::VisitAllObjects(mover, searcher, 2*mover->GetObjectBoundingRadius()); if (!tempTargetGOList.empty()) { for(std::list<GameObject*>::iterator iter = tempTargetGOList.begin(); iter != tempTargetGOList.end(); ++iter) { if (((*iter)->GetGoType() == GAMEOBJECT_TYPE_DOOR && (*iter)->GetGoState() == GO_STATE_READY) || (*iter)->GetGoType() != GAMEOBJECT_TYPE_DOOR) { ++errorsCount; goodCount = 0; } } } else ++goodCount; } else { ++goodCount; } if (!errorsCount) { lastGoodX = prevX; lastGoodY = prevY; lastGoodZ = prevZ; } // DEBUG_LOG("TerrainInfo::CheckPathAccurate test data %f %f %f good=%u, errors=%u vmap=%u",tstX,tstY,tstZ, goodCount, errorsCount, vmaperrorsCount); if (errorsCount && goodCount > 10) { --errorsCount; goodCount -= 10; } } if (errorsCount) { dstX = lastGoodX; dstY = lastGoodY; dstZ = GetHeight(lastGoodX, lastGoodY, lastGoodZ+2.0f) + 0.5f; } else { dstX = tstX; dstY = tstY; dstZ = GetHeight(tstX, tstY, tstZ+2.0f) + 0.5f; } return (errorsCount == 0); }