TrinityCore
Loading...
Searching...
No Matches
MoveSpline.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "MoveSpline.h"
19#include "Log.h"
20#include "Creature.h"
21#include "DB2Stores.h"
22
23#include <sstream>
24
25namespace Movement{
26
27Location MoveSpline::computePosition(int32 time_point, int32 point_index) const
28{
30
31 float u = 1.0f;
32 float seg_time = float(spline.length(point_index, point_index + 1));
33 if (seg_time > 0)
34 u = std::min(float(time_point - spline.length(point_index)) / seg_time, 1.0f);
35
36 Location c;
38 spline.evaluate_percent(point_index, u, c);
39
40 if (anim_tier)
41 ;// MoveSplineFlag::Animation disables falling or parabolic movement
42 else if (splineflags.Parabolic)
43 computeParabolicElevation(time_point, c.z);
44 else if (splineflags.Falling)
45 computeFallElevation(time_point, c.z);
46
48 {
52 c.orientation = std::atan2(facing.f.y - c.y, facing.f.x - c.x);
53 //nothing to do for MoveSplineFlag::Final_Target flag
54 }
55 else if (splineflags.Turning)
56 {
57 c.orientation = Position::NormalizeOrientation(turn->StartFacing + std::copysign(float(time_point) / float(IN_MILLISECONDS) * turn->RadsPerSec, turn->TotalTurnRads));
58 }
59 else
60 {
62 {
63 Vector3 hermite;
64 spline.evaluate_derivative(point_index, u, hermite);
65 if (hermite.x != 0.f || hermite.y != 0.f)
66 c.orientation = std::atan2(hermite.y, hermite.x);
67 }
68
69 if (splineflags.Backward)
70 c.orientation = c.orientation - float(M_PI);
71 }
72 return c;
73}
74
79
81{
82 int32 time_point = time_passed + time_offset;
83 if (time_point >= Duration())
84 return computePosition(Duration(), spline.last() - 1);
85 if (time_point <= 0)
86 return computePosition(0, spline.first());
87
88 // find point_index where spline.length(point_index) < time_point < spline.length(point_index + 1)
89 int32 point_index = point_Idx;
90 while (time_point >= spline.length(point_index + 1))
91 ++point_index;
92
93 while (time_point < spline.length(point_index))
94 --point_index;
95
96 return computePosition(time_point, point_index);
97}
98
99void MoveSpline::computeParabolicElevation(int32 time_point, float& el) const
100{
101 if (time_point > effect_start_time)
102 {
103 float t_passedf = MSToSec(time_point - effect_start_time);
104 float t_durationf = MSToSec(Duration() - effect_start_time); //client use not modified duration here
105 if (spell_effect_extra && spell_effect_extra->ParabolicCurveId)
106 t_passedf *= sDB2Manager.GetCurveValueAt(spell_effect_extra->ParabolicCurveId, float(time_point) / Duration());
107
108 // -a*x*x + bx + c:
109 //(dur * v3->z_acceleration * dt)/2 - (v3->z_acceleration * dt * dt)/2 + Z;
110 el += (t_durationf - t_passedf) * 0.5f * vertical_acceleration * t_passedf;
111 }
112}
113
114void MoveSpline::computeFallElevation(int32 time_point, float& el) const
115{
116 float z_now = spline.getPoint(spline.first()).z - Movement::computeFallElevation(MSToSec(time_point), false);
117 float final_z = FinalDestination().z;
118 el = std::max(z_now, final_z);
119}
120
122{
124 inline int32 operator()(Spline<int32> const& s, int32 i) const
125 {
126 return Movement::computeFallTime(start_elevation - s.getPoint(i+1).z, false) * 1000.f;
127 }
128};
129
130enum{
133
135{
137 inline int32 operator()(Spline<int32> const& /*s*/, int32 /*i*/)
138 {
139 return time += Movement::computeFallTime(parabolic_amplitude, false) * 1000.f;
140 }
141
143};
144
146{
150 {
151 time += (s.SegLength(i) * velocityInv);
152 return time;
153 }
154};
155
157{
159 if (args.flags.Cyclic)
160 {
161 uint32 cyclic_point = 0;
162 if (splineflags.Enter_Cycle)
163 cyclic_point = 1; // shouldn't be modified, came from client
164 spline.init_cyclic_spline(args.path.data(), args.path.size(), mode, cyclic_point, args.initialOrientation);
165 }
166 else
167 spline.init_spline(args.path.data(), args.path.size(), mode, args.initialOrientation);
168
169 // init spline timestamps
170 if (splineflags.Falling)
171 {
173 spline.initLengths(init);
174 }
175 else if (splineflags.Parabolic && args.velocity < 0.01f)
176 {
178 spline.initLengths(init);
179 }
180 else
181 {
182 CommonInitializer init{ .velocityInv = 1000.0f / args.velocity };
183 spline.initLengths(init);
184 }
185
188 {
189 TC_LOG_ERROR("misc", "MoveSpline::init_spline: zero length spline, wrong input data?");
190 spline.set_length(spline.last(), spline.isCyclic() ? 1000 : 1);
191 }
192
193 if (turn)
194 {
195 MySpline::LengthType totalTurnTime = std::abs(static_cast<MySpline::LengthType>(turn->TotalTurnRads / turn->RadsPerSec * float(IN_MILLISECONDS)));
196 spline.set_length(spline.last(), std::max(spline.length(), totalTurnTime));
197 }
198
200}
201
203{
204 splineflags = args.flags;
205 facing = args.facing;
206 m_Id = args.splineId;
209
210 time_passed = 0;
214 turn = args.turnData;
215 anim_tier = args.animTier;
216 splineIsFacingOnly = args.path.size() == 2 && args.facing.type != MONSTER_MOVE_NORMAL && ((args.path[1] - args.path[0]).squaredLength() < 0.01f);
217
218 velocity = args.velocity;
219
220 // Check if its a stop spline
221 if (args.flags.Done)
222 {
223 spline.clear();
224 return;
225 }
226
227 init_spline(args);
228
229 // init parabolic / animation
230 // spline initialized, duration known and i able to compute parabolic acceleration
231 if (args.flags.Parabolic || args.animTier)
232 {
233 int32 spline_duration = Duration();
235 if (effect_start_time > spline_duration)
236 effect_start_time = spline_duration;
237
238 if (args.flags.Parabolic && effect_start_time < spline_duration)
239 {
240 if (args.parabolic_amplitude != 0.0f)
241 {
242 float f_duration = MSToSec(spline_duration - effect_start_time);
243 vertical_acceleration = args.parabolic_amplitude * 8.f / (f_duration * f_duration);
244 }
245 }
246 }
247 else if (args.flags.FadeObject)
248 effect_start_time = std::max(Duration() - args.fade_object_duration_ms, 0);
249}
250
251MoveSpline::MoveSpline() : m_Id(0), time_passed(0),
252 vertical_acceleration(0.f), initialOrientation(0.f), effect_start_time(0), point_Idx(0), point_Idx_offset(0), velocity(0.f),
253 onTransport(false), splineIsFacingOnly(false)
254{
255 splineflags.Done = true;
256}
257
259
261{
262#define CHECK(exp, verbose) \
263 if (!(exp)) return [&]{ \
264 TC_LOG_ERROR("misc.movesplineinitargs", "MoveSplineInitArgs::Validate: expression '{}' failed for {}", #exp, unit ? std::string_view(verbose) : "cyclic spline continuation"sv); \
265 return false; \
266 }()
267
268 CHECK(path.size() > 1, unit->GetDebugInfo());
269 CHECK(velocity >= 0.01f || (flags.Parabolic && parabolic_amplitude != 0.0f), unit->GetDebugInfo());
270 CHECK(effect_start_point < std::ssize(path), unit->GetDebugInfo());
273 {
274 CHECK(!spellEffectExtra->ProgressCurveId || sCurveStore.LookupEntry(spellEffectExtra->ProgressCurveId), unit->GetDebugInfo());
275 CHECK(!spellEffectExtra->ParabolicCurveId || sCurveStore.LookupEntry(spellEffectExtra->ParabolicCurveId), unit->GetDebugInfo());
276 }
277 if (turnData)
278 {
279 CHECK(G3D::fuzzyNe(turnData->TotalTurnRads, 0.0f), unit->GetDebugInfo());
280 CHECK(turnData->RadsPerSec > 0.0f, unit->GetDebugInfo());
281 }
282 return true;
283#undef CHECK
284}
285
286// check path lengths - why are we even starting such short movement?
288{
289 constexpr float MAX_XY_OFFSET = (1 << 10) / 4.0f;
290 constexpr float MAX_Z_OFFSET = (1 << 9) / 4.0f;
291
292 auto isValidPackedXYOffset = [](float coord) -> bool { return coord > -MAX_XY_OFFSET && coord < MAX_XY_OFFSET; };
293 auto isValidPackedZOffset = [](float coord) -> bool { return coord > -MAX_Z_OFFSET && coord < MAX_Z_OFFSET; };
294
295 if (path.size() > 2)
296 {
297 Vector3 middle = (path.front() + path.back()) / 2;
298 for (uint32 i = 1; i < path.size() - 1; ++i)
299 {
300 if ((path[i + 1] - path[i]).squaredLength() < 0.01f)
301 return false;
302
303 // when compression is enabled, each point coord is packed into 11 bits (10 for Z)
304 if (!flags.UncompressedPath)
305 if (!isValidPackedXYOffset(middle.x - path[i].x)
306 || !isValidPackedXYOffset(middle.y - path[i].y)
307 || !isValidPackedZOffset(middle.z - path[i].z))
308 flags.UncompressedPath = true;
309 }
310 }
311 return true;
312}
313
314MoveSplineInitArgs::MoveSplineInitArgs() : path_Idx_offset(0), velocity(0.f),
315parabolic_amplitude(0.f), effect_start_point(0), fade_object_duration_ms(0),
316splineId(0), initialOrientation(0.f),
317walk(false), HasVelocity(false), TransformForTransport(true)
318{
319}
320
322
324
326
328{
329 if (Finalized())
330 {
331 ms_time_diff = 0;
332 return Result_Arrived;
333 }
334
335 UpdateResult result = Result_None;
336
337 int32 minimal_diff = std::min(ms_time_diff, segment_time_elapsed());
338 ASSERT(minimal_diff >= 0);
339 time_passed += minimal_diff;
340 ms_time_diff -= minimal_diff;
341
343 {
344 ++point_Idx;
345 if (point_Idx < spline.last())
346 {
347 result = Result_NextSegment;
348 }
349 else
350 {
351 if (spline.isCyclic())
352 {
355 result = Result_NextCycle;
356
357 // Remove first point from the path after one full cycle.
358 // That point was the position of the unit prior to entering the cycle and it shouldn't be repeated with continuous cycles.
359 if (splineflags.Enter_Cycle)
360 {
361 splineflags.Enter_Cycle = false;
363 }
364 }
365 else
366 {
367 _Finalize();
368 ms_time_diff = 0;
369 result = Result_Arrived;
370 }
371 }
372 }
373
374 return result;
375}
376
378{
380 args.path.assign(spline.getPoints().begin() + spline.first() + 1, spline.getPoints().begin() + spline.last());
381 args.facing = facing;
382 args.flags = splineflags;
384 args.splineId = m_Id;
386 args.velocity = 1.0f; // Calculated below
387 args.HasVelocity = true;
389 if (args.Validate(nullptr))
390 {
391 // New cycle should preserve previous cycle's duration for some weird reason, even though
392 // the path is really different now. Blizzard is weird. Or this was just a simple oversight.
393 // Since our splines precalculate length with velocity in mind, if we want to find the desired
394 // velocity, we have to make a fake spline, calculate its duration and then compare it to the
395 // desired duration, thus finding out how much the velocity has to be increased for them to match.
396 MoveSpline tempSpline;
397 tempSpline.Initialize(args);
398 args.velocity = (float)tempSpline.Duration() / Duration();
399
400 if (args.Validate(nullptr))
401 init_spline(args);
402 }
403}
404
405std::string MoveSpline::ToString() const
406{
407 std::stringstream str;
408 str << "MoveSpline\n";
409 str << "spline Id: " << GetId() << '\n';
410 str << "flags: " << splineflags.ToString() << '\n';
412 str << "facing angle: " << facing.angle << '\n';
414 str << "facing target: " << facing.target.ToString() << '\n';
416 str << "facing point: " << facing.f.x << " " << facing.f.y << " " << facing.f.z << '\n';
417 str << "time passed: " << time_passed << '\n';
418 str << "total time: " << Duration() << '\n';
419 str << "spline point Id: " << point_Idx << '\n';
420 str << "path point Id: " << currentPathIdx() << '\n';
421 str << spline.ToString();
422 return std::move(str).str();
423}
424
426{
427 splineflags.Done = true;
428 point_Idx = spline.last() - 1;
430}
431
433{
434 int32 point = point_Idx_offset + point_Idx - spline.first() + (int)Finalized();
435 if (isCyclic())
436 point = point % (spline.last()-spline.first());
437 return point;
438}
439}
@ IN_MILLISECONDS
Definition Common.h:38
#define M_PI
Definition Common.h:118
DB2Storage< CurveEntry > sCurveStore("Curve.db2", &CurveLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:569
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
#define ASSERT
Definition Errors.h:80
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define CHECK(exp, verbose)
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
int32 next_timestamp() const
Definition MoveSpline.h:100
bool isCyclic() const
Definition MoveSpline.h:140
std::string ToString() const
UpdateResult _updateState(int32 &ms_time_diff)
============================================================================================
Location ComputePosition() const
Optional< TurnData > turn
Definition MoveSpline.h:87
void computeFallElevation(int32 time_point, float &el) const
uint32 GetId() const
Definition MoveSpline.h:138
bool Finalized() const
Definition MoveSpline.h:139
Optional< AnimTierTransition > anim_tier
Definition MoveSpline.h:88
void reinit_spline_for_next_cycle()
void Initialize(MoveSplineInitArgs const &)
int32 currentPathIdx() const
Vector3 const & FinalDestination() const
Definition MoveSpline.h:143
int32 Duration() const
Definition MoveSpline.h:106
bool Initialized() const
Definition MoveSpline.h:115
int32 segment_time_elapsed() const
Definition MoveSpline.h:101
void computeParabolicElevation(int32 time_point, float &el) const
MoveSplineFlag splineflags
Definition MoveSpline.h:74
void init_spline(MoveSplineInitArgs const &args)
Optional< SpellEffectExtraData > spell_effect_extra
Definition MoveSpline.h:86
Location computePosition(int32 time_point, int32 point_index) const
bool isCyclic() const
Definition Spline.h:115
ControlArray const & getPoints() const
Definition Spline.h:117
float SegLength(index_type i) const
Definition Spline.h:136
index_type first() const
Definition Spline.h:110
Vector3 const & getPoint(index_type i) const
Definition Spline.h:119
void init_cyclic_spline(const Vector3 *controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation=0.0f)
Definition Spline.cpp:210
void init_spline(const Vector3 *controls, index_type count, EvaluationMode m, float orientation=0.0f)
Definition Spline.cpp:201
index_type last() const
Definition Spline.h:111
std::string ToString() const
Definition Spline.cpp:298
void set_length(index_type i, length_type length)
Definition Spline.h:209
void clear() override
Definition SplineImpl.h:90
length_type length() const
Definition Spline.h:199
void evaluate_derivative(float t, Vector3 &hermite) const
Definition SplineImpl.h:28
void evaluate_percent(float t, Vector3 &c) const
Definition SplineImpl.h:20
std::string ToString() const
Definition Unit.h:635
std::string GetDebugInfo() const override
Definition Unit.cpp:14591
float computeFallTime(float path_length, bool isSafeFall)
float computeFallElevation(float t_passed, bool isSafeFall, float start_velocity=0.0f)
float MSToSec(uint32 ms)
@ MONSTER_MOVE_FACING_TARGET
@ MONSTER_MOVE_FACING_ANGLE
@ MONSTER_MOVE_FACING_SPOT
int32 operator()(Spline< int32 > &s, int32 i)
struct Movement::FacingInfo::@309 f
int32 operator()(Spline< int32 > const &s, int32 i) const
Optional< AnimTierTransition > animTier
Optional< SpellEffectExtraData > spellEffectExtra
bool Validate(Unit const *unit)
============================================================================================
int32 operator()(Spline< int32 > const &, int32)
static float NormalizeOrientation(float o)
Definition Position.cpp:207
std::string ToString() const
constexpr bool isSmooth() const
constexpr bool HasFlag(MoveSplineFlagEnum f) const