Core/Gossip: Track started interaction types and reset only questgiver on quest accept

Closes #31120
This commit is contained in:
Shauren
2025-07-05 22:34:45 +02:00
parent 2a2e09e8f4
commit c02e311eef
13 changed files with 195 additions and 134 deletions

View File

@@ -23,6 +23,7 @@
#include "DatabaseEnv.h"
#include "DisableMgr.h"
#include "GameEventMgr.h"
#include "GossipDef.h"
#include "Language.h"
#include "Log.h"
#include "MapManager.h"
@@ -452,6 +453,8 @@ void BattlegroundMgr::SendBattlegroundList(Player* player, ObjectGuid const& gui
if (!bgTemplate)
return;
player->PlayerTalkClass->GetInteractionData().StartInteraction(guid, PlayerInteractionType::BattleMaster);
WorldPackets::Battleground::BattlefieldList battlefieldList;
battlefieldList.BattlemasterGuid = guid;
battlefieldList.BattlemasterListID = bgTypeId;

View File

@@ -219,8 +219,7 @@ void PlayerMenu::ClearMenus()
void PlayerMenu::SendGossipMenu(uint32 titleTextId, ObjectGuid objectGUID)
{
_interactionData.Reset();
_interactionData.SourceGuid = objectGUID;
_interactionData.StartInteraction(objectGUID, PlayerInteractionType::Gossip);
WorldPackets::NPC::GossipMessage packet;
packet.GossipGUID = objectGUID;
@@ -364,6 +363,39 @@ void PlayerChoiceData::AddResponse(uint32 id, uint16 clientIdentifier)
_responses.push_back({ .Id = id, .ClientIdentifier = clientIdentifier });
}
InteractionData::InteractionData() = default;
InteractionData::InteractionData(InteractionData const& other) = default;
InteractionData::InteractionData(InteractionData&& other) noexcept = default;
InteractionData& InteractionData::operator=(InteractionData const& other) = default;
InteractionData& InteractionData::operator=(InteractionData&& other) noexcept = default;
InteractionData::~InteractionData() = default;
void InteractionData::StartInteraction(ObjectGuid target, PlayerInteractionType type)
{
SourceGuid = target;
Type = type;
IsLaunchedByQuest = false;
switch (type)
{
case PlayerInteractionType::Trainer:
_data.emplace<TrainerData>();
break;
case PlayerInteractionType::PlayerChoice:
_data.emplace<PlayerChoiceData>();
break;
default:
break;
}
}
void InteractionData::Reset()
{
SourceGuid.Clear();
Type = PlayerInteractionType::None;
IsLaunchedByQuest = false;
_data.emplace<std::monostate>();
}
void QuestMenu::ClearMenu()
{
_questMenuItems.clear();
@@ -372,6 +404,9 @@ void QuestMenu::ClearMenu()
void PlayerMenu::SendQuestGiverQuestListMessage(Object* questgiver)
{
ObjectGuid guid = questgiver->GetGUID();
GetInteractionData().StartInteraction(guid, PlayerInteractionType::QuestGiver);
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
WorldPackets::Quest::QuestGiverQuestListMessage questList;
@@ -432,8 +467,10 @@ void PlayerMenu::SendQuestGiverStatus(QuestGiverStatus questStatus, ObjectGuid n
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC={}, status={}", npcGUID.ToString(), AsUnderlyingType(questStatus));
}
void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup) const
void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup)
{
GetInteractionData().StartInteraction(npcGUID, PlayerInteractionType::QuestGiver);
WorldPackets::Quest::QuestGiverQuestDetails packet;
packet.QuestTitle = quest->GetLogTitle();
@@ -532,8 +569,10 @@ void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid={}", quest->GetQuestId());
}
void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched) const
void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched)
{
GetInteractionData().StartInteraction(npcGUID, PlayerInteractionType::QuestGiver);
WorldPackets::Quest::QuestGiverOfferRewardMessage packet;
packet.QuestTitle = quest->GetLogTitle();
@@ -602,7 +641,7 @@ void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUI
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
}
void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched) const
void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched)
{
// We can always call to RequestItems, but this packet only goes out if there are actually
// items. Otherwise, we'll skip straight to the OfferReward
@@ -613,6 +652,8 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGU
return;
}
GetInteractionData().StartInteraction(npcGUID, PlayerInteractionType::QuestGiver);
WorldPackets::Quest::QuestGiverRequestItems packet;
packet.QuestTitle = quest->GetLogTitle();

View File

@@ -28,6 +28,7 @@ class Object;
class Quest;
class WorldSession;
struct GossipMenuItems;
enum class PlayerInteractionType : int32;
enum class QuestGiverStatus : uint64;
#define GOSSIP_MAX_MENU_ITEMS 32
@@ -260,7 +261,7 @@ private:
Optional<SystemTimePoint> _expireTime;
};
class InteractionData
class TC_GAME_API InteractionData
{
template <typename>
struct TaggedId
@@ -275,20 +276,23 @@ class InteractionData
using TrainerData = TaggedId<TrainerTag>;
public:
void Reset()
{
SourceGuid.Clear();
IsLaunchedByQuest = false;
_data.emplace<std::monostate>();
}
InteractionData();
InteractionData(InteractionData const& other);
InteractionData(InteractionData&& other) noexcept;
InteractionData& operator=(InteractionData const& other);
InteractionData& operator=(InteractionData&& other) noexcept;
~InteractionData();
void StartInteraction(ObjectGuid target, PlayerInteractionType type);
bool IsInteractingWith(ObjectGuid target, PlayerInteractionType type) const { return SourceGuid == target && Type == type; }
void Reset();
ObjectGuid SourceGuid;
PlayerInteractionType Type = { };
Optional<uint32> GetTrainerId() const { return std::holds_alternative<TrainerData>(_data) ? std::get<TrainerData>(_data).Id : Optional<uint32>(); }
void SetTrainerId(uint32 trainerId) { _data.emplace<TrainerData>(trainerId); }
TrainerData* GetTrainer() { return std::holds_alternative<TrainerData>(_data) ? &std::get<TrainerData>(_data) : nullptr; }
PlayerChoiceData* GetPlayerChoice() { return std::holds_alternative<PlayerChoiceData>(_data) ? &std::get<PlayerChoiceData>(_data) : nullptr; }
void SetPlayerChoice(uint32 choiceId) { _data.emplace<PlayerChoiceData>(choiceId); }
uint16 AddPlayerChoiceResponse(uint32 responseId)
{
@@ -336,10 +340,10 @@ class TC_GAME_API PlayerMenu
void SendQuestGiverQuestListMessage(Object* questgiver);
void SendQuestQueryResponse(Quest const* quest) const;
void SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup) const;
void SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup);
void SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched) const;
void SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched) const;
void SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched);
void SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched);
private:
GossipMenu _gossipMenu;

View File

@@ -9237,6 +9237,18 @@ void Player::SetBindPoint(ObjectGuid guid) const
void Player::SendRespecWipeConfirm(ObjectGuid const& guid, uint32 cost, SpecResetType respecType) const
{
switch (respecType)
{
case SPEC_RESET_TALENTS:
PlayerTalkClass->GetInteractionData().StartInteraction(guid, PlayerInteractionType::TalentMaster);
break;
case SPEC_RESET_SPECIALIZATION:
PlayerTalkClass->GetInteractionData().StartInteraction(guid, PlayerInteractionType::SpecializationMaster);
break;
default:
break;
}
WorldPackets::Talent::RespecWipeConfirm respecWipeConfirm;
respecWipeConfirm.RespecMaster = guid;
respecWipeConfirm.Cost = cost;
@@ -14148,7 +14160,10 @@ void Player::OnGossipSelect(WorldObject* source, int32 gossipOptionId, uint32 me
break;
case GossipOptionNpc::GuildBanker:
if (Guild* const guild = GetGuild())
{
PlayerTalkClass->GetInteractionData().StartInteraction(source->GetGUID(), PlayerInteractionType::GuildBanker);
guild->SendBankList(GetSession(), 0, true);
}
else
Guild::SendCommandResult(GetSession(), GUILD_COMMAND_VIEW_TAB, ERR_GUILD_PLAYER_NOT_IN_GUILD);
break;
@@ -14197,6 +14212,36 @@ void Player::OnGossipSelect(WorldObject* source, int32 gossipOptionId, uint32 me
if (!handled)
{
static constexpr std::array<PlayerInteractionType, AsUnderlyingType(GossipOptionNpc::Count)> GossipOptionNpcToInteractionType =
{
PlayerInteractionType::None, PlayerInteractionType::Vendor, PlayerInteractionType::TaxiNode,
PlayerInteractionType::Trainer, PlayerInteractionType::SpiritHealer, PlayerInteractionType::Binder,
PlayerInteractionType::Banker, PlayerInteractionType::PetitionVendor, PlayerInteractionType::GuildTabardVendor,
PlayerInteractionType::BattleMaster, PlayerInteractionType::Auctioneer, PlayerInteractionType::TalentMaster,
PlayerInteractionType::StableMaster, PlayerInteractionType::None, PlayerInteractionType::GuildBanker,
PlayerInteractionType::None, PlayerInteractionType::None, PlayerInteractionType::None,
PlayerInteractionType::MailInfo, PlayerInteractionType::None, PlayerInteractionType::LFGDungeon,
PlayerInteractionType::ArtifactForge, PlayerInteractionType::None, PlayerInteractionType::SpecializationMaster,
PlayerInteractionType::None, PlayerInteractionType::None, PlayerInteractionType::GarrArchitect,
PlayerInteractionType::GarrMission, PlayerInteractionType::ShipmentCrafter, PlayerInteractionType::GarrTradeskill,
PlayerInteractionType::GarrRecruitment, PlayerInteractionType::AdventureMap, PlayerInteractionType::GarrTalent,
PlayerInteractionType::ContributionCollector, PlayerInteractionType::Transmogrifier, PlayerInteractionType::AzeriteRespec,
PlayerInteractionType::IslandQueue, PlayerInteractionType::ItemInteraction, PlayerInteractionType::WorldMap,
PlayerInteractionType::Soulbind, PlayerInteractionType::ChromieTime, PlayerInteractionType::CovenantPreview,
PlayerInteractionType::LegendaryCrafting, PlayerInteractionType::NewPlayerGuide, PlayerInteractionType::LegendaryCrafting,
PlayerInteractionType::Renown, PlayerInteractionType::BlackMarketAuctioneer, PlayerInteractionType::PerksProgramVendor,
PlayerInteractionType::ProfessionsCraftingOrder, PlayerInteractionType::Professions, PlayerInteractionType::ProfessionsCustomerOrder,
PlayerInteractionType::TraitSystem, PlayerInteractionType::BarbersChoice, PlayerInteractionType::MajorFactionRenown,
PlayerInteractionType::PersonalTabardVendor, PlayerInteractionType::ForgeMaster, PlayerInteractionType::CharacterBanker,
PlayerInteractionType::AccountBanker, PlayerInteractionType::ProfessionRespec, PlayerInteractionType::PlaceholderType72,
PlayerInteractionType::PlaceholderType75, PlayerInteractionType::PlaceholderType76, PlayerInteractionType::GuildRename,
PlayerInteractionType::PlaceholderType77, PlayerInteractionType::ItemUpgrade
};
PlayerInteractionType interactionType = GossipOptionNpcToInteractionType[AsUnderlyingType(gossipOptionNpc)];
if (interactionType != PlayerInteractionType::None)
PlayerTalkClass->GetInteractionData().StartInteraction(source->GetGUID(), interactionType);
if (item->GossipNpcOptionID)
{
GossipMenuAddon const* addon = sObjectMgr->GetGossipMenuAddon(menuId);
@@ -14211,33 +14256,6 @@ void Player::OnGossipSelect(WorldObject* source, int32 gossipOptionId, uint32 me
}
else
{
static constexpr std::array<PlayerInteractionType, AsUnderlyingType(GossipOptionNpc::Count)> GossipOptionNpcToInteractionType =
{
PlayerInteractionType::None, PlayerInteractionType::Vendor, PlayerInteractionType::TaxiNode,
PlayerInteractionType::Trainer, PlayerInteractionType::SpiritHealer, PlayerInteractionType::Binder,
PlayerInteractionType::Banker, PlayerInteractionType::PetitionVendor, PlayerInteractionType::GuildTabardVendor,
PlayerInteractionType::BattleMaster, PlayerInteractionType::Auctioneer, PlayerInteractionType::TalentMaster,
PlayerInteractionType::StableMaster, PlayerInteractionType::None, PlayerInteractionType::GuildBanker,
PlayerInteractionType::None, PlayerInteractionType::None, PlayerInteractionType::None,
PlayerInteractionType::MailInfo, PlayerInteractionType::None, PlayerInteractionType::LFGDungeon,
PlayerInteractionType::ArtifactForge, PlayerInteractionType::None, PlayerInteractionType::SpecializationMaster,
PlayerInteractionType::None, PlayerInteractionType::None, PlayerInteractionType::GarrArchitect,
PlayerInteractionType::GarrMission, PlayerInteractionType::ShipmentCrafter, PlayerInteractionType::GarrTradeskill,
PlayerInteractionType::GarrRecruitment, PlayerInteractionType::AdventureMap, PlayerInteractionType::GarrTalent,
PlayerInteractionType::ContributionCollector, PlayerInteractionType::Transmogrifier, PlayerInteractionType::AzeriteRespec,
PlayerInteractionType::IslandQueue, PlayerInteractionType::ItemInteraction, PlayerInteractionType::WorldMap,
PlayerInteractionType::Soulbind, PlayerInteractionType::ChromieTime, PlayerInteractionType::CovenantPreview,
PlayerInteractionType::LegendaryCrafting, PlayerInteractionType::NewPlayerGuide, PlayerInteractionType::LegendaryCrafting,
PlayerInteractionType::Renown, PlayerInteractionType::BlackMarketAuctioneer, PlayerInteractionType::PerksProgramVendor,
PlayerInteractionType::ProfessionsCraftingOrder, PlayerInteractionType::Professions, PlayerInteractionType::ProfessionsCustomerOrder,
PlayerInteractionType::TraitSystem, PlayerInteractionType::BarbersChoice, PlayerInteractionType::MajorFactionRenown,
PlayerInteractionType::PersonalTabardVendor, PlayerInteractionType::ForgeMaster, PlayerInteractionType::CharacterBanker,
PlayerInteractionType::AccountBanker, PlayerInteractionType::ProfessionRespec, PlayerInteractionType::PlaceholderType72,
PlayerInteractionType::PlaceholderType75, PlayerInteractionType::PlaceholderType76, PlayerInteractionType::GuildRename,
PlayerInteractionType::PlaceholderType77, PlayerInteractionType::ItemUpgrade
};
PlayerInteractionType interactionType = GossipOptionNpcToInteractionType[AsUnderlyingType(gossipOptionNpc)];
if (interactionType != PlayerInteractionType::None)
{
WorldPackets::NPC::NPCInteractionOpenResult npcInteraction;
@@ -29978,9 +29996,8 @@ void Player::SendPlayerChoice(ObjectGuid sender, int32 choiceId)
LocaleConstant locale = GetSession()->GetSessionDbLocaleIndex();
PlayerChoiceLocale const* playerChoiceLocale = locale != DEFAULT_LOCALE ? sObjectMgr->GetPlayerChoiceLocale(choiceId) : nullptr;
PlayerTalkClass->GetInteractionData().Reset();
PlayerTalkClass->GetInteractionData().SourceGuid = sender;
PlayerTalkClass->GetInteractionData().SetPlayerChoice(choiceId);
PlayerTalkClass->GetInteractionData().StartInteraction(sender, PlayerInteractionType::PlayerChoice);
PlayerTalkClass->GetInteractionData().GetPlayerChoice()->SetChoiceId(choiceId);
WorldPackets::Quest::DisplayPlayerChoice displayPlayerChoice;
displayPlayerChoice.SenderGUID = sender;

View File

@@ -23,6 +23,7 @@
#include "Creature.h"
#include "DatabaseEnv.h"
#include "GameTime.h"
#include "GossipDef.h"
#include "Item.h"
#include "Language.h"
#include "Log.h"
@@ -1004,6 +1005,8 @@ void WorldSession::SendAuctionHello(ObjectGuid guid, Unit const* unit)
if (!ahEntry)
return;
GetPlayer()->PlayerTalkClass->GetInteractionData().StartInteraction(guid, PlayerInteractionType::Auctioneer);
WorldPackets::AuctionHouse::AuctionHelloResponse auctionHelloResponse;
auctionHelloResponse.Auctioneer = guid;
auctionHelloResponse.AuctionHouseID = ahEntry->ID;

View File

@@ -324,8 +324,8 @@ void WorldSession::HandleAutoStoreBankReagentOpcode(WorldPackets::Bank::AutoStor
void WorldSession::SendShowBank(ObjectGuid guid, PlayerInteractionType interactionType)
{
_player->PlayerTalkClass->GetInteractionData().Reset();
_player->PlayerTalkClass->GetInteractionData().SourceGuid = guid;
_player->PlayerTalkClass->GetInteractionData().StartInteraction(guid, interactionType);
WorldPackets::NPC::NPCInteractionOpenResult npcInteraction;
npcInteraction.Npc = guid;
npcInteraction.InteractionType = interactionType;

View File

@@ -19,6 +19,7 @@
#include "AchievementPackets.h"
#include "Common.h"
#include "GameTime.h"
#include "GossipDef.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "GuildPackets.h"
@@ -249,6 +250,8 @@ void WorldSession::HandleGuildBankActivate(WorldPackets::Guild::GuildBankActivat
return;
}
GetPlayer()->PlayerTalkClass->GetInteractionData().StartInteraction(packet.Banker, PlayerInteractionType::GuildBanker);
guild->SendBankList(this, 0, packet.FullUpdate);
}

View File

@@ -606,6 +606,8 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
GetPlayer()->PlayerTalkClass->GetInteractionData().StartInteraction(vendorGuid, PlayerInteractionType::Vendor);
// Stop the npc if moving
if (uint32 pause = vendor->GetMovementTemplate().GetInteractionPauseTimer())
vendor->PauseMovement(pause);

View File

@@ -576,8 +576,7 @@ void WorldSession::HandleGetMailList(WorldPackets::Mail::MailGetList& getList)
++response.TotalNumRecords;
}
player->PlayerTalkClass->GetInteractionData().Reset();
player->PlayerTalkClass->GetInteractionData().SourceGuid = getList.Mailbox;
player->PlayerTalkClass->GetInteractionData().StartInteraction(getList.Mailbox, PlayerInteractionType::MailInfo);
SendPacket(response.Write());
// recalculate m_nextMailDelivereTime and unReadMails

View File

@@ -122,9 +122,8 @@ void WorldSession::SendTrainerList(Creature* npc, uint32 trainerId)
return;
}
_player->PlayerTalkClass->GetInteractionData().Reset();
_player->PlayerTalkClass->GetInteractionData().SourceGuid = npc->GetGUID();
_player->PlayerTalkClass->GetInteractionData().SetTrainerId(trainerId);
_player->PlayerTalkClass->GetInteractionData().StartInteraction(npc->GetGUID(), PlayerInteractionType::Trainer);
_player->PlayerTalkClass->GetInteractionData().GetTrainer()->Id = trainerId;
trainer->SendSpells(npc, _player, GetSessionDbLocaleIndex());
}
@@ -143,10 +142,10 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPackets::NPC::TrainerBuySpel
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (_player->PlayerTalkClass->GetInteractionData().SourceGuid != packet.TrainerGUID)
if (!_player->PlayerTalkClass->GetInteractionData().IsInteractingWith(packet.TrainerGUID, PlayerInteractionType::Trainer))
return;
if (_player->PlayerTalkClass->GetInteractionData().GetTrainerId() != uint32(packet.TrainerID))
if (_player->PlayerTalkClass->GetInteractionData().GetTrainer()->Id != uint32(packet.TrainerID))
return;
Trainer::Trainer const* trainer = sObjectMgr->GetTrainer(packet.TrainerID);
@@ -198,7 +197,7 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPackets::NPC::GossipSelec
return;
// Prevent cheating on C++ scripted menus
if (_player->PlayerTalkClass->GetInteractionData().SourceGuid != packet.GossipUnit)
if (!_player->PlayerTalkClass->GetInteractionData().IsInteractingWith(packet.GossipUnit, PlayerInteractionType::Gossip))
return;
Creature* unit = nullptr;

View File

@@ -19,6 +19,7 @@
#include "Common.h"
#include "CharacterCache.h"
#include "DatabaseEnv.h"
#include "GossipDef.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "Item.h"
@@ -455,10 +456,10 @@ void WorldSession::SendPetitionShowList(ObjectGuid guid)
return;
}
GetPlayer()->PlayerTalkClass->GetInteractionData().StartInteraction(guid, PlayerInteractionType::PetitionVendor);
WorldPackets::Petition::ServerPetitionShowList packet;
packet.Unit = guid;
packet.Price = uint32(sWorld->getIntConfig(CONFIG_CHARTER_COST_GUILD));
SendPacket(packet.Write());
TC_LOG_DEBUG("network", "Sent SMSG_PETITION_SHOW_LIST");
}

View File

@@ -28,6 +28,7 @@
#include "GossipDef.h"
#include "Group.h"
#include "Log.h"
#include "Memory.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Player.h"
@@ -94,118 +95,103 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPackets::Quest::QuestG
else
object = ObjectAccessor::FindPlayer(packet.QuestGiverGUID);
auto CLOSE_GOSSIP_CLEAR_SHARING_INFO = ([this]()
auto CLOSE_GOSSIP_CLEAR_SHARING_INFO = Trinity::make_unique_ptr_with_deleter(_player, [](Player* player)
{
_player->PlayerTalkClass->SendCloseGossip();
_player->ClearQuestSharingInfo();
player->PlayerTalkClass->SendCloseGossip();
player->ClearQuestSharingInfo();
});
// no or incorrect quest giver
if (!object)
{
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
return;
}
if (Player* playerQuestObject = object->ToPlayer())
{
if ((_player->GetPlayerSharingQuest().IsEmpty() && _player->GetPlayerSharingQuest() != packet.QuestGiverGUID) || !playerQuestObject->CanShareQuest(packet.QuestID))
{
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
return;
}
if (!_player->IsInSameRaidWith(playerQuestObject))
{
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
return;
}
}
else
{
if (!object->hasQuest(packet.QuestID))
{
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
return;
}
}
// some kind of WPE protection
if (!_player->CanInteractWithQuestGiver(object))
{
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
return;
Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
if (!quest)
return;
// prevent cheating
if (!GetPlayer()->CanTakeQuest(quest, true))
return;
if (!_player->GetPlayerSharingQuest().IsEmpty())
{
Player* player = ObjectAccessor::FindPlayer(_player->GetPlayerSharingQuest());
if (player)
{
player->SendPushToPartyResponse(_player, QuestPushReason::Accepted);
_player->ClearQuestSharingInfo();
}
}
if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID))
if (!_player->CanAddQuest(quest, true))
return;
(void)CLOSE_GOSSIP_CLEAR_SHARING_INFO.release();
_player->AddQuestAndCheckCompletion(quest, object);
if (quest->IsPushedToPartyOnAccept())
{
// prevent cheating
if (!GetPlayer()->CanTakeQuest(quest, true))
if (Group* group = _player->GetGroup())
{
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
return;
}
if (!_player->GetPlayerSharingQuest().IsEmpty())
{
Player* player = ObjectAccessor::FindPlayer(_player->GetPlayerSharingQuest());
if (player)
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
player->SendPushToPartyResponse(_player, QuestPushReason::Accepted);
_player->ClearQuestSharingInfo();
}
}
Player* player = itr->GetSource();
if (_player->CanAddQuest(quest, true))
{
_player->AddQuestAndCheckCompletion(quest, object);
if (!player || player == _player || !player->IsInMap(_player)) // not self and in same map
continue;
if (quest->IsPushedToPartyOnAccept())
{
if (Group* group = _player->GetGroup())
if (player->CanTakeQuest(quest, true))
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* player = itr->GetSource();
player->SetQuestSharingInfo(_player->GetGUID(), quest->GetQuestId());
if (!player || player == _player || !player->IsInMap(_player)) // not self and in same map
continue;
// need confirmation that any gossip window will close
player->PlayerTalkClass->SendCloseGossip();
if (player->CanTakeQuest(quest, true))
{
player->SetQuestSharingInfo(_player->GetGUID(), quest->GetQuestId());
// need confirmation that any gossip window will close
player->PlayerTalkClass->SendCloseGossip();
_player->SendQuestConfirmAccept(quest, player);
}
}
_player->SendQuestConfirmAccept(quest, player);
}
}
if (quest->HasFlag(QUEST_FLAGS_LAUNCH_GOSSIP_ACCEPT) && !quest->HasFlagEx(QUEST_FLAGS_EX_SUPPRESS_GOSSIP_ACCEPT))
{
auto launchGossip = [&](WorldObject* worldObject)
{
_player->PlayerTalkClass->ClearMenus();
_player->PrepareGossipMenu(worldObject, _player->GetGossipMenuForSource(worldObject), true);
_player->SendPreparedGossip(worldObject);
_player->PlayerTalkClass->GetInteractionData().IsLaunchedByQuest = true;
};
if (Creature* creature = object->ToCreature())
launchGossip(creature);
else if (GameObject* go = object->ToGameObject())
launchGossip(go);
}
else
_player->PlayerTalkClass->SendCloseGossip();
return;
}
}
CLOSE_GOSSIP_CLEAR_SHARING_INFO();
if (quest->HasFlag(QUEST_FLAGS_LAUNCH_GOSSIP_ACCEPT) && !quest->HasFlagEx(QUEST_FLAGS_EX_SUPPRESS_GOSSIP_ACCEPT))
{
auto launchGossip = [&](WorldObject* worldObject)
{
_player->PlayerTalkClass->ClearMenus();
_player->PrepareGossipMenu(worldObject, _player->GetGossipMenuForSource(worldObject), true);
_player->SendPreparedGossip(worldObject);
_player->PlayerTalkClass->GetInteractionData().IsLaunchedByQuest = true;
};
if (Creature* creature = object->ToCreature())
launchGossip(creature);
else if (GameObject* go = object->ToGameObject())
launchGossip(go);
}
// do not close gossip if quest accept script started a new interaction
else if (!_player->PlayerTalkClass->GetInteractionData().IsInteractingWith(object->GetGUID(), PlayerInteractionType::QuestGiver))
_player->PlayerTalkClass->GetInteractionData().IsLaunchedByQuest = true;
else
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPackets::Quest::QuestGiverQueryQuest& packet)

View File

@@ -23,6 +23,7 @@
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "FlightPathMovementGenerator.h"
#include "GossipDef.h"
#include "Log.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
@@ -96,6 +97,8 @@ void WorldSession::SendTaxiMenu(Creature* unit)
if (!curloc)
return;
GetPlayer()->PlayerTalkClass->GetInteractionData().StartInteraction(unit->GetGUID(), PlayerInteractionType::TaxiNode);
bool lastTaxiCheaterState = GetPlayer()->isTaxiCheater();
if (unit->GetEntry() == 29480)
GetPlayer()->SetTaxiCheater(true); // Grimwing in Ebon Hold, special case. NOTE: Not perfect, Zul'Aman should not be included according to WoWhead, and I think taxicheat includes it.