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
21 #include "Transports.h"
22 #include "MapManager.h"
23 #include "ObjectMgr.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");
42 sLog
.outString( ">> Loaded %u transports", count
);
46 barGoLink
bar( result
->GetRowCount() );
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
);
64 sLog
.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry
, name
.c_str());
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());
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
);
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))
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);
109 } while(result
->NextRow());
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());
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
)
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)",
151 Object::_Create(guidlow
, 0, HIGHGUID_MO_TRANSPORT
);
153 GameObjectInfo
const* goinfo
= objmgr
.GetGameObjectInfo(guidlow
);
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
);
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
);
178 SetUInt32Value(GAMEOBJECT_DYNAMIC
, MAKE_PAIR32(0, dynflags
));
180 SetName(goinfo
->name
);
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;
205 bool Transport::GenerateWaypoints(uint32 pathid
, std::set
<uint32
> &mapids
)
208 objmgr
.GetTransportPathNodes(pathid
, path
);
213 std::vector
<keyFrame
> keyFrames
;
216 for (size_t i
= 1; i
< path
.Size() - 1; ++i
)
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
);
240 // first cell is arrived at by teleportation :S
241 keyFrames
[0].distFromPrev
= 0;
242 if (keyFrames
[0].actionflag
== 2)
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;
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
271 for (size_t i
= 0; i
< keyFrames
.size(); ++i
)
273 int j
= (i
+ lastStop
) % keyFrames
.size();
274 if (keyFrames
[j
].actionflag
== 2)
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)
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
);
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
);
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);
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
313 bool teleport
= false;
314 if (keyFrames
[keyFrames
.size() - 1].mapid
!= keyFrames
[0].mapid
)
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
)
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))
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
)
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
);
353 m_WayPoints
[t
] = pos
;
356 if (tFrom
< tTo
) // caught in tFrom dock's "gravitational pull"
360 d
= 0.5f
* (tFrom
/ 1000) * (tFrom
/ 1000);
364 d
= 0.5f
* 30 * 30 + 30 * ((tFrom
- 30000) / 1000);
366 d
= d
- keyFrames
[i
].distSinceStop
;
372 d
= 0.5f
* (tTo
/ 1000) * (tTo
/ 1000);
376 d
= 0.5f
* 30 * 30 + 30 * ((tTo
- 30000) / 1000);
378 d
= keyFrames
[i
].distUntilStop
- d
;
385 if (keyFrames
[i
+ 1].tFrom
> keyFrames
[i
+ 1].tTo
)
386 t
+= 100 - ((long)keyFrames
[i
+ 1].tTo
% 100);
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
))
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);
402 m_WayPoints
[t
] = pos
;
404 t
+= keyFrames
[i
+ 1].delay
* 1000;
405 // sLog.outString("------");
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();
417 m_nextNodeTime
= m_curr
->first
;
422 Transport::WayPointMap::const_iterator
Transport::GetNextWayPoint()
424 WayPointMap::const_iterator iter
= m_curr
;
426 if (iter
== m_WayPoints
.end())
427 iter
= m_WayPoints
.begin();
431 void Transport::TeleportTransport(uint32 newMapid
, float x
, float y
, float z
)
433 Map
const* oldMap
= GetMap();
436 for(PlayerSet::iterator itr
= m_passengers
.begin(); itr
!= m_passengers
.end();)
438 PlayerSet::iterator it2
= itr
;
444 m_passengers
.erase(it2
);
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);
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);
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
);
482 bool Transport::RemovePassenger(Player
* passenger
)
484 if (m_passengers
.erase(passenger
))
485 sLog
.outDetail("Player %s removed from transport %s.", passenger
->GetName(), GetName());
489 void Transport::Update(uint32
/*p_time*/)
491 if (m_WayPoints
.size() <= 1)
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
);
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;
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();
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());
544 transData
.BuildPacket(&packet
);
545 itr
->getSource()->SendDirectMessage(&packet
);
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
);