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
21 #include "Transports.h"
22 #include "MapManager.h"
23 #include "ObjectMgr.h"
24 #include "ObjectGuid.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");
44 sLog
.outString(">> Loaded %u transports", count
);
48 BarGoLink
bar(result
->GetRowCount());
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
);
66 sLog
.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry
, name
.c_str());
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());
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
);
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())
102 // creates the Gameobject
103 if (!t
->Create(entry
, mapid
, x
, y
, z
, o
, GO_ANIMPROGRESS_DEFAULT
, 0))
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);
120 while (result
->NextRow());
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());
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)",
162 Object::_Create(guidlow
, 0, HIGHGUID_MO_TRANSPORT
);
164 GameObjectInfo
const* goinfo
= ObjectMgr::GetGameObjectInfo(guidlow
);
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
);
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
));
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
);
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
;
213 bool Transport::GenerateWaypoints(uint32 pathid
, std::set
<uint32
>& mapids
)
215 if (pathid
>= sTaxiPathNodesByPath
.size())
218 TaxiPathNodeList
const& path
= sTaxiPathNodesByPath
[pathid
];
220 std::vector
<keyFrame
> keyFrames
;
223 for (size_t i
= 1; i
< path
.size() - 1; ++i
)
227 TaxiPathNodeEntry
const& node_i
= path
[i
];
228 if (node_i
.mapid
== path
[i
+ 1].mapid
)
231 keyFrames
.push_back(k
);
232 mapids
.insert(k
.node
->mapid
);
248 // first cell is arrived at by teleportation :S
249 keyFrames
[0].distFromPrev
= 0;
250 if (keyFrames
[0].node
->actionFlag
== 2)
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;
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
279 for (size_t i
= 0; i
< keyFrames
.size(); ++i
)
281 int j
= (i
+ lastStop
) % keyFrames
.size();
282 if (keyFrames
[j
].node
->actionFlag
== 2)
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)
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
);
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
);
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);
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
321 bool teleport
= false;
322 if (keyFrames
[keyFrames
.size() - 1].node
->mapid
!= keyFrames
[0].node
->mapid
)
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
)
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))
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
)
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
);
362 m_WayPoints
[t
] = pos
;
365 if (tFrom
< tTo
) // caught in tFrom dock's "gravitational pull"
369 d
= 0.5f
* (tFrom
/ 1000) * (tFrom
/ 1000);
373 d
= 0.5f
* 30 * 30 + 30 * ((tFrom
- 30000) / 1000);
375 d
= d
- keyFrames
[i
].distSinceStop
;
381 d
= 0.5f
* (tTo
/ 1000) * (tTo
/ 1000);
385 d
= 0.5f
* 30 * 30 + 30 * ((tTo
- 30000) / 1000);
387 d
= keyFrames
[i
].distUntilStop
- d
;
394 if (keyFrames
[i
+ 1].tFrom
> keyFrames
[i
+ 1].tTo
)
395 t
+= 100 - ((long)keyFrames
[i
+ 1].tTo
% 100);
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
))
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);
412 m_WayPoints
[t
] = pos
;
414 t
+= keyFrames
[i
+ 1].node
->delay
* 1000;
415 // sLog.outString("------");
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
428 m_nextNodeTime
= m_curr
->first
;
433 void Transport::MoveToNextWayPoint()
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();
447 for (PlayerSet::iterator itr
= m_passengers
.begin(); itr
!= m_passengers
.end();)
449 PlayerSet::iterator it2
= itr
;
455 m_passengers
.erase(it2
);
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);
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
);
493 bool Transport::RemovePassenger(Player
* passenger
)
495 if (m_passengers
.erase(passenger
))
496 DETAIL_LOG("Player %s removed from transport %s.", passenger
->GetName(), GetName());
500 void Transport::Update(uint32 update_diff
, uint32
/*p_time*/)
502 if (m_WayPoints
.size() <= 1)
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
);
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;
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();
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());
558 transData
.BuildPacket(&packet
);
560 // Prevent sending transport maps in player update object
561 if (packet
.ReadUInt16() != itr
->getSource()->GetMapId())
564 itr
->getSource()->SendDirectMessage(&packet
);
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())
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);