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