mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 09:17:18 +00:00
425 lines
16 KiB
C++
425 lines
16 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*/
|
|
|
|
#include "BattlegroundMgr.h"
|
|
#include "Language.h"
|
|
#include "OutdoorPvPMgr.h"
|
|
#include "Pet.h"
|
|
#include "Player.h"
|
|
|
|
/*********************************************************/
|
|
/*** GOSSIP SYSTEM ***/
|
|
/*********************************************************/
|
|
|
|
void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool showQuests /*= false*/)
|
|
{
|
|
PlayerMenu* menu = PlayerTalkClass;
|
|
menu->ClearMenus();
|
|
|
|
menu->GetGossipMenu().SetMenuId(menuId);
|
|
|
|
GossipMenuItemsMapBounds menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(menuId);
|
|
|
|
// if default menuId and no menu options exist for this, use options from default options
|
|
if (menuItemBounds.first == menuItemBounds.second && menuId == GetDefaultGossipMenuForSource(source))
|
|
menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(0);
|
|
|
|
uint32 npcflags = 0;
|
|
|
|
if (source->GetTypeId() == TYPEID_UNIT)
|
|
{
|
|
npcflags = source->GetUInt32Value(UNIT_NPC_FLAGS);
|
|
if (showQuests && npcflags & UNIT_NPC_FLAG_QUESTGIVER)
|
|
PrepareQuestMenu(source->GetGUID());
|
|
}
|
|
else if (source->GetTypeId() == TYPEID_GAMEOBJECT)
|
|
if (showQuests && source->ToGameObject()->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
|
|
PrepareQuestMenu(source->GetGUID());
|
|
|
|
for (GossipMenuItemsContainer::const_iterator itr = menuItemBounds.first; itr != menuItemBounds.second; ++itr)
|
|
{
|
|
bool canTalk = true;
|
|
if (!sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.Conditions))
|
|
continue;
|
|
|
|
if (Creature* creature = source->ToCreature())
|
|
{
|
|
if (!(itr->second.OptionNpcFlag & npcflags))
|
|
continue;
|
|
|
|
switch (itr->second.OptionType)
|
|
{
|
|
case GOSSIP_OPTION_ARMORER:
|
|
canTalk = false; // added in special mode
|
|
break;
|
|
case GOSSIP_OPTION_SPIRITHEALER:
|
|
if (!isDead())
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_VENDOR:
|
|
{
|
|
if (!creature->isVendorWithIconSpeak())
|
|
{
|
|
VendorItemData const* vendorItems = itr->second.ActionMenuID ? sObjectMgr->GetNpcVendorItemList(itr->second.ActionMenuID) : creature->GetVendorItems();
|
|
if (!vendorItems || vendorItems->Empty())
|
|
{
|
|
LOG_ERROR("sql.sql", "Creature %s have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", creature->GetGUID().ToString().c_str());
|
|
canTalk = false;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case GOSSIP_OPTION_LEARNDUALSPEC:
|
|
case GOSSIP_OPTION_DUALSPEC_INFO:
|
|
if (!(GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))))
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_UNLEARNTALENTS:
|
|
if (!creature->isCanTrainingAndResetTalentsOf(this))
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_UNLEARNPETTALENTS:
|
|
if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || creature->GetCreatureTemplate()->trainer_type != TRAINER_TYPE_PETS || creature->GetCreatureTemplate()->trainer_class != CLASS_HUNTER)
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_TAXIVENDOR:
|
|
if (GetSession()->SendLearnNewTaxiNode(creature))
|
|
return;
|
|
break;
|
|
case GOSSIP_OPTION_BATTLEFIELD:
|
|
if (!creature->isCanInteractWithBattleMaster(this, false))
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_STABLEPET:
|
|
if (getClass() != CLASS_HUNTER)
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_QUESTGIVER:
|
|
canTalk = false;
|
|
break;
|
|
case GOSSIP_OPTION_TRAINER:
|
|
if (!creature->IsValidTrainerForPlayer(this))
|
|
{
|
|
canTalk = false;
|
|
}
|
|
break;
|
|
case GOSSIP_OPTION_GOSSIP:
|
|
if (creature->isVendorWithIconSpeak())
|
|
{
|
|
VendorItemData const* vendorItems = creature->GetVendorItems();
|
|
if (!vendorItems || vendorItems->Empty())
|
|
{
|
|
canTalk = false;
|
|
}
|
|
}
|
|
break;
|
|
case GOSSIP_OPTION_SPIRITGUIDE:
|
|
case GOSSIP_OPTION_INNKEEPER:
|
|
case GOSSIP_OPTION_BANKER:
|
|
case GOSSIP_OPTION_PETITIONER:
|
|
case GOSSIP_OPTION_TABARDDESIGNER:
|
|
case GOSSIP_OPTION_AUCTIONEER:
|
|
break; // no checks
|
|
case GOSSIP_OPTION_OUTDOORPVP:
|
|
if (!sOutdoorPvPMgr->CanTalkTo(this, creature, itr->second))
|
|
canTalk = false;
|
|
break;
|
|
default:
|
|
LOG_ERROR("sql.sql", "Creature entry %u has unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuID);
|
|
canTalk = false;
|
|
break;
|
|
}
|
|
}
|
|
else if (GameObject* go = source->ToGameObject())
|
|
{
|
|
switch (itr->second.OptionType)
|
|
{
|
|
case GOSSIP_OPTION_GOSSIP:
|
|
if (go->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER && go->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
|
|
canTalk = false;
|
|
break;
|
|
default:
|
|
canTalk = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (canTalk)
|
|
{
|
|
std::string strOptionText, strBoxText;
|
|
BroadcastText const* optionBroadcastText = sObjectMgr->GetBroadcastText(itr->second.OptionBroadcastTextID);
|
|
BroadcastText const* boxBroadcastText = sObjectMgr->GetBroadcastText(itr->second.BoxBroadcastTextID);
|
|
LocaleConstant locale = GetSession()->GetSessionDbLocaleIndex();
|
|
|
|
if (optionBroadcastText)
|
|
ObjectMgr::GetLocaleString(getGender() == GENDER_MALE ? optionBroadcastText->MaleText : optionBroadcastText->FemaleText, locale, strOptionText);
|
|
else
|
|
strOptionText = itr->second.OptionText;
|
|
|
|
if (boxBroadcastText)
|
|
ObjectMgr::GetLocaleString(getGender() == GENDER_MALE ? boxBroadcastText->MaleText : boxBroadcastText->FemaleText, locale, strBoxText);
|
|
else
|
|
strBoxText = itr->second.BoxText;
|
|
|
|
if (locale != DEFAULT_LOCALE)
|
|
{
|
|
if (!optionBroadcastText)
|
|
{
|
|
/// Find localizations from database.
|
|
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, itr->second.OptionID)))
|
|
ObjectMgr::GetLocaleString(gossipMenuLocale->OptionText, locale, strOptionText);
|
|
}
|
|
|
|
if (!boxBroadcastText)
|
|
{
|
|
/// Find localizations from database.
|
|
if (GossipMenuItemsLocale const* gossipMenuLocale = sObjectMgr->GetGossipMenuItemsLocale(MAKE_PAIR32(menuId, itr->second.OptionID)))
|
|
ObjectMgr::GetLocaleString(gossipMenuLocale->BoxText, locale, strBoxText);
|
|
}
|
|
}
|
|
|
|
menu->GetGossipMenu().AddMenuItem(itr->second.OptionID, itr->second.OptionIcon, strOptionText, 0, itr->second.OptionType, strBoxText, itr->second.BoxMoney, itr->second.BoxCoded);
|
|
menu->GetGossipMenu().AddGossipMenuItemData(itr->second.OptionID, itr->second.ActionMenuID, itr->second.ActionPoiID);
|
|
}
|
|
}
|
|
|
|
if (sWorld->getIntConfig(CONFIG_INSTANT_TAXI) == 2 && npcflags & UNIT_NPC_FLAG_FLIGHTMASTER)
|
|
menu->GetGossipMenu().AddMenuItem(-1, GOSSIP_ICON_INTERACT_1, GetSession()->GetAcoreString(LANG_TOGGLE_INSTANT_FLIGHT), 0, GOSSIP_ACTION_TOGGLE_INSTANT_FLIGHT, "", 0, false); // instant flight toggle option
|
|
}
|
|
|
|
void Player::SendPreparedGossip(WorldObject* source)
|
|
{
|
|
if (!source)
|
|
return;
|
|
|
|
if (source->GetTypeId() == TYPEID_UNIT)
|
|
{
|
|
// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
|
|
if (!source->ToCreature()->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
|
|
{
|
|
SendPreparedQuest(source->GetGUID());
|
|
return;
|
|
}
|
|
}
|
|
else if (source->GetTypeId() == TYPEID_GAMEOBJECT)
|
|
{
|
|
// probably need to find a better way here
|
|
if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty())
|
|
{
|
|
SendPreparedQuest(source->GetGUID());
|
|
return;
|
|
}
|
|
}
|
|
|
|
// in case non empty gossip menu (that not included quests list size) show it
|
|
// (quest entries from quest menu will be included in list)
|
|
|
|
uint32 textId = GetGossipTextId(source);
|
|
|
|
if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
|
|
textId = GetGossipTextId(menuId, source);
|
|
|
|
PlayerTalkClass->SendGossipMenu(textId, source->GetGUID());
|
|
}
|
|
|
|
void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 menuId)
|
|
{
|
|
GossipMenu& gossipMenu = PlayerTalkClass->GetGossipMenu();
|
|
|
|
// if not same, then something funky is going on
|
|
if (menuId != gossipMenu.GetMenuId())
|
|
return;
|
|
|
|
GossipMenuItem const* item = gossipMenu.GetItem(gossipListId);
|
|
if (!item)
|
|
return;
|
|
|
|
uint32 gossipOptionId = item->OptionType;
|
|
ObjectGuid guid = source->GetGUID();
|
|
|
|
if (sWorld->getIntConfig(CONFIG_INSTANT_TAXI) == 2 && source->GetTypeId() == TYPEID_UNIT)
|
|
{
|
|
if (gossipOptionId == GOSSIP_ACTION_TOGGLE_INSTANT_FLIGHT && source->GetUInt32Value(UNIT_NPC_FLAGS) & UNIT_NPC_FLAG_FLIGHTMASTER)
|
|
{
|
|
ToggleInstantFlight();
|
|
|
|
if (m_isInstantFlightOn)
|
|
GetSession()->SendNotification(LANG_INSTANT_FLIGHT_ON);
|
|
else
|
|
GetSession()->SendNotification(LANG_INSTANT_FLIGHT_OFF);
|
|
|
|
PlayerTalkClass->SendCloseGossip();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (source->GetTypeId() == TYPEID_GAMEOBJECT)
|
|
{
|
|
if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
|
|
{
|
|
LOG_ERROR("entities.player", "Player guid %s request invalid gossip option for GameObject entry %u", GetGUID().ToString().c_str(), source->GetEntry());
|
|
return;
|
|
}
|
|
}
|
|
|
|
GossipMenuItemData const* menuItemData = gossipMenu.GetItemData(gossipListId);
|
|
if (!menuItemData)
|
|
return;
|
|
|
|
int32 cost = int32(item->BoxMoney);
|
|
if (!HasEnoughMoney(cost))
|
|
{
|
|
SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
|
|
PlayerTalkClass->SendCloseGossip();
|
|
return;
|
|
}
|
|
|
|
switch (gossipOptionId)
|
|
{
|
|
case GOSSIP_OPTION_GOSSIP:
|
|
case GOSSIP_OPTION_DUALSPEC_INFO:
|
|
{
|
|
if (menuItemData->GossipActionPoi)
|
|
PlayerTalkClass->SendPointOfInterest(menuItemData->GossipActionPoi);
|
|
|
|
if (menuItemData->GossipActionMenuId)
|
|
{
|
|
PrepareGossipMenu(source, menuItemData->GossipActionMenuId);
|
|
SendPreparedGossip(source);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case GOSSIP_OPTION_OUTDOORPVP:
|
|
sOutdoorPvPMgr->HandleGossipOption(this, source->ToCreature(), gossipListId);
|
|
break;
|
|
case GOSSIP_OPTION_SPIRITHEALER:
|
|
if (isDead())
|
|
source->ToCreature()->CastSpell(source->ToCreature(), 17251, true, nullptr, nullptr, GetGUID());
|
|
break;
|
|
case GOSSIP_OPTION_QUESTGIVER:
|
|
PrepareQuestMenu(guid);
|
|
SendPreparedQuest(guid);
|
|
break;
|
|
case GOSSIP_OPTION_VENDOR:
|
|
case GOSSIP_OPTION_ARMORER:
|
|
GetSession()->SendListInventory(guid, menuItemData->GossipActionMenuId);
|
|
break;
|
|
case GOSSIP_OPTION_STABLEPET:
|
|
GetSession()->SendStablePet(guid);
|
|
break;
|
|
case GOSSIP_OPTION_TRAINER:
|
|
GetSession()->SendTrainerList(guid);
|
|
break;
|
|
case GOSSIP_OPTION_LEARNDUALSPEC:
|
|
if (GetSpecsCount() == 1 && getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))
|
|
{
|
|
// Cast spells that teach dual spec
|
|
// Both are also ImplicitTarget self and must be cast by player
|
|
CastSpell(this, 63680, true, nullptr, nullptr, GetGUID());
|
|
CastSpell(this, 63624, true, nullptr, nullptr, GetGUID());
|
|
|
|
PrepareGossipMenu(source, menuItemData->GossipActionMenuId);
|
|
SendPreparedGossip(source);
|
|
}
|
|
break;
|
|
case GOSSIP_OPTION_UNLEARNTALENTS:
|
|
PlayerTalkClass->SendCloseGossip();
|
|
SendTalentWipeConfirm(guid);
|
|
break;
|
|
case GOSSIP_OPTION_UNLEARNPETTALENTS:
|
|
PlayerTalkClass->SendCloseGossip();
|
|
ResetPetTalents();
|
|
break;
|
|
case GOSSIP_OPTION_TAXIVENDOR:
|
|
GetSession()->SendTaxiMenu(source->ToCreature());
|
|
break;
|
|
case GOSSIP_OPTION_INNKEEPER:
|
|
PlayerTalkClass->SendCloseGossip();
|
|
SetBindPoint(guid);
|
|
break;
|
|
case GOSSIP_OPTION_BANKER:
|
|
GetSession()->SendShowBank(guid);
|
|
break;
|
|
case GOSSIP_OPTION_PETITIONER:
|
|
PlayerTalkClass->SendCloseGossip();
|
|
GetSession()->SendPetitionShowList(guid);
|
|
break;
|
|
case GOSSIP_OPTION_TABARDDESIGNER:
|
|
PlayerTalkClass->SendCloseGossip();
|
|
GetSession()->SendTabardVendorActivate(guid);
|
|
break;
|
|
case GOSSIP_OPTION_AUCTIONEER:
|
|
GetSession()->SendAuctionHello(guid, source->ToCreature());
|
|
break;
|
|
case GOSSIP_OPTION_SPIRITGUIDE:
|
|
PrepareGossipMenu(source);
|
|
SendPreparedGossip(source);
|
|
break;
|
|
case GOSSIP_OPTION_BATTLEFIELD:
|
|
{
|
|
BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(source->GetEntry());
|
|
|
|
if (bgTypeId == BATTLEGROUND_TYPE_NONE)
|
|
{
|
|
LOG_ERROR("entities.player", "A user (%s) requested battlegroundlist from a npc who is no battlemaster", GetGUID().ToString().c_str());
|
|
return;
|
|
}
|
|
|
|
GetSession()->SendBattleGroundList(guid, bgTypeId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ModifyMoney(-cost);
|
|
}
|
|
|
|
uint32 Player::GetGossipTextId(WorldObject* source)
|
|
{
|
|
if (!source)
|
|
return DEFAULT_GOSSIP_MESSAGE;
|
|
|
|
return GetGossipTextId(GetDefaultGossipMenuForSource(source), source);
|
|
}
|
|
|
|
uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* source)
|
|
{
|
|
uint32 textId = DEFAULT_GOSSIP_MESSAGE;
|
|
|
|
if (!menuId)
|
|
return textId;
|
|
|
|
GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuId);
|
|
|
|
for (GossipMenusContainer::const_iterator itr = menuBounds.first; itr != menuBounds.second; ++itr)
|
|
{
|
|
if (sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.Conditions))
|
|
textId = itr->second.TextID;
|
|
}
|
|
|
|
return textId;
|
|
}
|
|
|
|
uint32 Player::GetDefaultGossipMenuForSource(WorldObject* source)
|
|
{
|
|
switch (source->GetTypeId())
|
|
{
|
|
case TYPEID_UNIT:
|
|
return source->ToCreature()->GetCreatureTemplate()->GossipMenuId;
|
|
case TYPEID_GAMEOBJECT:
|
|
return source->ToGameObject()->GetGOInfo()->GetGossipMenuId();
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Player::ToggleInstantFlight()
|
|
{
|
|
m_isInstantFlightOn = !m_isInstantFlightOn;
|
|
} |