TrinityCore
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 int32 seg_time = spline.length(point_index, point_index + 1);
33 if (seg_time > 0)
34 u = (time_point - spline.length(point_index)) / (float)seg_time;
35
36 Location c;
38 spline.evaluate_percent(point_index, u, c);
39
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
56 {
58 {
59 Vector3 hermite;
61 if (hermite.x != 0.f || hermite.y != 0.f)
62 c.orientation = std::atan2(hermite.y, hermite.x);
63 }
64
66 c.orientation = c.orientation - float(M_PI);
67 }
68 return c;
69}
70
72{
74}
75
77{
78 int32 time_point = time_passed + time_offset;
79 if (time_point >= Duration())
80 return computePosition(Duration(), spline.last() - 1);
81 if (time_point <= 0)
82 return computePosition(0, spline.first());
83
84 // find point_index where spline.length(point_index) < time_point < spline.length(point_index + 1)
85 int32 point_index = point_Idx;
86 while (time_point >= spline.length(point_index + 1))
87 ++point_index;
88
89 while (time_point < spline.length(point_index))
90 --point_index;
91
92 return computePosition(time_point, point_index);
93}
94
95void MoveSpline::computeParabolicElevation(int32 time_point, float& el) const
96{
97 if (time_point > effect_start_time)
98 {
99 float t_passedf = MSToSec(time_point - effect_start_time);
100 float t_durationf = MSToSec(Duration() - effect_start_time); //client use not modified duration here
101 if (spell_effect_extra && spell_effect_extra->ParabolicCurveId)
102 t_passedf *= sDB2Manager.GetCurveValueAt(spell_effect_extra->ParabolicCurveId, float(time_point) / Duration());
103
104 // -a*x*x + bx + c:
105 //(dur * v3->z_acceleration * dt)/2 - (v3->z_acceleration * dt * dt)/2 + Z;
106 el += (t_durationf - t_passedf) * 0.5f * vertical_acceleration * t_passedf;
107 }
108}
109
110void MoveSpline::computeFallElevation(int32 time_point, float& el) const
111{
112 float z_now = spline.getPoint(spline.first()).z - Movement::computeFallElevation(MSToSec(time_point), false);
113 float final_z = FinalDestination().z;
114 el = std::max(z_now, final_z);
115}
116
117inline uint32 computeDuration(float length, float velocity)
118{
119 return SecToMS(length / velocity);
120}
121
123{
124 FallInitializer(float _start_elevation) : start_elevation(_start_elevation) { }
127 {
128 return Movement::computeFallTime(start_elevation - s.getPoint(i+1).z, false) * 1000.f;
129 }
130};
131
132enum{
135
137{
138 CommonInitializer(float _velocity) : velocityInv(1000.f/_velocity), time(minimal_duration) { }
142 {
143 time += (s.SegLength(i) * velocityInv);
144 return time;
145 }
146};
147
149{
151 if (args.flags.cyclic)
152 {
153 uint32 cyclic_point = 0;
155 cyclic_point = 1; // shouldn't be modified, came from client
156 spline.init_cyclic_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], cyclic_point, args.initialOrientation);
157 }
158 else
159 spline.init_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], args.initialOrientation);
160
161 // init spline timestamps
163 {
165 spline.initLengths(init);
166 }
167 else
168 {
169 CommonInitializer init(args.velocity);
170 spline.initLengths(init);
171 }
172
175 {
176 TC_LOG_ERROR("misc", "MoveSpline::init_spline: zero length spline, wrong input data?");
177 spline.set_length(spline.last(), spline.isCyclic() ? 1000 : 1);
178 }
180}
181
183{
184 splineflags = args.flags;
185 facing = args.facing;
186 m_Id = args.splineId;
189
190 time_passed = 0;
194 anim_tier = args.animTier;
195 splineIsFacingOnly = args.path.size() == 2 && args.facing.type != MONSTER_MOVE_NORMAL && ((args.path[1] - args.path[0]).length() < 0.1f);
196
197 velocity = args.velocity;
198
199 // Check if its a stop spline
200 if (args.flags.done)
201 {
202 spline.clear();
203 return;
204 }
205
206 init_spline(args);
207
208 // init parabolic / animation
209 // spline initialized, duration known and i able to compute parabolic acceleration
211 {
212 int32 spline_duration = Duration();
213 effect_start_time = spline_duration * args.effect_start_time_percent + args.effect_start_time.count();
214 if (effect_start_time > spline_duration)
215 effect_start_time = spline_duration;
216
217 if (args.flags.parabolic && effect_start_time < spline_duration)
218 {
219 if (args.parabolic_amplitude != 0.0f)
220 {
221 float f_duration = MSToSec(spline_duration - effect_start_time);
222 vertical_acceleration = args.parabolic_amplitude * 8.f / (f_duration * f_duration);
223 }
224 else if (args.vertical_acceleration != 0.0f)
225 {
227 }
228 }
229 }
230}
231
232MoveSpline::MoveSpline() : m_Id(0), time_passed(0),
233 vertical_acceleration(0.f), initialOrientation(0.f), effect_start_time(0), point_Idx(0), point_Idx_offset(0), velocity(0.f),
234 onTransport(false), splineIsFacingOnly(false)
235{
236 splineflags.done = true;
237}
238
240
242{
243#define CHECK(exp, verbose) \
244 if (!(exp))\
245 {\
246 if (unit)\
247 TC_LOG_ERROR("misc.movesplineinitargs", "MoveSplineInitArgs::Validate: expression '{}' failed for {}", #exp, (verbose ? unit->GetDebugInfo() : unit->GetGUID().ToString()));\
248 else\
249 TC_LOG_ERROR("misc.movesplineinitargs", "MoveSplineInitArgs::Validate: expression '{}' failed for cyclic spline continuation", #exp); \
250 return false;\
251 }
252 CHECK(path.size() > 1, true);
253 CHECK(velocity >= 0.01f, true);
255 CHECK(_checkPathLengths(), false);
257 {
258 CHECK(!spellEffectExtra->ProgressCurveId || sCurveStore.LookupEntry(spellEffectExtra->ProgressCurveId), true);
259 CHECK(!spellEffectExtra->ParabolicCurveId || sCurveStore.LookupEntry(spellEffectExtra->ParabolicCurveId), true);
260 }
261 return true;
262#undef CHECK
263}
264
265// check path lengths - why are we even starting such short movement?
267{
268 if (path.size() > 2 || facing.type == MONSTER_MOVE_NORMAL)
269 for (uint32 i = 0; i < path.size() - 1; ++i)
270 if ((path[i + 1] - path[i]).length() < 0.1f)
271 return false;
272 return true;
273}
274
275MoveSplineInitArgs::MoveSplineInitArgs(size_t path_capacity /*= 16*/) : path_Idx_offset(0), velocity(0.f),
276parabolic_amplitude(0.f), vertical_acceleration(0.0f), effect_start_time_percent(0.f), effect_start_time(0ms),
277splineId(0), initialOrientation(0.f),
278walk(false), HasVelocity(false), TransformForTransport(true)
279{
280 path.reserve(path_capacity);
281}
282
284
286
288
290{
291 if (Finalized())
292 {
293 ms_time_diff = 0;
294 return Result_Arrived;
295 }
296
297 UpdateResult result = Result_None;
298
299 int32 minimal_diff = std::min(ms_time_diff, segment_time_elapsed());
300 ASSERT(minimal_diff >= 0);
301 time_passed += minimal_diff;
302 ms_time_diff -= minimal_diff;
303
305 {
306 ++point_Idx;
307 if (point_Idx < spline.last())
308 {
309 result = Result_NextSegment;
310 }
311 else
312 {
313 if (spline.isCyclic())
314 {
317 result = Result_NextCycle;
318
319 // Remove first point from the path after one full cycle.
320 // That point was the position of the unit prior to entering the cycle and it shouldn't be repeated with continuous cycles.
322 {
323 splineflags.enter_cycle = false;
324
325 MoveSplineInitArgs args{ (size_t)spline.getPointCount() };
326 args.path.assign(spline.getPoints().begin() + spline.first() + 1, spline.getPoints().begin() + spline.last());
327 args.facing = facing;
328 args.flags = splineflags;
329 args.path_Idx_offset = point_Idx_offset;
330 // MoveSplineFlag::Parabolic | MoveSplineFlag::Animation not supported currently
331 //args.parabolic_amplitude = ?;
332 //args.time_perc = ?;
333 args.splineId = m_Id;
334 args.initialOrientation = initialOrientation;
335 args.velocity = 1.0f; // Calculated below
336 args.HasVelocity = true;
337 args.TransformForTransport = onTransport;
338 if (args.Validate(nullptr))
339 {
340 // New cycle should preserve previous cycle's duration for some weird reason, even though
341 // the path is really different now. Blizzard is weird. Or this was just a simple oversight.
342 // Since our splines precalculate length with velocity in mind, if we want to find the desired
343 // velocity, we have to make a fake spline, calculate its duration and then compare it to the
344 // desired duration, thus finding out how much the velocity has to be increased for them to match.
345 MoveSpline tempSpline;
346 tempSpline.Initialize(args);
347 args.velocity = (float)tempSpline.Duration() / Duration();
348
349 if (args.Validate(nullptr))
350 init_spline(args);
351 }
352 }
353 }
354 else
355 {
356 _Finalize();
357 ms_time_diff = 0;
358 result = Result_Arrived;
359 }
360 }
361 }
362
363 return result;
364}
365
366std::string MoveSpline::ToString() const
367{
368 std::stringstream str;
369 str << "MoveSpline" << std::endl;
370 str << "spline Id: " << GetId() << std::endl;
371 str << "flags: " << splineflags.ToString() << std::endl;
373 str << "facing angle: " << facing.angle << std::endl;
375 str << "facing target: " << facing.target.ToString() << std::endl;
377 str << "facing point: " << facing.f.x << " " << facing.f.y << " " << facing.f.z << std::endl;
378 str << "time passed: " << time_passed << std::endl;
379 str << "total time: " << Duration() << std::endl;
380 str << "spline point Id: " << point_Idx << std::endl;
381 str << "path point Id: " << currentPathIdx() << std::endl;
382 str << spline.ToString();
383 return str.str();
384}
385
387{
388 splineflags.done = true;
389 point_Idx = spline.last() - 1;
391}
392
394{
395 int32 point = point_Idx_offset + point_Idx - spline.first() + (int)Finalized();
396 if (isCyclic())
397 point = point % (spline.last()-spline.first());
398 return point;
399}
400}
#define M_PI
Definition: Common.h:115
DB2Storage< CurveEntry > sCurveStore("Curve.db2", &CurveLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:538
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define CHECK(exp, verbose)
std::string ToString() const
bool hasFlag(uint32 f) const
int32 next_timestamp() const
Definition: MoveSpline.h:98
bool isCyclic() const
Definition: MoveSpline.h:138
std::string ToString() const
Definition: MoveSpline.cpp:366
UpdateResult _updateState(int32 &ms_time_diff)
============================================================================================
Definition: MoveSpline.cpp:289
Location ComputePosition() const
Definition: MoveSpline.cpp:71
void computeFallElevation(int32 time_point, float &el) const
Definition: MoveSpline.cpp:110
uint32 GetId() const
Definition: MoveSpline.h:136
bool Finalized() const
Definition: MoveSpline.h:137
Optional< AnimTierTransition > anim_tier
Definition: MoveSpline.h:87
void Initialize(MoveSplineInitArgs const &)
Definition: MoveSpline.cpp:182
int32 currentPathIdx() const
Definition: MoveSpline.cpp:393
float vertical_acceleration
Definition: MoveSpline.h:80
Vector3 const & FinalDestination() const
Definition: MoveSpline.h:140
int32 Duration() const
Definition: MoveSpline.h:104
bool Initialized() const
Definition: MoveSpline.h:113
int32 segment_time_elapsed() const
Definition: MoveSpline.h:99
FacingInfo facing
Definition: MoveSpline.h:70
void computeParabolicElevation(int32 time_point, float &el) const
Definition: MoveSpline.cpp:95
MoveSplineFlag splineflags
Definition: MoveSpline.h:74
void init_spline(MoveSplineInitArgs const &args)
Definition: MoveSpline.cpp:148
Optional< SpellEffectExtraData > spell_effect_extra
Definition: MoveSpline.h:86
Location computePosition(int32 time_point, int32 point_index) const
Definition: MoveSpline.cpp:27
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
index_type getPointCount() const
Definition: Spline.h:118
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 initLengths()
Definition: SplineImpl.h:78
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: ObjectGuid.cpp:554
Definition: Unit.h:627
uint32 computeDuration(float length, float velocity)
Definition: MoveSpline.cpp:117
float computeFallTime(float path_length, bool isSafeFall)
uint32 SecToMS(float sec)
@ minimal_duration
Definition: MoveSpline.cpp:133
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
CommonInitializer(float _velocity)
Definition: MoveSpline.cpp:138
int32 operator()(Spline< int32 > &s, int32 i)
Definition: MoveSpline.cpp:141
struct Movement::FacingInfo::@323 f
FallInitializer(float _start_elevation)
Definition: MoveSpline.cpp:124
int32 operator()(Spline< int32 > &s, int32 i)
Definition: MoveSpline.cpp:126
bool Validate(Unit *unit) const
============================================================================================
Definition: MoveSpline.cpp:241
MoveSplineInitArgs(size_t path_capacity=16)
Definition: MoveSpline.cpp:275
Optional< AnimTierTransition > animTier
Optional< SpellEffectExtraData > spellEffectExtra