TrinityCore
TaxiPathGraph.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "TaxiPathGraph.h"
19#include "DB2Stores.h"
20#include "MapUtils.h"
21#include "ObjectMgr.h"
22#include "Player.h"
23#include <boost/graph/adjacency_list.hpp>
24#include <boost/graph/depth_first_search.hpp>
25#include <boost/graph/dijkstra_shortest_paths.hpp>
26#include <boost/property_map/transform_value_property_map.hpp>
27
28namespace
29{
30struct EdgeCost
31{
32 TaxiNodesEntry const* To;
33 uint32 Distance;
34 uint32 EvaluateDistance(Player const* player) const
35 {
37 if (!To->GetFlags().HasFlag(requireFlag))
38 return std::numeric_limits<uint16>::max();
39
40 if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(To->ConditionID))
41 if (!sConditionMgr->IsPlayerMeetingCondition(player, condition))
42 return std::numeric_limits<uint16>::max();
43
44 return Distance;
45 }
46};
47
48typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::property<boost::vertex_index_t, uint32>, boost::property<boost::edge_weight_t, EdgeCost>> Graph;
49typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap;
50typedef Graph::vertex_descriptor vertex_descriptor;
51typedef Graph::edge_descriptor edge_descriptor;
52typedef std::pair<vertex_descriptor, vertex_descriptor> edge;
53
54Graph m_graph;
55std::vector<TaxiNodesEntry const*> m_nodesByVertex;
56std::unordered_map<uint32, vertex_descriptor> m_verticesByNode;
57
58void GetTaxiMapPosition(DBCPosition3D const& position, int32 mapId, DBCPosition2D* uiMapPosition, int32* uiMapId)
59{
60 if (!DB2Manager::GetUiMapPosition(position.X, position.Y, position.Z, mapId, 0, 0, 0, UI_MAP_SYSTEM_ADVENTURE, false, uiMapId, uiMapPosition))
61 DB2Manager::GetUiMapPosition(position.X, position.Y, position.Z, mapId, 0, 0, 0, UI_MAP_SYSTEM_TAXI, false, uiMapId, uiMapPosition);
62}
63
64vertex_descriptor CreateVertexFromFromNodeInfoIfNeeded(TaxiNodesEntry const* node)
65{
66 auto itr = m_verticesByNode.find(node->ID);
67 if (itr == m_verticesByNode.end())
68 {
69 itr = m_verticesByNode.emplace(node->ID, m_nodesByVertex.size()).first;
70 m_nodesByVertex.push_back(node);
71 }
72
73 return itr->second;
74}
75
76void AddVerticeAndEdgeFromNodeInfo(TaxiNodesEntry const* from, TaxiNodesEntry const* to, uint32 pathId, std::vector<std::pair<edge, EdgeCost>>& edges)
77{
78 if (from != to)
79 {
80 vertex_descriptor fromVertexID = CreateVertexFromFromNodeInfoIfNeeded(from);
81 vertex_descriptor toVertexID = CreateVertexFromFromNodeInfoIfNeeded(to);
82
83 float totalDist = 0.0f;
84 TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[pathId];
85 if (nodes.size() < 2)
86 {
87 edges.emplace_back(edge(fromVertexID, toVertexID), EdgeCost{ to, 0xFFFF });
88 return;
89 }
90
91 std::size_t last = nodes.size();
92 std::size_t first = 0;
93 if (nodes.size() > 2)
94 {
95 --last;
96 ++first;
97 }
98
99 for (std::size_t i = first + 1; i < last; ++i)
100 {
101 if (nodes[i - 1]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT)
102 continue;
103
104 int32 uiMap1, uiMap2;
105 DBCPosition2D pos1, pos2;
106
107 GetTaxiMapPosition(nodes[i - 1]->Loc, nodes[i - 1]->ContinentID, &pos1, &uiMap1);
108 GetTaxiMapPosition(nodes[i]->Loc, nodes[i]->ContinentID, &pos2, &uiMap2);
109
110 if (uiMap1 != uiMap2)
111 continue;
112
113 totalDist += std::sqrt(
114 std::pow(pos2.X - pos1.X, 2) +
115 std::pow(pos2.Y - pos1.Y, 2));
116 }
117
118 uint32 dist = uint32(totalDist * 32767.0f);
119 if (dist > 0xFFFF)
120 dist = 0xFFFF;
121
122 edges.emplace_back(edge(fromVertexID, toVertexID), EdgeCost{ to, dist });
123 }
124}
125
126vertex_descriptor const* GetVertexIDFromNodeID(TaxiNodesEntry const* node)
127{
128 return Trinity::Containers::MapGetValuePtr(m_verticesByNode, node->ID);
129}
130
131uint32 GetNodeIDFromVertexID(vertex_descriptor vertexID)
132{
133 if (vertexID < m_nodesByVertex.size())
134 return m_nodesByVertex[vertexID]->ID;
135
136 return std::numeric_limits<uint32>::max();
137}
138
139template<typename T>
140struct DiscoverVertexVisitor : public boost::base_visitor<DiscoverVertexVisitor<T>>
141{
142 using event_filter = boost::on_discover_vertex;
143
144 DiscoverVertexVisitor(T&& func) : _func(std::forward<T>(func)) { }
145
146 template <class Vertex, class Graph>
147 void operator()(Vertex v, Graph& /*g*/)
148 {
149 _func(v);
150 }
151
152private:
153 T _func;
154};
155
156template<typename T>
157auto make_discover_vertex_dfs_visitor(T&& t)
158{
159 return boost::make_dfs_visitor(DiscoverVertexVisitor<T>(std::forward<T>(t)));
160}
161}
162
164{
165 if (boost::num_vertices(m_graph) > 0)
166 return;
167
168 std::vector<std::pair<edge, EdgeCost>> edges;
169
170 // Initialize here
171 for (TaxiPathEntry const* path : sTaxiPathStore)
172 {
173 TaxiNodesEntry const* from = sTaxiNodesStore.LookupEntry(path->FromTaxiNode);
174 TaxiNodesEntry const* to = sTaxiNodesStore.LookupEntry(path->ToTaxiNode);
175 if (from && to && from->IsPartOfTaxiNetwork() && to->IsPartOfTaxiNetwork())
176 AddVerticeAndEdgeFromNodeInfo(from, to, path->ID, edges);
177 }
178
179 // create graph
180 m_graph = Graph(m_nodesByVertex.size());
181 WeightMap weightmap = boost::get(boost::edge_weight, m_graph);
182
183 for (std::size_t j = 0; j < edges.size(); ++j)
184 {
185 edge_descriptor e = boost::add_edge(edges[j].first.first, edges[j].first.second, m_graph).first;
186 weightmap[e] = edges[j].second;
187 }
188}
189
190std::size_t TaxiPathGraph::GetCompleteNodeRoute(TaxiNodesEntry const* from, TaxiNodesEntry const* to, Player const* player, std::vector<uint32>& shortestPath)
191{
192 /*
193 Information about node algorithm from client
194 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.
195 Examining several paths I discovered the following algorithm:
196 * If destinationNodeID has is the next destination, connected directly to sourceNodeID, then, client just pick up this route regardless of distance
197 * else we use dijkstra to find the shortest path.
198 * When early landing is requested, according to behavior on retail, you can never end in a node you did not discovered before
199 */
200
201 // Find if we have a direct path
202 uint32 pathId, goldCost;
203 sObjectMgr->GetTaxiPath(from->ID, to->ID, pathId, goldCost);
204 if (pathId)
205 shortestPath = { from->ID, to->ID };
206 else
207 {
208 shortestPath.clear();
209
210 vertex_descriptor const* fromVertexId = GetVertexIDFromNodeID(from);
211 vertex_descriptor const* toVertexId = GetVertexIDFromNodeID(to);
212 if (fromVertexId && toVertexId)
213 {
214 std::vector<vertex_descriptor> p(boost::num_vertices(m_graph));
215 std::vector<uint32> d(boost::num_vertices(m_graph));
216
217 boost::dijkstra_shortest_paths(m_graph, *fromVertexId,
218 boost::predecessor_map(boost::make_iterator_property_map(p.begin(), boost::get(boost::vertex_index, m_graph)))
219 .distance_map(boost::make_iterator_property_map(d.begin(), boost::get(boost::vertex_index, m_graph)))
220 .vertex_index_map(boost::get(boost::vertex_index, m_graph))
221 .distance_compare(std::less<uint32>())
222 .distance_combine(boost::closed_plus<uint32>())
223 .distance_inf(std::numeric_limits<uint32>::max())
224 .distance_zero(0)
225 .visitor(boost::dijkstra_visitor<boost::null_visitor>())
226 .weight_map(boost::make_transform_value_property_map(
227 [player](EdgeCost const& edgeCost) { return edgeCost.EvaluateDistance(player); },
228 boost::get(boost::edge_weight, m_graph))));
229
230 // found a path to the goal
231 for (vertex_descriptor v = *toVertexId; ; v = p[v])
232 {
233 shortestPath.push_back(GetNodeIDFromVertexID(v));
234 if (v == p[v])
235 break;
236 }
237
238 std::reverse(shortestPath.begin(), shortestPath.end());
239 }
240 }
241
242 return shortestPath.size();
243}
244
246{
247 vertex_descriptor const* vertexId = GetVertexIDFromNodeID(from);
248 if (!vertexId)
249 return;
250
251 boost::vector_property_map<boost::default_color_type> color(boost::num_vertices(m_graph));
252 std::fill(color.storage_begin(), color.storage_end(), boost::white_color);
253 boost::depth_first_visit(m_graph, *vertexId, make_discover_vertex_dfs_visitor([mask](vertex_descriptor vertex)
254 {
255 if (TaxiNodesEntry const* taxiNode = sTaxiNodesStore.LookupEntry(GetNodeIDFromVertexID(vertex)))
256 (*mask)[(taxiNode->ID - 1) / 8] |= 1 << ((taxiNode->ID - 1) % 8);
257 }), color);
258}
#define sConditionMgr
Definition: ConditionMgr.h:365
DB2Storage< TaxiNodesEntry > sTaxiNodesStore("TaxiNodes.db2", &TaxiNodesLoadInfo::Instance)
TaxiPathNodesByPath sTaxiPathNodesByPath
Definition: DB2Stores.cpp:383
DB2Storage< PlayerConditionEntry > sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance)
DB2Storage< TaxiPathEntry > sTaxiPathStore("TaxiPath.db2", &TaxiPathLoadInfo::Instance)
std::vector< TaxiPathNodeEntry const * > TaxiPathNodeList
Definition: DB2Stores.h:332
@ UI_MAP_SYSTEM_ADVENTURE
Definition: DBCEnums.h:2281
@ UI_MAP_SYSTEM_TAXI
Definition: DBCEnums.h:2280
TaxiNodeFlags
Definition: DBCEnums.h:2137
@ TAXI_PATH_NODE_FLAG_TELEPORT
Definition: DBCEnums.h:2152
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
#define sObjectMgr
Definition: ObjectMgr.h:1946
@ ALLIANCE
static bool GetUiMapPosition(float x, float y, float z, int32 mapId, int32 areaId, int32 wmoDoodadPlacementId, int32 wmoGroupId, UiMapSystem system, bool local, int32 *uiMapId=nullptr, DBCPosition2D *newPos=nullptr)
Definition: DB2Stores.cpp:3295
Team GetTeam() const
Definition: Player.h:2235
void GetReachableNodesMask(TaxiNodesEntry const *from, TaxiMask *mask)
std::size_t GetCompleteNodeRoute(TaxiNodesEntry const *from, TaxiNodesEntry const *to, Player const *player, std::vector< uint32 > &shortestPath)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
STL namespace.
EnumFlag< TaxiNodeFlags > GetFlags() const
bool IsPartOfTaxiNetwork() const