[8483] Implement glyph 43361.
[getmangos.git] / src / game / Transports.cpp
blob43e854fd0ffd5f2ec557b68ca4d00216c6c92df4
1 /*
2 * Copyright (C) 2005-2009 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 "Path.h"
26 #include "WorldPacket.h"
27 #include "DBCStores.h"
28 #include "ProgressBar.h"
30 void MapManager::LoadTransports()
32 QueryResult *result = WorldDatabase.Query("SELECT entry, name, period FROM transports");
34 uint32 count = 0;
36 if( !result )
38 barGoLink bar( 1 );
39 bar.step();
41 sLog.outString();
42 sLog.outString( ">> Loaded %u transports", count );
43 return;
46 barGoLink bar( result->GetRowCount() );
50 bar.step();
52 Transport *t = new Transport;
54 Field *fields = result->Fetch();
56 uint32 entry = fields[0].GetUInt32();
57 std::string name = fields[1].GetCppString();
58 t->m_period = fields[2].GetUInt32();
60 const GameObjectInfo *goinfo = objmgr.GetGameObjectInfo(entry);
62 if(!goinfo)
64 sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str());
65 delete t;
66 continue;
69 if(goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT)
71 sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str());
72 delete t;
73 continue;
76 // sLog.outString("Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name);
78 std::set<uint32> mapsUsed;
80 if(!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed))
81 // skip transports with empty waypoints list
83 sLog.outErrorDb("Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.",goinfo->moTransport.taxiPathId);
84 delete t;
85 continue;
88 float x, y, z, o;
89 uint32 mapid;
90 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;
92 // creates the Gameobject
93 if(!t->Create(entry, mapid, x, y, z, o, 100, 0))
95 delete t;
96 continue;
99 m_Transports.insert(t);
101 for (std::set<uint32>::const_iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i)
102 m_TransportsByMap[*i].insert(t);
104 //If we someday decide to use the grid to track transports, here:
105 t->SetMap(MapManager::Instance().CreateMap(mapid, t));
107 //t->GetMap()->Add<GameObject>((GameObject *)t);
108 ++count;
109 } while(result->NextRow());
110 delete result;
112 sLog.outString();
113 sLog.outString( ">> Loaded %u transports", count );
115 // check transport data DB integrity
116 result = WorldDatabase.Query("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry");
117 if(result) // wrong data found
121 Field *fields = result->Fetch();
123 uint32 guid = fields[0].GetUInt32();
124 uint32 entry = fields[1].GetUInt32();
125 std::string name = fields[2].GetCppString();
126 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);
128 while(result->NextRow());
130 delete result;
134 Transport::Transport() : GameObject()
136 m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_ROTATION);
139 bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags)
141 Relocate(x,y,z,ang);
142 // instance id and phaseMask isn't set to values different from std.
144 if(!IsPositionValid())
146 sLog.outError("Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
147 guidlow,x,y);
148 return false;
151 Object::_Create(guidlow, 0, HIGHGUID_MO_TRANSPORT);
153 GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(guidlow);
155 if (!goinfo)
157 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);
158 return false;
161 m_goInfo = goinfo;
163 SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
165 SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
166 //SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
167 SetUInt32Value(GAMEOBJECT_FLAGS, MAKE_PAIR32(0x28, 0x64));
168 SetUInt32Value(GAMEOBJECT_LEVEL, m_period);
169 SetEntry(goinfo->id);
171 SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
173 SetGoState(GO_STATE_READY);
174 SetGoType(GameobjectTypes(goinfo->type));
176 SetGoAnimProgress(animprogress);
177 if(dynflags)
178 SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags));
180 SetName(goinfo->name);
182 return true;
185 struct keyFrame
187 keyFrame(float _x, float _y, float _z, uint32 _mapid, int _actionflag, int _delay)
189 x = _x; y = _y; z = _z; mapid = _mapid; actionflag = _actionflag; delay = _delay; distFromPrev = -1; distSinceStop = -1; distUntilStop = -1;
190 tFrom = 0; tTo = 0;
193 float x;
194 float y;
195 float z;
196 uint32 mapid;
197 int actionflag;
198 int delay;
199 float distSinceStop;
200 float distUntilStop;
201 float distFromPrev;
202 float tFrom, tTo;
205 bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
207 TransportPath path;
208 objmgr.GetTransportPathNodes(pathid, path);
210 if (path.Empty())
211 return false;
213 std::vector<keyFrame> keyFrames;
214 int mapChange = 0;
215 mapids.clear();
216 for (size_t i = 1; i < path.Size() - 1; ++i)
218 if (mapChange == 0)
220 if ((path[i].mapid == path[i+1].mapid))
222 keyFrame k(path[i].x, path[i].y, path[i].z, path[i].mapid, path[i].actionFlag, path[i].delay);
223 keyFrames.push_back(k);
224 mapids.insert(k.mapid);
226 else
228 mapChange = 1;
231 else
233 --mapChange;
237 int lastStop = -1;
238 int firstStop = -1;
240 // first cell is arrived at by teleportation :S
241 keyFrames[0].distFromPrev = 0;
242 if (keyFrames[0].actionflag == 2)
244 lastStop = 0;
247 // find the rest of the distances between key points
248 for (size_t i = 1; i < keyFrames.size(); ++i)
250 if ((keyFrames[i].actionflag == 1) || (keyFrames[i].mapid != keyFrames[i-1].mapid))
252 keyFrames[i].distFromPrev = 0;
254 else
256 keyFrames[i].distFromPrev =
257 sqrt(pow(keyFrames[i].x - keyFrames[i - 1].x, 2) +
258 pow(keyFrames[i].y - keyFrames[i - 1].y, 2) +
259 pow(keyFrames[i].z - keyFrames[i - 1].z, 2));
261 if (keyFrames[i].actionflag == 2)
263 // remember first stop frame
264 if(firstStop == -1)
265 firstStop = i;
266 lastStop = i;
270 float tmpDist = 0;
271 for (size_t i = 0; i < keyFrames.size(); ++i)
273 int j = (i + lastStop) % keyFrames.size();
274 if (keyFrames[j].actionflag == 2)
275 tmpDist = 0;
276 else
277 tmpDist += keyFrames[j].distFromPrev;
278 keyFrames[j].distSinceStop = tmpDist;
281 for (int i = int(keyFrames.size()) - 1; i >= 0; i--)
283 int j = (i + (firstStop+1)) % keyFrames.size();
284 tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev;
285 keyFrames[j].distUntilStop = tmpDist;
286 if (keyFrames[j].actionflag == 2)
287 tmpDist = 0;
290 for (size_t i = 0; i < keyFrames.size(); ++i)
292 if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f))
293 keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop);
294 else
295 keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30;
297 if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f))
298 keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop);
299 else
300 keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30;
302 keyFrames[i].tFrom *= 1000;
303 keyFrames[i].tTo *= 1000;
306 // for (int i = 0; i < keyFrames.size(); ++i) {
307 // 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);
308 // }
310 // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals
311 // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2
312 int t = 0;
313 bool teleport = false;
314 if (keyFrames[keyFrames.size() - 1].mapid != keyFrames[0].mapid)
315 teleport = true;
317 WayPoint pos(keyFrames[0].mapid, keyFrames[0].x, keyFrames[0].y, keyFrames[0].z, teleport);
318 m_WayPoints[0] = pos;
319 t += keyFrames[0].delay * 1000;
321 uint32 cM = keyFrames[0].mapid;
322 for (size_t i = 0; i < keyFrames.size() - 1; ++i)
324 float d = 0;
325 float tFrom = keyFrames[i].tFrom;
326 float tTo = keyFrames[i].tTo;
328 // keep the generation of all these points; we use only a few now, but may need the others later
329 if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)))
331 while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))
333 tFrom += 100;
334 tTo -= 100;
336 if (d > 0)
338 float newX, newY, newZ;
339 newX = keyFrames[i].x + (keyFrames[i + 1].x - keyFrames[i].x) * d / keyFrames[i + 1].distFromPrev;
340 newY = keyFrames[i].y + (keyFrames[i + 1].y - keyFrames[i].y) * d / keyFrames[i + 1].distFromPrev;
341 newZ = keyFrames[i].z + (keyFrames[i + 1].z - keyFrames[i].z) * d / keyFrames[i + 1].distFromPrev;
343 bool teleport = false;
344 if (keyFrames[i].mapid != cM)
346 teleport = true;
347 cM = keyFrames[i].mapid;
350 // sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ);
351 WayPoint pos(keyFrames[i].mapid, newX, newY, newZ, teleport);
352 if (teleport)
353 m_WayPoints[t] = pos;
356 if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
358 if (tFrom <= 30000)
360 d = 0.5f * (tFrom / 1000) * (tFrom / 1000);
362 else
364 d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000);
366 d = d - keyFrames[i].distSinceStop;
368 else
370 if (tTo <= 30000)
372 d = 0.5f * (tTo / 1000) * (tTo / 1000);
374 else
376 d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000);
378 d = keyFrames[i].distUntilStop - d;
380 t += 100;
382 t -= 100;
385 if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo)
386 t += 100 - ((long)keyFrames[i + 1].tTo % 100);
387 else
388 t += (long)keyFrames[i + 1].tTo % 100;
390 bool teleport = false;
391 if ((keyFrames[i + 1].actionflag == 1) || (keyFrames[i + 1].mapid != keyFrames[i].mapid))
393 teleport = true;
394 cM = keyFrames[i + 1].mapid;
397 WayPoint pos(keyFrames[i + 1].mapid, keyFrames[i + 1].x, keyFrames[i + 1].y, keyFrames[i + 1].z, teleport);
399 // sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
401 //if (teleport)
402 m_WayPoints[t] = pos;
404 t += keyFrames[i + 1].delay * 1000;
405 // sLog.outString("------");
408 uint32 timer = t;
410 // sLog.outDetail(" Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer);
412 m_curr = m_WayPoints.begin();
413 m_curr = GetNextWayPoint();
414 m_next = GetNextWayPoint();
415 m_pathTime = timer;
417 m_nextNodeTime = m_curr->first;
419 return true;
422 Transport::WayPointMap::const_iterator Transport::GetNextWayPoint()
424 WayPointMap::const_iterator iter = m_curr;
425 ++iter;
426 if (iter == m_WayPoints.end())
427 iter = m_WayPoints.begin();
428 return iter;
431 void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
433 Map const* oldMap = GetMap();
434 Relocate(x, y, z);
436 for(PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();)
438 PlayerSet::iterator it2 = itr;
439 ++itr;
441 Player *plr = *it2;
442 if(!plr)
444 m_passengers.erase(it2);
445 continue;
448 if (plr->isDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
450 plr->ResurrectPlayer(1.0);
452 plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT);
454 //WorldPacket data(SMSG_811, 4);
455 //data << uint32(0);
456 //plr->GetSession()->SendPacket(&data);
459 //we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference...
460 //player far teleport would try to create same instance, but we need it NOW for transport...
461 //correct me if I'm wrong O.o
462 Map * newMap = MapManager::Instance().CreateMap(newMapid, this);
463 SetMap(newMap);
465 if(oldMap != newMap)
467 UpdateForMap(oldMap);
468 UpdateForMap(newMap);
472 bool Transport::AddPassenger(Player* passenger)
474 if (m_passengers.find(passenger) == m_passengers.end())
476 sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), GetName());
477 m_passengers.insert(passenger);
479 return true;
482 bool Transport::RemovePassenger(Player* passenger)
484 if (m_passengers.erase(passenger))
485 sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), GetName());
486 return true;
489 void Transport::Update(uint32 /*p_time*/)
491 if (m_WayPoints.size() <= 1)
492 return;
494 m_timer = getMSTime() % m_period;
495 while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime))
497 m_curr = GetNextWayPoint();
498 m_next = GetNextWayPoint();
500 // first check help in case client-server transport coordinates de-synchronization
501 if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport)
503 TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z);
505 else
507 Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z);
511 for(PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
513 PlayerSet::const_iterator it2 = itr;
514 ++itr;
515 //(*it2)->SetPosition( m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO() );
519 m_nextNodeTime = m_curr->first;
521 if (m_curr == m_WayPoints.begin() && (sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0)
522 sLog.outDetail(" ************ BEGIN ************** %s", GetName());
524 if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0)
525 sLog.outDetail("%s moved to %f %f %f %d", GetName(), m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid);
529 void Transport::UpdateForMap(Map const* targetMap)
531 Map::PlayerList const& pl = targetMap->GetPlayers();
532 if(pl.isEmpty())
533 return;
535 if(GetMapId()==targetMap->GetId())
537 for(Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
539 if(this != itr->getSource()->GetTransport())
541 UpdateData transData;
542 BuildCreateUpdateBlockForPlayer(&transData, itr->getSource());
543 WorldPacket packet;
544 transData.BuildPacket(&packet);
545 itr->getSource()->SendDirectMessage(&packet);
549 else
551 UpdateData transData;
552 BuildOutOfRangeUpdateBlock(&transData);
553 WorldPacket out_packet;
554 transData.BuildPacket(&out_packet);
556 for(Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
557 if(this != itr->getSource()->GetTransport())
558 itr->getSource()->SendDirectMessage(&out_packet);