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.QuestInfoID = quest->GetQuestInfoID();
267 text.QuestFlags[0] = quest->GetFlags();
268 text.QuestFlags[1] = quest->GetFlagsEx();
269 text.QuestFlags[2] = quest->GetFlagsEx2();
270 text.QuestFlags[3] = quest->GetFlagsEx3();
271 text.Repeatable = quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
272 text.ResetByScheduler = quest->IsResetByScheduler();
273 text.Important = quest->IsImportant();
274 text.Meta = quest->IsMeta();
275
276 text.QuestTitle = quest->GetLogTitle();
278 if (localeConstant != LOCALE_enUS)
279 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(questID))
280 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, localeConstant, text.QuestTitle);
281
282 ++count;
283 }
284 }
285
286 // Shrink to the real size
287 packet.GossipText.resize(count);
288
289 _session->SendPacket(packet.Write());
290}
291
299
301{
302 PointOfInterest const* pointOfInterest = sObjectMgr->GetPointOfInterest(id);
303 if (!pointOfInterest)
304 {
305 TC_LOG_ERROR("sql.sql", "Request to send non-existing PointOfInterest (Id: {}), ignored.", id);
306 return;
307 }
308
310 packet.ID = pointOfInterest->ID;
311 packet.Name = pointOfInterest->Name;
312
314 if (localeConstant != LOCALE_enUS)
315 if (PointOfInterestLocale const* localeData = sObjectMgr->GetPointOfInterestLocale(id))
316 ObjectMgr::GetLocaleString(localeData->Name, localeConstant, packet.Name);
317
318 packet.Flags = pointOfInterest->Flags;
319 packet.Pos = pointOfInterest->Pos;
320 packet.Icon = pointOfInterest->Icon;
321 packet.Importance = pointOfInterest->Importance;
322 packet.WMOGroupID = pointOfInterest->WMOGroupID;
323
324 _session->SendPacket(packet.Write());
325}
326
327/*********************************************************/
328/*** QUEST SYSTEM ***/
329/*********************************************************/
330
332{
333 _questMenuItems.reserve(4); // can be set for max from most often sizes to speedup push_back and less memory use
334}
335
336QuestMenu::~QuestMenu() = default;
337
339{
340 if (!sObjectMgr->GetQuestTemplate(QuestId))
341 return;
342
344
345 QuestMenuItem& questMenuItem = _questMenuItems.emplace_back();
346
347 questMenuItem.QuestId = QuestId;
348 questMenuItem.QuestIcon = Icon;
349}
350
355
357{
358 auto itr = std::ranges::find(_responses, clientIdentifier, &Response::ClientIdentifier);
359 return itr != _responses.end() ? itr->Id : Optional<uint32>();
360}
361
363{
364 _responses.push_back({ .Id = id, .ClientIdentifier = clientIdentifier });
365}
366
369InteractionData::InteractionData(InteractionData&& other) noexcept = default;
373
375{
376 SourceGuid = target;
377 Type = type;
378 IsLaunchedByQuest = false;
379 switch (type)
380 {
382 _data.emplace<TrainerData>();
383 break;
385 _data.emplace<PlayerChoiceData>();
386 break;
387 default:
388 break;
389 }
390}
391
393{
396 IsLaunchedByQuest = false;
397 _data.emplace<std::monostate>();
398}
399
401{
402 _questMenuItems.clear();
403}
404
406{
407 ObjectGuid guid = questgiver->GetGUID();
408
410
412
414 questList.QuestGiverGUID = guid;
415
416 if (QuestGreeting const* questGreeting = sObjectMgr->GetQuestGreeting(questgiver->GetTypeId(), questgiver->GetEntry()))
417 {
418 questList.GreetEmoteDelay = questGreeting->EmoteDelay;
419 questList.GreetEmoteType = questGreeting->EmoteType;
420 questList.Greeting = questGreeting->Text;
421
422 if (localeConstant != LOCALE_enUS)
423 if (QuestGreetingLocale const* questGreetingLocale = sObjectMgr->GetQuestGreetingLocale(questgiver->GetTypeId(), questgiver->GetEntry()))
424 ObjectMgr::GetLocaleString(questGreetingLocale->Greeting, localeConstant, questList.Greeting);
425 }
426
427 for (uint32 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
428 {
429 QuestMenuItem const& questMenuItem = _questMenu.GetItem(i);
430
431 uint32 questID = questMenuItem.QuestId;
432
433 if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID))
434 {
435 WorldPackets::NPC::ClientGossipText& text = questList.QuestDataText.emplace_back();
436 text.QuestID = questID;
437 text.ContentTuningID = quest->GetContentTuningId();
438 text.QuestType = questMenuItem.QuestIcon;
439 text.QuestInfoID = quest->GetQuestInfoID();
440 text.QuestFlags[0] = quest->GetFlags();
441 text.QuestFlags[1] = quest->GetFlagsEx();
442 text.QuestFlags[2] = quest->GetFlagsEx2();
443 text.QuestFlags[3] = quest->GetFlagsEx3();
444 text.Repeatable = quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
445 text.ResetByScheduler = quest->IsResetByScheduler();
446 text.Important = quest->IsImportant();
447 text.Meta = quest->IsMeta();
448
449 text.QuestTitle = quest->GetLogTitle();
451 if (localeConstant != LOCALE_enUS)
452 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(questID))
453 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, localeConstant, text.QuestTitle);
454 }
455 }
456
457 _session->SendPacket(questList.Write());
458 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_QUEST_LIST_MESSAGE NPC={}", guid.ToString());
459}
460
462{
464 packet.QuestGiver.Guid = npcGUID;
465 packet.QuestGiver.Status = questStatus;
466
467 _session->SendPacket(packet.Write());
468 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC={}, status={}", npcGUID.ToString(), AsUnderlyingType(questStatus));
469}
470
471void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched, bool displayPopup)
472{
474
476
477 packet.QuestTitle = quest->GetLogTitle();
478 packet.LogDescription = quest->GetLogDescription();
479 packet.DescriptionText = quest->GetQuestDescription();
480 packet.PortraitGiverText = quest->GetPortraitGiverText();
481 packet.PortraitGiverName = quest->GetPortraitGiverName();
484
486 std::ranges::transform(quest->GetConditionalQuestDescription(), std::back_inserter(packet.ConditionalDescriptionText), [localeConstant](QuestConditionalText const& text)
487 {
488 std::string_view content = text.Text[LOCALE_enUS];
489 ObjectMgr::GetLocaleString(text.Text, localeConstant, content);
490 return WorldPackets::Quest::ConditionalQuestText{ text.PlayerConditionId, text.QuestgiverCreatureId, content };
491 });
492
493 if (localeConstant != LOCALE_enUS)
494 {
495 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
496 {
497 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, localeConstant, packet.QuestTitle);
498 ObjectMgr::GetLocaleString(questTemplateLocale->LogDescription, localeConstant, packet.LogDescription);
499 ObjectMgr::GetLocaleString(questTemplateLocale->QuestDescription, localeConstant, packet.DescriptionText);
500 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverText, localeConstant, packet.PortraitGiverText);
501 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverName, localeConstant, packet.PortraitGiverName);
502 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInText, localeConstant, packet.PortraitTurnInText);
503 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInName, localeConstant, packet.PortraitTurnInName);
504 }
505 }
506
507 packet.QuestGiverGUID = npcGUID;
508 packet.InformUnit = _session->GetPlayer()->GetPlayerSharingQuest();
509 packet.QuestID = quest->GetQuestId();
510 packet.QuestPackageID = quest->GetQuestPackageID();
511 packet.PortraitGiver = quest->GetQuestGiverPortrait();
512 packet.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
513 packet.PortraitGiverModelSceneID = quest->GetQuestGiverPortraitModelSceneId();
514 packet.PortraitTurnIn = quest->GetQuestTurnInPortrait();
515 packet.QuestInfoID = quest->GetQuestInfoID();
516 packet.QuestSessionBonus = 0; //quest->GetQuestSessionBonus(); // this is only sent while quest session is active
517 packet.AutoLaunched = autoLaunched;
518 packet.ResetByScheduler = quest->IsResetByScheduler();
519 packet.DisplayPopup = displayPopup;
520 packet.QuestFlags[0] = quest->GetFlags() & (sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) ? ~QUEST_FLAGS_AUTO_ACCEPT : ~0);
521 packet.QuestFlags[1] = quest->GetFlagsEx();
522 packet.QuestFlags[2] = quest->GetFlagsEx2();
523 packet.QuestFlags[3] = quest->GetFlagsEx3();
524 packet.SuggestedPartyMembers = quest->GetSuggestedPlayers();
525
526 // Is there a better way? what about game objects?
527 if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID))
528 packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
529
530 // RewardSpell can teach multiple spells in trigger spell effects. But not all effects must be SPELL_EFFECT_LEARN_SPELL. See example spell 33950
531 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(quest->GetRewSpell(), DIFFICULTY_NONE))
532 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
533 if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL))
534 packet.LearnSpells.push_back(spellEffectInfo.TriggerSpell);
535
536 quest->BuildQuestRewards(packet.Rewards, _session->GetPlayer());
537
538 packet.DescEmotes.resize(QUEST_EMOTE_COUNT);
539 for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
540 {
541 packet.DescEmotes[i].Type = quest->DetailsEmote[i];
542 packet.DescEmotes[i].Delay = quest->DetailsEmoteDelay[i];
543 }
544
545 QuestObjectives const& objs = quest->GetObjectives();
546 packet.Objectives.resize(objs.size());
547 for (uint32 i = 0; i < objs.size(); ++i)
548 {
549 packet.Objectives[i].ID = objs[i].ID;
550 packet.Objectives[i].Type = objs[i].Type;
551 packet.Objectives[i].ObjectID = objs[i].ObjectID;
552 packet.Objectives[i].Amount = objs[i].Amount;
553 }
554
555 _session->SendPacket(packet.Write());
556
557 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_QUEST_DETAILS NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
558}
559
561{
562 if (quest->QueryData)
564 else
565 {
567 _session->SendPacket(&queryPacket);
568 }
569
570 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid={}", quest->GetQuestId());
571}
572
573void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched)
574{
576
578
579 packet.QuestTitle = quest->GetLogTitle();
580 packet.RewardText = quest->GetOfferRewardText();
581 packet.PortraitGiverText = quest->GetPortraitGiverText();
582 packet.PortraitGiverName = quest->GetPortraitGiverName();
585
587 std::ranges::transform(quest->GetConditionalOfferRewardText(), std::back_inserter(packet.ConditionalRewardText), [locale](QuestConditionalText const& text)
588 {
589 std::string_view content = text.Text[LOCALE_enUS];
590 ObjectMgr::GetLocaleString(text.Text, locale, content);
591 return WorldPackets::Quest::ConditionalQuestText{ text.PlayerConditionId, text.QuestgiverCreatureId, content };
592 });
593
594 if (locale != LOCALE_enUS)
595 {
596 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
597 {
598 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, locale, packet.QuestTitle);
599 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverText, locale, packet.PortraitGiverText);
600 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitGiverName, locale, packet.PortraitGiverName);
601 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInText, locale, packet.PortraitTurnInText);
602 ObjectMgr::GetLocaleString(questTemplateLocale->PortraitTurnInName, locale, packet.PortraitTurnInName);
603 }
604
605 if (QuestOfferRewardLocale const* questOfferRewardLocale = sObjectMgr->GetQuestOfferRewardLocale(quest->GetQuestId()))
606 ObjectMgr::GetLocaleString(questOfferRewardLocale->RewardText, locale, packet.RewardText);
607 }
608
609 WorldPackets::Quest::QuestGiverOfferReward& offer = packet.QuestData;
610
611 quest->BuildQuestRewards(offer.Rewards, _session->GetPlayer());
612 offer.QuestGiverGUID = npcGUID;
613
614 // Is there a better way? what about game objects?
615 if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID))
616 {
617 packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
618 offer.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
619 }
620
621 offer.QuestID = quest->GetQuestId();
622 offer.AutoLaunched = autoLaunched;
623 offer.ResetByScheduler = quest->IsResetByScheduler();
624 offer.SuggestedPartyMembers = quest->GetSuggestedPlayers();
625 offer.QuestInfoID = quest->GetQuestInfoID();
626
627 for (uint32 i = 0; i < QUEST_EMOTE_COUNT && quest->OfferRewardEmote[i]; ++i)
628 offer.Emotes.emplace_back(quest->OfferRewardEmote[i], quest->OfferRewardEmoteDelay[i]);
629
630 offer.QuestFlags[0] = quest->GetFlags();
631 offer.QuestFlags[1] = quest->GetFlagsEx();
632 offer.QuestFlags[2] = quest->GetFlagsEx2();
633 offer.QuestFlags[3] = quest->GetFlagsEx3();
634
635 packet.PortraitTurnIn = quest->GetQuestTurnInPortrait();
636 packet.PortraitGiver = quest->GetQuestGiverPortrait();
637 packet.PortraitGiverMount = quest->GetQuestGiverPortraitMount();
638 packet.PortraitGiverModelSceneID = quest->GetQuestGiverPortraitModelSceneId();
639 packet.QuestPackageID = quest->GetQuestPackageID();
640
641 _session->SendPacket(packet.Write());
642 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
643}
644
645void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched)
646{
647 // We can always call to RequestItems, but this packet only goes out if there are actually
648 // items. Otherwise, we'll skip straight to the OfferReward
649
650 if (!quest->HasQuestObjectiveType(QUEST_OBJECTIVE_ITEM) && canComplete)
651 {
652 SendQuestGiverOfferReward(quest, npcGUID, true);
653 return;
654 }
655
657
659
660 packet.QuestTitle = quest->GetLogTitle();
661 packet.CompletionText = quest->GetRequestItemsText();
662
664 std::ranges::transform(quest->GetConditionalRequestItemsText(), std::back_inserter(packet.ConditionalCompletionText), [locale](QuestConditionalText const& text)
665 {
666 std::string_view content = text.Text[LOCALE_enUS];
667 ObjectMgr::GetLocaleString(text.Text, locale, content);
668 return WorldPackets::Quest::ConditionalQuestText{ text.PlayerConditionId, text.QuestgiverCreatureId, content };
669 });
670
671 if (locale != LOCALE_enUS)
672 {
673 if (QuestTemplateLocale const* questTemplateLocale = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
674 ObjectMgr::GetLocaleString(questTemplateLocale->LogTitle, locale, packet.QuestTitle);
675
676 if (QuestRequestItemsLocale const* questRequestItemsLocale = sObjectMgr->GetQuestRequestItemsLocale(quest->GetQuestId()))
677 ObjectMgr::GetLocaleString(questRequestItemsLocale->CompletionText, locale, packet.CompletionText);
678 }
679
680 packet.QuestGiverGUID = npcGUID;
681
682 // Is there a better way? what about game objects?
683 if (Creature const* creature = ObjectAccessor::GetCreature(*_session->GetPlayer(), npcGUID))
684 packet.QuestGiverCreatureID = creature->GetCreatureTemplate()->Entry;
685
686 packet.QuestID = quest->GetQuestId();
687
688 if (canComplete)
689 {
690 packet.CompEmoteDelay = quest->GetCompleteEmoteDelay();
691 packet.CompEmoteType = quest->GetCompleteEmote();
692 }
693 else
694 {
695 packet.CompEmoteDelay = quest->GetIncompleteEmoteDelay();
696 packet.CompEmoteType = quest->GetIncompleteEmote();
697 }
698
699 packet.QuestFlags[0] = quest->GetFlags();
700 packet.QuestFlags[1] = quest->GetFlagsEx();
701 packet.QuestFlags[2] = quest->GetFlagsEx2();
702 packet.QuestFlags[3] = quest->GetFlagsEx3();
703 packet.SuggestPartyMembers = quest->GetSuggestedPlayers();
704 packet.QuestInfoID = quest->GetQuestInfoID();
705
706 // incomplete: FD
707 // incomplete quest with item objective but item objective is complete DD
708 packet.StatusFlags = canComplete ? 0xFF : 0xFD;
709
710 packet.MoneyToGet = 0;
711 for (QuestObjective const& obj : quest->GetObjectives())
712 {
713 switch (obj.Type)
714 {
716 packet.Collect.emplace_back(obj.ObjectID, obj.Amount, obj.Flags);
717 break;
719 packet.Currency.emplace_back(obj.ObjectID, obj.Amount);
720 break;
722 packet.MoneyToGet += obj.Amount;
723 break;
724 default:
725 break;
726 }
727 }
728
729 packet.AutoLaunched = autoLaunched;
730 packet.ResetByScheduler = quest->IsResetByScheduler();
731
732 _session->SendPacket(packet.Write());
733 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
734}
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:2226
@ DIFFICULTY_NONE
Definition DBCEnums.h:936
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:72
#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:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define sObjectMgr
Definition ObjectMgr.h:1873
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
std::vector< QuestObjective > QuestObjectives
Definition QuestDef.h:552
@ QUEST_OBJECTIVE_ITEM
Definition QuestDef.h:355
@ QUEST_OBJECTIVE_MONEY
Definition QuestDef.h:362
@ QUEST_OBJECTIVE_CURRENCY
Definition QuestDef.h:358
QuestGiverStatus
Definition QuestDef.h:154
#define QUEST_EMOTE_COUNT
Definition QuestDef.h:49
@ 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:1608
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:674
bool HasQuestObjectiveType(QuestObjectiveType type) const
Definition QuestDef.h:621
std::string const & GetPortraitTurnInName() const
Definition QuestDef.h:676
std::string const & GetRequestItemsText() const
Definition QuestDef.h:669
WorldPacket BuildQueryData(LocaleConstant loc, Player *player) const
Definition QuestDef.cpp:664
std::string const & GetLogTitle() const
Definition QuestDef.h:662
uint32 GetQuestId() const
Definition QuestDef.h:633
std::string const & GetPortraitGiverText() const
Definition QuestDef.h:673
std::vector< QuestConditionalText > const & GetConditionalOfferRewardText() const
Definition QuestDef.h:668
std::string const & GetPortraitTurnInText() const
Definition QuestDef.h:675
std::string const & GetOfferRewardText() const
Definition QuestDef.h:667
std::vector< QuestConditionalText > const & GetConditionalRequestItemsText() const
Definition QuestDef.h:670
std::string const & GetLogDescription() const
Definition QuestDef.h:663
std::unique_ptr< WorldPacket[]> QueryData
Definition QuestDef.h:777
std::string const & GetQuestDescription() const
Definition QuestDef.h:664
std::vector< QuestConditionalText > const & GetConditionalQuestDescription() const
Definition QuestDef.h:665
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:919
@ 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:757
uint32 BoxBroadcastTextID
Definition ObjectMgr.h:756
std::string OptionText
Definition ObjectMgr.h:746
int32 GossipOptionID
Definition ObjectMgr.h:743
uint32 ActionMenuID
Definition ObjectMgr.h:750
uint32 OrderIndex
Definition ObjectMgr.h:744
Optional< int32 > GossipNpcOptionID
Definition ObjectMgr.h:752
Optional< int32 > OverrideIconID
Definition ObjectMgr.h:758
uint32 ActionPoiID
Definition ObjectMgr.h:751
GossipOptionNpc OptionNpc
Definition ObjectMgr.h:745
std::string BoxText
Definition ObjectMgr.h:755
GossipOptionFlags Flags
Definition ObjectMgr.h:749
uint32 OptionBroadcastTextID
Definition ObjectMgr.h:747
std::string Name
Definition ObjectMgr.h:737
uint32 Importance
Definition ObjectMgr.h:735
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