TrinityCore
Loading...
Searching...
No Matches
GossipDef.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 "GossipDef.h"
19#include "Containers.h"
20#include "Creature.h"
21#include "DB2Stores.h"
22#include "Log.h"
23#include "NPCPackets.h"
24#include "ObjectAccessor.h"
25#include "ObjectMgr.h"
26#include "Player.h"
27#include "QuestDef.h"
28#include "QuestPackets.h"
29#include "SpellInfo.h"
30#include "SpellMgr.h"
31#include "Util.h"
32#include "World.h"
33#include "WorldSession.h"
34
40
41GossipMenu::~GossipMenu() = default;
42
43uint32 GossipMenu::AddMenuItem(int32 gossipOptionId, int32 orderIndex, GossipOptionNpc optionNpc, std::string optionText, uint32 language,
44 GossipOptionFlags flags, Optional<int32> gossipNpcOptionId, uint32 actionMenuId, uint32 actionPoiId, bool boxCoded, uint64 boxMoney,
45 std::string boxText, Optional<int32> spellId, Optional<int32> overrideIconId, uint32 sender, uint32 action)
46{
48
49 // Find a free new id - script case
50 if (orderIndex == -1)
51 {
52 orderIndex = 0;
53 if (_menuId)
54 {
55 // set baseline orderIndex as higher than whatever exists in db
56 Trinity::IteratorPair bounds = sObjectMgr->GetGossipMenuItemsMapBounds(_menuId);
57 auto itr = std::ranges::max_element(bounds, std::ranges::less(),
58 [](GossipMenuItemsContainer::value_type const& a) { return a.second.OrderIndex; });
59
60 if (itr != bounds.end())
61 orderIndex = itr->second.OrderIndex + 1;
62 }
63
64 for (GossipMenuItem const& _menuItem : _menuItems)
65 {
66 if (int32(_menuItem.OrderIndex) > orderIndex)
67 break;
68
69 orderIndex = _menuItem.OrderIndex + 1;
70 }
71 }
72
73 if (!gossipOptionId)
74 gossipOptionId = -(int32(_menuId) * 100 + orderIndex);
75
76 auto where = std::ranges::lower_bound(_menuItems, uint32(orderIndex), std::ranges::less(), &GossipMenuItem::OrderIndex);
77
78 GossipMenuItem& menuItem = *_menuItems.emplace(where);
79 menuItem.GossipOptionID = gossipOptionId;
80 menuItem.OrderIndex = orderIndex;
81 menuItem.OptionNpc = optionNpc;
82 menuItem.OptionText = std::move(optionText);
83 menuItem.Language = language;
84 menuItem.Flags = flags;
85 menuItem.GossipNpcOptionID = gossipNpcOptionId;
86 menuItem.BoxCoded = boxCoded;
87 menuItem.BoxMoney = boxMoney;
88 menuItem.BoxText = std::move(boxText);
89 menuItem.SpellID = spellId;
90 menuItem.OverrideIconID = overrideIconId;
91 menuItem.ActionMenuID = actionMenuId;
92 menuItem.ActionPoiID = actionPoiId;
93 menuItem.Sender = sender;
94 menuItem.Action = action;
95 return orderIndex;
96}
97
106void GossipMenu::AddMenuItem(uint32 menuId, uint32 menuItemId, uint32 sender, uint32 action)
107{
109 Trinity::IteratorPair bounds = sObjectMgr->GetGossipMenuItemsMapBounds(menuId);
110
112 auto itr = std::ranges::find(bounds, menuItemId,
113 [](std::pair<uint32 const, GossipMenuItems> const& itemPair) { return itemPair.second.OrderIndex; });
114
115 if (itr == bounds.end())
116 return;
117
118 AddMenuItem(itr->second, sender, action);
119}
120
121void GossipMenu::AddMenuItem(GossipMenuItems const& menuItem, uint32 sender, uint32 action)
122{
124 std::string strOptionText, strBoxText;
125 BroadcastTextEntry const* optionBroadcastText = sBroadcastTextStore.LookupEntry(menuItem.OptionBroadcastTextID);
126 BroadcastTextEntry const* boxBroadcastText = sBroadcastTextStore.LookupEntry(menuItem.BoxBroadcastTextID);
127
129 if (optionBroadcastText)
130 strOptionText = DB2Manager::GetBroadcastTextValue(optionBroadcastText, GetLocale());
131 else
132 {
133 strOptionText = menuItem.OptionText;
134
136 if (GetLocale() != LOCALE_enUS)
137 if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(menuItem.MenuID, menuItem.OrderIndex))
138 ObjectMgr::GetLocaleString(gossipMenuLocale->OptionText, GetLocale(), strOptionText);
139 }
140
142 if (boxBroadcastText)
143 strBoxText = DB2Manager::GetBroadcastTextValue(boxBroadcastText, GetLocale());
144 else
145 {
146 strBoxText = menuItem.BoxText;
147
149 if (GetLocale() != LOCALE_enUS)
150 if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(menuItem.MenuID, menuItem.OrderIndex))
151 ObjectMgr::GetLocaleString(gossipMenuLocale->BoxText, GetLocale(), strBoxText);
152 }
153
154 AddMenuItem(menuItem.GossipOptionID, menuItem.OrderIndex, menuItem.OptionNpc, std::move(strOptionText), menuItem.Language, menuItem.Flags,
155 menuItem.GossipNpcOptionID, menuItem.ActionMenuID, menuItem.ActionPoiID, menuItem.BoxCoded, menuItem.BoxMoney, std::move(strBoxText),
156 menuItem.SpellID, menuItem.OverrideIconID, sender, action);
157}
158
159GossipMenuItem const* GossipMenu::GetItem(int32 gossipOptionId) const
160{
161 auto itr = std::ranges::find(_menuItems, gossipOptionId, &GossipMenuItem::GossipOptionID);
162 if (itr != _menuItems.end())
163 return &*itr;
164
165 return nullptr;
166}
167
169{
170 auto itr = std::ranges::find(_menuItems, orderIndex, &GossipMenuItem::OrderIndex);
171 if (itr != _menuItems.end())
172 return &*itr;
173
174 return nullptr;
175}
176
178{
179 if (GossipMenuItem const* item = GetItemByIndex(orderIndex))
180 return item->Sender;
181
182 return 0;
183}
184
186{
187 if (GossipMenuItem const* item = GetItemByIndex(orderIndex))
188 return item->Action;
189
190 return 0;
191}
192
194{
195 if (GossipMenuItem const* item = GetItemByIndex(orderIndex))
196 return item->BoxCoded;
197
198 return false;
199}
200
202{
203 _menuItems.clear();
204}
205
206PlayerMenu::PlayerMenu(WorldSession* session) : _session(session)
207{
208 if (_session)
210}
211
212PlayerMenu::~PlayerMenu() = default;
213
219
220void PlayerMenu::SendGossipMenu(uint32 titleTextId, ObjectGuid objectGUID)
221{
223
225 packet.GossipGUID = objectGUID;
226 packet.GossipID = _gossipMenu.GetMenuId();
227 if (GossipMenuAddon const* addon = sObjectMgr->GetGossipMenuAddon(packet.GossipID))
228 {
229 packet.FriendshipFactionID = addon->FriendshipFactionID;
230 packet.LfgDungeonsID = addon->LfgDungeonsID;
231 }
232
233 if (NpcText const* text = sObjectMgr->GetNpcText(titleTextId))
234 packet.BroadcastTextID = Trinity::Containers::SelectRandomWeightedContainerElement(text->Data, [](NpcTextData const& data) { return data.Probability; })->BroadcastTextID;
235
236 packet.GossipOptions.reserve(_gossipMenu.GetMenuItems().size());
237 for (GossipMenuItem const& item : _gossipMenu.GetMenuItems())
238 {
239 WorldPackets::NPC::ClientGossipOptions& opt = packet.GossipOptions.emplace_back();
240 opt.GossipOptionID = item.GossipOptionID;
241 opt.OptionNPC = item.OptionNpc;
242 opt.OptionFlags = item.BoxCoded; // makes pop up box password
243 opt.OptionCost = item.BoxMoney; // money required to open menu, 2.0.3
244 opt.OptionLanguage = item.Language;
245 opt.Flags = item.Flags;
246 opt.OrderIndex = item.OrderIndex;
247 opt.Text = item.OptionText; // text for gossip item
248 opt.Confirm = item.BoxText; // accept text (related to money) pop up box, 2.0.3
250 opt.SpellID = item.SpellID;
251 opt.OverrideIconID = item.OverrideIconID;
252 }
253
254 packet.GossipText.resize(_questMenu.GetMenuItemCount());
255 uint32 count = 0;
256 for (uint8 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
257 {
258 QuestMenuItem const& item = _questMenu.GetItem(i);
259 uint32 questID = item.QuestId;
260 if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID))
261 {
263 text.QuestID = questID;
264 text.ContentTuningID = quest->GetContentTuningId();
265 text.QuestType = item.QuestIcon;
266 text.QuestFlags[0] = quest->GetFlags();
267 text.QuestFlags[1] = quest->GetFlagsEx();
268 text.QuestFlags[2] = quest->GetFlagsEx2();
269 text.QuestFlags[3] = quest->GetFlagsEx3();
270 text.Repeatable = quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
271 text.ResetByScheduler = quest->IsResetByScheduler();
272 text.Important = quest->IsImportant();
273 text.Meta = quest->IsMeta();
274
275 text.QuestTitle = quest->GetLogTitle();
277 if (localeConstant != LOCALE_enUS)
278 if (QuestTemplateLocale const* localeData = sObjectMgr->GetQuestLocale(questID))
279 ObjectMgr::GetLocaleString(localeData->LogTitle, localeConstant, text.QuestTitle);
280
281 ++count;
282 }
283 }
284
285 // Shrink to the real size
286 packet.GossipText.resize(count);
287
288 _session->SendPacket(packet.Write());
289}
290
298
300{
301 PointOfInterest const* pointOfInterest = sObjectMgr->GetPointOfInterest(id);
302 if (!pointOfInterest)
303 {
304 TC_LOG_ERROR("sql.sql", "Request to send non-existing PointOfInterest (Id: {}), ignored.", id);
305 return;
306 }
307
309 packet.ID = pointOfInterest->ID;
310 packet.Name = pointOfInterest->Name;
311
313 if (localeConstant != LOCALE_enUS)
314 if (PointOfInterestLocale const* localeData = sObjectMgr->GetPointOfInterestLocale(id))
315 ObjectMgr::GetLocaleString(localeData->Name, localeConstant, packet.Name);
316
317 packet.Flags = pointOfInterest->Flags;
318 packet.Pos = pointOfInterest->Pos;
319 packet.Icon = pointOfInterest->Icon;
320 packet.Importance = pointOfInterest->Importance;
321 packet.WMOGroupID = pointOfInterest->WMOGroupID;
322
323 _session->SendPacket(packet.Write());
324}
325
326/*********************************************************/
327/*** QUEST SYSTEM ***/
328/*********************************************************/
329
331{
332 _questMenuItems.reserve(4); // can be set for max from most often sizes to speedup push_back and less memory use
333}
334
335QuestMenu::~QuestMenu() = default;
336
338{
339 if (!sObjectMgr->GetQuestTemplate(QuestId))
340 return;
341
343
344 QuestMenuItem& questMenuItem = _questMenuItems.emplace_back();
345
346 questMenuItem.QuestId = QuestId;
347 questMenuItem.QuestIcon = Icon;
348}
349
354
356{
357 auto itr = std::ranges::find(_responses, clientIdentifier, &Response::ClientIdentifier);
358 return itr != _responses.end() ? itr->Id : Optional<uint32>();
359}
360
362{
363 _responses.push_back({ .Id = id, .ClientIdentifier = clientIdentifier });
364}
365
368InteractionData::InteractionData(InteractionData&& other) noexcept = default;
372
374{
375 SourceGuid = target;
376 Type = type;
377 IsLaunchedByQuest = false;
378 switch (type)
379 {
381 _data.emplace<TrainerData>();
382 break;
384 _data.emplace<PlayerChoiceData>();
385 break;
386 default:
387 break;
388 }
389}
390
392{
395 IsLaunchedByQuest = false;
396 _data.emplace<std::monostate>();
397}
398
400{
401 _questMenuItems.clear();
402}
403
405{
406 ObjectGuid guid = questgiver->GetGUID();
407
409
411
413 questList.QuestGiverGUID = guid;
414
415 if (QuestGreeting const* questGreeting = sObjectMgr->GetQuestGreeting(questgiver->GetTypeId(), questgiver->GetEntry()))
416 {
417 questList.GreetEmoteDelay = questGreeting->EmoteDelay;
418 questList.GreetEmoteType = questGreeting->EmoteType;
419 questList.Greeting = questGreeting->Text;
420
421 if (localeConstant != LOCALE_enUS)
422 if (QuestGreetingLocale const* questGreetingLocale = sObjectMgr->GetQuestGreetingLocale(questgiver->GetTypeId(), questgiver->GetEntry()))
423 ObjectMgr::GetLocaleString(questGreetingLocale->Greeting, localeConstant, questList.Greeting);
424 }
425
426 for (uint32 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
427 {
428 QuestMenuItem const& questMenuItem = _questMenu.GetItem(i);
429
430 uint32 questID = questMenuItem.QuestId;
431
432 if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID))
433 {
434 questList.QuestDataText.emplace_back();
435 WorldPackets::NPC::ClientGossipText& text = questList.QuestDataText.back();
436 text.QuestID = questID;
437 text.ContentTuningID = quest->GetContentTuningId();
438 text.QuestType = questMenuItem.QuestIcon;
439 text.QuestFlags[0] = quest->GetFlags();
440 text.QuestFlags[1] = quest->GetFlagsEx();
441 text.QuestFlags[2] = quest->GetFlagsEx2();
442 text.QuestFlags[3] = quest->GetFlagsEx3();
443 text.Repeatable = quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
444 text.ResetByScheduler = quest->IsResetByScheduler();
445 text.Important = quest->IsImportant();
446 text.Meta = quest->IsMeta();
447
448 text.QuestTitle = quest->GetLogTitle();
450 if (localeConstant != LOCALE_enUS)
451 if (QuestTemplateLocale const* localeData = sObjectMgr->GetQuestLocale(questID))
452 ObjectMgr::GetLocaleString(localeData->LogTitle, localeConstant, text.QuestTitle);
453 }
454 }
455
456 _session->SendPacket(questList.Write());
457 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_QUEST_LIST_MESSAGE NPC={}", guid.ToString());
458}
459
461{
463 packet.QuestGiver.Guid = npcGUID;
464 packet.QuestGiver.Status = questStatus;
465
466 _session->SendPacket(packet.Write());
467 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC={}, status={}", npcGUID.ToString(), AsUnderlyingType(questStatus));
468}
469
470void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup)
471{
473
475
476 packet.QuestTitle = quest->GetLogTitle();
477 packet.LogDescription = quest->GetLogDescription();
478 packet.DescriptionText = quest->GetQuestDescription();
479 packet.PortraitGiverText = quest->GetPortraitGiverText();
480 packet.PortraitGiverName = quest->GetPortraitGiverName();
483
485 std::ranges::transform(quest->GetConditionalQuestDescription(), std::back_inserter(packet.ConditionalDescriptionText), [localeConstant](QuestConditionalText const& text)
486 {
487 std::string_view content = text.Text[LOCALE_enUS];
488 ObjectMgr::GetLocaleString(text.Text, localeConstant, content);
489 return WorldPackets::Quest::ConditionalQuestText{ text.PlayerConditionId, text.QuestgiverCreatureId, content };
490 });
491
492 if (localeConstant != LOCALE_enUS)
493 {
494 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
495 {
496 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, localeConstant, packet.QuestTitle);
497 ObjectMgr::GetLocaleString(questTemplateLocale->LogDescription, localeConstant, packet.LogDescription);
498 ObjectMgr::GetLocaleString(questTemplateLocale->QuestDescription, localeConstant, packet.DescriptionText);
499 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverText, localeConstant, packet.PortraitGiverText);
500 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverName, localeConstant, packet.PortraitGiverName);
501 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInText, localeConstant, packet.PortraitTurnInText);
502 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInName, localeConstant, packet.PortraitTurnInName);
503 }
504 }
505
506 packet.QuestGiverGUID = npcGUID;
507 packet.InformUnit = _session->GetPlayer()->GetPlayerSharingQuest();
508 packet.QuestID = quest->GetQuestId();
509 packet.QuestPackageID = quest->GetQuestPackageID();
510 packet.PortraitGiver = quest->GetQuestGiverPortrait();
511 packet.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
512 packet.PortraitGiverModelSceneID = quest->GetQuestGiverPortraitModelSceneId();
513 packet.PortraitTurnIn = quest->GetQuestTurnInPortrait();
514 packet.QuestInfoID = quest->GetQuestInfoID();
515 packet.QuestSessionBonus = 0; //quest->GetQuestSessionBonus(); // this is only sent while quest session is active
516 packet.AutoLaunched = autoLaunched;
517 packet.ResetByScheduler = quest->IsResetByScheduler();
518 packet.DisplayPopup = displayPopup;
519 packet.QuestFlags[0] = quest->GetFlags() & (sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) ? ~QUEST_FLAGS_AUTO_ACCEPT : ~0);
520 packet.QuestFlags[1] = quest->GetFlagsEx();
521 packet.QuestFlags[2] = quest->GetFlagsEx2();
522 packet.QuestFlags[3] = quest->GetFlagsEx3();
523 packet.SuggestedPartyMembers = quest->GetSuggestedPlayers();
524
525 // Is there a better way? what about game objects?
526 if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID))
527 packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
528
529 // RewardSpell can teach multiple spells in trigger spell effects. But not all effects must be SPELL_EFFECT_LEARN_SPELL. See example spell 33950
530 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(quest->GetRewSpell(), DIFFICULTY_NONE))
531 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
532 if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL))
533 packet.LearnSpells.push_back(spellEffectInfo.TriggerSpell);
534
535 quest->BuildQuestRewards(packet.Rewards, _session->GetPlayer());
536
537 packet.DescEmotes.resize(QUEST_EMOTE_COUNT);
538 for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
539 {
540 packet.DescEmotes[i].Type = quest->DetailsEmote[i];
541 packet.DescEmotes[i].Delay = quest->DetailsEmoteDelay[i];
542 }
543
544 QuestObjectives const& objs = quest->GetObjectives();
545 packet.Objectives.resize(objs.size());
546 for (uint32 i = 0; i < objs.size(); ++i)
547 {
548 packet.Objectives[i].ID = objs[i].ID;
549 packet.Objectives[i].Type = objs[i].Type;
550 packet.Objectives[i].ObjectID = objs[i].ObjectID;
551 packet.Objectives[i].Amount = objs[i].Amount;
552 }
553
554 _session->SendPacket(packet.Write());
555
556 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_QUEST_DETAILS NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
557}
558
560{
561 if (quest->QueryData)
563 else
564 {
566 _session->SendPacket(&queryPacket);
567 }
568
569 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid={}", quest->GetQuestId());
570}
571
572void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched)
573{
575
577
578 packet.QuestTitle = quest->GetLogTitle();
579 packet.RewardText = quest->GetOfferRewardText();
580 packet.PortraitGiverText = quest->GetPortraitGiverText();
581 packet.PortraitGiverName = quest->GetPortraitGiverName();
584
586 std::ranges::transform(quest->GetConditionalOfferRewardText(), std::back_inserter(packet.ConditionalRewardText), [locale](QuestConditionalText const& text)
587 {
588 std::string_view content = text.Text[LOCALE_enUS];
589 ObjectMgr::GetLocaleString(text.Text, locale, content);
590 return WorldPackets::Quest::ConditionalQuestText{ text.PlayerConditionId, text.QuestgiverCreatureId, content };
591 });
592
593 if (locale != LOCALE_enUS)
594 {
595 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
596 {
597 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, locale, packet.QuestTitle);
598 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverText, locale, packet.PortraitGiverText);
599 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverName, locale, packet.PortraitGiverName);
600 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInText, locale, packet.PortraitTurnInText);
601 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInName, locale, packet.PortraitTurnInName);
602 }
603
604 if (QuestOfferRewardLocale const* questOfferRewardLocale = sObjectMgr->GetQuestOfferRewardLocale(quest->GetQuestId()))
605 ObjectMgr::GetLocaleString(questOfferRewardLocale->RewardText, locale, packet.RewardText);
606 }
607
608 WorldPackets::Quest::QuestGiverOfferReward& offer = packet.QuestData;
609
610 quest->BuildQuestRewards(offer.Rewards, _session->GetPlayer());
611 offer.QuestGiverGUID = npcGUID;
612
613 // Is there a better way? what about game objects?
614 if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID))
615 {
616 packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
617 offer.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
618 }
619
620 offer.QuestID = quest->GetQuestId();
621 offer.AutoLaunched = autoLaunched;
622 offer.ResetByScheduler = quest->IsResetByScheduler();
623 offer.SuggestedPartyMembers = quest->GetSuggestedPlayers();
624 offer.QuestInfoID = quest->GetQuestInfoID();
625
626 for (uint32 i = 0; i < QUEST_EMOTE_COUNT && quest->OfferRewardEmote[i]; ++i)
627 offer.Emotes.emplace_back(quest->OfferRewardEmote[i], quest->OfferRewardEmoteDelay[i]);
628
629 offer.QuestFlags[0] = quest->GetFlags();
630 offer.QuestFlags[1] = quest->GetFlagsEx();
631 offer.QuestFlags[2] = quest->GetFlagsEx2();
632 offer.QuestFlags[3] = quest->GetFlagsEx3();
633
634 packet.PortraitTurnIn = quest->GetQuestTurnInPortrait();
635 packet.PortraitGiver = quest->GetQuestGiverPortrait();
636 packet.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
637 packet.PortraitGiverModelSceneID = quest->GetQuestGiverPortraitModelSceneId();
638 packet.QuestPackageID = quest->GetQuestPackageID();
639
640 _session->SendPacket(packet.Write());
641 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
642}
643
644void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched)
645{
646 // We can always call to RequestItems, but this packet only goes out if there are actually
647 // items. Otherwise, we'll skip straight to the OfferReward
648
649 if (!quest->HasQuestObjectiveType(QUEST_OBJECTIVE_ITEM) && canComplete)
650 {
651 SendQuestGiverOfferReward(quest, npcGUID, true);
652 return;
653 }
654
656
658
659 packet.QuestTitle = quest->GetLogTitle();
660 packet.CompletionText = quest->GetRequestItemsText();
661
663 std::ranges::transform(quest->GetConditionalRequestItemsText(), std::back_inserter(packet.ConditionalCompletionText), [locale](QuestConditionalText const& text)
664 {
665 std::string_view content = text.Text[LOCALE_enUS];
666 ObjectMgr::GetLocaleString(text.Text, locale, content);
667 return WorldPackets::Quest::ConditionalQuestText{ text.PlayerConditionId, text.QuestgiverCreatureId, content };
668 });
669
670 if (locale != LOCALE_enUS)
671 {
672 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
673 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, locale, packet.QuestTitle);
674
675 if (QuestRequestItemsLocale const* questRequestItemsLocale = sObjectMgr->GetQuestRequestItemsLocale(quest->GetQuestId()))
676 ObjectMgr::GetLocaleString(questRequestItemsLocale->CompletionText, locale, packet.CompletionText);
677 }
678
679 packet.QuestGiverGUID = npcGUID;
680
681 // Is there a better way? what about game objects?
682 if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID))
683 packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
684
685 packet.QuestID = quest->GetQuestId();
686
687 if (canComplete)
688 {
689 packet.CompEmoteDelay = quest->GetCompleteEmoteDelay();
690 packet.CompEmoteType = quest->GetCompleteEmote();
691 }
692 else
693 {
694 packet.CompEmoteDelay = quest->GetIncompleteEmoteDelay();
695 packet.CompEmoteType = quest->GetIncompleteEmote();
696 }
697
698 packet.QuestFlags[0] = quest->GetFlags();
699 packet.QuestFlags[1] = quest->GetFlagsEx();
700 packet.QuestFlags[2] = quest->GetFlagsEx2();
701 packet.QuestFlags[3] = quest->GetFlagsEx3();
702 packet.SuggestPartyMembers = quest->GetSuggestedPlayers();
703 packet.QuestInfoID = quest->GetQuestInfoID();
704
705 // incomplete: FD
706 // incomplete quest with item objective but item objective is complete DD
707 packet.StatusFlags = canComplete ? 0xFF : 0xFD;
708
709 packet.MoneyToGet = 0;
710 for (QuestObjective const& obj : quest->GetObjectives())
711 {
712 switch (obj.Type)
713 {
715 packet.Collect.emplace_back(obj.ObjectID, obj.Amount, obj.Flags);
716 break;
718 packet.Currency.emplace_back(obj.ObjectID, obj.Amount);
719 break;
721 packet.MoneyToGet += obj.Amount;
722 break;
723 default:
724 break;
725 }
726 }
727
728 packet.AutoLaunched = autoLaunched;
729 packet.ResetByScheduler = quest->IsResetByScheduler();
730
731 _session->SendPacket(packet.Write());
732 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
733}
LocaleConstant
Definition Common.h:51
@ LOCALE_enUS
Definition Common.h:52
#define DEFAULT_LOCALE
Definition Common.h:69
DB2Storage< BroadcastTextEntry > sBroadcastTextStore("BroadcastText.db2", &BroadcastTextLoadInfo::Instance)
PlayerInteractionType
Definition DBCEnums.h:2211
@ DIFFICULTY_NONE
Definition DBCEnums.h:933
uint8_t uint8
Definition Define.h:156
int32_t int32
Definition Define.h:150
uint64_t uint64
Definition Define.h:153
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
uint16 flags
#define ASSERT
Definition Errors.h:80
#define GOSSIP_MAX_MENU_ITEMS
Definition GossipDef.h:34
GossipOptionNpc
Definition GossipDef.h:38
GossipOptionFlags
Definition GossipDef.h:125
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define sObjectMgr
Definition ObjectMgr.h:1885
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
std::vector< QuestObjective > QuestObjectives
Definition QuestDef.h:556
@ QUEST_OBJECTIVE_ITEM
Definition QuestDef.h:359
@ QUEST_OBJECTIVE_MONEY
Definition QuestDef.h:366
@ QUEST_OBJECTIVE_CURRENCY
Definition QuestDef.h:362
QuestGiverStatus
Definition QuestDef.h:158
#define QUEST_EMOTE_COUNT
Definition QuestDef.h:53
@ SPELL_EFFECT_LEARN_SPELL
#define sSpellMgr
Definition SpellMgr.h:812
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
TypeID GetTypeId() const
Definition BaseEntity.h:166
static char const * GetBroadcastTextValue(BroadcastTextEntry const *broadcastText, LocaleConstant locale=DEFAULT_LOCALE, uint8 gender=GENDER_MALE, bool forceGender=false)
uint32 GetMenuItemAction(uint32 orderIndex) const
uint32 GetMenuItemSender(uint32 orderIndex) const
GossipMenuItem const * GetItem(int32 gossipOptionId) const
GossipMenuItemContainer _menuItems
Definition GossipDef.h:213
void SetLocale(LocaleConstant locale)
Definition GossipDef.h:185
LocaleConstant GetLocale() const
Definition GossipDef.h:186
bool IsMenuItemCoded(uint32 orderIndex) const
uint32 GetMenuId() const
Definition GossipDef.h:184
LocaleConstant _locale
Definition GossipDef.h:215
GossipMenuItemContainer const & GetMenuItems() const
Definition GossipDef.h:207
GossipMenuItem const * GetItemByIndex(uint32 orderIndex) const
uint32 _menuId
Definition GossipDef.h:214
void ClearMenu()
uint32 AddMenuItem(int32 gossipOptionId, int32 orderIndex, GossipOptionNpc optionNpc, std::string optionText, uint32 language, GossipOptionFlags flags, Optional< int32 > gossipNpcOptionId, uint32 actionMenuId, uint32 actionPoiId, bool boxCoded, uint64 boxMoney, std::string boxText, Optional< int32 > spellId, Optional< int32 > overrideIconId, uint32 sender, uint32 action)
Definition GossipDef.cpp:43
void StartInteraction(ObjectGuid target, PlayerInteractionType type)
ObjectGuid SourceGuid
Definition GossipDef.h:292
PlayerInteractionType Type
Definition GossipDef.h:293
InteractionData & operator=(InteractionData const &other)
bool IsLaunchedByQuest
Definition GossipDef.h:305
std::variant< std::monostate, TrainerData, PlayerChoiceData > _data
Definition GossipDef.h:309
std::string ToString() const
void Clear()
Definition ObjectGuid.h:329
static std::string_view GetLocaleString(std::vector< std::string > const &data, LocaleConstant locale)
Definition ObjectMgr.h:1611
uint32 GetEntry() const
Definition Object.h:89
void AddResponse(uint32 id, uint16 clientIdentifier)
std::vector< Response > _responses
Definition GossipDef.h:262
Optional< uint32 > FindIdByClientIdentifier(uint16 clientIdentifier) const
void SendCloseGossip()
void SendPointOfInterest(uint32 poiId) const
QuestMenu _questMenu
Definition GossipDef.h:352
InteractionData _interactionData
Definition GossipDef.h:354
void ClearMenus()
PlayerMenu(WorldSession *session)
void SendGossipMenu(uint32 titleTextId, ObjectGuid objectGUID)
GossipMenu _gossipMenu
Definition GossipDef.h:351
InteractionData & GetInteractionData()
Definition GossipDef.h:324
void SendQuestQueryResponse(Quest const *quest) const
void SendQuestGiverStatus(QuestGiverStatus questStatus, ObjectGuid npcGUID) const
WorldSession * _session
Definition GossipDef.h:353
void SendQuestGiverQuestDetails(Quest const *quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup)
void SendQuestGiverOfferReward(Quest const *quest, ObjectGuid npcGUID, bool autoLaunched)
void SendQuestGiverRequestItems(Quest const *quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched)
void SendQuestGiverQuestListMessage(Object *questgiver)
QuestMenuItem const & GetItem(uint16 index) const
Definition GossipDef.h:233
uint8 GetMenuItemCount() const
Definition GossipDef.h:230
QuestMenuItemList _questMenuItems
Definition GossipDef.h:236
bool HasItem(uint32 questId) const
void ClearMenu()
void AddMenuItem(uint32 QuestId, uint8 Icon)
std::string const & GetPortraitGiverName() const
Definition QuestDef.h:678
bool HasQuestObjectiveType(QuestObjectiveType type) const
Definition QuestDef.h:625
std::string const & GetPortraitTurnInName() const
Definition QuestDef.h:680
std::string const & GetRequestItemsText() const
Definition QuestDef.h:673
WorldPacket BuildQueryData(LocaleConstant loc, Player *player) const
Definition QuestDef.cpp:664
std::string const & GetLogTitle() const
Definition QuestDef.h:666
uint32 GetQuestId() const
Definition QuestDef.h:637
std::string const & GetPortraitGiverText() const
Definition QuestDef.h:677
std::vector< QuestConditionalText > const & GetConditionalOfferRewardText() const
Definition QuestDef.h:672
std::string const & GetPortraitTurnInText() const
Definition QuestDef.h:679
std::string const & GetOfferRewardText() const
Definition QuestDef.h:671
std::vector< QuestConditionalText > const & GetConditionalRequestItemsText() const
Definition QuestDef.h:674
std::string const & GetLogDescription() const
Definition QuestDef.h:667
std::unique_ptr< WorldPacket[]> QueryData
Definition QuestDef.h:781
std::string const & GetQuestDescription() const
Definition QuestDef.h:668
std::vector< QuestConditionalText > const & GetConditionalQuestDescription() const
Definition QuestDef.h:669
Utility class to enable range for loop syntax for multimap.equal_range uses.
constexpr end_iterator end() const
WorldPacket const * Write() override
WorldPacket const * Write() override
std::vector< ClientGossipOptions > GossipOptions
Definition NPCPackets.h:119
Optional< int32 > BroadcastTextID
Definition NPCPackets.h:124
std::vector< ClientGossipText > GossipText
Definition NPCPackets.h:122
WorldPacket const * Write() override
TaggedPosition< Position::XYZ > Pos
Definition NPCPackets.h:225
std::vector< ConditionalQuestText > ConditionalRewardText
std::vector< ConditionalQuestText > ConditionalDescriptionText
std::vector< NPC::ClientGossipText > QuestDataText
std::vector< ConditionalQuestText > ConditionalCompletionText
WorldPacket const * Write() override
Player session in the World.
LocaleConstant GetSessionDbLocaleIndex() const
Player * GetPlayer() const
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
#define sWorld
Definition World.h:916
@ CONFIG_QUEST_IGNORE_AUTO_ACCEPT
Definition World.h:165
TC_GAME_API Creature * GetCreature(WorldObject const &u, ObjectGuid const &guid)
auto SelectRandomWeightedContainerElement(C const &container, std::span< double > const &weights) -> decltype(std::ranges::begin(container))
Definition Containers.h:127
struct advstd::ranges::Contains contains
uint32 Language
Definition GossipDef.h:138
Optional< int32 > GossipNpcOptionID
Definition GossipDef.h:140
uint32 OrderIndex
Definition GossipDef.h:135
GossipOptionNpc OptionNpc
Definition GossipDef.h:136
Optional< int32 > OverrideIconID
Definition GossipDef.h:145
uint32 ActionPoiID
Definition GossipDef.h:149
std::string OptionText
Definition GossipDef.h:137
uint32 ActionMenuID
Definition GossipDef.h:148
uint64 BoxMoney
Definition GossipDef.h:142
GossipOptionFlags Flags
Definition GossipDef.h:139
std::string BoxText
Definition GossipDef.h:143
int32 GossipOptionID
Definition GossipDef.h:134
Optional< int32 > SpellID
Definition GossipDef.h:144
Optional< int32 > SpellID
Definition ObjectMgr.h:758
uint32 BoxBroadcastTextID
Definition ObjectMgr.h:757
std::string OptionText
Definition ObjectMgr.h:747
int32 GossipOptionID
Definition ObjectMgr.h:744
uint32 ActionMenuID
Definition ObjectMgr.h:751
uint32 OrderIndex
Definition ObjectMgr.h:745
Optional< int32 > GossipNpcOptionID
Definition ObjectMgr.h:753
Optional< int32 > OverrideIconID
Definition ObjectMgr.h:759
uint32 ActionPoiID
Definition ObjectMgr.h:752
GossipOptionNpc OptionNpc
Definition ObjectMgr.h:746
std::string BoxText
Definition ObjectMgr.h:756
GossipOptionFlags Flags
Definition ObjectMgr.h:750
uint32 OptionBroadcastTextID
Definition ObjectMgr.h:748
std::string Name
Definition ObjectMgr.h:738
uint32 Importance
Definition ObjectMgr.h:736
uint32 QuestId
Definition GossipDef.h:161
uint8 QuestIcon
Definition GossipDef.h:162
std::array< int32, 4 > QuestFlags
Definition NPCPackets.h:107
std::vector< QuestDescEmote > Emotes