mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-15 16:26:08 +00:00
fix(Core/Movement): Improvements to taxi flight movement generator: (#12347)
Changed multi-segment taxi paths to fly nearby flight masters along the way, not directly through them. Taxi cost on multi-segment paths is now charged per segment when it is started. Properly send taxi node status on login, as well as if the taxi master is out of range. Apply reputation discount to all points in multi-segment paths. Properly clean up list of taxi destinations upon arrival at final node. Teleport players to the destination taxi node location instead of their current ground position. Don't start a spline with just 1 point in FlightPathMovementGenerator Source: TrinityCore.
This commit is contained in:
@@ -1644,10 +1644,8 @@ void Player::ProcessDelayedOperations()
|
||||
{
|
||||
if (m_entryPointData.HasTaxiPath())
|
||||
{
|
||||
for (size_t i = 0; i < m_entryPointData.taxiPath.size() - 1; ++i)
|
||||
m_taxi.AddTaxiDestination(m_entryPointData.taxiPath[i]);
|
||||
m_taxi.SetTaxiSegment(m_entryPointData.taxiPath[m_entryPointData.taxiPath.size() - 1]);
|
||||
|
||||
m_taxi.AddTaxiDestination(m_entryPointData.taxiPath[0]);
|
||||
m_taxi.AddTaxiDestination(m_entryPointData.taxiPath[1]);
|
||||
m_entryPointData.ClearTaxiPath();
|
||||
ContinueTaxiFlight();
|
||||
}
|
||||
@@ -10002,26 +10000,6 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
||||
return false;
|
||||
}
|
||||
|
||||
// check node starting pos data set case if provided
|
||||
if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f)
|
||||
{
|
||||
if (node->map_id != GetMapId() ||
|
||||
(node->x - GetPositionX()) * (node->x - GetPositionX()) +
|
||||
(node->y - GetPositionY()) * (node->y - GetPositionY()) +
|
||||
(node->z - GetPositionZ()) * (node->z - GetPositionZ()) >
|
||||
(2 * INTERACTION_DISTANCE) * (2 * INTERACTION_DISTANCE) * (2 * INTERACTION_DISTANCE))
|
||||
{
|
||||
GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// node must have pos if taxi master case (npc != nullptr)
|
||||
else if (npc)
|
||||
{
|
||||
GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare to flight start now
|
||||
|
||||
// stop combat at start taxi flight if any
|
||||
@@ -10043,6 +10021,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
||||
// fill destinations path tail
|
||||
uint32 sourcepath = 0;
|
||||
uint32 totalcost = 0;
|
||||
uint32 firstcost = 0;
|
||||
|
||||
uint32 prevnode = sourcenode;
|
||||
uint32 lastnode = 0;
|
||||
@@ -10061,6 +10040,8 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
||||
}
|
||||
|
||||
totalcost += cost;
|
||||
if (i == 1)
|
||||
firstcost = cost;
|
||||
|
||||
if (prevnode == sourcenode)
|
||||
sourcepath = path;
|
||||
@@ -10089,7 +10070,16 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
||||
uint32 money = GetMoney();
|
||||
|
||||
if (npc)
|
||||
totalcost = (uint32)ceil(totalcost * GetReputationPriceDiscount(npc));
|
||||
{
|
||||
float discount = GetReputationPriceDiscount(npc);
|
||||
totalcost = uint32(ceil(totalcost * discount));
|
||||
firstcost = uint32(ceil(firstcost * discount));
|
||||
m_taxi.SetFlightMasterFactionTemplateId(npc->GetFaction());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_taxi.SetFlightMasterFactionTemplateId(0);
|
||||
}
|
||||
|
||||
if (money < totalcost)
|
||||
{
|
||||
@@ -10100,8 +10090,6 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
||||
|
||||
//Checks and preparations done, DO FLIGHT
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1);
|
||||
ModifyMoney(-(int32)totalcost);
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
|
||||
|
||||
// prevent stealth flight
|
||||
//RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
|
||||
@@ -10111,12 +10099,16 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
||||
{
|
||||
TaxiNodesEntry const* lastPathNode = sTaxiNodesStore.LookupEntry(nodes[nodes.size() - 1]);
|
||||
m_taxi.ClearTaxiDestinations();
|
||||
ModifyMoney(-(int32)totalcost);
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
|
||||
TeleportTo(lastPathNode->map_id, lastPathNode->x, lastPathNode->y, lastPathNode->z, GetOrientation());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flightSpellActivated = spellid;
|
||||
ModifyMoney(-(int32)firstcost);
|
||||
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, firstcost);
|
||||
GetSession()->SendActivateTaxiReply(ERR_TAXIOK);
|
||||
GetSession()->SendDoFlight(mount_display_id, sourcepath);
|
||||
}
|
||||
@@ -10212,6 +10204,39 @@ void Player::ContinueTaxiFlight()
|
||||
GetSession()->SendDoFlight(mountDisplayId, path, startNode);
|
||||
}
|
||||
|
||||
void Player::SendTaxiNodeStatusMultiple()
|
||||
{
|
||||
for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
|
||||
{
|
||||
if (!itr->IsCreature())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Creature* creature = ObjectAccessor::GetCreature(*this, *itr);
|
||||
if (!creature || creature->IsHostileTo(this))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!creature->HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeamId());
|
||||
if (!nearestNode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
WorldPacket data(SMSG_TAXINODE_STATUS, 9);
|
||||
data << *itr;
|
||||
data << uint8(m_taxi.IsTaximaskNodeKnown(nearestNode) ? 1 : 0);
|
||||
SendDirectMessage(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
|
||||
{
|
||||
PacketCooldowns cooldowns;
|
||||
@@ -10982,11 +11007,8 @@ void Player::SetEntryPoint()
|
||||
m_entryPointData.mountSpell = 0;
|
||||
m_entryPointData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
|
||||
|
||||
std::vector<uint32> const& taxi = m_taxi.GetPath();
|
||||
for (std::vector<uint32>::const_iterator itr = taxi.begin(); itr != taxi.end(); ++itr)
|
||||
m_entryPointData.taxiPath.push_back(*itr);
|
||||
|
||||
m_entryPointData.taxiPath.push_back(m_taxi.GetTaxiSegment());
|
||||
m_entryPointData.taxiPath[0] = m_taxi.GetTaxiSource();
|
||||
m_entryPointData.taxiPath[1] = m_taxi.GetTaxiDestination();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -11351,6 +11373,7 @@ void Player::SendInitialPacketsAfterAddToMap()
|
||||
SendEnchantmentDurations(); // must be after add to map
|
||||
SendItemDurations(); // must be after add to map
|
||||
SendQuestGiverStatusMultiple();
|
||||
SendTaxiNodeStatusMultiple();
|
||||
|
||||
// raid downscaling - send difficulty to player
|
||||
if (GetMap()->IsRaid())
|
||||
@@ -12005,13 +12028,21 @@ bool Player::GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const
|
||||
|
||||
float Player::GetReputationPriceDiscount(Creature const* creature) const
|
||||
{
|
||||
FactionTemplateEntry const* vendor_faction = creature->GetFactionTemplateEntry();
|
||||
if (!vendor_faction || !vendor_faction->faction)
|
||||
return 1.0f;
|
||||
return GetReputationPriceDiscount(creature->GetFactionTemplateEntry());
|
||||
}
|
||||
|
||||
ReputationRank rank = GetReputationRank(vendor_faction->faction);
|
||||
if (rank <= REP_NEUTRAL)
|
||||
float Player::GetReputationPriceDiscount(FactionTemplateEntry const* factionTemplate) const
|
||||
{
|
||||
if (!factionTemplate || !factionTemplate->faction)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
ReputationRank rank = GetReputationRank(factionTemplate->faction);
|
||||
if (rank <= REP_NEUTRAL)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return 1.0f - 0.05f * (rank - REP_NEUTRAL);
|
||||
}
|
||||
|
||||
@@ -1126,6 +1126,7 @@ public:
|
||||
bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 1);
|
||||
void CleanupAfterTaxiFlight();
|
||||
void ContinueTaxiFlight();
|
||||
void SendTaxiNodeStatusMultiple();
|
||||
// mount_id can be used in scripting calls
|
||||
|
||||
[[nodiscard]] bool IsDeveloper() const { return HasPlayerFlag(PLAYER_FLAGS_DEVELOPER); }
|
||||
@@ -1336,7 +1337,8 @@ public:
|
||||
bool BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot);
|
||||
bool _StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot, int32 price, ItemTemplate const* pProto, Creature* pVendor, VendorItem const* crItem, bool bStore);
|
||||
|
||||
float GetReputationPriceDiscount(Creature const* creature) const;
|
||||
[[nodiscard]] float GetReputationPriceDiscount(Creature const* creature) const;
|
||||
[[nodiscard]] float GetReputationPriceDiscount(FactionTemplateEntry const* factionTemplate) const;
|
||||
|
||||
[[nodiscard]] Player* GetTrader() const { return m_trade ? m_trade->GetTrader() : nullptr; }
|
||||
[[nodiscard]] TradeData* GetTradeData() const { return m_trade; }
|
||||
|
||||
@@ -5190,10 +5190,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
||||
// xinef: restore taxi flight from entry point data
|
||||
if (m_entryPointData.HasTaxiPath())
|
||||
{
|
||||
for (size_t i = 0; i < m_entryPointData.taxiPath.size() - 1; ++i)
|
||||
m_taxi.AddTaxiDestination(m_entryPointData.taxiPath[i]);
|
||||
m_taxi.SetTaxiSegment(m_entryPointData.taxiPath[m_entryPointData.taxiPath.size() - 1]);
|
||||
|
||||
m_taxi.AddTaxiDestination(m_entryPointData.taxiPath[0]);
|
||||
m_taxi.AddTaxiDestination(m_entryPointData.taxiPath[1]);
|
||||
m_entryPointData.ClearTaxiPath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,6 @@
|
||||
#include "Tokenize.h"
|
||||
#include "StringConvert.h"
|
||||
|
||||
PlayerTaxi::PlayerTaxi() : _taxiSegment(0)
|
||||
{
|
||||
memset(m_taximask, 0, sizeof(m_taximask));
|
||||
}
|
||||
|
||||
void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
|
||||
{
|
||||
// class specific initial known nodes
|
||||
@@ -136,9 +131,25 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, TeamI
|
||||
{
|
||||
ClearTaxiDestinations();
|
||||
|
||||
for (auto const& itr : Acore::Tokenize(values, ' ', false))
|
||||
std::vector<std::string_view> tokens = Acore::Tokenize(values, ' ', false);
|
||||
auto itr = tokens.begin();
|
||||
if (itr != tokens.end())
|
||||
{
|
||||
if (Optional<uint32> node = Acore::StringTo<uint32>(itr))
|
||||
if (Optional<uint32> faction = Acore::StringTo<uint32>(*itr))
|
||||
{
|
||||
m_flightMasterFactionId = *faction;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
while ((++itr) != tokens.end())
|
||||
{
|
||||
if (Optional<uint32> node = Acore::StringTo<uint32>(*itr))
|
||||
{
|
||||
AddTaxiDestination(*node);
|
||||
}
|
||||
@@ -148,26 +159,33 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, TeamI
|
||||
}
|
||||
}
|
||||
|
||||
if (m_TaxiDestinations.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check integrity
|
||||
if (m_TaxiDestinations.size() < 3)
|
||||
if (m_TaxiDestinations.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// xinef: current segment is saved as last destination in db
|
||||
_taxiSegment = m_TaxiDestinations[m_TaxiDestinations.size() - 1];
|
||||
m_TaxiDestinations.pop_back();
|
||||
|
||||
for (size_t i = 0; i < m_TaxiDestinations.size() - 1; ++i)
|
||||
for (size_t i = 1; i < m_TaxiDestinations.size(); ++i)
|
||||
{
|
||||
uint32 cost;
|
||||
uint32 path;
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[i], m_TaxiDestinations[i + 1], path, cost);
|
||||
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(), teamId, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -175,26 +193,34 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, TeamI
|
||||
std::string PlayerTaxi::SaveTaxiDestinationsToString()
|
||||
{
|
||||
if (m_TaxiDestinations.empty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
ASSERT(m_TaxiDestinations.size() >= 2);
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << m_flightMasterFactionId << ' ';
|
||||
|
||||
for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
|
||||
{
|
||||
ss << m_TaxiDestinations[i] << ' ';
|
||||
}
|
||||
|
||||
ss << _taxiSegment << ' ';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
uint32 PlayerTaxi::GetCurrentTaxiPath() const
|
||||
{
|
||||
if (m_TaxiDestinations.size() < 2 || m_TaxiDestinations.size() <= _taxiSegment + 1)
|
||||
if (m_TaxiDestinations.size() < 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 path;
|
||||
uint32 cost;
|
||||
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[_taxiSegment], m_TaxiDestinations[_taxiSegment + 1], path, cost);
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path, cost);
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -205,3 +231,8 @@ std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
|
||||
ss << taxi.m_taximask[i] << ' ';
|
||||
return ss;
|
||||
}
|
||||
|
||||
FactionTemplateEntry const* PlayerTaxi::GetFlightMasterFactionTemplate() const
|
||||
{
|
||||
return sFactionTemplateStore.LookupEntry(m_flightMasterFactionId);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class ByteBuffer;
|
||||
class AC_GAME_API PlayerTaxi
|
||||
{
|
||||
public:
|
||||
PlayerTaxi();
|
||||
PlayerTaxi() : m_flightMasterFactionId(0) { m_taximask.fill(0); }
|
||||
~PlayerTaxi() = default;
|
||||
|
||||
// Nodes
|
||||
@@ -59,29 +59,28 @@ public:
|
||||
bool LoadTaxiDestinationsFromString(std::string const& values, TeamId teamId);
|
||||
std::string SaveTaxiDestinationsToString();
|
||||
|
||||
void ClearTaxiDestinations() { m_TaxiDestinations.clear(); _taxiSegment = 0; }
|
||||
void ClearTaxiDestinations() { m_TaxiDestinations.clear(); }
|
||||
void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
|
||||
[[nodiscard]] uint32 GetTaxiSource() const { return m_TaxiDestinations.size() <= _taxiSegment + 1 ? 0 : m_TaxiDestinations[_taxiSegment]; }
|
||||
[[nodiscard]] uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() <= _taxiSegment + 1 ? 0 : m_TaxiDestinations[_taxiSegment + 1]; }
|
||||
[[nodiscard]] uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); }
|
||||
[[nodiscard]] uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; }
|
||||
[[nodiscard]] uint32 GetCurrentTaxiPath() const;
|
||||
uint32 NextTaxiDestination()
|
||||
{
|
||||
++_taxiSegment;
|
||||
m_TaxiDestinations.pop_front();
|
||||
return GetTaxiDestination();
|
||||
}
|
||||
|
||||
// xinef:
|
||||
void SetTaxiSegment(uint32 segment) { _taxiSegment = segment; }
|
||||
[[nodiscard]] uint32 GetTaxiSegment() const { return _taxiSegment; }
|
||||
|
||||
[[nodiscard]] std::vector<uint32> const& GetPath() const { return m_TaxiDestinations; }
|
||||
[[nodiscard]] std::deque<uint32> const& GetPath() const { return m_TaxiDestinations; }
|
||||
[[nodiscard]] bool empty() const { return m_TaxiDestinations.empty(); }
|
||||
[[nodiscard]] FactionTemplateEntry const* GetFlightMasterFactionTemplate() const;
|
||||
void SetFlightMasterFactionTemplateId(uint32 factionTemplateId) { m_flightMasterFactionId = factionTemplateId; }
|
||||
|
||||
friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
|
||||
|
||||
private:
|
||||
TaxiMask m_taximask;
|
||||
std::vector<uint32> m_TaxiDestinations;
|
||||
uint32 _taxiSegment;
|
||||
std::deque<uint32> m_TaxiDestinations;
|
||||
uint32 m_flightMasterFactionId;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user