TrinityCore
Loading...
Searching...
No Matches
QuestHandler.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 "WorldSession.h"
19#include "Battleground.h"
20#include "Common.h"
21#include "Creature.h"
22#include "CreatureAI.h"
23#include "DatabaseEnv.h"
24#include "DB2Stores.h"
25#include "GameObject.h"
26#include "GameObjectAI.h"
27#include "GameTime.h"
28#include "GossipDef.h"
29#include "Group.h"
30#include "Log.h"
31#include "Memory.h"
32#include "ObjectAccessor.h"
33#include "ObjectMgr.h"
34#include "Player.h"
35#include "PlayerChoice.h"
36#include "PoolMgr.h"
37#include "QuestDef.h"
38#include "QuestMgr.h"
39#include "QuestPackets.h"
40#include "QuestPools.h"
41#include "ReputationMgr.h"
42#include "ScriptMgr.h"
43#include "World.h"
44
46{
48 if (!questGiver)
49 {
50 TC_LOG_INFO("network", "Error in CMSG_QUESTGIVER_STATUS_QUERY, called for non-existing questgiver ({})", packet.QuestGiverGUID.ToString());
51 return;
52 }
53
54 QuestGiverStatus questStatus = _player->GetQuestDialogStatus(questGiver);
55
56 //inform client about status of quest
57 _player->PlayerTalkClass->SendQuestGiverStatus(questStatus, packet.QuestGiverGUID);
58}
59
61{
62 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_HELLO {}", packet.QuestGiverGUID.ToString());
63
65 if (!creature)
66 {
67 TC_LOG_DEBUG("network", "WORLD: HandleQuestgiverHelloOpcode - {} not found or you can't interact with him.",
68 packet.QuestGiverGUID.ToString());
69 return;
70 }
71
72 // remove fake death
73 if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
75
76 // Stop the npc if moving
77 if (uint32 pause = creature->GetMovementTemplate().GetInteractionPauseTimer())
78 creature->PauseMovement(pause);
79 creature->SetHomePosition(creature->GetPosition());
80
81 _player->PlayerTalkClass->ClearMenus();
82 if (creature->AI()->OnGossipHello(_player))
83 return;
84
85 _player->PrepareQuestMenu(creature->GetGUID());
86 _player->SendPreparedQuest(creature);
87}
88
90{
91 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST {}, quest = {}, startcheat = {}", packet.QuestGiverGUID.ToString(), packet.QuestID, packet.StartCheat);
92
93 Object* object;
94 if (!packet.QuestGiverGUID.IsPlayer())
96 else
98
99 auto CLOSE_GOSSIP_CLEAR_SHARING_INFO = Trinity::make_unique_ptr_with_deleter(_player, [](Player* player)
100 {
101 player->PlayerTalkClass->SendCloseGossip();
102 player->ClearQuestSharingInfo();
103 });
104
105 // no or incorrect quest giver
106 if (!object)
107 return;
108
109 if (Player* playerQuestObject = object->ToPlayer())
110 {
111 if ((_player->GetPlayerSharingQuest().IsEmpty() && _player->GetPlayerSharingQuest() != packet.QuestGiverGUID) || !playerQuestObject->CanShareQuest(packet.QuestID))
112 return;
113
114 if (!_player->IsInSameRaidWith(playerQuestObject))
115 return;
116 }
117 else
118 {
119 if (!object->hasQuest(packet.QuestID))
120 return;
121 }
122
123 // some kind of WPE protection
125 return;
126
127 Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
128 if (!quest)
129 return;
130
131 // prevent cheating
132 if (!GetPlayer()->CanTakeQuest(quest, true))
133 return;
134
136 {
138 if (player)
139 {
142 }
143 }
144
145 if (!_player->CanAddQuest(quest, true))
146 return;
147
148 (void)CLOSE_GOSSIP_CLEAR_SHARING_INFO.release();
149
150 _player->AddQuestAndCheckCompletion(quest, object);
151
152 if (quest->IsPushedToPartyOnAccept())
153 {
154 if (Group* group = _player->GetGroup())
155 {
156 for (GroupReference const& itr : group->GetMembers())
157 {
158 Player* player = itr.GetSource();
159
160 if (player == _player || !player->IsInMap(_player)) // not self and in same map
161 continue;
162
163 if (player->CanTakeQuest(quest, true))
164 {
165 player->SetQuestSharingInfo(_player->GetGUID(), quest->GetQuestId());
166
167 // need confirmation that any gossip window will close
168 player->PlayerTalkClass->SendCloseGossip();
169
170 _player->SendQuestConfirmAccept(quest, player);
171 }
172 }
173 }
174 }
175
177 {
178 auto launchGossip = [&](WorldObject* worldObject)
179 {
180 _player->PlayerTalkClass->ClearMenus();
181 _player->PrepareGossipMenu(worldObject, _player->GetGossipMenuForSource(worldObject), true);
182 _player->SendPreparedGossip(worldObject);
183 _player->PlayerTalkClass->GetInteractionData().IsLaunchedByQuest = true;
184 };
185
186 if (Creature* creature = object->ToCreature())
187 launchGossip(creature);
188 else if (GameObject* go = object->ToGameObject())
189 launchGossip(go);
190 }
191 // do not close gossip if quest accept script started a new interaction
192 else if (!_player->PlayerTalkClass->GetInteractionData().IsInteractingWith(object->GetGUID(), PlayerInteractionType::QuestGiver))
193 _player->PlayerTalkClass->GetInteractionData().IsLaunchedByQuest = true;
194 else
195 _player->PlayerTalkClass->SendCloseGossip();
196}
197
199{
200 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST QuestGiverGUID = {}, QuestID = {}, RespondToGiver = {}", packet.QuestGiverGUID.ToString(), packet.QuestID, packet.RespondToGiver);
201
202 // Verify that the guid is valid and is a questgiver or involved in the requested quest
204 if (!object || (!object->hasQuest(packet.QuestID) && !object->hasInvolvedQuest(packet.QuestID)))
205 {
206 _player->PlayerTalkClass->SendCloseGossip();
207 return;
208 }
209
210 if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID))
211 {
212 if (!_player->CanTakeQuest(quest, true))
213 return;
214
215 if (quest->IsAutoAccept() && _player->CanAddQuest(quest, true))
216 _player->AddQuestAndCheckCompletion(quest, object);
217
218 if (quest->IsTurnIn())
219 _player->PlayerTalkClass->SendQuestGiverRequestItems(quest, object->GetGUID(), _player->CanCompleteQuest(quest->GetQuestId()), true);
220 else
221 _player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, object->GetGUID(), true, false);
222 }
223}
224
226{
227 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_QUERY quest = {}", packet.QuestID);
228
229 if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID))
230 _player->PlayerTalkClass->SendQuestQueryResponse(quest);
231 else
232 {
234 response.QuestID = packet.QuestID;
235 SendPacket(response.Write());
236 }
237}
238
240{
241 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = {}, quest = {}, reward = {}",
242 packet.QuestGiverGUID.ToString(), packet.QuestID, packet.Choice.Item.ItemID);
243
244 Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
245 if (!quest)
246 return;
247
248 if (packet.Choice.Item.ItemID)
249 {
250 switch (packet.Choice.LootItemType)
251 {
253 {
254 ItemTemplate const* rewardProto = sObjectMgr->GetItemTemplate(packet.Choice.Item.ItemID);
255 if (!rewardProto)
256 {
257 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {} {} tried to get invalid reward item (Item Entry: {}) for quest {} (possible packet-hacking detected)",
258 _player->GetName(), _player->GetGUID().ToString(), packet.Choice.Item.ItemID, packet.QuestID);
259 return;
260 }
261
262 bool itemValid = false;
263 for (uint32 i = 0; i < quest->GetRewChoiceItemsCount(); ++i)
264 {
265 if (quest->RewardChoiceItemId[i] && quest->RewardChoiceItemType[i] == LootItemType::Item && quest->RewardChoiceItemId[i] == packet.Choice.Item.ItemID)
266 {
267 itemValid = true;
268 break;
269 }
270 }
271
272 if (!itemValid && quest->GetQuestPackageID())
273 {
274 if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItems(quest->GetQuestPackageID()))
275 {
276 for (QuestPackageItemEntry const* questPackageItem : *questPackageItems)
277 {
278 if (uint32(questPackageItem->ItemID) != packet.Choice.Item.ItemID)
279 continue;
280
281 if (_player->CanSelectQuestPackageItem(questPackageItem))
282 {
283 itemValid = true;
284 break;
285 }
286 }
287 }
288
289 if (!itemValid)
290 {
291 if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItemsFallback(quest->GetQuestPackageID()))
292 {
293 for (QuestPackageItemEntry const* questPackageItem : *questPackageItems)
294 {
295 if (uint32(questPackageItem->ItemID) != packet.Choice.Item.ItemID)
296 continue;
297
298 itemValid = true;
299 break;
300 }
301 }
302 }
303 }
304
305 if (!itemValid)
306 {
307 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {} {} tried to get reward item (Item Entry: {}) wich is not a reward for quest {} (possible packet-hacking detected)",
308 _player->GetName(), _player->GetGUID().ToString(), packet.Choice.Item.ItemID, packet.QuestID);
309 return;
310 }
311 break;
312 }
314 {
315 if (!sCurrencyTypesStore.HasRecord(packet.Choice.Item.ItemID))
316 {
317 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {} {} tried to get invalid reward currency (Currency ID: {}) for quest {} (possible packet-hacking detected)",
318 _player->GetName(), _player->GetGUID().ToString(), packet.Choice.Item.ItemID, packet.QuestID);
319 return;
320 }
321
322 bool currencyValid = false;
323 for (uint32 i = 0; i < quest->GetRewChoiceItemsCount(); ++i)
324 {
325 if (quest->RewardChoiceItemId[i] && quest->RewardChoiceItemType[i] == LootItemType::Currency && quest->RewardChoiceItemId[i] == packet.Choice.Item.ItemID)
326 {
327 currencyValid = true;
328 break;
329 }
330 }
331
332 if (!currencyValid)
333 {
334 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {} {} tried to get reward currency (Currency ID: {}) wich is not a reward for quest {} (possible packet-hacking detected)",
335 _player->GetName(), _player->GetGUID().ToString(), packet.Choice.Item.ItemID, packet.QuestID);
336 return;
337 }
338 break;
339 }
340 default:
341 break;
342 }
343 }
344
345 Object* object = _player;
346
348 {
350 if (!object || !object->hasInvolvedQuest(packet.QuestID))
351 return;
352
353 // some kind of WPE protection
355 return;
356 }
357
360 {
361 TC_LOG_ERROR("network", "Error in QUEST_STATUS_COMPLETE: player {} {} tried to complete quest {}, but is not allowed to do so (possible packet-hacking or high latency)",
362 _player->GetName(), _player->GetGUID().ToString(), packet.QuestID);
363 return;
364 }
365
366 if (_player->CanRewardQuest(quest, true)) // First, check if player is allowed to turn the quest in (all objectives completed). If not, we send players to the offer reward screen
367 {
368 if (_player->CanRewardQuest(quest, packet.Choice.LootItemType, packet.Choice.Item.ItemID, true)) // Then check if player can receive the reward item (if inventory is not full, if player doesn't have too many unique items, and so on). If not, the client will close the gossip window
369 {
370 _player->RewardQuest(quest, packet.Choice.LootItemType, packet.Choice.Item.ItemID, object);
371 }
372 }
373 else
374 _player->PlayerTalkClass->SendQuestGiverOfferReward(quest, packet.QuestGiverGUID, true);
375}
376
378{
379 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = {}, quest = {}", packet.QuestGiverGUID.ToString(), packet.QuestID);
380
381 Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
382 if (!quest)
383 return;
384
386 {
388 if (!object || !object->hasInvolvedQuest(packet.QuestID))
389 return;
390
391 // some kind of WPE protection
393 return;
394 }
395
396 if (_player->CanCompleteQuest(packet.QuestID))
398
400 return;
401
402 if (quest)
403 _player->PlayerTalkClass->SendQuestGiverOfferReward(quest, packet.QuestGiverGUID, true);
404}
405
407{
408 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = {}", packet.Entry);
409
410 if (packet.Entry < MAX_QUEST_LOG_SIZE)
411 {
412 if (uint32 questId = _player->GetQuestSlotQuestId(packet.Entry))
413 {
414 if (!_player->TakeQuestSourceItem(questId, true))
415 return; // can't un-equip some items, reject quest cancel
416
417 Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
418 QuestStatus oldStatus = _player->GetQuestStatus(questId);
419
420 _player->RemoveActiveQuest(questId);
421
422 if (quest)
423 {
425 for (QuestObjective const& objective : quest->Objectives)
426 if (_player->IsQuestObjectiveComplete(packet.Entry, quest, objective))
427 return;
428
429 if (quest->GetLimitTime())
430 _player->RemoveTimedQuest(questId);
431
432 if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
433 {
436 }
437 }
438
440 _player->TakeQuestSourceItem(questId, true); // remove quest src item from player
441 _player->AbandonQuest(questId); // remove all quest items player received before abandoning quest. Note, this does not remove normal drop items that happen to be quest requirements.
443
444 TC_LOG_INFO("network", "Player {} abandoned quest {}", _player->GetGUID().ToString(), questId);
445
446 if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled
447 {
448 // prepare Quest Tracker datas
450 stmt->setUInt32(0, questId);
451 stmt->setUInt64(1, _player->GetGUID().GetCounter());
452
453 // add to Quest Tracker
454 CharacterDatabase.Execute(stmt);
455 }
456
457 sScriptMgr->OnQuestStatusChange(_player, questId);
458
459 if (quest)
460 sScriptMgr->OnQuestStatusChange(_player, quest, oldStatus, QUEST_STATUS_NONE);
461 }
462
464 }
465}
466
468{
469 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT questId = {}", packet.QuestID);
470
471 if (_player->GetSharedQuestID() != uint32(packet.QuestID))
472 return;
473
475 Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
476 if (!quest)
477 return;
478
480 if (!originalPlayer)
481 return;
482
483 if (!_player->IsInSameRaidWith(originalPlayer))
484 return;
485
486 if (!originalPlayer->IsActiveQuest(packet.QuestID))
487 return;
488
489 if (!_player->CanTakeQuest(quest, true))
490 return;
491
492 if (!_player->CanAddQuest(quest, true))
493 return;
494
495 _player->AddQuestAndCheckCompletion(quest, nullptr); // NULL, this prevent DB script from duplicate running
496
497 if (quest->GetSrcSpell() > 0)
498 _player->CastSpell(_player, quest->GetSrcSpell(), true);
499}
500
502{
503 bool autoCompleteMode = packet.FromScript; // 0 - standart complete quest mode with npc, 1 - auto-complete mode
504
505 TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = {}, questId = {} self-complete: {}", packet.QuestGiverGUID.ToString(), packet.QuestID, autoCompleteMode ? 1 : 0);
506
507 Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
508 if (!quest)
509 return;
510
511 Object* object = nullptr;
512 if (autoCompleteMode)
513 object = _player;
514 else
516
517 if (!object)
518 return;
519
520 if (!quest->HasFlag(QUEST_FLAGS_AUTO_COMPLETE))
521 {
522 if (!object->hasInvolvedQuest(packet.QuestID))
523 return;
524
525 // some kind of WPE protection
527 return;
528 }
529 else
530 {
531 // Do not allow completing quests on other players.
532 if (packet.QuestGiverGUID != _player->GetGUID())
533 return;
534 }
535
537 {
538 TC_LOG_ERROR("entities.player.cheat", "Possible hacking attempt: Player {} [{}] tried to complete quest [entry: {}] without being in possession of the quest!",
539 _player->GetName(), _player->GetGUID().ToString(), packet.QuestID);
540 return;
541 }
542
544 {
545 if (quest->IsRepeatable())
546 _player->PlayerTalkClass->SendQuestGiverRequestItems(quest, packet.QuestGiverGUID, _player->CanCompleteRepeatableQuest(quest), false);
547 else
548 _player->PlayerTalkClass->SendQuestGiverRequestItems(quest, packet.QuestGiverGUID, _player->CanRewardQuest(quest, false), false);
549 }
550 else
551 {
552 if (quest->HasQuestObjectiveType(QUEST_OBJECTIVE_ITEM)) // some items required
553 _player->PlayerTalkClass->SendQuestGiverRequestItems(quest, packet.QuestGiverGUID, _player->CanRewardQuest(quest, false), false);
554 else // no items required
555 _player->PlayerTalkClass->SendQuestGiverOfferReward(quest, packet.QuestGiverGUID, true);
556 }
557}
558
560{
561 if (_player->FindQuestSlot(questGiverCloseQuest.QuestID) >= MAX_QUEST_LOG_SIZE)
562 return;
563
564 Quest const* quest = sObjectMgr->GetQuestTemplate(questGiverCloseQuest.QuestID);
565 if (!quest)
566 return;
567
568 sScriptMgr->OnQuestAcknowledgeAutoAccept(_player, quest);
569}
570
572{
573 Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
574 if (!quest)
575 return;
576
577 Player* const sender = GetPlayer();
578
579 if (!_player->CanShareQuest(packet.QuestID))
580 {
582 return;
583 }
584
585 // in pool and not currently available (wintergrasp weekly, dalaran weekly) - can't share
586 if (!sQuestPoolMgr->IsQuestActive(packet.QuestID))
587 {
589 return;
590 }
591
592 Group* group = sender->GetGroup();
593 if (!group)
594 {
596 return;
597 }
598
599 for (GroupReference const& itr : group->GetMembers())
600 {
601 Player* receiver = itr.GetSource();
602
603 if (receiver == sender)
604 continue;
605
606 if (!receiver->GetPlayerSharingQuest().IsEmpty())
607 {
609 continue;
610 }
611
612 if (!receiver->IsAlive())
613 {
616 continue;
617 }
618
619 switch (receiver->GetQuestStatus(packet.QuestID))
620 {
622 {
625 continue;
626 }
629 {
632 continue;
633 }
634 default:
635 break;
636 }
637
638 if (!receiver->SatisfyQuestLog(false))
639 {
642 continue;
643 }
644
645 if (!receiver->SatisfyQuestDay(quest, false))
646 {
649 continue;
650 }
651
652 if (!receiver->SatisfyQuestMinLevel(quest, false))
653 {
656 continue;
657 }
658
659 if (!receiver->SatisfyQuestMaxLevel(quest, false))
660 {
663 continue;
664 }
665
666 if (!receiver->SatisfyQuestClass(quest, false))
667 {
670 continue;
671 }
672
673 if (!receiver->SatisfyQuestRace(quest, false))
674 {
677 continue;
678 }
679
680 if (!receiver->SatisfyQuestMinReputation(quest, false))
681 {
684 continue;
685 }
686
687 if (!receiver->SatisfyQuestMaxReputation(quest, false))
688 {
691 continue;
692 }
693
694 if (!receiver->SatisfyQuestDependentQuests(quest, false))
695 {
698 continue;
699 }
700
701 if (!receiver->SatisfyQuestExpansion(quest, false))
702 {
705 continue;
706 }
707
708 if (!receiver->CanTakeQuest(quest, false))
709 {
712 continue;
713 }
714
716
717 if (quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
718 receiver->PlayerTalkClass->SendQuestGiverRequestItems(quest, sender->GetGUID(), receiver->CanCompleteRepeatableQuest(quest), true);
719 else
720 {
721 receiver->SetQuestSharingInfo(sender->GetGUID(), quest->GetQuestId());
722 receiver->PlayerTalkClass->SendQuestGiverQuestDetails(quest, receiver->GetGUID(), true, false);
723 if (quest->IsAutoAccept() && receiver->CanAddQuest(quest, true) && receiver->CanTakeQuest(quest, true))
724 {
725 receiver->AddQuestAndCheckCompletion(quest, sender);
727 receiver->ClearQuestSharingInfo();
728 }
729 }
730 }
731}
732
734{
736 {
739 player->SendPushToPartyResponse(_player, static_cast<QuestPushReason>(packet.Result));
740
742 }
743}
744
749
751{
753
755 //response.WorldQuestUpdates.push_back(WorldPackets::Quest::WorldQuestUpdateInfo(lastUpdate, questID, timer, variableID, value));
756
757 SendPacket(response.Write());
758}
759
761{
762 PlayerChoiceData const* playerChoiceData = _player->PlayerTalkClass->GetInteractionData().GetPlayerChoice();
763 if (!playerChoiceData)
764 {
765 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to invalid player choice {} (none allowed)",
766 GetPlayerInfo(), choiceResponse.ChoiceID);
767 return;
768 }
769
770 if (playerChoiceData->GetChoiceId() != uint32(choiceResponse.ChoiceID))
771 {
772 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to invalid player choice {} ({} allowed)",
773 GetPlayerInfo(), choiceResponse.ChoiceID, playerChoiceData->GetChoiceId());
774 return;
775 }
776
777 if (playerChoiceData->GetExpireTime() && playerChoiceData->GetExpireTime() < GameTime::GetSystemTime())
778 {
779 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to expired player choice {})",
780 GetPlayerInfo(), choiceResponse.ChoiceID);
781 return;
782 }
783
784 Optional<uint32> responseId = playerChoiceData->FindIdByClientIdentifier(choiceResponse.ResponseIdentifier);
785 if (!responseId)
786 {
787 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select invalid player choice response identifier {}",
788 GetPlayerInfo(), choiceResponse.ResponseIdentifier);
789 return;
790 }
791
792 PlayerChoice const* playerChoice = sObjectMgr->GetPlayerChoice(choiceResponse.ChoiceID);
793 if (!playerChoice)
794 return;
795
796 PlayerChoiceResponse const* playerChoiceResponse = playerChoice->GetResponse(*responseId);
797 if (!playerChoiceResponse)
798 {
799 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select invalid player choice response {}",
800 GetPlayerInfo(), *responseId);
801 return;
802 }
803
805 {
806 TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select disabled player choice response {}",
807 GetPlayerInfo(), *responseId);
808 return;
809 }
810
811 sScriptMgr->OnPlayerChoiceResponse(ObjectAccessor::GetWorldObject(*_player, _player->PlayerTalkClass->GetInteractionData().SourceGuid), _player,
812 playerChoice, playerChoiceResponse, choiceResponse.ResponseIdentifier);
813}
814
816{
817 UiMapEntry const* uiMap = sUiMapStore.LookupEntry(uiMapQuestLinesRequest.UiMapID);
818 if (!uiMap)
819 return;
820
822 response.UiMapID = uiMap->ID;
823
824 if (std::vector<uint32> const* questLines = sObjectMgr->GetUiMapQuestLinesList(uiMap->ID))
825 {
826 for (uint32 questLineId : *questLines)
827 {
828 std::span<QuestLineXQuestEntry const* const> questLineQuests = QuestMgr::GetQuestsForQuestLine(questLineId);
829 if (questLineQuests.empty())
830 continue;
831
832 bool isQuestLineCompleted = true;
833 for (QuestLineXQuestEntry const* questLineQuest : questLineQuests)
834 {
835 if (Quest const* quest = sObjectMgr->GetQuestTemplate(questLineQuest->QuestID))
836 {
837 if (_player->CanTakeQuest(quest, false))
838 response.QuestLineXQuestIDs.push_back(questLineQuest->ID);
839
840 if (isQuestLineCompleted && !_player->GetQuestRewardStatus(questLineQuest->QuestID))
841 isQuestLineCompleted = false;
842 }
843 }
844
845 if (!isQuestLineCompleted)
846 response.QuestLineIDs.push_back(questLineId);
847 }
848 }
849
850 if (std::vector<uint32> const* quests = sObjectMgr->GetUiMapQuestsList(uiMap->ID))
851 {
852 for (uint32 questId : *quests)
853 if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
854 if (_player->CanTakeQuest(quest, false))
855 response.QuestIDs.push_back(questId);
856 }
857
858 SendPacket(response.Write());
859}
860
862{
864
865 auto spawnTypeForObjectType = [](TypeMask objectTypeMask) -> SpawnObjectType
866 {
867 if (objectTypeMask & TYPEMASK_UNIT)
868 return SPAWN_TYPE_CREATURE;
869 if (objectTypeMask & TYPEMASK_GAMEOBJECT)
871
872 return NUM_SPAWN_TYPES;
873 };
874
875 for (WorldPackets::Quest::SpawnTrackingRequestInfo const& requestInfo : spawnTrackingUpdate.SpawnTrackingRequests)
876 {
877 WorldPackets::Quest::SpawnTrackingResponseInfo& responseInfo = response.SpawnTrackingResponses.emplace_back();
878 responseInfo.SpawnTrackingID = requestInfo.SpawnTrackingID;
879 responseInfo.ObjectID = requestInfo.ObjectID;
880
881 SpawnTrackingTemplateData const* spawnTrackingTemplateData = sObjectMgr->GetSpawnTrackingData(requestInfo.SpawnTrackingID);
882 QuestObjective const* activeQuestObjective = _player->GetActiveQuestObjectiveForSpawnTracking(requestInfo.SpawnTrackingID);
883
884 // Send phase info if map is the same or spawn tracking related quests are taken or completed
885 if (spawnTrackingTemplateData && (_player->GetMapId() == spawnTrackingTemplateData->MapId || activeQuestObjective))
886 {
887 responseInfo.PhaseID = spawnTrackingTemplateData->PhaseId;
888 responseInfo.PhaseGroupID = spawnTrackingTemplateData->PhaseGroup;
889 responseInfo.PhaseUseFlags = spawnTrackingTemplateData->PhaseUseFlags;
890
891 // Send spawn visibility data if available
892 SpawnObjectType spawnType = spawnTypeForObjectType(TypeMask(requestInfo.ObjectTypeMask));
893 if (spawnType != NUM_SPAWN_TYPES)
894 {
895 // There should only be one entity
896 for (auto const& [spawnTrackingId, data] : sObjectMgr->GetSpawnMetadataForSpawnTracking(requestInfo.SpawnTrackingID))
897 {
898 SpawnData const* spawnData = data->ToSpawnData();
899 if (!spawnData)
900 continue;
901
902 if (spawnData->id != uint32(requestInfo.ObjectID))
903 continue;
904
905 if (spawnType != data->type)
906 continue;
907
908 if (activeQuestObjective)
909 {
910 SpawnTrackingState state = _player->GetSpawnTrackingStateByObjectives(spawnTrackingId, data->spawnTrackingQuestObjectives);
911 responseInfo.Visible = data->spawnTrackingStates[AsUnderlyingType(state)].Visible;
912 break;
913 }
914 }
915 }
916 }
917 }
918
919 SendPacket(response.Write());
920}
@ CHAR_UPD_QUEST_TRACK_ABANDON_TIME
DB2Storage< UiMapEntry > sUiMapStore("UiMap.db2", &UiMapLoadInfo::Instance)
DB2Storage< CurrencyTypesEntry > sCurrencyTypesStore("CurrencyTypes.db2", &CurrencyTypesLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:569
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint32_t uint32
Definition Define.h:154
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
TypeMask
Definition ObjectGuid.h:61
@ TYPEMASK_ITEM
Definition ObjectGuid.h:63
@ TYPEMASK_UNIT
Definition ObjectGuid.h:67
@ TYPEMASK_GAMEOBJECT
Definition ObjectGuid.h:70
#define sObjectMgr
Definition ObjectMgr.h:1885
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
QuestPushReason
Definition QuestDef.h:78
@ QUEST_OBJECTIVE_ITEM
Definition QuestDef.h:359
QuestGiverStatus
Definition QuestDef.h:158
#define MAX_QUEST_LOG_SIZE
Definition QuestDef.h:46
@ QUEST_FLAGS_EX_SUPPRESS_GOSSIP_ACCEPT
Definition QuestDef.h:258
@ QUEST_FLAGS_EX_NO_ABANDON_ONCE_BEGUN
Definition QuestDef.h:267
QuestStatus
Definition QuestDef.h:146
@ QUEST_STATUS_REWARDED
Definition QuestDef.h:153
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:150
@ QUEST_STATUS_NONE
Definition QuestDef.h:147
@ QUEST_STATUS_COMPLETE
Definition QuestDef.h:148
@ QUEST_FLAGS_FLAGS_PVP
Definition QuestDef.h:231
@ QUEST_FLAGS_AUTO_COMPLETE
Definition QuestDef.h:234
@ QUEST_FLAGS_LAUNCH_GOSSIP_ACCEPT
Definition QuestDef.h:247
#define sQuestPoolMgr
Definition QuestPools.h:68
#define sScriptMgr
Definition ScriptMgr.h:1449
SpawnObjectType
Definition SpawnData.h:35
@ SPAWN_TYPE_GAMEOBJECT
Definition SpawnData.h:37
@ NUM_SPAWN_TYPES
Definition SpawnData.h:40
@ SPAWN_TYPE_CREATURE
Definition SpawnData.h:36
SpawnTrackingState
Definition SpawnData.h:94
@ SPELL_AURA_FEIGN_DEATH
@ UNIT_NPC_FLAG_QUESTGIVER
@ UNIT_NPC_FLAG_2_NONE
@ UNIT_STATE_DIED
Definition Unit.h:261
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
virtual bool OnGossipHello(Player *)
Definition CreatureAI.h:206
void SetHomePosition(float x, float y, float z, float o)
Definition Creature.h:386
CreatureMovementData const & GetMovementTemplate() const
CreatureAI * AI() const
Definition Creature.h:228
constexpr bool HasFlag(T flag) const
Definition EnumFlag.h:106
Definition Group.h:205
GroupRefManager & GetMembers()
Definition Group.h:332
LowType GetCounter() const
Definition ObjectGuid.h:336
bool IsEmpty() const
Definition ObjectGuid.h:362
bool IsPlayer() const
Definition ObjectGuid.h:369
std::string ToString() const
Player * ToPlayer()
Definition Object.h:126
virtual bool hasQuest(uint32) const
Definition Object.h:103
GameObject * ToGameObject()
Definition Object.h:131
Creature * ToCreature()
Definition Object.h:121
virtual bool hasInvolvedQuest(uint32) const
Definition Object.h:104
Optional< uint32 > FindIdByClientIdentifier(uint16 clientIdentifier) const
uint32 GetChoiceId() const
Definition GossipDef.h:245
Optional< SystemTimePoint > GetExpireTime() const
Definition GossipDef.h:251
void SendQuestConfirmAccept(Quest const *quest, Player *receiver) const
Definition Player.cpp:17389
bool CanInteractWithQuestGiver(Object *questGiver) const
Definition Player.cpp:1885
bool HasPvPForcingQuest() const
Definition Player.cpp:17520
bool IsInSameRaidWith(Player const *p) const
Definition Player.cpp:2149
bool CanCompleteQuest(uint32 quest_id, uint32 ignoredQuestObjectiveId=0)
Definition Player.cpp:14558
Creature * GetNPCIfCanInteractWith(ObjectGuid const &guid, NPCFlags npcFlags, NPCFlags2 npcFlags2) const
Definition Player.cpp:1903
void DespawnPersonalSummonsForQuest(uint32 questId)
Definition Player.cpp:16314
bool GetQuestRewardStatus(uint32 quest_id) const
Definition Player.cpp:15945
uint16 FindQuestSlot(uint32 quest_id) const
Definition Player.cpp:16383
void UpdatePvPState(bool onlyFFA=false)
Definition Player.cpp:24066
bool SatisfyQuestMinLevel(Quest const *qInfo, bool msg) const
Definition Player.cpp:15457
bool SatisfyQuestMinReputation(Quest const *qInfo, bool msg) const
Definition Player.cpp:15676
void SendForceSpawnTrackingUpdate(uint32 questId) const
Definition Player.cpp:17539
void SendPreparedGossip(WorldObject *source)
Definition Player.cpp:14028
bool CanSeeStartQuest(Quest const *quest) const
Definition Player.cpp:14508
bool SatisfyQuestDay(Quest const *qInfo, bool msg) const
Definition Player.cpp:15817
QuestGiverStatus GetQuestDialogStatus(Object const *questGiver) const
Definition Player.cpp:16129
bool CanAddQuest(Quest const *quest, bool msg) const
Definition Player.cpp:14534
bool CanRewardQuest(Quest const *quest, bool msg) const
Definition Player.cpp:14619
void CompleteQuest(uint32 quest_id)
Definition Player.cpp:14936
uint32 GetSharedQuestID() const
Definition Player.h:1834
bool SatisfyQuestLog(bool msg) const
Definition Player.cpp:15487
uint32 GetQuestSlotQuestId(uint16 slot) const
Definition Player.cpp:16389
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition Player.cpp:27588
bool CanShareQuest(uint32 questId) const
Definition Player.cpp:15976
bool SatisfyQuestRace(Quest const *qInfo, bool msg) const
Definition Player.cpp:15660
bool SatisfyQuestClass(Quest const *qInfo, bool msg) const
Definition Player.cpp:15638
QuestObjective const * GetActiveQuestObjectiveForSpawnTracking(uint32 spawnTrackingId) const
Definition Player.cpp:17549
bool SatisfyQuestMaxLevel(Quest const *qInfo, bool msg) const
Definition Player.cpp:15472
bool IsActiveQuest(uint32 quest_id) const
Definition Player.cpp:14477
PvPInfo pvpInfo
Definition Player.h:2124
void PrepareQuestMenu(ObjectGuid guid)
Definition Player.cpp:14376
bool SatisfyQuestExpansion(Quest const *qInfo, bool msg) const
Definition Player.cpp:15855
void AddQuestAndCheckCompletion(Quest const *quest, Object *questGiver)
Definition Player.cpp:14676
void SetQuestSharingInfo(ObjectGuid guid, uint32 id)
Definition Player.h:1836
void ClearQuestSharingInfo()
Definition Player.h:1837
bool CanTakeQuest(Quest const *quest, bool msg) const
Definition Player.cpp:14522
bool TakeQuestSourceItem(uint32 questId, bool msg)
Definition Player.cpp:15911
bool IsQuestObjectiveComplete(uint16 slot, Quest const *quest, QuestObjective const &objective) const
Definition Player.cpp:17208
void RemoveActiveQuest(uint32 questId, bool update=true)
Definition Player.cpp:16005
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15962
bool CanCompleteRepeatableQuest(Quest const *quest)
Definition Player.cpp:14605
bool SatisfyQuestDependentQuests(Quest const *qInfo, bool msg) const
Definition Player.cpp:15502
bool CanSelectQuestPackageItem(QuestPackageItemEntry const *questPackageItem) const
Definition Player.cpp:14993
void SendPushToPartyResponse(Player const *player, QuestPushReason reason, Quest const *quest=nullptr) const
Definition Player.cpp:17410
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2796
bool SatisfyQuestMaxReputation(Quest const *qInfo, bool msg) const
Definition Player.cpp:15693
void PrepareGossipMenu(WorldObject *source, uint32 menuId, bool showQuests=false)
Definition Player.cpp:13927
void AbandonQuest(uint32 quest_id)
Definition Player.cpp:15409
ObjectGuid GetPlayerSharingQuest() const
Definition Player.h:1835
void RemoveTimedQuest(uint32 questId)
Definition Player.cpp:17515
void SendPreparedQuest(WorldObject *source)
Definition Player.cpp:14436
SpawnTrackingState GetSpawnTrackingStateByObjectives(uint32 spawnTrackingId, std::vector< uint32 > const &questObjectives) const
Definition Player.cpp:17559
void SendQuestGiverStatusMultiple()
Definition Player.cpp:17480
std::unique_ptr< PlayerMenu > PlayerTalkClass
Definition Player.h:2570
void RewardQuest(Quest const *quest, LootItemType rewardType, uint32 rewardId, Object *questGiver, bool announce=true)
Definition Player.cpp:15061
uint32 GetGossipMenuForSource(WorldObject const *source) const
Definition Player.cpp:14291
void setUInt32(uint8 index, uint32 value)
void setUInt64(uint8 index, uint64 value)
bool IsTurnIn() const
Definition QuestDef.cpp:593
std::array< LootItemType, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemType
Definition QuestDef.h:747
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemId
Definition QuestDef.h:748
uint32 GetQuestPackageID() const
Definition QuestDef.h:639
QuestObjectives Objectives
Definition QuestDef.h:757
bool IsPushedToPartyOnAccept() const
Definition QuestDef.h:736
uint32 GetSrcSpell() const
Definition QuestDef.h:665
bool IsRepeatable() const
Definition QuestDef.h:705
int64 GetLimitTime() const
Definition QuestDef.h:653
uint32 GetQuestId() const
Definition QuestDef.h:637
bool IsAutoAccept() const
Definition QuestDef.cpp:588
uint32 GetRewChoiceItemsCount() const
Definition QuestDef.h:763
bool IsDailyOrWeekly() const
Definition QuestDef.h:732
bool HasFlagEx(QuestFlagsEx flag) const
Definition QuestDef.h:620
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:619
void RemoveAurasByType(AuraType auraType, std::function< bool(AuraApplication const *)> const &check, AuraRemoveMode removeMode=AURA_REMOVE_BY_DEFAULT)
Definition Unit.cpp:3955
void PauseMovement(uint32 timer=0, uint8 slot=0, bool forced=true)
Definition Unit.cpp:10695
bool IsAlive() const
Definition Unit.h:1185
constexpr uint32 GetMapId() const
Definition Position.h:216
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2217
std::string const & GetName() const
Definition Object.h:342
bool IsInMap(WorldObject const *obj) const
Definition Object.cpp:469
WorldPacket const * Write() override
WorldPacket const * Write() override
std::vector< SpawnTrackingResponseInfo > SpawnTrackingResponses
std::vector< SpawnTrackingRequestInfo > SpawnTrackingRequests
void HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemoveQuest &packet)
void HandleRequestWorldQuestUpdate(WorldPackets::Quest::RequestWorldQuestUpdate &packet)
void HandleUiMapQuestLinesRequest(WorldPackets::Quest::UiMapQuestLinesRequest &uiMapQuestLinesRequest)
void HandleQuestgiverRequestRewardOpcode(WorldPackets::Quest::QuestGiverRequestReward &packet)
void HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty &packet)
void HandleSpawnTrackingUpdate(WorldPackets::Quest::SpawnTrackingUpdate &spawnTrackingUpdate)
void HandleQuestgiverStatusMultipleQuery(WorldPackets::Quest::QuestGiverStatusMultipleQuery &packet)
void HandleQuestgiverAcceptQuestOpcode(WorldPackets::Quest::QuestGiverAcceptQuest &packet)
void HandleQuestgiverHelloOpcode(WorldPackets::Quest::QuestGiverHello &packet)
std::string GetPlayerInfo() const
void HandleQuestQueryOpcode(WorldPackets::Quest::QueryQuestInfo &packet)
void HandleQuestPushResult(WorldPackets::Quest::QuestPushResult &packet)
Player * GetPlayer() const
void HandleQuestgiverStatusQueryOpcode(WorldPackets::Quest::QuestGiverStatusQuery &packet)
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
void HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceResponse const &choiceResponse)
Player * _player
void HandleQuestgiverChooseRewardOpcode(WorldPackets::Quest::QuestGiverChooseReward &packet)
void HandleQuestgiverCompleteQuest(WorldPackets::Quest::QuestGiverCompleteQuest &packet)
void HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAccept &packet)
void HandleQuestgiverQueryQuestOpcode(WorldPackets::Quest::QuestGiverQueryQuest &packet)
void HandleQuestgiverCloseQuest(WorldPackets::Quest::QuestGiverCloseQuest &questGiverCloseQuest)
#define sWorld
Definition World.h:916
@ CONFIG_QUEST_ENABLE_QUEST_TRACKER
Definition World.h:167
SystemTimePoint GetSystemTime()
Current chrono system_clock time point.
Definition GameTime.cpp:62
TC_GAME_API WorldObject * GetWorldObject(WorldObject const &, ObjectGuid const &)
TC_GAME_API Object * GetObjectByTypeMask(WorldObject const &, ObjectGuid const &, uint32 typemask)
TC_GAME_API Player * FindPlayer(ObjectGuid const &)
TC_GAME_API std::span< QuestLineXQuestEntry const *const > GetQuestsForQuestLine(uint32 questLineId)
Definition QuestMgr.cpp:91
std::unique_ptr< T, Impl::stateful_unique_ptr_deleter< Ptr, Del > > make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
Definition Memory.h:133
uint32 GetInteractionPauseTimer() const
EnumFlag< PlayerChoiceResponseFlags > Flags
PlayerChoiceResponse const * GetResponse(int32 responseId) const
constexpr void GetPosition(float &x, float &y) const
Definition Position.h:92
bool IsHostile
Definition Player.h:441
bool IsInHostileArea
Definition Player.h:442
uint32 id
Definition SpawnData.h:135
SpawnData const * ToSpawnData() const
Definition SpawnData.h:118