Updated Copyright year to 2013
[getmangos.git] / src / game / Transports.cpp
blob8a7c24139a3c8823e749d7af8b41a12176aa9b22
1 /*
2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
21 #include "Transports.h"
22 #include "MapManager.h"
23 #include "ObjectMgr.h"
24 #include "ObjectGuid.h"
25 #include "Path.h"
27 #include "WorldPacket.h"
28 #include "DBCStores.h"
29 #include "ProgressBar.h"
30 #include "ScriptMgr.h"
32 void MapManager::LoadTransports()
34 QueryResult* result = WorldDatabase.Query("SELECT entry, name, period FROM transports");
36 uint32 count = 0;
38 if (!result)
40 BarGoLink bar(1);
41 bar.step();
43 sLog.outString();
44 sLog.outString(">> Loaded %u transports", count);
45 return;
48 BarGoLink bar(result->GetRowCount());
52 bar.step();
54 Transport* t = new Transport;
56 Field* fields = result->Fetch();
58 uint32 entry = fields[0].GetUInt32();
59 std::string name = fields[1].GetCppString();
60 t->m_period = fields[2].GetUInt32();
62 const GameObjectInfo* goinfo = ObjectMgr::GetGameObjectInfo(entry);
64 if (!goinfo)
66 sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str());
67 delete t;
68 continue;
71 if (goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT)
73 sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str());
74 delete t;
75 continue;
78 // sLog.outString("Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name);
80 std::set<uint32> mapsUsed;
82 if (!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed))
83 // skip transports with empty waypoints list
85 sLog.outErrorDb("Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.", goinfo->moTransport.taxiPathId);
86 delete t;
87 continue;
90 float x, y, z, o;
91 uint32 mapid;
92 x = t->m_WayPoints[0].x; y = t->m_WayPoints[0].y; z = t->m_WayPoints[0].z; mapid = t->m_WayPoints[0].mapid; o = 1;
94 // current code does not support transports in dungeon!
95 const MapEntry* pMapInfo = sMapStore.LookupEntry(mapid);
96 if (!pMapInfo || pMapInfo->Instanceable())
98 delete t;
99 continue;
102 // creates the Gameobject
103 if (!t->Create(entry, mapid, x, y, z, o, GO_ANIMPROGRESS_DEFAULT, 0))
105 delete t;
106 continue;
109 m_Transports.insert(t);
111 for (std::set<uint32>::const_iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i)
112 m_TransportsByMap[*i].insert(t);
114 // If we someday decide to use the grid to track transports, here:
115 t->SetMap(sMapMgr.CreateMap(mapid, t));
117 // t->GetMap()->Add<GameObject>((GameObject *)t);
118 ++count;
120 while (result->NextRow());
121 delete result;
123 sLog.outString();
124 sLog.outString(">> Loaded %u transports", count);
126 // check transport data DB integrity
127 result = WorldDatabase.Query("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry");
128 if (result) // wrong data found
132 Field* fields = result->Fetch();
134 uint32 guid = fields[0].GetUInt32();
135 uint32 entry = fields[1].GetUInt32();
136 std::string name = fields[2].GetCppString();
137 sLog.outErrorDb("Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports DON'T must have any records in `gameobject` or its behavior will be unpredictable/bugged.", entry, name.c_str(), guid);
139 while (result->NextRow());
141 delete result;
145 Transport::Transport() : GameObject()
147 m_updateFlag = UPDATEFLAG_TRANSPORT | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_ROTATION;
150 bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint8 animprogress, uint16 dynamicHighValue)
152 Relocate(x, y, z, ang);
153 // instance id and phaseMask isn't set to values different from std.
155 if (!IsPositionValid())
157 sLog.outError("Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
158 guidlow, x, y);
159 return false;
162 Object::_Create(guidlow, 0, HIGHGUID_MO_TRANSPORT);
164 GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(guidlow);
166 if (!goinfo)
168 sLog.outErrorDb("Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (X: %f Y: %f Z: %f) ang: %f", guidlow, mapid, x, y, z, ang);
169 return false;
172 m_goInfo = goinfo;
174 SetObjectScale(goinfo->size);
176 SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
177 // SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
178 SetUInt32Value(GAMEOBJECT_FLAGS, (GO_FLAG_TRANSPORT | GO_FLAG_NODESPAWN));
179 SetUInt32Value(GAMEOBJECT_LEVEL, m_period);
180 SetEntry(goinfo->id);
182 SetDisplayId(goinfo->displayId);
184 SetGoState(GO_STATE_READY);
185 SetGoType(GameobjectTypes(goinfo->type));
186 SetGoArtKit(0);
187 SetGoAnimProgress(animprogress);
189 // low part always 0, dynamicHighValue is some kind of progression (not implemented)
190 SetUInt16Value(GAMEOBJECT_DYNAMIC, 0, 0);
191 SetUInt16Value(GAMEOBJECT_DYNAMIC, 1, dynamicHighValue);
193 SetName(goinfo->name);
195 return true;
198 struct keyFrame
200 explicit keyFrame(TaxiPathNodeEntry const& _node) : node(&_node),
201 distSinceStop(-1.0f), distUntilStop(-1.0f), distFromPrev(-1.0f), tFrom(0.0f), tTo(0.0f)
205 TaxiPathNodeEntry const* node;
207 float distSinceStop;
208 float distUntilStop;
209 float distFromPrev;
210 float tFrom, tTo;
213 bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32>& mapids)
215 if (pathid >= sTaxiPathNodesByPath.size())
216 return false;
218 TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathid];
220 std::vector<keyFrame> keyFrames;
221 int mapChange = 0;
222 mapids.clear();
223 for (size_t i = 1; i < path.size() - 1; ++i)
225 if (mapChange == 0)
227 TaxiPathNodeEntry const& node_i = path[i];
228 if (node_i.mapid == path[i + 1].mapid)
230 keyFrame k(node_i);
231 keyFrames.push_back(k);
232 mapids.insert(k.node->mapid);
234 else
236 mapChange = 1;
239 else
241 --mapChange;
245 int lastStop = -1;
246 int firstStop = -1;
248 // first cell is arrived at by teleportation :S
249 keyFrames[0].distFromPrev = 0;
250 if (keyFrames[0].node->actionFlag == 2)
252 lastStop = 0;
255 // find the rest of the distances between key points
256 for (size_t i = 1; i < keyFrames.size(); ++i)
258 if ((keyFrames[i].node->actionFlag == 1) || (keyFrames[i].node->mapid != keyFrames[i - 1].node->mapid))
260 keyFrames[i].distFromPrev = 0;
262 else
264 keyFrames[i].distFromPrev =
265 sqrt(pow(keyFrames[i].node->x - keyFrames[i - 1].node->x, 2) +
266 pow(keyFrames[i].node->y - keyFrames[i - 1].node->y, 2) +
267 pow(keyFrames[i].node->z - keyFrames[i - 1].node->z, 2));
269 if (keyFrames[i].node->actionFlag == 2)
271 // remember first stop frame
272 if (firstStop == -1)
273 firstStop = i;
274 lastStop = i;
278 float tmpDist = 0;
279 for (size_t i = 0; i < keyFrames.size(); ++i)
281 int j = (i + lastStop) % keyFrames.size();
282 if (keyFrames[j].node->actionFlag == 2)
283 tmpDist = 0;
284 else
285 tmpDist += keyFrames[j].distFromPrev;
286 keyFrames[j].distSinceStop = tmpDist;
289 for (int i = int(keyFrames.size()) - 1; i >= 0; --i)
291 int j = (i + (firstStop + 1)) % keyFrames.size();
292 tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev;
293 keyFrames[j].distUntilStop = tmpDist;
294 if (keyFrames[j].node->actionFlag == 2)
295 tmpDist = 0;
298 for (size_t i = 0; i < keyFrames.size(); ++i)
300 if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f))
301 keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop);
302 else
303 keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30;
305 if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f))
306 keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop);
307 else
308 keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30;
310 keyFrames[i].tFrom *= 1000;
311 keyFrames[i].tTo *= 1000;
314 // for (int i = 0; i < keyFrames.size(); ++i) {
315 // sLog.outString("%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo);
316 // }
318 // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals
319 // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2
320 int t = 0;
321 bool teleport = false;
322 if (keyFrames[keyFrames.size() - 1].node->mapid != keyFrames[0].node->mapid)
323 teleport = true;
325 WayPoint pos(keyFrames[0].node->mapid, keyFrames[0].node->x, keyFrames[0].node->y, keyFrames[0].node->z, teleport,
326 keyFrames[0].node->arrivalEventID, keyFrames[0].node->departureEventID);
327 m_WayPoints[0] = pos;
328 t += keyFrames[0].node->delay * 1000;
330 uint32 cM = keyFrames[0].node->mapid;
331 for (size_t i = 0; i < keyFrames.size() - 1; ++i)
333 float d = 0;
334 float tFrom = keyFrames[i].tFrom;
335 float tTo = keyFrames[i].tTo;
337 // keep the generation of all these points; we use only a few now, but may need the others later
338 if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)))
340 while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))
342 tFrom += 100;
343 tTo -= 100;
345 if (d > 0)
347 float newX, newY, newZ;
348 newX = keyFrames[i].node->x + (keyFrames[i + 1].node->x - keyFrames[i].node->x) * d / keyFrames[i + 1].distFromPrev;
349 newY = keyFrames[i].node->y + (keyFrames[i + 1].node->y - keyFrames[i].node->y) * d / keyFrames[i + 1].distFromPrev;
350 newZ = keyFrames[i].node->z + (keyFrames[i + 1].node->z - keyFrames[i].node->z) * d / keyFrames[i + 1].distFromPrev;
352 bool teleport = false;
353 if (keyFrames[i].node->mapid != cM)
355 teleport = true;
356 cM = keyFrames[i].node->mapid;
359 // sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ);
360 WayPoint pos(keyFrames[i].node->mapid, newX, newY, newZ, teleport);
361 if (teleport)
362 m_WayPoints[t] = pos;
365 if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
367 if (tFrom <= 30000)
369 d = 0.5f * (tFrom / 1000) * (tFrom / 1000);
371 else
373 d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000);
375 d = d - keyFrames[i].distSinceStop;
377 else
379 if (tTo <= 30000)
381 d = 0.5f * (tTo / 1000) * (tTo / 1000);
383 else
385 d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000);
387 d = keyFrames[i].distUntilStop - d;
389 t += 100;
391 t -= 100;
394 if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo)
395 t += 100 - ((long)keyFrames[i + 1].tTo % 100);
396 else
397 t += (long)keyFrames[i + 1].tTo % 100;
399 bool teleport = false;
400 if ((keyFrames[i + 1].node->actionFlag == 1) || (keyFrames[i + 1].node->mapid != keyFrames[i].node->mapid))
402 teleport = true;
403 cM = keyFrames[i + 1].node->mapid;
406 WayPoint pos(keyFrames[i + 1].node->mapid, keyFrames[i + 1].node->x, keyFrames[i + 1].node->y, keyFrames[i + 1].node->z, teleport,
407 keyFrames[i + 1].node->arrivalEventID, keyFrames[i + 1].node->departureEventID);
409 // sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
411 // if (teleport)
412 m_WayPoints[t] = pos;
414 t += keyFrames[i + 1].node->delay * 1000;
415 // sLog.outString("------");
418 uint32 timer = t;
420 // sLog.outDetail(" Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer);
422 m_next = m_WayPoints.begin(); // will used in MoveToNextWayPoint for init m_curr
423 MoveToNextWayPoint(); // m_curr -> first point
424 MoveToNextWayPoint(); // skip first point
426 m_pathTime = timer;
428 m_nextNodeTime = m_curr->first;
430 return true;
433 void Transport::MoveToNextWayPoint()
435 m_curr = m_next;
437 ++m_next;
438 if (m_next == m_WayPoints.end())
439 m_next = m_WayPoints.begin();
442 void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
444 Map const* oldMap = GetMap();
445 Relocate(x, y, z);
447 for (PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();)
449 PlayerSet::iterator it2 = itr;
450 ++itr;
452 Player* plr = *it2;
453 if (!plr)
455 m_passengers.erase(it2);
456 continue;
459 if (plr->isDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
461 plr->ResurrectPlayer(1.0);
463 plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT);
465 // WorldPacket data(SMSG_811, 4);
466 // data << uint32(0);
467 // plr->GetSession()->SendPacket(&data);
470 // we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference...
471 // player far teleport would try to create same instance, but we need it NOW for transport...
472 // correct me if I'm wrong O.o
473 Map* newMap = sMapMgr.CreateMap(newMapid, this);
474 SetMap(newMap);
476 if (oldMap != newMap)
478 UpdateForMap(oldMap);
479 UpdateForMap(newMap);
483 bool Transport::AddPassenger(Player* passenger)
485 if (m_passengers.find(passenger) == m_passengers.end())
487 DETAIL_LOG("Player %s boarded transport %s.", passenger->GetName(), GetName());
488 m_passengers.insert(passenger);
490 return true;
493 bool Transport::RemovePassenger(Player* passenger)
495 if (m_passengers.erase(passenger))
496 DETAIL_LOG("Player %s removed from transport %s.", passenger->GetName(), GetName());
497 return true;
500 void Transport::Update(uint32 update_diff, uint32 /*p_time*/)
502 if (m_WayPoints.size() <= 1)
503 return;
505 m_timer = WorldTimer::getMSTime() % m_period;
506 while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime))
509 DoEventIfAny(*m_curr, true);
511 MoveToNextWayPoint();
513 DoEventIfAny(*m_curr, false);
515 // first check help in case client-server transport coordinates de-synchronization
516 if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport)
518 TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z);
520 else
522 Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z);
526 for(PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
528 PlayerSet::const_iterator it2 = itr;
529 ++itr;
530 //(*it2)->SetPosition( m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO() );
534 m_nextNodeTime = m_curr->first;
536 if (m_curr == m_WayPoints.begin())
537 DETAIL_FILTER_LOG(LOG_FILTER_TRANSPORT_MOVES, " ************ BEGIN ************** %s", GetName());
539 DETAIL_FILTER_LOG(LOG_FILTER_TRANSPORT_MOVES, "%s moved to %f %f %f %d", GetName(), m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid);
543 void Transport::UpdateForMap(Map const* targetMap)
545 Map::PlayerList const& pl = targetMap->GetPlayers();
546 if (pl.isEmpty())
547 return;
549 if (GetMapId() == targetMap->GetId())
551 for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
553 if (this != itr->getSource()->GetTransport())
555 UpdateData transData(itr->getSource()->GetMapId());
556 BuildCreateUpdateBlockForPlayer(&transData, itr->getSource());
557 WorldPacket packet;
558 transData.BuildPacket(&packet);
560 // Prevent sending transport maps in player update object
561 if (packet.ReadUInt16() != itr->getSource()->GetMapId())
562 return;
564 itr->getSource()->SendDirectMessage(&packet);
568 else
570 UpdateData transData(GetMapId());
571 BuildOutOfRangeUpdateBlock(&transData);
572 WorldPacket out_packet;
573 transData.BuildPacket(&out_packet);
575 for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
577 if (this != itr->getSource()->GetTransport())
579 // Prevent sending transport maps in player update object
580 if (out_packet.ReadUInt16() != itr->getSource()->GetMapId())
581 return;
583 itr->getSource()->SendDirectMessage(&out_packet);
589 void Transport::DoEventIfAny(WayPointMap::value_type const& node, bool departure)
591 if (uint32 eventid = departure ? node.second.departureEventID : node.second.arrivalEventID)
593 DEBUG_FILTER_LOG(LOG_FILTER_TRANSPORT_MOVES, "Taxi %s event %u of node %u of %s \"%s\") path", departure ? "departure" : "arrival", eventid, node.first, GetGuidStr().c_str(), GetName());
595 if (!sScriptMgr.OnProcessEvent(eventid, this, this, departure))
596 GetMap()->ScriptsStart(sEventScripts, eventid, this, this);