Files
TrinityCore/src/server/game/Entities/Creature/GossipDef.cpp

554 lines
20 KiB
C++

/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GossipDef.h"
#include "Log.h"
#include "NPCPackets.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "QuestDef.h"
#include "QuestPackets.h"
#include "World.h"
#include "WorldSession.h"
GossipMenu::GossipMenu()
{
_menuId = 0;
_locale = DEFAULT_LOCALE;
_senderGUID.Clear();
}
GossipMenu::~GossipMenu()
{
ClearMenu();
}
uint32 GossipMenu::AddMenuItem(int32 menuItemId, GossipOptionIcon icon, std::string const& message, uint32 sender, uint32 action, std::string const& boxMessage, uint32 boxMoney, bool coded /*= false*/)
{
ASSERT(_menuItems.size() <= GOSSIP_MAX_MENU_ITEMS);
// Find a free new id - script case
if (menuItemId == -1)
{
menuItemId = 0;
if (_menuId)
{
// set baseline menuItemId as higher than whatever exists in db
auto [itr, end] = sObjectMgr->GetGossipMenuItemsMapBounds(_menuId);
itr = std::max_element(itr, end, [](GossipMenuItemsContainer::value_type const& a, GossipMenuItemsContainer::value_type const& b)
{
return a.second.OptionID < b.second.OptionID;
});
if (itr != end)
menuItemId = itr->second.OptionID + 1;
}
if (!_menuItems.empty())
{
for (GossipMenuItemContainer::const_iterator itr = _menuItems.begin(); itr != _menuItems.end(); ++itr)
{
if (int32(itr->first) > menuItemId)
break;
menuItemId = itr->first + 1;
}
}
}
GossipMenuItem& menuItem = _menuItems[menuItemId];
menuItem.MenuItemIcon = icon;
menuItem.Message = message;
menuItem.IsCoded = coded;
menuItem.Sender = sender;
menuItem.OptionType = action;
menuItem.BoxMessage = boxMessage;
menuItem.BoxMoney = boxMoney;
return menuItemId;
}
/**
* @name AddMenuItem
* @brief Adds a localized gossip menu item from db by menu id and menu item id.
* @param menuId Gossip menu id.
* @param menuItemId Gossip menu item id.
* @param sender Identifier of the current menu.
* @param action Custom action given to OnGossipHello.
*/
void GossipMenu::AddMenuItem(uint32 menuId, uint32 menuItemId, uint32 sender, uint32 action)
{
/// Find items for given menu id.
GossipMenuItemsMapBounds bounds = sObjectMgr->GetGossipMenuItemsMapBounds(menuId);
auto itr = std::find_if(bounds.first, bounds.second, [menuItemId](std::pair<uint32 const, GossipMenuItems> const& itemPair)
{
return itemPair.second.OptionID == menuItemId;
});
if (itr == bounds.second)
return;
/// Store texts for localization.
std::string strOptionText, strBoxText;
BroadcastText const* optionBroadcastText = sObjectMgr->GetBroadcastText(itr->second.OptionBroadcastTextID);
BroadcastText const* boxBroadcastText = sObjectMgr->GetBroadcastText(itr->second.BoxBroadcastTextID);
/// OptionText
if (optionBroadcastText)
strOptionText = optionBroadcastText->GetText(GetLocale());
else
strOptionText = itr->second.OptionText;
/// BoxText
if (boxBroadcastText)
strBoxText = boxBroadcastText->GetText(GetLocale());
else
strBoxText = itr->second.BoxText;
/// Check need of localization.
if (GetLocale() != DEFAULT_LOCALE)
{
if (!optionBroadcastText)
{
/// Find localizations from database.
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(menuId, menuItemId))
ObjectMgr::GetLocaleString(gossipMenuLocale->OptionText, GetLocale(), strOptionText);
}
if (!boxBroadcastText)
{
/// Find localizations from database.
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(menuId, menuItemId))
ObjectMgr::GetLocaleString(gossipMenuLocale->BoxText, GetLocale(), strBoxText);
}
}
/// Add menu item with existing method. Menu item id -1 is also used in ADD_GOSSIP_ITEM macro.
AddMenuItem(itr->second.OptionID, itr->second.OptionIcon, strOptionText, sender, action, strBoxText, itr->second.BoxMoney, itr->second.BoxCoded);
AddGossipMenuItemData(itr->second.OptionID, itr->second.ActionMenuID, itr->second.ActionPoiID);
}
void GossipMenu::AddGossipMenuItemData(uint32 menuItemId, uint32 gossipActionMenuId, uint32 gossipActionPoi)
{
GossipMenuItemData& itemData = _menuItemData[menuItemId];
itemData.GossipActionMenuId = gossipActionMenuId;
itemData.GossipActionPoi = gossipActionPoi;
}
uint32 GossipMenu::GetMenuItemSender(uint32 menuItemId) const
{
GossipMenuItemContainer::const_iterator itr = _menuItems.find(menuItemId);
if (itr == _menuItems.end())
return 0;
return itr->second.Sender;
}
uint32 GossipMenu::GetMenuItemAction(uint32 menuItemId) const
{
GossipMenuItemContainer::const_iterator itr = _menuItems.find(menuItemId);
if (itr == _menuItems.end())
return 0;
return itr->second.OptionType;
}
bool GossipMenu::IsMenuItemCoded(uint32 menuItemId) const
{
GossipMenuItemContainer::const_iterator itr = _menuItems.find(menuItemId);
if (itr == _menuItems.end())
return false;
return itr->second.IsCoded;
}
void GossipMenu::ClearMenu()
{
_menuItems.clear();
_menuItemData.clear();
}
PlayerMenu::PlayerMenu(WorldSession* session) : _session(session)
{
if (_session)
_gossipMenu.SetLocale(_session->GetSessionDbLocaleIndex());
}
PlayerMenu::~PlayerMenu()
{
ClearMenus();
}
void PlayerMenu::ClearMenus()
{
_gossipMenu.ClearMenu();
_questMenu.ClearMenu();
}
void PlayerMenu::SendGossipMenu(uint32 titleTextId, ObjectGuid objectGUID)
{
_gossipMenu.SetSenderGUID(objectGUID);
WorldPacket data(SMSG_GOSSIP_MESSAGE, 100); // guess size
data << uint64(objectGUID);
data << uint32(_gossipMenu.GetMenuId()); // new 2.4.0
data << uint32(titleTextId);
data << uint32(_gossipMenu.GetMenuItemCount()); // max count 0x10
for (GossipMenuItemContainer::const_iterator itr = _gossipMenu.GetMenuItems().begin(); itr != _gossipMenu.GetMenuItems().end(); ++itr)
{
GossipMenuItem const& item = itr->second;
data << uint32(itr->first);
data << uint8(item.MenuItemIcon);
data << uint8(item.IsCoded); // makes pop up box password
data << uint32(item.BoxMoney); // money required to open menu, 2.0.3
data << item.Message; // text for gossip item
data << item.BoxMessage; // accept text (related to money) pop up box, 2.0.3
}
size_t count_pos = data.wpos();
data << uint32(0); // max count 0x20
uint32 count = 0;
for (uint8 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
{
QuestMenuItem const& item = _questMenu.GetItem(i);
uint32 questID = item.QuestId;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID))
{
++count;
data << uint32(questID);
data << uint32(item.QuestIcon);
data << int32(quest->GetQuestLevel());
data << uint32(quest->GetFlags()); // 3.3.3 quest flags
data << uint8(quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly()); // 3.3.3 icon changes - 0: yellow exclapamtion mark, 1: blue question mark
std::string title = quest->GetTitle();
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(questID))
ObjectMgr::GetLocaleString(localeData->Title, localeConstant, title);
data << title; // max 0x200
}
}
data.put<uint8>(count_pos, count);
_session->SendPacket(&data);
}
void PlayerMenu::SendCloseGossip()
{
_gossipMenu.SetSenderGUID(ObjectGuid::Empty);
WorldPacket data(SMSG_GOSSIP_COMPLETE, 0);
_session->SendPacket(&data);
}
void PlayerMenu::SendPointOfInterest(uint32 id) const
{
PointOfInterest const* pointOfInterest = sObjectMgr->GetPointOfInterest(id);
if (!pointOfInterest)
{
TC_LOG_ERROR("sql.sql", "Request to send non-existing POI (Id: {}), ignored.", id);
return;
}
WorldPackets::NPC::GossipPOI packet;
packet.Name = pointOfInterest->Name;
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
if (PointOfInterestLocale const* localeData = sObjectMgr->GetPointOfInterestLocale(id))
ObjectMgr::GetLocaleString(localeData->Name, localeConstant, packet.Name);
packet.Flags = pointOfInterest->Flags;
packet.Pos.Pos.Relocate(pointOfInterest->PositionX, pointOfInterest->PositionY);
packet.Icon = pointOfInterest->Icon;
packet.Importance = pointOfInterest->Importance;
_session->SendPacket(packet.Write());
}
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
QuestMenu::QuestMenu()
{
_questMenuItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
}
QuestMenu::~QuestMenu()
{
ClearMenu();
}
void QuestMenu::AddMenuItem(uint32 QuestId, uint8 Icon)
{
if (!sObjectMgr->GetQuestTemplate(QuestId))
return;
ASSERT(_questMenuItems.size() <= GOSSIP_MAX_MENU_ITEMS);
QuestMenuItem questMenuItem;
questMenuItem.QuestId = QuestId;
questMenuItem.QuestIcon = Icon;
_questMenuItems.push_back(questMenuItem);
}
bool QuestMenu::HasItem(uint32 questId) const
{
for (QuestMenuItemList::const_iterator i = _questMenuItems.begin(); i != _questMenuItems.end(); ++i)
if (i->QuestId == questId)
return true;
return false;
}
void QuestMenu::ClearMenu()
{
_questMenuItems.clear();
}
void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string& Title, ObjectGuid guid)
{
WorldPacket data(SMSG_QUESTGIVER_QUEST_LIST, 100); // guess size
data << uint64(guid);
if (QuestGreeting const* questGreeting = sObjectMgr->GetQuestGreeting(guid))
{
std::string strGreeting = questGreeting->greeting;
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
if (QuestGreetingLocale const* questGreetingLocale = sObjectMgr->GetQuestGreetingLocale(MAKE_PAIR32(guid.GetEntry(), guid.GetTypeId())))
ObjectMgr::GetLocaleString(questGreetingLocale->greeting, localeConstant, strGreeting);
data << strGreeting;
data << uint32(questGreeting->greetEmoteDelay);
data << uint32(questGreeting->greetEmoteType);
}
else
{
data << Title;
data << uint32(eEmote._Delay); // player emote
data << uint32(eEmote._Emote); // NPC emote
}
size_t count_pos = data.wpos();
data << uint8(0);
uint32 count = 0;
for (uint32 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
{
QuestMenuItem const& questMenuItem = _questMenu.GetItem(i);
uint32 questID = questMenuItem.QuestId;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID))
{
++count;
std::string title = quest->GetTitle();
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
if (QuestLocale const* questTemplateLocaleData = sObjectMgr->GetQuestLocale(questID))
ObjectMgr::GetLocaleString(questTemplateLocaleData->Title, localeConstant, title);
data << uint32(questID);
data << uint32(questMenuItem.QuestIcon);
data << int32(quest->GetQuestLevel());
data << uint32(quest->GetFlags()); // 3.3.3 quest flags
data << uint8(quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly()); // 3.3.3 icon changes - 0: yellow exclapamtion mark, 1: blue question mark
data << title;
}
}
data.put<uint8>(count_pos, count);
_session->SendPacket(&data);
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST (QuestGiver: {})", guid.ToString());
}
void PlayerMenu::SendQuestGiverStatus(uint8 questStatus, ObjectGuid npcGUID) const
{
WorldPacket data(SMSG_QUESTGIVER_STATUS, 9);
data << uint64(npcGUID);
data << uint8(questStatus);
_session->SendPacket(&data);
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC={}, status={}", npcGUID.ToString(), questStatus);
}
void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGUID, bool activateAccept) const
{
WorldPackets::Quest::QuestGiverQuestDetails packet;
packet.Title = quest->GetTitle();
packet.Details = quest->GetDetails();
packet.Objectives = quest->GetObjectives();
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
{
if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
{
ObjectMgr::GetLocaleString(localeData->Title, localeConstant, packet.Title);
ObjectMgr::GetLocaleString(localeData->Details, localeConstant, packet.Details);
ObjectMgr::GetLocaleString(localeData->Objectives, localeConstant, packet.Objectives);
}
}
packet.QuestGiverGUID = npcGUID;
packet.InformUnit = _session->GetPlayer()->GetPlayerSharingQuest();
packet.QuestID = quest->GetQuestId();
packet.AutoLaunched = activateAccept;
packet.Flags = quest->GetFlags() & (sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) ? ~QUEST_FLAGS_AUTO_ACCEPT : ~0);
packet.SuggestedGroupNum = quest->GetSuggestedPlayers();
quest->BuildQuestRewards(packet.Rewards, _session->GetPlayer());
packet.DescEmotes.reserve(QUEST_EMOTE_COUNT);
for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
packet.DescEmotes.emplace_back(quest->DetailsEmote[i], quest->DetailsEmoteDelay[i]);
_session->SendPacket(packet.Write());
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_QUEST_DETAILS NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
}
void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
{
if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
_session->SendPacket(&quest->QueryData[static_cast<uint32>(_session->GetSessionDbLocaleIndex())]);
else
{
WorldPacket queryPacket = quest->BuildQueryData(_session->GetSessionDbLocaleIndex());
_session->SendPacket(&queryPacket);
}
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid={}", quest->GetQuestId());
}
void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched) const
{
WorldPackets::Quest::QuestGiverOfferRewardMessage packet;
packet.Title = quest->GetTitle();
packet.RewardText = quest->GetOfferRewardText();
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
{
if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
ObjectMgr::GetLocaleString(localeData->Title, localeConstant, packet.Title);
if (QuestOfferRewardLocale const* questOfferRewardLocale = sObjectMgr->GetQuestOfferRewardLocale(quest->GetQuestId()))
ObjectMgr::GetLocaleString(questOfferRewardLocale->RewardText, localeConstant, packet.RewardText);
}
packet.QuestGiverGUID = npcGUID;
packet.QuestID = quest->GetQuestId();
packet.AutoLaunched = autoLaunched;
packet.Flags = quest->GetFlags();
packet.SuggestedGroupNum = quest->GetSuggestedPlayers();
for (uint32 i = 0; i < QUEST_EMOTE_COUNT && quest->OfferRewardEmote[i]; ++i)
packet.Emotes.emplace_back(quest->OfferRewardEmote[i], quest->OfferRewardEmoteDelay[i]);
quest->BuildQuestRewards(packet.Rewards, _session->GetPlayer(), true);
_session->SendPacket(packet.Write());
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_OFFER_REWARD NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
}
void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool closeOnCancel) const
{
// We can always call to RequestItems, but this packet only goes out if there are actually
// items. Otherwise, we'll skip straight to the OfferReward
std::string questTitle = quest->GetTitle();
std::string requestItemsText = quest->GetRequestItemsText();
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
if (localeConstant != LOCALE_enUS)
{
if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(quest->GetQuestId()))
ObjectMgr::GetLocaleString(localeData->Title, localeConstant, questTitle);
if (QuestRequestItemsLocale const* questRequestItemsLocale = sObjectMgr->GetQuestRequestItemsLocale(quest->GetQuestId()))
ObjectMgr::GetLocaleString(questRequestItemsLocale->CompletionText, localeConstant, requestItemsText);
}
if (!quest->GetReqItemsCount() && canComplete)
{
SendQuestGiverOfferReward(quest, npcGUID, true);
return;
}
WorldPacket data(SMSG_QUESTGIVER_REQUEST_ITEMS, 50); // guess size
data << uint64(npcGUID);
data << uint32(quest->GetQuestId());
data << questTitle;
data << requestItemsText;
data << uint32(0); // unknown
if (canComplete)
data << quest->GetCompleteEmote();
else
data << quest->GetIncompleteEmote();
// Close Window after cancel
data << uint32(closeOnCancel);
data << uint32(quest->GetFlags()); // 3.3.3 questFlags
data << uint32(quest->GetSuggestedPlayers()); // SuggestedGroupNum
// Required Money
data << uint32(quest->GetRewOrReqMoney() < 0 ? -quest->GetRewOrReqMoney() : 0);
data << uint32(quest->GetReqItemsCount());
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if (!quest->RequiredItemId[i])
continue;
data << uint32(quest->RequiredItemId[i]);
data << uint32(quest->RequiredItemCount[i]);
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
data << uint32(itemTemplate->DisplayInfoID);
else
data << uint32(0);
}
if (!canComplete)
data << uint32(0x00);
else
data << uint32(0x03);
data << uint32(0x04);
data << uint32(0x08);
data << uint32(0x10);
_session->SendPacket(&data);
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPC={}, questid={}", npcGUID.ToString(), quest->GetQuestId());
}