/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "Position.h" #include "ByteBuffer.h" #include "DB2Stores.h" #include "GridDefines.h" #include "StringFormat.h" #include "World.h" #include bool Position::operator==(Position const& a) const { return (G3D::fuzzyEq(a.m_positionX, m_positionX) && G3D::fuzzyEq(a.m_positionY, m_positionY) && G3D::fuzzyEq(a.m_positionZ, m_positionZ) && G3D::fuzzyEq(a.m_orientation, m_orientation)); } void Position::RelocateOffset(Position const& offset) { m_positionX = GetPositionX() + (offset.GetPositionX() * std::cos(GetOrientation()) + offset.GetPositionY() * std::sin(GetOrientation() + float(M_PI))); m_positionY = GetPositionY() + (offset.GetPositionY() * std::cos(GetOrientation()) + offset.GetPositionX() * std::sin(GetOrientation())); m_positionZ = GetPositionZ() + offset.GetPositionZ(); SetOrientation(GetOrientation() + offset.GetOrientation()); } bool Position::IsPositionValid() const { return Trinity::IsValidMapCoord(m_positionX, m_positionY, m_positionZ, m_orientation); } void Position::GetPositionOffsetTo(Position const& endPos, Position& retOffset) const { float dx = endPos.GetPositionX() - GetPositionX(); float dy = endPos.GetPositionY() - GetPositionY(); retOffset.m_positionX = dx * std::cos(GetOrientation()) + dy * std::sin(GetOrientation()); retOffset.m_positionY = dy * std::cos(GetOrientation()) - dx * std::sin(GetOrientation()); retOffset.m_positionZ = endPos.GetPositionZ() - GetPositionZ(); retOffset.SetOrientation(endPos.GetOrientation() - GetOrientation()); } Position Position::GetPositionWithOffset(Position const& offset) const { Position ret(*this); ret.RelocateOffset(offset); return ret; } bool Position::IsWithinBox(Position const& boxOrigin, float length, float width, float height) const { // rotate the WorldObject position instead of rotating the whole cube, that way we can make a simplified // is-in-cube check and we have to calculate only one point instead of 4 // 2PI = 360*, keep in mind that ingame orientation is counter-clockwise double rotation = 2 * M_PI - boxOrigin.GetOrientation(); double sinVal = std::sin(rotation); double cosVal = std::cos(rotation); float BoxDistX = GetPositionX() - boxOrigin.GetPositionX(); float BoxDistY = GetPositionY() - boxOrigin.GetPositionY(); float rotX = float(boxOrigin.GetPositionX() + BoxDistX * cosVal - BoxDistY * sinVal); float rotY = float(boxOrigin.GetPositionY() + BoxDistY * cosVal + BoxDistX * sinVal); // box edges are parallel to coordiante axis, so we can treat every dimension independently :D float dz = GetPositionZ() - boxOrigin.GetPositionZ(); float dx = rotX - boxOrigin.GetPositionX(); float dy = rotY - boxOrigin.GetPositionY(); if ((std::fabs(dx) > length) || (std::fabs(dy) > width) || (std::fabs(dz) > height)) return false; return true; } bool Position::IsWithinVerticalCylinder(Position const& cylinderOrigin, float radius, float height, bool isDoubleVertical) const { float verticalDelta = GetPositionZ() - cylinderOrigin.GetPositionZ(); bool isValidPositionZ = isDoubleVertical ? std::abs(verticalDelta) <= height : 0 <= verticalDelta && verticalDelta <= height; return isValidPositionZ && IsInDist2d(cylinderOrigin, radius); } bool Position::IsInPolygon2D(Position const& polygonOrigin, std::span vertices) const { float testX = GetPositionX(); float testY = GetPositionY(); //this method uses the ray tracing algorithm to determine if the point is in the polygon bool locatedInPolygon = false; for (std::size_t vertex = 0; vertex < vertices.size(); ++vertex) { std::size_t nextVertex; //repeat loop for all sets of points if (vertex == (vertices.size() - 1)) { //if i is the last vertex, let j be the first vertex nextVertex = 0; } else { //for all-else, let j=(i+1)th vertex nextVertex = vertex + 1; } float vertX_i = polygonOrigin.GetPositionX() + vertices[vertex].GetPositionX(); float vertY_i = polygonOrigin.GetPositionY() + vertices[vertex].GetPositionY(); float vertX_j = polygonOrigin.GetPositionX() + vertices[nextVertex].GetPositionX(); float vertY_j = polygonOrigin.GetPositionY() + vertices[nextVertex].GetPositionY(); // following statement checks if testPoint.Y is below Y-coord of i-th vertex bool belowLowY = vertY_i > testY; // following statement checks if testPoint.Y is below Y-coord of i+1-th vertex bool belowHighY = vertY_j > testY; /* following statement is true if testPoint.Y satisfies either (only one is possible) -->(i).Y < testPoint.Y < (i+1).Y OR -->(i).Y > testPoint.Y > (i+1).Y (Note) Both of the conditions indicate that a point is located within the edges of the Y-th coordinate of the (i)-th and the (i+1)- th vertices of the polygon. If neither of the above conditions is satisfied, then it is assured that a semi-infinite horizontal line draw to the right from the testpoint will NOT cross the line that connects vertices i and i+1 of the polygon */ bool withinYsEdges = belowLowY != belowHighY; if (withinYsEdges) { // this is the slope of the line that connects vertices i and i+1 of the polygon float slopeOfLine = (vertX_j - vertX_i) / (vertY_j - vertY_i); // this looks up the x-coord of a point lying on the above line, given its y-coord float pointOnLine = (slopeOfLine * (testY - vertY_i)) + vertX_i; //checks to see if x-coord of testPoint is smaller than the point on the line with the same y-coord bool isLeftToLine = testX < pointOnLine; if (isLeftToLine) { //this statement changes true to false (and vice-versa) locatedInPolygon = !locatedInPolygon; }//end if (isLeftToLine) }//end if (withinYsEdges } return locatedInPolygon; } bool Position::HasInArc(float arc, Position const* obj, float border) const { // always have self in arc if (obj == this) return true; // move arc to range 0.. 2*pi arc = NormalizeOrientation(arc); // move angle to range -pi ... +pi float angle = GetRelativeAngle(obj); if (angle > float(M_PI)) angle -= 2.0f * float(M_PI); float lborder = -1 * (arc / border); // in range -pi..0 float rborder = (arc / border); // in range 0..pi return ((angle >= lborder) && (angle <= rborder)); } bool Position::HasInLine(Position const* pos, float objSize, float width) const { if (!HasInArc(float(M_PI), pos, 2.0f)) return false; width += objSize; float angle = GetRelativeAngle(pos); return std::fabs(std::sin(angle)) * GetExactDist2d(pos->GetPositionX(), pos->GetPositionY()) < width; } std::string Position::ToString() const { return Trinity::StringFormat("X: {} Y: {} Z: {} O: {}", m_positionX, m_positionY, m_positionZ, m_orientation); } float Position::NormalizeOrientation(float o) { // fmod only supports positive numbers. Thus we have // to emulate negative numbers if (o < 0) return -std::fmod(-o, 2.0f * static_cast(M_PI)) + 2.0f * static_cast(M_PI); return std::fmod(o, 2.0f * static_cast(M_PI)); } ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer const& streamer) { buf << float(streamer.Pos->GetPositionX()); buf << float(streamer.Pos->GetPositionY()); return buf; } ByteBuffer& operator>>(ByteBuffer& buf, Position::Streamer const& streamer) { buf >> streamer.Pos->m_positionX; buf >> streamer.Pos->m_positionY; return buf; } ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer const& streamer) { buf << float(streamer.Pos->GetPositionX()); buf << float(streamer.Pos->GetPositionY()); buf << float(streamer.Pos->GetPositionZ()); return buf; } ByteBuffer& operator>>(ByteBuffer& buf, Position::Streamer const& streamer) { buf >> streamer.Pos->m_positionX; buf >> streamer.Pos->m_positionY; buf >> streamer.Pos->m_positionZ; return buf; } ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer const& streamer) { buf << float(streamer.Pos->GetPositionX()); buf << float(streamer.Pos->GetPositionY()); buf << float(streamer.Pos->GetPositionZ()); buf << float(streamer.Pos->GetOrientation()); return buf; } ByteBuffer& operator>>(ByteBuffer& buf, Position::Streamer const& streamer) { buf >> streamer.Pos->m_positionX; buf >> streamer.Pos->m_positionY; buf >> streamer.Pos->m_positionZ; streamer.Pos->SetOrientation(buf.read()); return buf; } ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer const& streamer) { int32 packed = 0; packed |= (int32(streamer.Pos->GetPositionX() / 0.25f) & 0x7FF); packed |= (int32(streamer.Pos->GetPositionY() / 0.25f) & 0x7FF) << 11; packed |= (int32(streamer.Pos->GetPositionZ() / 0.25f) & 0x3FF) << 22; buf << int32(packed); return buf; } std::string WorldLocation::GetDebugInfo() const { MapEntry const* mapEntry = sMapStore.LookupEntry(m_mapId); return Trinity::StringFormat("MapID: {} Map name: '{}' {}", m_mapId, mapEntry ? mapEntry->MapName[sWorld->GetDefaultDbcLocale()] : "", Position::ToString()); }