aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities/Object/Position.cpp
blob: 1ad21820e0971e226912f30a5d8e6e832cba49c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#include "Position.h"
#include "ByteBuffer.h"
#include "DB2Stores.h"
#include "GridDefines.h"
#include "StringFormat.h"
#include "World.h"
#include <G3D/g3dmath.h>

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<Position const> 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<float>(M_PI)) + 2.0f * static_cast<float>(M_PI);

    return std::fmod(o, 2.0f * static_cast<float>(M_PI));
}

ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer<Position::XY> const& streamer)
{
    buf << float(streamer.Pos->GetPositionX());
    buf << float(streamer.Pos->GetPositionY());
    return buf;
}

ByteBuffer& operator>>(ByteBuffer& buf, Position::Streamer<Position::XY> const& streamer)
{
    buf >> streamer.Pos->m_positionX;
    buf >> streamer.Pos->m_positionY;
    return buf;
}

ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer<Position::XYZ> 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<Position::XYZ> 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<Position::XYZO> 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<Position::XYZO> const& streamer)
{
    buf >> streamer.Pos->m_positionX;
    buf >> streamer.Pos->m_positionY;
    buf >> streamer.Pos->m_positionZ;
    streamer.Pos->SetOrientation(buf.read<float>());
    return buf;
}

ByteBuffer& operator<<(ByteBuffer& buf, Position::ConstStreamer<Position::PackedXYZ> 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()] : "<not found>", Position::ToString());
}