aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2015-09-19 17:41:24 +0200
committerShauren <shauren.trinity@gmail.com>2015-09-19 17:41:24 +0200
commit3b52fcc7693f14f2b7e6f2130a73379ec33bd5c0 (patch)
tree5e9aad7a3564e7d3d74a1ffd91579db41bb0c388 /src/server/game/Entities
parent10b068ce5dbf9da7be6a727e0cdb0360f5c4b615 (diff)
parentf34bae89d3869ddc5f74a85d9a6fa44f67ca4635 (diff)
Merge remote-tracking branch 'origin/6.x_implement_taxi' into 6.x
Diffstat (limited to 'src/server/game/Entities')
-rw-r--r--src/server/game/Entities/Object/Object.h33
-rw-r--r--src/server/game/Entities/Object/Position.h33
-rw-r--r--src/server/game/Entities/Player/Player.cpp147
-rw-r--r--src/server/game/Entities/Player/Player.h60
-rw-r--r--src/server/game/Entities/Player/PlayerTaxi.cpp180
-rw-r--r--src/server/game/Entities/Player/PlayerTaxi.h66
-rw-r--r--src/server/game/Entities/Taxi/TaxiPathGraph.cpp186
-rw-r--r--src/server/game/Entities/Taxi/TaxiPathGraph.h71
8 files changed, 541 insertions, 235 deletions
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 42e5137e6f9..54b6677f714 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -351,39 +351,6 @@ struct MovementInfo
void OutDebug();
};
-#define MAPID_INVALID 0xFFFFFFFF
-
-class WorldLocation : public Position
-{
- public:
- explicit WorldLocation(uint32 _mapId = MAPID_INVALID, float _x = 0.f, float _y = 0.f, float _z = 0.f, float _o = 0.f)
- : Position(_x, _y, _z, _o), m_mapId(_mapId) { }
-
- WorldLocation(WorldLocation const& loc)
- : Position(loc), m_mapId(loc.GetMapId()) { }
-
- void WorldRelocate(WorldLocation const& loc)
- {
- m_mapId = loc.GetMapId();
- Relocate(loc);
- }
-
- void WorldRelocate(uint32 _mapId = MAPID_INVALID, float _x = 0.f, float _y = 0.f, float _z = 0.f, float _o = 0.f)
- {
- m_mapId = _mapId;
- Relocate(_x, _y, _z, _o);
- }
-
- WorldLocation GetWorldLocation() const
- {
- return *this;
- }
-
- uint32 GetMapId() const { return m_mapId; }
-
- uint32 m_mapId;
-};
-
template<class T>
class GridObject
{
diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h
index 5bd37567811..109ff09d66c 100644
--- a/src/server/game/Entities/Object/Position.h
+++ b/src/server/game/Entities/Object/Position.h
@@ -215,6 +215,39 @@ public:
}
};
+#define MAPID_INVALID 0xFFFFFFFF
+
+class WorldLocation : public Position
+{
+public:
+ explicit WorldLocation(uint32 mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
+ : Position(x, y, z, o), m_mapId(mapId) { }
+
+ WorldLocation(WorldLocation const& loc)
+ : Position(loc), m_mapId(loc.GetMapId()) { }
+
+ void WorldRelocate(WorldLocation const& loc)
+ {
+ m_mapId = loc.GetMapId();
+ Relocate(loc);
+ }
+
+ void WorldRelocate(uint32 mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f)
+ {
+ m_mapId = mapId;
+ Relocate(x, y, z, o);
+ }
+
+ WorldLocation GetWorldLocation() const
+ {
+ return *this;
+ }
+
+ uint32 GetMapId() const { return m_mapId; }
+
+ uint32 m_mapId;
+};
+
ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYStreamer const& streamer);
ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYStreamer const& streamer);
ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 33f3b3cab16..6af50b89b43 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -87,6 +87,7 @@
#include "SpellMgr.h"
#include "SpellPackets.h"
#include "TalentPackets.h"
+#include "TaxiPackets.h"
#include "ToyPackets.h"
#include "TradePackets.h"
#include "Transport.h"
@@ -129,148 +130,6 @@ uint32 const MasterySpells[MAX_CLASSES] =
uint64 const MAX_MONEY_AMOUNT = 9999999999ULL;
-// == PlayerTaxi ================================================
-
-PlayerTaxi::PlayerTaxi()
-{
- memset(m_taximask, 0, sizeof(m_taximask));
-}
-
-void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
-{
- // class specific initial known nodes
- switch (chrClass)
- {
- case CLASS_DEATH_KNIGHT:
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- m_taximask[i] |= sOldContinentsNodesMask[i];
- break;
- }
- }
-
- // race specific initial known nodes: capital and taxi hub masks
- switch (race)
- {
- case RACE_HUMAN: SetTaximaskNode(2); break; // Human
- case RACE_ORC: SetTaximaskNode(23); break; // Orc
- case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
- case RACE_NIGHTELF: SetTaximaskNode(26);
- SetTaximaskNode(27); break; // Night Elf
- case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
- case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
- case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
- case RACE_TROLL: SetTaximaskNode(23); break; // Troll
- case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
- case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
- }
-
- // new continent starting masks (It will be accessible only at new map)
- switch (Player::TeamForRace(race))
- {
- case ALLIANCE: SetTaximaskNode(100); break;
- case HORDE: SetTaximaskNode(99); break;
- }
- // level dependent taxi hubs
- if (level >= 68)
- SetTaximaskNode(213); //Shattered Sun Staging Area
-}
-
-void PlayerTaxi::LoadTaxiMask(std::string const &data)
-{
- Tokenizer tokens(data, ' ');
-
- uint8 index = 0;
- for (Tokenizer::const_iterator iter = tokens.begin(); index < TaxiMaskSize && iter != tokens.end(); ++iter, ++index)
- {
- // load and set bits only for existing taxi nodes
- m_taximask[index] = sTaxiNodesMask[index] & atoul(*iter);
- }
-}
-
-void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all)
-{
- data << uint32(TaxiMaskSize);
- if (all)
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- data << uint8(sTaxiNodesMask[i]); // all existed nodes
- }
- else
- {
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- data << uint8(m_taximask[i]); // known nodes
- }
-}
-
-bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, uint32 team)
-{
- ClearTaxiDestinations();
-
- Tokenizer Tokenizer(values, ' ');
-
- for (Tokenizer::const_iterator iter = Tokenizer.begin(); iter != Tokenizer.end(); ++iter)
- {
- uint32 node = atoul(*iter);
- AddTaxiDestination(node);
- }
-
- if (m_TaxiDestinations.empty())
- return true;
-
- // Check integrity
- if (m_TaxiDestinations.size() < 2)
- return false;
-
- for (size_t i = 1; i < m_TaxiDestinations.size(); ++i)
- {
- uint32 cost;
- uint32 path;
- sObjectMgr->GetTaxiPath(m_TaxiDestinations[i-1], m_TaxiDestinations[i], path, cost);
- if (!path)
- return false;
- }
-
- // can't load taxi path without mount set (quest taxi path?)
- if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), team, true))
- return false;
-
- return true;
-}
-
-std::string PlayerTaxi::SaveTaxiDestinationsToString()
-{
- if (m_TaxiDestinations.empty())
- return "";
-
- std::ostringstream ss;
-
- for (size_t i=0; i < m_TaxiDestinations.size(); ++i)
- ss << m_TaxiDestinations[i] << ' ';
-
- return ss.str();
-}
-
-uint32 PlayerTaxi::GetCurrentTaxiPath() const
-{
- if (m_TaxiDestinations.size() < 2)
- return 0;
-
- uint32 path;
- uint32 cost;
-
- sObjectMgr->GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost);
-
- return path;
-}
-
-std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
-{
- for (uint8 i = 0; i < TaxiMaskSize; ++i)
- ss << uint32(taxi.m_taximask[i]) << ' ';
- return ss;
-}
-
//== TradeData =================================================
TradeData* TradeData::GetTraderData() const
@@ -20916,7 +20775,10 @@ void Player::SetRestBonus(float rest_bonus_new)
bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/)
{
if (nodes.size() < 2)
+ {
+ GetSession()->SendActivateTaxiReply(ERR_TAXINOSUCHPATH);
return false;
+ }
// not let cheating with start flight in time of logout process || while in combat || has type state: stunned || has type state: root
if (GetSession()->isLogingOut() || IsInCombat() || HasUnitState(UNIT_STATE_STUNNED) || HasUnitState(UNIT_STATE_ROOT))
@@ -20999,7 +20861,6 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR);
return false;
}
-
// Prepare to flight start now
// stop combat at start taxi flight if any
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 20f6e201f0f..d16fc7a1fc1 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -31,10 +31,7 @@
#include "Unit.h"
#include "Opcodes.h"
#include "WorldSession.h"
-
-#include <limits>
-#include <string>
-#include <vector>
+#include "PlayerTaxi.h"
struct CreatureTemplate;
struct Mail;
@@ -1086,61 +1083,6 @@ enum PlayerLogXPReason
LOG_XP_REASON_NO_KILL = 1
};
-class PlayerTaxi
-{
- public:
- PlayerTaxi();
- ~PlayerTaxi() { }
- // Nodes
- void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level);
- void LoadTaxiMask(std::string const& data);
-
- bool IsTaximaskNodeKnown(uint32 nodeidx) const
- {
- uint8 field = uint8((nodeidx - 1) / 8);
- uint32 submask = 1 << ((nodeidx-1) % 8);
- return (m_taximask[field] & submask) == submask;
- }
- bool SetTaximaskNode(uint32 nodeidx)
- {
- uint8 field = uint8((nodeidx - 1) / 8);
- uint32 submask = 1 << ((nodeidx- 1) % 8);
- if ((m_taximask[field] & submask) != submask)
- {
- m_taximask[field] |= submask;
- return true;
- }
- else
- return false;
- }
- void AppendTaximaskTo(ByteBuffer& data, bool all);
-
- // Destinations
- bool LoadTaxiDestinationsFromString(std::string const& values, uint32 team);
- std::string SaveTaxiDestinationsToString();
-
- void ClearTaxiDestinations() { m_TaxiDestinations.clear(); }
- void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
- uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); }
- uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; }
- uint32 GetCurrentTaxiPath() const;
- uint32 NextTaxiDestination()
- {
- m_TaxiDestinations.pop_front();
- return GetTaxiDestination();
- }
-
- std::deque<uint32> const& GetPath() const { return m_TaxiDestinations; }
- bool empty() const { return m_TaxiDestinations.empty(); }
-
- friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
- private:
- TaxiMask m_taximask;
- std::deque<uint32> m_TaxiDestinations;
-};
-
-std::ostringstream& operator << (std::ostringstream& ss, PlayerTaxi const& taxi);
-
class Player;
/// Holder for Battleground data
diff --git a/src/server/game/Entities/Player/PlayerTaxi.cpp b/src/server/game/Entities/Player/PlayerTaxi.cpp
new file mode 100644
index 00000000000..466cb7b947b
--- /dev/null
+++ b/src/server/game/Entities/Player/PlayerTaxi.cpp
@@ -0,0 +1,180 @@
+#include "Player.h"
+#include "TaxiPackets.h"
+#include "ObjectMgr.h"
+#include <limits>
+#include <math.h>
+
+void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
+{
+ // class specific initial known nodes
+ TaxiMask const& factionMask = Player::TeamForRace(race) == HORDE ? sHordeTaxiNodesMask : sAllianceTaxiNodesMask;
+ switch (chrClass)
+ {
+ case CLASS_DEATH_KNIGHT:
+ {
+ for (uint8 i = 0; i < TaxiMaskSize; ++i)
+ m_taximask[i] |= sOldContinentsNodesMask[i] & factionMask[i];
+ break;
+ }
+ }
+
+ // race specific initial known nodes: capital and taxi hub masks
+ switch (race)
+ {
+ case RACE_HUMAN:
+ case RACE_DWARF:
+ case RACE_NIGHTELF:
+ case RACE_GNOME:
+ case RACE_DRAENEI:
+ case RACE_WORGEN:
+ case RACE_PANDAREN_ALLIANCE:
+ SetTaximaskNode(2); // Stormwind, Elwynn
+ SetTaximaskNode(6); // Ironforge, Dun Morogh
+ SetTaximaskNode(26); // Lor'danel, Darkshore
+ SetTaximaskNode(27); // Rut'theran Village, Teldrassil
+ SetTaximaskNode(49); // Moonglade (Alliance)
+ SetTaximaskNode(94); // The Exodar
+ SetTaximaskNode(456); // Dolanaar, Teldrassil
+ SetTaximaskNode(457); // Darnassus, Teldrassil
+ SetTaximaskNode(582); // Goldshire, Elwynn
+ SetTaximaskNode(589); // Eastvale Logging Camp, Elwynn
+ SetTaximaskNode(619); // Kharanos, Dun Morogh
+ SetTaximaskNode(620); // Gol'Bolar Quarry, Dun Morogh
+ SetTaximaskNode(624); // Azure Watch, Azuremyst Isle
+ break;
+ case RACE_ORC:
+ case RACE_UNDEAD_PLAYER:
+ case RACE_TAUREN:
+ case RACE_TROLL:
+ case RACE_BLOODELF:
+ case RACE_GOBLIN:
+ case RACE_PANDAREN_HORDE:
+ SetTaximaskNode(11); // Undercity, Tirisfal
+ SetTaximaskNode(22); // Thunder Bluff, Mulgore
+ SetTaximaskNode(23); // Orgrimmar, Durotar
+ SetTaximaskNode(69); // Moonglade (Horde)
+ SetTaximaskNode(82); // Silvermoon City
+ SetTaximaskNode(384); // The Bulwark, Tirisfal
+ SetTaximaskNode(402); // Bloodhoof Village, Mulgore
+ SetTaximaskNode(460); // Brill, Tirisfal Glades
+ SetTaximaskNode(536); // Sen'jin Village, Durotar
+ SetTaximaskNode(537); // Razor Hill, Durotar
+ SetTaximaskNode(625); // Fairbreeze Village, Eversong Woods
+ SetTaximaskNode(631); // Falconwing Square, Eversong Woods
+ break;
+ }
+
+ // new continent starting masks (It will be accessible only at new map)
+ switch (Player::TeamForRace(race))
+ {
+ case ALLIANCE: SetTaximaskNode(100); break;
+ case HORDE: SetTaximaskNode(99); break;
+ }
+
+ // level dependent taxi hubs
+ if (level >= 68)
+ SetTaximaskNode(213); //Shattered Sun Staging Area
+}
+
+void PlayerTaxi::LoadTaxiMask(std::string const &data)
+{
+ Tokenizer tokens(data, ' ');
+
+ uint8 index = 0;
+ for (Tokenizer::const_iterator iter = tokens.begin(); index < TaxiMaskSize && iter != tokens.end(); ++iter, ++index)
+ {
+ // load and set bits only for existing taxi nodes
+ m_taximask[index] = sTaxiNodesMask[index] & atoul(*iter);
+ }
+}
+
+void PlayerTaxi::AppendTaximaskTo(WorldPackets::Taxi::ShowTaxiNodes& data, bool all)
+{
+ if (all)
+ data.Nodes = &sTaxiNodesMask; // all existed nodes
+ else
+ data.Nodes = &m_taximask; // known nodes
+}
+
+bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, uint32 team)
+{
+ ClearTaxiDestinations();
+
+ Tokenizer tokens(values, ' ');
+
+ for (Tokenizer::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
+ {
+ uint32 node = atoul(*iter);
+ AddTaxiDestination(node);
+ }
+
+ if (m_TaxiDestinations.empty())
+ return true;
+
+ // Check integrity
+ if (m_TaxiDestinations.size() < 2)
+ return false;
+
+ for (size_t i = 1; i < m_TaxiDestinations.size(); ++i)
+ {
+ uint32 cost;
+ uint32 path;
+ sObjectMgr->GetTaxiPath(m_TaxiDestinations[i - 1], m_TaxiDestinations[i], path, cost);
+ if (!path)
+ return false;
+ }
+
+ // can't load taxi path without mount set (quest taxi path?)
+ if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), team, true))
+ return false;
+
+ return true;
+}
+
+std::string PlayerTaxi::SaveTaxiDestinationsToString()
+{
+ if (m_TaxiDestinations.empty())
+ return "";
+
+ std::ostringstream ss;
+
+ for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
+ ss << m_TaxiDestinations[i] << ' ';
+
+ return ss.str();
+}
+
+uint32 PlayerTaxi::GetCurrentTaxiPath() const
+{
+ if (m_TaxiDestinations.size() < 2)
+ return 0;
+
+ uint32 path;
+ uint32 cost;
+
+ sObjectMgr->GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost);
+
+ return path;
+}
+
+std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
+{
+ for (uint8 i = 0; i < TaxiMaskSize; ++i)
+ ss << uint32(taxi.m_taximask[i]) << ' ';
+ return ss;
+}
+
+void PlayerTaxi::RequestEarlyLanding()
+{
+ if (m_TaxiDestinations.empty())
+ return;
+
+ for (std::deque<uint32>::iterator it = m_TaxiDestinations.begin(); it != m_TaxiDestinations.end(); it++)
+ {
+ if (IsTaximaskNodeKnown(*it))
+ {
+ m_TaxiDestinations.erase(++it, m_TaxiDestinations.end());
+ return;
+ }
+ }
+}
diff --git a/src/server/game/Entities/Player/PlayerTaxi.h b/src/server/game/Entities/Player/PlayerTaxi.h
new file mode 100644
index 00000000000..6739f5f6ab1
--- /dev/null
+++ b/src/server/game/Entities/Player/PlayerTaxi.h
@@ -0,0 +1,66 @@
+#ifndef __PLAYERTAXI_H__
+#define __PLAYERTAXI_H__
+
+#include "DB2Stores.h"
+#include "Define.h"
+#include "WorldSession.h"
+#include <map>
+
+class PlayerTaxi
+{
+ public:
+ PlayerTaxi() { }
+ ~PlayerTaxi() { }
+ // Nodes
+ void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level);
+ void LoadTaxiMask(std::string const& data);
+
+ bool IsTaximaskNodeKnown(uint32 nodeidx) const
+ {
+ uint8 field = uint8((nodeidx - 1) / 8);
+ uint32 submask = 1 << ((nodeidx-1) % 8);
+ return (m_taximask[field] & submask) == submask;
+ }
+ bool SetTaximaskNode(uint32 nodeidx)
+ {
+ uint8 field = uint8((nodeidx - 1) / 8);
+ uint32 submask = 1 << ((nodeidx- 1) % 8);
+ if ((m_taximask[field] & submask) != submask)
+ {
+ m_taximask[field] |= submask;
+ return true;
+ }
+ else
+ return false;
+ }
+ void AppendTaximaskTo(WorldPackets::Taxi::ShowTaxiNodes& data, bool all);
+ TaxiMask const& GetTaxiMask() const { return m_taximask; }
+
+ // Destinations
+ bool LoadTaxiDestinationsFromString(std::string const& values, uint32 team);
+ std::string SaveTaxiDestinationsToString();
+
+ void ClearTaxiDestinations() { m_TaxiDestinations.clear(); }
+ void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
+ void SetTaxiDestination(std::vector<uint32>& nodes) { m_TaxiDestinations.clear(); m_TaxiDestinations.insert(m_TaxiDestinations.begin(), nodes.begin(), nodes.end()); }
+ uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); }
+ uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; }
+ uint32 GetCurrentTaxiPath() const;
+ uint32 NextTaxiDestination()
+ {
+ m_TaxiDestinations.pop_front();
+ return GetTaxiDestination();
+ }
+ void RequestEarlyLanding();
+ std::deque<uint32> const& GetPath() const { return m_TaxiDestinations; }
+ bool empty() const { return m_TaxiDestinations.empty(); }
+
+ friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
+ private:
+ TaxiMask m_taximask = { };
+ std::deque<uint32> m_TaxiDestinations;
+};
+
+std::ostringstream& operator << (std::ostringstream& ss, PlayerTaxi const& taxi);
+
+#endif
diff --git a/src/server/game/Entities/Taxi/TaxiPathGraph.cpp b/src/server/game/Entities/Taxi/TaxiPathGraph.cpp
new file mode 100644
index 00000000000..ef899795bfd
--- /dev/null
+++ b/src/server/game/Entities/Taxi/TaxiPathGraph.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TaxiPathGraph.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "DBCStores.h"
+#include "DB2Stores.h"
+#include "Config.h"
+#include "Util.h"
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+#include <boost/property_map/transform_value_property_map.hpp>
+
+void TaxiPathGraph::Initialize()
+{
+ if (GetVertexCount() > 0)
+ return;
+
+ std::vector<std::pair<edge, EdgeCost>> edges;
+
+ // Initialize here
+ for (TaxiPathEntry const* path : sTaxiPathStore)
+ {
+ TaxiNodesEntry const* from = sTaxiNodesStore.LookupEntry(path->From);
+ TaxiNodesEntry const* to = sTaxiNodesStore.LookupEntry(path->To);
+ if (from && to && from->Flags & (TAXI_NODE_FLAG_ALLIANCE | TAXI_NODE_FLAG_HORDE) && to->Flags & (TAXI_NODE_FLAG_ALLIANCE | TAXI_NODE_FLAG_HORDE))
+ AddVerticeAndEdgeFromNodeInfo(from, to, path->ID, edges);
+ }
+
+ // create graph
+ m_graph = Graph(GetVertexCount());
+ WeightMap weightmap = boost::get(boost::edge_weight, m_graph);
+
+ for (std::size_t j = 0; j < edges.size(); ++j)
+ {
+ edge_descriptor e = boost::add_edge(edges[j].first.first, edges[j].first.second, m_graph).first;
+ weightmap[e] = edges[j].second;
+ }
+}
+
+uint32 TaxiPathGraph::GetNodeIDFromVertexID(vertex_descriptor vertexID)
+{
+ if (vertexID < m_vertices.size())
+ return m_vertices[vertexID]->ID;
+
+ return std::numeric_limits<uint32>::max();
+}
+
+TaxiPathGraph::vertex_descriptor TaxiPathGraph::GetVertexIDFromNodeID(TaxiNodesEntry const* node)
+{
+ return node->LearnableIndex;
+}
+
+std::size_t TaxiPathGraph::GetVertexCount()
+{
+ //So we can use this function for readability, we define either max defined vertices or already loaded in graph count
+ return std::max(boost::num_vertices(m_graph), m_vertices.size());
+}
+
+void TaxiPathGraph::AddVerticeAndEdgeFromNodeInfo(TaxiNodesEntry const* from, TaxiNodesEntry const* to, uint32 pathId, std::vector<std::pair<edge, EdgeCost>>& edges)
+{
+ if (from != to)
+ {
+ vertex_descriptor fromVertexID = CreateVertexFromFromNodeInfoIfNeeded(from);
+ vertex_descriptor toVertexID = CreateVertexFromFromNodeInfoIfNeeded(to);
+
+ float totalDist = 0.0f;
+ TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[pathId];
+ if (nodes.size() < 2)
+ {
+ edges.push_back(std::make_pair(edge(fromVertexID, toVertexID), EdgeCost{ to, 0xFFFF }));
+ return;
+ }
+
+ std::size_t last = nodes.size();
+ std::size_t first = 0;
+ if (nodes.size() > 2)
+ {
+ --last;
+ ++first;
+ }
+
+ for (std::size_t i = first + 1; i < last; ++i)
+ {
+ if (nodes[i - 1]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT)
+ continue;
+
+ uint32 map1, map2;
+ DBCPosition2D pos1, pos2;
+
+ DeterminaAlternateMapPosition(nodes[i - 1]->MapID, nodes[i - 1]->Loc.X, nodes[i - 1]->Loc.Y, nodes[i - 1]->Loc.Z, &map1, &pos1);
+ DeterminaAlternateMapPosition(nodes[i]->MapID, nodes[i]->Loc.X, nodes[i]->Loc.Y, nodes[i]->Loc.Z, &map2, &pos2);
+
+ if (map1 != map2)
+ continue;
+
+ totalDist += std::sqrt(
+ std::pow(pos2.X - pos1.X, 2) +
+ std::pow(pos2.Y - pos1.Y, 2) +
+ std::pow(nodes[i]->Loc.Z - nodes[i - 1]->Loc.Z, 2));
+ }
+
+ uint32 dist = uint32(totalDist);
+ if (dist > 0xFFFF)
+ return;
+
+ edges.push_back(std::make_pair(edge(fromVertexID, toVertexID), EdgeCost{ to, dist }));
+ }
+}
+
+std::size_t TaxiPathGraph::GetCompleteNodeRoute(TaxiNodesEntry const* from, TaxiNodesEntry const* to, Player const* player, std::vector<uint32>& shortestPath)
+{
+ /*
+ Information about node algorithm from client
+ Since client does not give information about *ALL* nodes you have to pass by when going from sourceNodeID to destinationNodeID, we need to use Dijkstra algorithm.
+ Examining several paths I discovered the following algorithm:
+ * If destinationNodeID has is the next destination, connected directly to sourceNodeID, then, client just pick up this route regardless of distance
+ * else we use dijkstra to find the shortest path.
+ * When early landing is requested, according to behavior on retail, you can never end in a node you did not discovered before
+ */
+
+ // Find if we have a direct path
+ uint32 pathId, goldCost;
+ sObjectMgr->GetTaxiPath(from->ID, to->ID, pathId, goldCost);
+ if (pathId)
+ shortestPath = { from->ID, to->ID };
+ else
+ {
+ shortestPath.clear();
+ std::vector<vertex_descriptor> p(boost::num_vertices(m_graph));
+
+ boost::dijkstra_shortest_paths(m_graph, GetVertexIDFromNodeID(from),
+ boost::predecessor_map(boost::make_iterator_property_map(p.begin(), boost::get(boost::vertex_index, m_graph)))
+ .weight_map(boost::make_transform_value_property_map(
+ [player](EdgeCost const& edgeCost) { return edgeCost.EvaluateDistance(player); },
+ boost::get(boost::edge_weight, m_graph))));
+
+ // found a path to the goal
+ for (vertex_descriptor v = GetVertexIDFromNodeID(to); ; v = p[v])
+ {
+ shortestPath.push_back(GetNodeIDFromVertexID(v));
+ if (v == p[v])
+ break;
+ }
+
+ std::reverse(shortestPath.begin(), shortestPath.end());
+ }
+
+ return shortestPath.size();
+}
+
+TaxiPathGraph::vertex_descriptor TaxiPathGraph::CreateVertexFromFromNodeInfoIfNeeded(TaxiNodesEntry const* node)
+{
+ //Check if we need a new one or if it may be already created
+ if (m_vertices.size() <= node->LearnableIndex)
+ m_vertices.resize(node->LearnableIndex + 1);
+
+ m_vertices[node->LearnableIndex] = node;
+ return node->LearnableIndex;
+}
+
+uint32 TaxiPathGraph::EdgeCost::EvaluateDistance(Player const* player) const
+{
+ uint32 requireFlag = (player->GetTeam() == ALLIANCE) ? TAXI_NODE_FLAG_ALLIANCE : TAXI_NODE_FLAG_HORDE;
+ if (!(To->Flags & requireFlag))
+ return std::numeric_limits<uint16>::max();
+
+ //if (To->ConditionID && !player->MeetsCondition(To->ConditionID))
+ // return std::numeric_limits<uint16>::max();
+
+ return Distance;
+}
diff --git a/src/server/game/Entities/Taxi/TaxiPathGraph.h b/src/server/game/Entities/Taxi/TaxiPathGraph.h
new file mode 100644
index 00000000000..15d139fb55d
--- /dev/null
+++ b/src/server/game/Entities/Taxi/TaxiPathGraph.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TAXIPATHGRAPH_HPP
+#define TAXIPATHGRAPH_HPP
+
+#include "Position.h"
+#include "Define.h"
+#include <boost/graph/adjacency_list.hpp>
+
+class Player;
+struct TaxiNodesEntry;
+
+class TaxiPathGraph
+{
+public:
+ static TaxiPathGraph& Instance()
+ {
+ static TaxiPathGraph instance;
+ return instance;
+ }
+
+ void Initialize();
+ std::size_t GetCompleteNodeRoute(TaxiNodesEntry const* from, TaxiNodesEntry const* to, Player const* player, std::vector<uint32>& shortestPath);
+
+private:
+ struct EdgeCost
+ {
+ TaxiNodesEntry const* To;
+ uint32 Distance;
+ uint32 EvaluateDistance(Player const* player) const;
+ };
+ typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::property<boost::vertex_index_t, uint32>, boost::property<boost::edge_weight_t, EdgeCost>> Graph;
+ typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap;
+ typedef Graph::vertex_descriptor vertex_descriptor;
+ typedef Graph::edge_descriptor edge_descriptor;
+ typedef std::pair<uint32, uint32> edge;
+
+ TaxiPathGraph() { }
+ ~TaxiPathGraph() { }
+
+ void AddVerticeAndEdgeFromNodeInfo(TaxiNodesEntry const* from, TaxiNodesEntry const* to, uint32 pathId, std::vector<std::pair<edge, EdgeCost>>& edges);
+ vertex_descriptor GetVertexIDFromNodeID(TaxiNodesEntry const* node);
+ uint32 GetNodeIDFromVertexID(vertex_descriptor vertexID);
+ vertex_descriptor CreateVertexFromFromNodeInfoIfNeeded(TaxiNodesEntry const* node);
+ std::size_t GetVertexCount();
+
+ Graph m_graph;
+ std::vector<TaxiNodesEntry const*> m_vertices;
+
+ TaxiPathGraph(TaxiPathGraph const&) = delete;
+ TaxiPathGraph& operator=(TaxiPathGraph const&) = delete;
+};
+
+#define sTaxiPathGraph TaxiPathGraph::Instance()
+
+#endif /* TAXIPATHGRAPH_HPP */