TrinityCore
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
FlightPathMovementGenerator.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
19#include "DB2Stores.h"
20#include "GameEventSender.h"
21#include "Log.h"
22#include "Map.h"
23#include "MovementDefines.h"
24#include "MoveSpline.h"
25#include "MoveSplineInit.h"
26#include "ObjectMgr.h"
27#include "Player.h"
28#include <sstream>
29
30#define FLIGHT_TRAVEL_UPDATE 100
31#define TIMEDIFF_NEXT_WP 250
32#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.f * 40.f)
33#define PLAYER_FLIGHT_SPEED 32.0f
34
37{
38 _speed = speed;
39 _endGridX = 0.0f;
40 _endGridY = 0.0f;
41 _endMapId = 0;
43
48 ScriptResult = std::move(scriptResult);
49}
50
52{
53 return FLIGHT_MOTION_TYPE;
54}
55
56bool FlightPathMovementGenerator::GetResetPosition(Unit* /*owner*/, float& x, float& y, float& z)
57{
59 x = node->Loc.X;
60 y = node->Loc.Y;
61 z = node->Loc.Z;
62 return true;
63}
64
66{
69
70 DoReset(owner);
72}
73
75{
77
78 owner->CombatStopWithPets();
80
81 uint32 end = GetPathAtMapEnd();
82 uint32 currentNodeId = GetCurrentNode();
83
84 if (currentNodeId == end)
85 {
86 TC_LOG_DEBUG("movement.flightpath", "FlightPathMovementGenerator::DoReset: trying to start a flypath from the end point. {}", owner->GetDebugInfo());
87 return;
88 }
89
90 Movement::MoveSplineInit init(owner);
91 init.Path().reserve(end - currentNodeId + 1);
92 // Providing a starting vertex since the taxi paths do not provide such
93 init.Path().emplace_back(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ());
94 for (uint32 i = currentNodeId; i != end; ++i)
95 init.Path().emplace_back(_path[i]->Loc.X, _path[i]->Loc.Y, _path[i]->Loc.Z);
96
98 init.SetFly();
99 init.SetSmooth();
100 init.SetUncompressed();
101 init.SetWalk(true);
102 init.SetVelocity(_speed.value_or(PLAYER_FLIGHT_SPEED));
103 init.Launch();
104}
105
107{
108 if (!owner)
109 return false;
110
111 // skipping the first spline path point because it's our starting point and not a taxi path point
112 uint32 pointId = owner->movespline->currentPathIdx() <= 0 ? 0 : owner->movespline->currentPathIdx() - 1;
113 if (pointId > _currentNode && _currentNode < _path.size() - 1)
114 {
115 bool departureEvent = true;
116 do
117 {
118 ASSERT(_currentNode < _path.size(), "Point Id: %u\n%s", pointId, owner->GetDebugInfo().c_str());
119
120 DoEventIfAny(owner, _path[_currentNode], departureEvent);
121 while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= _currentNode)
122 {
123 _pointsForPathSwitch.pop_front();
125 if (!_pointsForPathSwitch.empty())
126 {
128 owner->ModifyMoney(-_pointsForPathSwitch.front().Cost);
129 }
130 }
131
132 if (pointId == _currentNode)
133 break;
134
136 PreloadEndGrid(owner);
137
138 _currentNode += departureEvent ? 1 : 0;
139 departureEvent = !departureEvent;
140 } while (_currentNode < _path.size() - 1);
141 }
142
143 if (_currentNode >= (_path.size() - 1))
144 {
146 return false;
147 }
148 return true;
149}
150
152{
154}
155
156void FlightPathMovementGenerator::DoFinalize(Player* owner, bool active, bool movementInform)
157{
159 if (!active)
160 return;
161
163 owner->Dismount();
165
166 // update z position to ground and orientation for landing point
167 // this prevent cheating with landing point at lags
168 // when client side flight end early in comparison server side
169 owner->StopMoving();
170
171 // When the player reaches the last flight point, teleport to destination taxi node location
172 if (!_path.empty() && (_path.size() < 2 || !(_path[_path.size() - 2]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT)))
173 {
174 TaxiPathEntry const* lastPath = sTaxiPathStore.AssertEntry(_path.back()->PathID);
175 if (TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(lastPath->ToTaxiNode))
176 {
177 owner->SetFallInformation(0, node->Pos.Z);
178 owner->TeleportTo(node->ContinentID, node->Pos.X, node->Pos.Y, node->Pos.Z, owner->GetOrientation());
179 }
180 }
181
183
184 if (movementInform)
186}
187
189{
190 if (_currentNode >= _path.size())
191 return _path.size();
192
193 uint32 curMapId = _path[_currentNode]->ContinentID;
194 for (uint32 itr = _currentNode; itr < _path.size(); ++itr)
195 {
196 if (_path[itr]->ContinentID != curMapId)
197 return itr;
198 if (itr > 0 && _path[itr - 1]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT)
199 return itr;
200 }
201
202 return _path.size();
203}
204
206{
207 return p1->ContinentID != p2->ContinentID
208 || std::pow(p1->Loc.X - p2->Loc.X, 2) + std::pow(p1->Loc.Y - p2->Loc.Y, 2) > SKIP_SPLINE_POINT_DISTANCE_SQ
210 || (p2->Flags & TAXI_PATH_NODE_FLAG_STOP && p2->Delay);
211}
212
214{
215 _path.clear();
216 _currentNode = startNode;
217 _pointsForPathSwitch.clear();
218 std::deque<uint32> const& taxi = owner->m_taxi.GetPath();
219 float discount = owner->GetReputationPriceDiscount(owner->m_taxi.GetFlightMasterFactionTemplate());
220 for (uint32 src = 0, dst = 1; dst < taxi.size(); src = dst++)
221 {
222 uint32 path, cost;
223 sObjectMgr->GetTaxiPath(taxi[src], taxi[dst], path, cost);
224 if (path >= sTaxiPathNodesByPath.size())
225 return;
226
227 TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[path];
228 if (!nodes.empty())
229 {
230 TaxiPathNodeEntry const* start = nodes[0];
231 TaxiPathNodeEntry const* end = nodes[nodes.size() - 1];
232 bool passedPreviousSegmentProximityCheck = false;
233 for (uint32 i = 0; i < nodes.size(); ++i)
234 {
235 if (passedPreviousSegmentProximityCheck || !src || _path.empty() || IsNodeIncludedInShortenedPath(_path.back(), nodes[i]))
236 {
237 if ((!src || (IsNodeIncludedInShortenedPath(start, nodes[i]) && i >= 2)) &&
238 (dst == taxi.size() - 1 || (IsNodeIncludedInShortenedPath(end, nodes[i]) && (i < nodes.size() - 1 || _path.empty()))) &&
239 (!(nodes[i]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT) || _path.empty() || !(_path.back()->Flags & TAXI_PATH_NODE_FLAG_TELEPORT))) // skip consecutive teleports, only keep the first one
240 {
241 passedPreviousSegmentProximityCheck = true;
242 _path.push_back(nodes[i]);
243 }
244 }
245 else
246 {
247 _path.pop_back();
248 --_pointsForPathSwitch.back().PathIndex;
249 }
250 }
251 }
252
253 _pointsForPathSwitch.push_back({ uint32(std::max<std::size_t>(_path.size(), 1) - 1), int64(ceil(cost * discount)) });
254 }
255}
256
258{
259 if (_path.empty() || _currentNode >= _path.size())
260 return;
261
262 uint32 map0 = _path[_currentNode]->ContinentID;
263 for (size_t i = _currentNode + 1; i < _path.size(); ++i)
264 {
265 if (_path[i]->ContinentID != map0 || _path[i - 1]->Flags & TAXI_PATH_NODE_FLAG_TELEPORT)
266 {
267 _currentNode = i;
268 return;
269 }
270 }
271}
272
274{
275 ASSERT(node, "%s", owner->GetDebugInfo().c_str());
276
277 if (uint32 eventid = departure ? node->DepartureEventID : node->ArrivalEventID)
278 {
279 TC_LOG_DEBUG("maps.script", "FlightPathMovementGenerator::DoEventIfAny: taxi {} event {} of node {} of path {} for player {}", departure ? "departure" : "arrival", eventid, node->NodeIndex, node->PathID, owner->GetName());
280 GameEvents::Trigger(eventid, owner, owner);
281 }
282}
283
285{
290 uint32 nodeCount = _path.size(); // Number of nodes in path.
291 ASSERT(nodeCount, "FlightPathMovementGenerator::InitEndGridInfo() called with empty _path");
292 _endMapId = _path[nodeCount - 1]->ContinentID; // MapId of last node
293 if (nodeCount < 3)
295 else
296 _preloadTargetNode = nodeCount - 3;
297
298 while (_path[_preloadTargetNode]->ContinentID != _endMapId)
300
301 _endGridX = _path[nodeCount - 1]->Loc.X;
302 _endGridY = _path[nodeCount - 1]->Loc.Y;
303}
304
306{
307 // Used to preload the final grid where the flightmaster is
308 Map* endMap = owner->GetMap();
309
310 // Load the grid
311 if (endMap)
312 {
313 TC_LOG_DEBUG("movement.flightpath", "FlightPathMovementGenerator::PreloadEndGrid: preloading grid ({}, {}) for map {} at node index {}/{}", _endGridX, _endGridY, _endMapId, _preloadTargetNode, uint32(_path.size() - 1));
314 endMap->LoadGrid(_endGridX, _endGridY);
315 }
316 else
317 TC_LOG_DEBUG("movement.flightpath", "FlightPathMovementGenerator::PreloadEndGrid: unable to determine map to preload flightmaster grid");
318}
319
321{
322 if (index >= _path.size())
323 return 0;
324
325 return _path[index]->PathID;
326}
327
329{
330 std::stringstream sstr;
331 sstr << std::boolalpha
334 << "Start Path Id: " << GetPathId(0)
335 << " Path Size: " << _path.size()
336 << " HasArrived: " << HasArrived()
337 << " End Grid X: " << _endGridX
338 << " End Grid Y: " << _endGridY
339 << " End Map Id: " << _endMapId
340 << " Preloaded Target Node: " << _preloadTargetNode;
341 return sstr.str();
342}
DB2Storage< TaxiNodesEntry > sTaxiNodesStore("TaxiNodes.db2", &TaxiNodesLoadInfo::Instance)
TaxiPathNodesByPath sTaxiPathNodesByPath
Definition: DB2Stores.cpp:399
DB2Storage< TaxiPathEntry > sTaxiPathStore("TaxiPath.db2", &TaxiPathLoadInfo::Instance)
std::vector< TaxiPathNodeEntry const * > TaxiPathNodeList
Definition: DB2Stores.h:346
@ TAXI_PATH_NODE_FLAG_TELEPORT
Definition: DBCEnums.h:2352
@ TAXI_PATH_NODE_FLAG_STOP
Definition: DBCEnums.h:2353
int64_t int64
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:148
#define ASSERT
Definition: Errors.h:68
#define PLAYER_FLIGHT_SPEED
bool IsNodeIncludedInShortenedPath(TaxiPathNodeEntry const *p1, TaxiPathNodeEntry const *p2)
#define SKIP_SPLINE_POINT_DISTANCE_SQ
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition: Log.h:180
@ MOTION_MODE_DEFAULT
@ MOTION_PRIORITY_HIGHEST
MovementGeneratorType
@ FLIGHT_MOTION_TYPE
@ MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING
@ MOVEMENTGENERATOR_FLAG_DEACTIVATED
@ MOVEMENTGENERATOR_FLAG_FINALIZED
@ MOVEMENTGENERATOR_FLAG_INFORM_ENABLED
@ MOVEMENTGENERATOR_FLAG_INITIALIZED
#define sObjectMgr
Definition: ObjectMgr.h:1985
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
@ PLAYER_FLAGS_TAXI_BENCHMARK
Definition: Player.h:478
@ UNIT_FLAG_ON_TAXI
Definition: UnitDefines.h:187
@ UNIT_FLAG_REMOVE_CLIENT_CONTROL
Definition: UnitDefines.h:169
@ UNIT_STATE_IN_FLIGHT
Definition: Unit.h:267
float _endGridY
Y coord of last node location.
bool GetResetPosition(Unit *owner, float &x, float &y, float &z) override
uint32 _preloadTargetNode
node index where preloading starts
std::string GetDebugInfo() const override
void DoEventIfAny(Player *owner, TaxiPathNodeEntry const *node, bool departure)
MovementGeneratorType GetMovementGeneratorType() const override
void LoadPath(Player *owner, uint32 startNode=0)
float _endGridX
X coord of last node location.
std::deque< TaxiNodeChangeInfo > _pointsForPathSwitch
node indexes and costs where TaxiPath changes
uint32 _endMapId
map Id of last node location
FlightPathMovementGenerator(Optional< float > speed, Optional< Scripting::v2::ActionResultSetter< MovementStopReason > > &&scriptResult)
Definition: Map.h:223
void LoadGrid(float x, float y)
Definition: Map.cpp:377
void SetScriptResult(MovementStopReason reason)
void AddFlag(uint16 const flag)
Optional< Scripting::v2::ActionResultSetter< MovementStopReason > > ScriptResult
void RemoveFlag(uint16 const flag)
virtual std::string GetDebugInfo() const
void SetWalk(bool enable)
void SetFirstPointId(int32 pointId)
void SetVelocity(float velocity)
virtual std::string GetDebugInfo() const
std::deque< uint32 > const & GetPath() const
Definition: PlayerTaxi.h:81
FactionTemplateEntry const * GetFlightMasterFactionTemplate() const
Definition: PlayerTaxi.cpp:240
uint32 NextTaxiDestination()
Definition: PlayerTaxi.h:75
void ClearTaxiDestinations()
Definition: PlayerTaxi.h:70
bool ModifyMoney(int64 amount, bool sendError=true)
Definition: Player.cpp:24473
void RemovePlayerFlag(PlayerFlags flags)
Definition: Player.h:2852
float GetReputationPriceDiscount(Creature const *creature) const
Definition: Player.cpp:25501
void SetFallInformation(uint32 time, float z)
Definition: Player.cpp:27076
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition: Player.cpp:27171
bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, TeleportToOptions options=TELE_TO_NONE, Optional< uint32 > instanceId={}, uint32 teleportSpellId=0)
Definition: Player.cpp:1225
PlayerTaxi m_taxi
Definition: Player.h:1241
std::string GetDebugInfo() const override
Definition: Player.cpp:30663
Definition: Unit.h:632
void Dismount()
Definition: Unit.cpp:8125
void StopMoving()
Definition: Unit.cpp:10454
std::unique_ptr< Movement::MoveSpline > movespline
Definition: Unit.h:1818
void SetUnitFlag(UnitFlags flags)
Definition: Unit.h:843
void RemoveUnitFlag(UnitFlags flags)
Definition: Unit.h:844
void CombatStopWithPets(bool includingCast=false)
Definition: Unit.cpp:5967
Map * GetMap() const
Definition: Object.h:773
std::string const & GetName() const
Definition: Object.h:704
TC_GAME_API void Trigger(uint32 gameEventId, WorldObject *source, WorldObject *target)
constexpr float GetPositionX() const
Definition: Position.h:86
constexpr float GetPositionY() const
Definition: Position.h:87
constexpr float GetOrientation() const
Definition: Position.h:89
constexpr float GetPositionZ() const
Definition: Position.h:88
DBCPosition3D Loc