TrinityCore
Loading...
Searching...
No Matches
TransportMgr.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 "TransportMgr.h"
19#include "DatabaseEnv.h"
20#include "InstanceScript.h"
21#include "Log.h"
22#include "MapManager.h"
23#include "MoveSplineInitArgs.h"
24#include "ObjectAccessor.h"
25#include "ObjectMgr.h"
26#include "Spline.h"
27#include "Transport.h"
28
32
34
36
42
44{
45 _transportTemplates.clear();
46}
47
49{
50 uint32 oldMSTime = getMSTime();
51
52 QueryResult result = WorldDatabase.Query("SELECT entry FROM gameobject_template WHERE type = 15 ORDER BY entry ASC");
53
54 if (!result)
55 {
56 TC_LOG_INFO("server.loading", ">> Loaded 0 transport templates. DB table `gameobject_template` has no transports!");
57 return;
58 }
59
60 uint32 count = 0;
61
62 do
63 {
64 Field* fields = result->Fetch();
65 uint32 entry = fields[0].GetUInt32();
66 GameObjectTemplate const* goInfo = sObjectMgr->GetGameObjectTemplate(entry);
67 if (goInfo == nullptr)
68 {
69 TC_LOG_ERROR("sql.sql", "Transport {} has no associated GameObjectTemplate from `gameobject_template` , skipped.", entry);
70 continue;
71 }
72
73 if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size())
74 {
75 TC_LOG_ERROR("sql.sql", "Transport {} (name: {}) has an invalid path specified in `gameobject_template`.`Data0` ({}) field, skipped.", entry, goInfo->name, goInfo->moTransport.taxiPathId);
76 continue;
77 }
78
79 if (!goInfo->moTransport.taxiPathId)
80 continue;
81
82 // paths are generated per template, saves us from generating it again in case of instanced transports
83 TransportTemplate& transport = _transportTemplates[entry];
84 transport.entry = entry;
85 GeneratePath(goInfo, &transport);
86
87 // transports in instance are only on one map
88 if (transport.inInstance)
89 _instanceTransports[*transport.mapsUsed.begin()].insert(entry);
90
91 ++count;
92 } while (result->NextRow());
93
94 TC_LOG_INFO("server.loading", ">> Loaded {} transport templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
95}
96
98{
99 for (uint32 i = 0; i < sTransportAnimationStore.GetNumRows(); ++i)
100 if (TransportAnimationEntry const* anim = sTransportAnimationStore.LookupEntry(i))
101 AddPathNodeToTransport(anim->TransportID, anim->TimeIndex, anim);
102
103 for (uint32 i = 0; i < sTransportRotationStore.GetNumRows(); ++i)
104 if (TransportRotationEntry const* rot = sTransportRotationStore.LookupEntry(i))
105 AddPathRotationToTransport(rot->GameObjectsID, rot->TimeIndex, rot);
106}
107
109{
110public:
112
113 void operator()(uint8& mode, bool& cyclic, Movement::PointsArray& points, int& lo, int& hi) const
114 {
116 cyclic = false;
117 points.assign(_points.begin(), _points.end());
118 lo = 1;
119 hi = points.size() - 2;
120 }
121
123};
124
126{
127 uint32 pathId = goInfo->moTransport.taxiPathId;
128 TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId];
129 std::vector<KeyFrame>& keyFrames = transport->keyFrames;
130 Movement::PointsArray splinePath, allPoints;
131 bool mapChange = false;
132 for (size_t i = 0; i < path.size(); ++i)
133 allPoints.push_back(G3D::Vector3(path[i]->Loc.X, path[i]->Loc.Y, path[i]->Loc.Z));
134
135 // Add extra points to allow derivative calculations for all path nodes
136 allPoints.insert(allPoints.begin(), allPoints.front().lerp(allPoints[1], -0.2f));
137 allPoints.push_back(allPoints.back().lerp(allPoints[allPoints.size() - 2], -0.2f));
138 allPoints.push_back(allPoints.back().lerp(allPoints[allPoints.size() - 2], -1.0f));
139
140 SplineRawInitializer initer(allPoints);
141 TransportSpline orientationSpline;
142 orientationSpline.init_spline_custom(initer);
143 orientationSpline.initLengths();
144
145 for (size_t i = 0; i < path.size(); ++i)
146 {
147 if (!mapChange)
148 {
149 TaxiPathNodeEntry const* node_i = path[i];
150 if (i != path.size() - 1 && (node_i->Flags & 1 || node_i->ContinentID != path[i + 1]->ContinentID))
151 {
152 keyFrames.back().Teleport = true;
153 mapChange = true;
154 }
155 else
156 {
157 KeyFrame k(node_i);
158 G3D::Vector3 h;
159 orientationSpline.evaluate_derivative(i + 1, 0.0f, h);
160 k.InitialOrientation = Position::NormalizeOrientation(std::atan2(h.y, h.x) + float(M_PI));
161
162 keyFrames.push_back(k);
163 splinePath.push_back(G3D::Vector3(node_i->Loc.X, node_i->Loc.Y, node_i->Loc.Z));
164 transport->mapsUsed.insert(k.Node->ContinentID);
165 }
166 }
167 else
168 mapChange = false;
169 }
170
171 if (splinePath.size() >= 2)
172 {
173 // Remove special catmull-rom spline points
174 if (!keyFrames.front().IsStopFrame() && !keyFrames.front().Node->ArrivalEventID && !keyFrames.front().Node->DepartureEventID)
175 {
176 splinePath.erase(splinePath.begin());
177 keyFrames.erase(keyFrames.begin());
178 }
179 if (!keyFrames.back().IsStopFrame() && !keyFrames.back().Node->ArrivalEventID && !keyFrames.back().Node->DepartureEventID)
180 {
181 splinePath.pop_back();
182 keyFrames.pop_back();
183 }
184 }
185
186 ASSERT(!keyFrames.empty());
187
188 if (transport->mapsUsed.size() > 1)
189 {
190 for (std::set<uint32>::const_iterator itr = transport->mapsUsed.begin(); itr != transport->mapsUsed.end(); ++itr)
191 ASSERT(!sMapStore.LookupEntry(*itr)->Instanceable());
192
193 transport->inInstance = false;
194 }
195 else
196 transport->inInstance = sMapStore.LookupEntry(*transport->mapsUsed.begin())->Instanceable();
197
198 // last to first is always "teleport", even for closed paths
199 keyFrames.back().Teleport = true;
200
201 const float speed = float(goInfo->moTransport.moveSpeed);
202 const float accel = float(goInfo->moTransport.accelRate);
203 const float accel_dist = 0.5f * speed * speed / accel;
204
205 transport->accelTime = speed / accel;
206 transport->accelDist = accel_dist;
207
208 int32 firstStop = -1;
209 int32 lastStop = -1;
210
211 // first cell is arrived at by teleportation :S
212 keyFrames[0].DistFromPrev = 0;
213 keyFrames[0].Index = 1;
214 if (keyFrames[0].IsStopFrame())
215 {
216 firstStop = 0;
217 lastStop = 0;
218 }
219
220 // find the rest of the distances between key points
221 // Every path segment has its own spline
222 size_t start = 0;
223 for (size_t i = 1; i < keyFrames.size(); ++i)
224 {
225 if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.size())
226 {
227 size_t extra = !keyFrames[i - 1].Teleport ? 1 : 0;
228 std::shared_ptr<TransportSpline> spline = std::make_shared<TransportSpline>();
229 spline->init_spline(&splinePath[start], i - start + extra, Movement::SplineBase::ModeCatmullrom);
230 spline->initLengths();
231 for (size_t j = start; j < i + extra; ++j)
232 {
233 keyFrames[j].Index = j - start + 1;
234 keyFrames[j].DistFromPrev = float(spline->length(j - start, j + 1 - start));
235 if (j > 0)
236 keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev;
237 keyFrames[j].Spline = spline;
238 }
239
240 if (keyFrames[i - 1].Teleport)
241 {
242 keyFrames[i].Index = i - start + 1;
243 keyFrames[i].DistFromPrev = 0.0f;
244 keyFrames[i - 1].NextDistFromPrev = 0.0f;
245 keyFrames[i].Spline = spline;
246 }
247
248 start = i;
249 }
250
251 if (keyFrames[i].IsStopFrame())
252 {
253 // remember first stop frame
254 if (firstStop == -1)
255 firstStop = i;
256 lastStop = i;
257 }
258 }
259
260 keyFrames.back().NextDistFromPrev = keyFrames.front().DistFromPrev;
261
262 if (firstStop == -1 || lastStop == -1)
263 firstStop = lastStop = 0;
264
265 // at stopping keyframes, we define distSinceStop == 0,
266 // and distUntilStop is to the next stopping keyframe.
267 // this is required to properly handle cases of two stopping frames in a row (yes they do exist)
268 float tmpDist = 0.0f;
269 for (size_t i = 0; i < keyFrames.size(); ++i)
270 {
271 int32 j = (i + lastStop) % keyFrames.size();
272 if (keyFrames[j].IsStopFrame() || j == lastStop)
273 tmpDist = 0.0f;
274 else
275 tmpDist += keyFrames[j].DistFromPrev;
276 keyFrames[j].DistSinceStop = tmpDist;
277 }
278
279 tmpDist = 0.0f;
280 for (int32 i = int32(keyFrames.size()) - 1; i >= 0; i--)
281 {
282 int32 j = (i + firstStop) % keyFrames.size();
283 tmpDist += keyFrames[(j + 1) % keyFrames.size()].DistFromPrev;
284 keyFrames[j].DistUntilStop = tmpDist;
285 if (keyFrames[j].IsStopFrame() || j == firstStop)
286 tmpDist = 0.0f;
287 }
288
289 for (size_t i = 0; i < keyFrames.size(); ++i)
290 {
291 float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop;
292 if (total_dist < 2 * accel_dist) // won't reach full speed
293 {
294 if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating
295 {
296 // calculate accel+brake time for this short segment
297 float segment_time = 2.0f * std::sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel);
298 // substract acceleration time
299 keyFrames[i].TimeTo = segment_time - std::sqrt(2 * keyFrames[i].DistSinceStop / accel);
300 }
301 else // slowing down
302 keyFrames[i].TimeTo = std::sqrt(2 * keyFrames[i].DistUntilStop / accel);
303 }
304 else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed)
305 {
306 // calculate accel + cruise + brake time for this long segment
307 float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel);
308 // substract acceleration time
309 keyFrames[i].TimeTo = segment_time - std::sqrt(2 * keyFrames[i].DistSinceStop / accel);
310 }
311 else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed)
312 keyFrames[i].TimeTo = std::sqrt(2 * keyFrames[i].DistUntilStop / accel);
313 else // at full speed
314 keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel);
315 }
316
317 // calculate tFrom times from tTo times
318 float segmentTime = 0.0f;
319 for (size_t i = 0; i < keyFrames.size(); ++i)
320 {
321 int32 j = (i + lastStop) % keyFrames.size();
322 if (keyFrames[j].IsStopFrame() || j == lastStop)
323 segmentTime = keyFrames[j].TimeTo;
324 keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo;
325 }
326
327 // calculate path times
328 keyFrames[0].ArriveTime = 0;
329 float curPathTime = 0.0f;
330 if (keyFrames[0].IsStopFrame())
331 {
332 curPathTime = float(keyFrames[0].Node->Delay);
333 keyFrames[0].DepartureTime = uint32(curPathTime * float(IN_MILLISECONDS));
334 }
335
336 for (size_t i = 1; i < keyFrames.size(); ++i)
337 {
338 curPathTime += keyFrames[i - 1].TimeTo;
339 if (keyFrames[i].IsStopFrame())
340 {
341 keyFrames[i].ArriveTime = uint32(curPathTime * float(IN_MILLISECONDS));
342 keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime;
343 curPathTime += float(keyFrames[i].Node->Delay);
344 keyFrames[i].DepartureTime = uint32(curPathTime * float(IN_MILLISECONDS));
345 }
346 else
347 {
348 curPathTime -= keyFrames[i].TimeTo;
349 keyFrames[i].ArriveTime = uint32(curPathTime * float(IN_MILLISECONDS));
350 keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime;
351 keyFrames[i].DepartureTime = keyFrames[i].ArriveTime;
352 }
353 }
354
355 keyFrames.back().NextArriveTime = keyFrames.back().DepartureTime;
356
357 transport->pathTime = keyFrames.back().DepartureTime;
358}
359
361{
362 TransportAnimation& animNode = _transportAnimations[transportEntry];
363 if (animNode.TotalTime < timeSeg)
364 animNode.TotalTime = timeSeg;
365
366 animNode.Path[timeSeg] = node;
367}
368
370{
371 // instance case, execute GetGameObjectEntry hook
372 if (map)
373 {
374 // SetZoneScript() is called after adding to map, so fetch the script using map
375 if (map->IsDungeon())
376 if (InstanceScript* instance = static_cast<InstanceMap*>(map)->GetInstanceScript())
377 entry = instance->GetGameObjectEntry(0, entry);
378
379 if (!entry)
380 return nullptr;
381 }
382
383 TransportTemplate const* tInfo = GetTransportTemplate(entry);
384 if (!tInfo)
385 {
386 TC_LOG_ERROR("sql.sql", "Transport {} will not be loaded, `transport_template` missing", entry);
387 return nullptr;
388 }
389
390 // create transport...
391 Transport* trans = new Transport();
392
393 // ...at first waypoint
394 TaxiPathNodeEntry const* startNode = tInfo->keyFrames.begin()->Node;
395 uint32 mapId = startNode->ContinentID;
396 float x = startNode->Loc.X;
397 float y = startNode->Loc.Y;
398 float z = startNode->Loc.Z;
399 float o = tInfo->keyFrames.begin()->InitialOrientation;
400
401 // initialize the gameobject base
402 ObjectGuid::LowType guidLow = guid ? guid : sObjectMgr->GetGenerator<HighGuid::Mo_Transport>().Generate();
403
404 if (!trans->Create(guidLow, entry, mapId, x, y, z, o, 255))
405 {
406 delete trans;
407 return nullptr;
408 }
409
410 if (MapEntry const* mapEntry = sMapStore.LookupEntry(mapId))
411 {
412 if (mapEntry->Instanceable() != tInfo->inInstance)
413 {
414 TC_LOG_ERROR("entities.transport", "Transport {} (name: {}) attempted creation in instance map (id: {}) but it is not an instanced transport!", entry, trans->GetName(), mapId);
415 delete trans;
416 return nullptr;
417 }
418 }
419
420 // use preset map for instances (need to know which instance)
421 trans->SetMap(map ? map : sMapMgr->CreateMap(mapId, nullptr));
422 if (map && map->IsDungeon())
424
425 // Passengers will be loaded once a player is near
427 trans->GetMap()->AddToMap<Transport>(trans);
428 return trans;
429}
430
432{
433 if (_transportTemplates.empty())
434 return;
435
436 uint32 oldMSTime = getMSTime();
437
438 QueryResult result = WorldDatabase.Query("SELECT guid, entry FROM transports");
439
440 uint32 count = 0;
441 if (result)
442 {
443 do
444 {
445 Field* fields = result->Fetch();
446 ObjectGuid::LowType guid = fields[0].GetUInt32();
447 uint32 entry = fields[1].GetUInt32();
448
449 if (TransportTemplate const* tInfo = GetTransportTemplate(entry))
450 if (!tInfo->inInstance)
451 if (CreateTransport(entry, guid))
452 ++count;
453
454 } while (result->NextRow());
455 }
456
457 TC_LOG_INFO("server.loading", ">> Spawned {} continent transports in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
458}
459
461{
462 TransportInstanceMap::const_iterator mapTransports = _instanceTransports.find(map->GetId());
463
464 // no transports here
465 if (mapTransports == _instanceTransports.end() || mapTransports->second.empty())
466 return;
467
468 // create transports
469 for (std::set<uint32>::const_iterator itr = mapTransports->second.begin(); itr != mapTransports->second.end(); ++itr)
470 CreateTransport(*itr, 0, map);
471}
472
474{
475 auto itr = Path.lower_bound(time);
476 if (itr != Path.end())
477 return itr->second;
478
479 return nullptr;
480}
481
483{
484 auto itr = Rotations.lower_bound(time);
485 if (itr != Rotations.end())
486 return itr->second;
487
488 return nullptr;
489}
@ IN_MILLISECONDS
Definition Common.h:35
#define M_PI
Definition Common.h:72
TaxiPathNodesByPath sTaxiPathNodesByPath
DBCStorage< TransportRotationEntry > sTransportRotationStore(TransportRotationfmt)
DBCStorage< TransportAnimationEntry > sTransportAnimationStore(TransportAnimationfmt)
DBCStorage< MapEntry > sMapStore(MapEntryfmt)
std::vector< TaxiPathNodeEntry const * > TaxiPathNodeList
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
#define ASSERT
Definition Errors.h:68
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
#define sMapMgr
Definition MapManager.h:211
#define sObjectMgr
Definition ObjectMgr.h:1721
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
Class used to access individual fields of database query result.
Definition Field.h:92
uint32 GetUInt32() const
Definition Field.cpp:61
static void Insert(T *o)
InstanceScript * GetInstanceScript()
Definition Map.h:887
Definition Map.h:281
bool IsDungeon() const
Definition Map.cpp:4236
bool AddToMap(T *)
Definition Map.cpp:630
uint32 GetId() const
Definition Map.cpp:4216
InstanceMap * ToInstanceMap()
Definition Map.h:520
void init_spline_custom(Init &initializer)
Definition Spline.h:125
void evaluate_derivative(float t, Vector3 &hermite) const
Definition SplineImpl.h:28
uint32 LowType
Definition ObjectGuid.h:142
SplineRawInitializer(Movement::PointsArray &points)
Movement::PointsArray & _points
void operator()(uint8 &mode, bool &cyclic, Movement::PointsArray &points, int &lo, int &hi) const
void LoadTransportTemplates()
TransportTemplates _transportTemplates
TransportAnimationContainer _transportAnimations
void LoadTransportAnimationAndRotation()
Transport * CreateTransport(uint32 entry, ObjectGuid::LowType guid=0, Map *map=nullptr)
void CreateInstanceTransports(Map *map)
static TransportMgr * instance()
TransportTemplate const * GetTransportTemplate(uint32 entry) const
TransportInstanceMap _instanceTransports
void AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const *node)
void AddPathRotationToTransport(uint32 transportEntry, uint32 timeSeg, TransportRotationEntry const *node)
void GeneratePath(GameObjectTemplate const *goInfo, TransportTemplate *transport)
void SpawnContinentTransports()
bool Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress)
Definition Transport.cpp:49
Map * GetMap() const
Definition Object.h:449
ZoneScript * m_zoneScript
Definition Object.h:605
std::string const & GetName() const
Definition Object.h:382
virtual void SetMap(Map *map)
Definition Object.cpp:1808
std::vector< Vector3 > PointsArray
struct GameObjectTemplate::@191::@206 moTransport
float InitialOrientation
TaxiPathNodeEntry const * Node
static float NormalizeOrientation(float o)
Definition Position.cpp:156
DBCPosition3D Loc
TransportRotationEntry const * GetAnimRotation(uint32 time) const
TransportPathRotationContainer Rotations
TransportAnimationEntry const * GetAnimNode(uint32 time) const
TransportPathContainer Path
std::set< uint32 > mapsUsed
KeyFrameVec keyFrames