TrinityCore
Loading...
Searching...
No Matches
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 {
36 bool isVisibleForFaction = [&]
37 {
38 switch (player->GetTeam())
39 {
40 case HORDE: return To->GetFlags().HasFlag(TaxiNodeFlags::ShowOnHordeMap);
41 case ALLIANCE: return To->GetFlags().HasFlag(TaxiNodeFlags::ShowOnAllianceMap);
42 default: break;
43 }
44 return false;
45 }();
46 if (!isVisibleForFaction)
47 return std::numeric_limits<uint16>::max();
48
50 return std::numeric_limits<uint16>::max();
51
52 return Distance;
53 }
54};
55
56typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::property<boost::vertex_index_t, uint32>, boost::property<boost::edge_weight_t, EdgeCost>> Graph;
57typedef boost::property_map<Graph, boost::edge_weight_t>::type WeightMap;
58typedef Graph::vertex_descriptor vertex_descriptor;
59typedef Graph::edge_descriptor edge_descriptor;
60typedef std::pair<vertex_descriptor, vertex_descriptor> edge;
61
62Graph m_graph;
63std::vector<TaxiNodesEntry const*> m_nodesByVertex;
64std::unordered_map<uint32, vertex_descriptor> m_verticesByNode;
65
66void GetTaxiMapPosition(DBCPosition3D const& position, int32 mapId, DBCPosition2D* uiMapPosition, uint32* uiMapId)
67{
68 if (!DB2Manager::GetUiMapPosition(position.X, position.Y, position.Z, mapId, 0, 0, 0, UI_MAP_SYSTEM_ADVENTURE, false, uiMapId, uiMapPosition))
69 DB2Manager::GetUiMapPosition(position.X, position.Y, position.Z, mapId, 0, 0, 0, UI_MAP_SYSTEM_TAXI, false, uiMapId, uiMapPosition);
70}
71
72vertex_descriptor CreateVertexFromFromNodeInfoIfNeeded(TaxiNodesEntry const* node)
73{
74 auto itr = m_verticesByNode.find(node->ID);
75 if (itr == m_verticesByNode.end())
76 {
77 itr = m_verticesByNode.emplace(node->ID, m_nodesByVertex.size()).first;
78 m_nodesByVertex.push_back(node);
79 }
80
81 return itr->second;
82}
83
84void AddVerticeAndEdgeFromNodeInfo(TaxiNodesEntry const* from, TaxiNodesEntry const* to, uint32 pathId, std::vector<std::pair<edge, EdgeCost>>& edges)
85{
86 if (from != to)
87 {
88 vertex_descriptor fromVertexID = CreateVertexFromFromNodeInfoIfNeeded(from);
89 vertex_descriptor toVertexID = CreateVertexFromFromNodeInfoIfNeeded(to);
90
91 float totalDist = 0.0f;
92 TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[pathId];
93 if (nodes.size() < 2)
94 {
95 edges.emplace_back(edge(fromVertexID, toVertexID), EdgeCost{ to, 0xFFFF });
96 return;
97 }
98
99 std::size_t last = nodes.size();
100 std::size_t first = 0;
101 if (nodes.size() > 2)
102 {
103 --last;
104 ++first;
105 }
106
107 for (std::size_t i = first + 1; i < last; ++i)
108 {
109 if (nodes[i - 1]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT)
110 continue;
111
112 uint32 uiMap1, uiMap2;
113 DBCPosition2D pos1, pos2;
114
115 GetTaxiMapPosition(nodes[i - 1]->Loc, nodes[i - 1]->ContinentID, &pos1, &uiMap1);
116 GetTaxiMapPosition(nodes[i]->Loc, nodes[i]->ContinentID, &pos2, &uiMap2);
117
118 if (uiMap1 != uiMap2)
119 continue;
120
121 totalDist += std::sqrt(
122 std::pow(pos2.X - pos1.X, 2) +
123 std::pow(pos2.Y - pos1.Y, 2));
124 }
125
126 uint32 dist = uint32(totalDist * 32767.0f);
127 if (dist > 0xFFFF)
128 dist = 0xFFFF;
129
130 edges.emplace_back(edge(fromVertexID, toVertexID), EdgeCost{ to, dist });
131 }
132}
133
134vertex_descriptor const* GetVertexIDFromNodeID(TaxiNodesEntry const* node)
135{
136 return Trinity::Containers::MapGetValuePtr(m_verticesByNode, node->ID);
137}
138
139uint32 GetNodeIDFromVertexID(vertex_descriptor vertexID)
140{
141 if (vertexID < m_nodesByVertex.size())
142 return m_nodesByVertex[vertexID]->ID;
143
144 return std::numeric_limits<uint32>::max();
145}
146
147template<typename T>
148struct DiscoverVertexVisitor : public boost::base_visitor<DiscoverVertexVisitor<T>>
149{
150 using event_filter = boost::on_discover_vertex;
151
152 DiscoverVertexVisitor(T&& func) : _func(std::forward<T>(func)) { }
153
154 template <class Vertex, class Graph>
155 void operator()(Vertex v, Graph& /*g*/)
156 {
157 _func(v);
158 }
159
160private:
161 T _func;
162};
163
164template<typename T>
165auto make_discover_vertex_dfs_visitor(T&& t)
166{
167 return boost::make_dfs_visitor(DiscoverVertexVisitor<T>(std::forward<T>(t)));
168}
169}
170
172{
173 if (boost::num_vertices(m_graph) > 0)
174 return;
175
176 std::vector<std::pair<edge, EdgeCost>> edges;
177
178 // Initialize here
179 for (TaxiPathEntry const* path : sTaxiPathStore)
180 {
181 TaxiNodesEntry const* from = sTaxiNodesStore.LookupEntry(path->FromTaxiNode);
182 TaxiNodesEntry const* to = sTaxiNodesStore.LookupEntry(path->ToTaxiNode);
183 if (from && to && from->IsPartOfTaxiNetwork() && to->IsPartOfTaxiNetwork())
184 AddVerticeAndEdgeFromNodeInfo(from, to, path->ID, edges);
185 }
186
187 // create graph
188 m_graph = Graph(m_nodesByVertex.size());
189 WeightMap weightmap = boost::get(boost::edge_weight, m_graph);
190
191 for (std::size_t j = 0; j < edges.size(); ++j)
192 {
193 edge_descriptor e = boost::add_edge(edges[j].first.first, edges[j].first.second, m_graph).first;
194 weightmap[e] = edges[j].second;
195 }
196}
197
198std::size_t TaxiPathGraph::GetCompleteNodeRoute(TaxiNodesEntry const* from, TaxiNodesEntry const* to, Player const* player, std::vector<uint32>& shortestPath)
199{
200 /*
201 Information about node algorithm from client
202 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.
203 Examining several paths I discovered the following algorithm:
204 * If destinationNodeID has is the next destination, connected directly to sourceNodeID, then, client just pick up this route regardless of distance
205 * else we use dijkstra to find the shortest path.
206 * When early landing is requested, according to behavior on retail, you can never end in a node you did not discovered before
207 */
208
209 // Find if we have a direct path
210 uint32 pathId, goldCost;
211 sObjectMgr->GetTaxiPath(from->ID, to->ID, pathId, goldCost);
212 if (pathId)
213 shortestPath = { from->ID, to->ID };
214 else
215 {
216 shortestPath.clear();
217
218 vertex_descriptor const* fromVertexId = GetVertexIDFromNodeID(from);
219 vertex_descriptor const* toVertexId = GetVertexIDFromNodeID(to);
220 if (fromVertexId && toVertexId)
221 {
222 std::vector<vertex_descriptor> p(boost::num_vertices(m_graph));
223 std::vector<uint32> d(boost::num_vertices(m_graph));
224
225 boost::dijkstra_shortest_paths(m_graph, *fromVertexId,
226 boost::predecessor_map(boost::make_iterator_property_map(p.begin(), boost::get(boost::vertex_index, m_graph)))
227 .distance_map(boost::make_iterator_property_map(d.begin(), boost::get(boost::vertex_index, m_graph)))
228 .vertex_index_map(boost::get(boost::vertex_index, m_graph))
229 .distance_compare(std::less<uint32>())
230 .distance_combine(boost::closed_plus<uint32>())
231 .distance_inf(std::numeric_limits<uint32>::max())
232 .distance_zero(0)
233 .visitor(boost::dijkstra_visitor<boost::null_visitor>())
234 .weight_map(boost::make_transform_value_property_map(
235 [player](EdgeCost const& edgeCost) { return edgeCost.EvaluateDistance(player); },
236 boost::get(boost::edge_weight, m_graph))));
237
238 // found a path to the goal
239 for (vertex_descriptor v = *toVertexId; ; v = p[v])
240 {
241 shortestPath.push_back(GetNodeIDFromVertexID(v));
242 if (v == p[v])
243 break;
244 }
245
246 std::reverse(shortestPath.begin(), shortestPath.end());
247 }
248 }
249
250 return shortestPath.size();
251}
252
254{
255 vertex_descriptor const* vertexId = GetVertexIDFromNodeID(from);
256 if (!vertexId)
257 return;
258
259 boost::vector_property_map<boost::default_color_type> color(boost::num_vertices(m_graph));
260 std::fill(color.storage_begin(), color.storage_end(), boost::white_color);
261 boost::depth_first_visit(m_graph, *vertexId, make_discover_vertex_dfs_visitor([mask](vertex_descriptor vertex)
262 {
263 if (TaxiNodesEntry const* taxiNode = sTaxiNodesStore.LookupEntry(GetNodeIDFromVertexID(vertex)))
264 (*mask)[(taxiNode->ID - 1) / 8] |= 1 << ((taxiNode->ID - 1) % 8);
265 }), color);
266}
DB2Storage< TaxiNodesEntry > sTaxiNodesStore("TaxiNodes.db2", &TaxiNodesLoadInfo::Instance)
TaxiPathNodesByPath sTaxiPathNodesByPath
DB2Storage< TaxiPathEntry > sTaxiPathStore("TaxiPath.db2", &TaxiPathLoadInfo::Instance)
std::vector< TaxiPathNodeEntry const * > TaxiPathNodeList
Definition DB2Stores.h:365
@ UI_MAP_SYSTEM_ADVENTURE
Definition DBCEnums.h:2956
@ UI_MAP_SYSTEM_TAXI
Definition DBCEnums.h:2955
@ TAXI_PATH_NODE_FLAG_TELEPORT
Definition DBCEnums.h:2816
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
#define sObjectMgr
Definition ObjectMgr.h:1885
@ ALLIANCE
@ HORDE
static bool IsPlayerMeetingCondition(Player const *player, uint32 conditionId)
static bool GetUiMapPosition(float x, float y, float z, int32 mapId, int32 areaId, int32 wmoDoodadPlacementId, int32 wmoGroupId, UiMapSystem system, bool local, uint32 *uiMapId=nullptr, DBCPosition2D *newPos=nullptr)
Team GetTeam() const
Definition Player.h:2423
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:37
STL namespace.
EnumFlag< TaxiNodeFlags > GetFlags() const
bool IsPartOfTaxiNetwork() const