Files
azerothcore-wotlk/src/server/game/Handlers/NPCHandler.cpp

1044 lines
34 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#include "Common.h"
#include "Language.h"
#include "DatabaseEnv.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Player.h"
#include "GossipDef.h"
#include "UpdateMask.h"
#include "ObjectAccessor.h"
#include "Creature.h"
#include "Pet.h"
#include "ReputationMgr.h"
#include "BattlegroundMgr.h"
#include "Battleground.h"
#include "ScriptMgr.h"
#include "CreatureAI.h"
#include "SpellInfo.h"
#include "GameGraveyard.h"
enum StableResultCode
{
STABLE_ERR_MONEY = 0x01, // "you don't have enough money"
STABLE_ERR_STABLE = 0x06, // currently used in most fail cases
STABLE_SUCCESS_STABLE = 0x08, // stable success
STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success
STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success
STABLE_ERR_EXOTIC = 0x0C, // "you are unable to control exotic creatures"
};
void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket & recvData)
{
uint64 guid;
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TABARDDESIGNER);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTabardVendorActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendTabardVendorActivate(guid);
}
void WorldSession::SendTabardVendorActivate(uint64 guid)
{
WorldPacket data(MSG_TABARDVENDOR_ACTIVATE, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleBankerActivateOpcode(WorldPacket & recvData)
{
uint64 guid;
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BANKER_ACTIVATE");
#endif
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBankerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendShowBank(guid);
}
void WorldSession::SendShowBank(uint64 guid)
{
WorldPacket data(SMSG_SHOW_BANK, 8);
data << guid;
m_currentBankerGUID = guid;
SendPacket(&data);
}
void WorldSession::SendShowMailBox(uint64 guid)
{
WorldPacket data(SMSG_SHOW_MAILBOX, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleTrainerListOpcode(WorldPacket & recvData)
{
uint64 guid;
recvData >> guid;
SendTrainerList(guid);
}
void WorldSession::SendTrainerList(uint64 guid)
{
std::string str = GetAcoreString(LANG_NPC_TAINER_HELLO);
SendTrainerList(guid, str);
}
void WorldSession::SendTrainerList(uint64 guid, const std::string& strTitle)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList");
#endif
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
CreatureTemplate const* ci = unit->GetCreatureTemplate();
if (!ci)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - (GUID: %u) NO CREATUREINFO!", GUID_LOPART(guid));
#endif
return;
}
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendTrainerList - Training spells not found for creature (GUID: %u Entry: %u)", GUID_LOPART(guid), unit->GetEntry());
#endif
return;
}
WorldPacket data(SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
data << guid;
data << uint32(trainer_spells->trainerType);
size_t count_pos = data.wpos();
data << uint32(trainer_spells->spellList.size());
// reputation discount
float fDiscountMod = _player->GetReputationPriceDiscount(unit);
bool can_learn_primary_prof = GetPlayer()->GetFreePrimaryProfessionPoints() > 0;
uint32 count = 0;
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
{
TrainerSpell const* tSpell = &itr->second;
bool valid = true;
bool primary_prof_first_rank = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if (!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
{
valid = false;
break;
}
SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(tSpell->learnedSpell[i]);
if (learnedSpellInfo && learnedSpellInfo->IsPrimaryProfessionFirstRank())
primary_prof_first_rank = true;
}
if (!valid)
continue;
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
data << uint8(state == TRAINER_SPELL_GREEN_DISABLED ? TRAINER_SPELL_GREEN : state);
data << uint32(floor(tSpell->spellCost * fDiscountMod));
data << uint32(primary_prof_first_rank && can_learn_primary_prof ? 1 : 0);
// primary prof. learn confirmation dialog
data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
data << uint8(tSpell->reqLevel);
data << uint32(tSpell->reqSkill);
data << uint32(tSpell->reqSkillValue);
//prev + req or req + 0
uint8 maxReq = 0;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if (uint32 prevSpellId = sSpellMgr->GetPrevSpellInChain(tSpell->learnedSpell[i]))
{
data << uint32(prevSpellId);
++maxReq;
}
if (maxReq == 3)
break;
SpellsRequiringSpellMapBounds spellsRequired = sSpellMgr->GetSpellsRequiredForSpellBounds(tSpell->learnedSpell[i]);
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
{
data << uint32(itr2->second);
++maxReq;
}
if (maxReq == 3)
break;
}
while (maxReq < 3)
{
data << uint32(0);
++maxReq;
}
++count;
}
data << strTitle;
data.put<uint32>(count_pos, count);
SendPacket(&data);
}
void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket & recvData)
{
uint64 guid;
uint32 spellId = 0;
recvData >> guid >> spellId;
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u", uint32(GUID_LOPART(guid)), spellId);
#endif
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTrainerBuySpellOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// check present spell in trainer spell list
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
return;
// not found, cheat?
TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
if (!trainer_spell)
return;
// can't be learn, cheat? Or double learn with lags...
if (_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
return;
// apply reputation discount
uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit)));
// check money requirement
if (!_player->HasEnoughMoney(nSpellCost))
return;
_player->ModifyMoney(-int32(nSpellCost));
unit->SendPlaySpellVisual(179); // 53 SpellCastDirected
unit->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute
// learn explicitly or cast explicitly
if (trainer_spell->IsCastable())
_player->CastSpell(_player, trainer_spell->spell, true);
else
_player->learnSpell(spellId);
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, 12);
data << uint64(guid);
data << uint32(spellId); // should be same as in packet from client
SendPacket(&data);
}
void WorldSession::HandleGossipHelloOpcode(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_GOSSIP_HELLO");
#endif
uint64 guid;
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleGossipHelloOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// xinef: check if we have ANY npc flags
if (unit->GetUInt32Value(UNIT_NPC_FLAGS) == UNIT_NPC_FLAG_NONE)
return;
// xinef: do not allow to open gossip when npc is in combat
if (unit->GetUInt32Value(UNIT_NPC_FLAGS) == UNIT_NPC_FLAG_GOSSIP && unit->IsInCombat()) // should work on all flags?
return;
// set faction visible if needed
if (FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction()))
_player->GetReputationMgr().SetVisible(factionTemplateEntry);
GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
// remove fake death
//if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// xinef: and if he has pure gossip or is banker and moves or is tabard designer?
//if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard())
{
//if (!unit->GetTransport()) // pussywizard: reverted with new spline (old: without this check, npc would stay in place and the transport would continue moving, so the npc falls off. NPCs on transports don't have waypoints, so stopmoving is not needed)
unit->StopMoving();
}
// If spiritguide, no need for gossip menu, just put player into resurrect queue
if (unit->IsSpiritGuide())
{
Battleground* bg = _player->GetBattleground();
if (bg)
{
bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
return;
}
}
if (!sScriptMgr->OnGossipHello(_player, unit))
{
// _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID());
_player->PrepareGossipMenu(unit, unit->GetCreatureTemplate()->GossipMenuId, true);
_player->SendPreparedGossip(unit);
}
unit->AI()->sGossipHello(_player);
}
/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_GOSSIP_SELECT_OPTION");
#endif
uint32 option;
uint32 unk;
uint64 guid;
std::string code = "";
recvData >> guid >> unk >> option;
if (_player->PlayerTalkClass->GossipOptionCoded(option))
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_PACKETIO, "reading string");
#endif
recvData >> code;
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_PACKETIO, "string read: %s", code.c_str());
#endif
}
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (!code.empty())
{
if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
unit->OnGossipSelect (_player, option);
}
else
{
if (!Script->OnGossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option)))
unit->OnGossipSelect (_player, option);
}
}*/
void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
#endif
uint64 guid;
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleSpiritHealerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendSpiritResurrect();
}
void WorldSession::SendSpiritResurrect()
{
_player->ResurrectPlayer(0.5f, true);
_player->DurabilityLossAll(0.25f, true);
// get corpse nearest graveyard
GraveyardStruct const* corpseGrave = NULL;
Corpse* corpse = _player->GetCorpse();
if (corpse)
corpseGrave = sGraveyard->GetClosestGraveyard(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeamId());
// now can spawn bones
_player->SpawnCorpseBones();
// teleport to nearest from corpse graveyard, if different from nearest to player ghost
if (corpseGrave)
{
GraveyardStruct const* ghostGrave = sGraveyard->GetClosestGraveyard(_player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeamId());
if (corpseGrave != ghostGrave)
_player->TeleportTo(corpseGrave->Map, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
// or update at original position
//else
// _player->UpdateObjectVisibility(); // xinef: not needed, called in ResurrectPlayer
}
// or update at original position
//else
// _player->UpdateObjectVisibility(); // xinef: not needed, called in ResurrectPlayer
}
void WorldSession::HandleBinderActivateOpcode(WorldPacket & recvData)
{
uint64 npcGUID;
recvData >> npcGUID;
if (!GetPlayer()->IsInWorld() || !GetPlayer()->IsAlive())
return;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_INNKEEPER);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendBindPoint(unit);
}
void WorldSession::SendBindPoint(Creature* npc)
{
// prevent set homebind to instances in any case
if (GetPlayer()->GetMap()->Instanceable())
return;
uint32 bindspell = 3286;
// send spell for homebinding (3286)
npc->CastSpell(_player, bindspell, true);
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
data << uint64(npc->GetGUID());
data << uint32(bindspell);
SendPacket(&data);
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleListStabledPetsOpcode(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS");
#endif
uint64 npcGUID;
recvData >> npcGUID;
if (!CheckStableMaster(npcGUID))
return;
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// remove mounts this fix bug where getting pet from stable while mounted deletes pet.
if (GetPlayer()->IsMounted())
GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED);
SendStablePet(npcGUID);
}
void WorldSession::SendStablePet(uint64 guid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_sendStabledPetCallback.SetParam(guid);
_sendStabledPetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
}
void WorldSession::SendStablePetCallback(PreparedQueryResult result, uint64 guid)
{
if (!GetPlayer())
return;
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
#endif
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
data << uint64 (guid);
Pet* pet = _player->GetPet();
size_t wpos = data.wpos();
data << uint8(0); // place holder for slot show number
data << uint8(GetPlayer()->m_stableSlots);
uint8 num = 0; // counter for place holder
// not let move dead pet in slot
if (pet && pet->IsAlive() && pet->getPetType() == HUNTER_PET)
{
data << uint32(pet->GetCharmInfo()->GetPetNumber());
data << uint32(pet->GetEntry());
data << uint32(pet->getLevel());
data << pet->GetName(); // petname
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
++num;
}
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt8(1, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
if (PreparedQueryResult _result = CharacterDatabase.AsyncQuery(stmt))
{
Field* fields = _result->Fetch();
data << uint32(fields[0].GetUInt32()); // id
data << uint32(fields[1].GetUInt32()); // entry
data << uint32(fields[4].GetUInt16()); // level
data << fields[8].GetString(); // petname
data << uint8(1);
++num;
}
}
if (result)
{
do
{
Field* fields = result->Fetch();
data << uint32(fields[1].GetUInt32()); // petnumber
data << uint32(fields[2].GetUInt32()); // creature entry
data << uint32(fields[3].GetUInt16()); // level
data << fields[4].GetString(); // name
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
++num;
}
while (result->NextRow());
}
data.put<uint8>(wpos, num); // set real data to placeholder
SendPacket(&data);
}
void WorldSession::SendStableResult(uint8 res)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(res);
SendPacket(&data);
}
void WorldSession::HandleStablePet(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_PET");
#endif
uint64 npcGUID;
recvData >> npcGUID;
if (!GetPlayer()->IsAlive())
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
// can't place in stable dead pet
if (pet)
{
if (!pet->IsAlive() || pet->getPetType() != HUNTER_PET)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
else
{
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
if (loadResult != SPELL_CAST_OK)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_stablePetCallback = CharacterDatabase.AsyncQuery(stmt);
}
void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
{
if (!GetPlayer())
return;
uint8 freeSlot = 1;
if (result)
{
do
{
Field* fields = result->Fetch();
uint8 slot = fields[1].GetUInt8();
// slots ordered in query, and if not equal then free
if (slot != freeSlot)
break;
// this slot not free, skip
++freeSlot;
}
while (result->NextRow());
}
WorldPacket data(SMSG_STABLE_RESULT, 1);
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
{
if (_player->GetPetGUID())
{
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
SendStableResult(STABLE_SUCCESS_STABLE);
return;
}
// change pet slot directly in database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, freeSlot);
stmt->setUInt32(1, _player->GetGUIDLow());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
_player->SetTemporaryUnsummonedPetNumber(0);
SendStableResult(STABLE_SUCCESS_STABLE);
return;
}
else
SendStableResult(STABLE_ERR_STABLE);
}
void WorldSession::HandleUnstablePet(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_UNSTABLE_PET.");
#endif
uint64 npcGUID;
uint32 petnumber;
recvData >> npcGUID >> petnumber;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt32(1, petnumber);
stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
_unstablePetCallback.SetParam(petnumber);
_unstablePetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
}
void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32 petId)
{
if (!GetPlayer())
return;
uint32 petEntry = 0;
uint32 slot = 0;
if (result)
{
Field* fields = result->Fetch();
petEntry = fields[0].GetUInt32();
slot = fields[1].GetUInt32();
}
if (!petEntry)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
{
// if problem in exotic pet
if (creatureInfo && creatureInfo->IsTameable(true))
SendStableResult(STABLE_ERR_EXOTIC);
else
SendStableResult(STABLE_ERR_STABLE);
return;
}
Pet* pet = _player->GetPet();
if (pet && pet->IsAlive())
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// delete dead pet
if (pet)
{
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
}
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
{
// try to find if pet is actually temporary unsummoned and alive
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
if (loadResult != SPELL_CAST_OK)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// change pet slot directly in database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, slot);
stmt->setUInt32(1, _player->GetGUIDLow());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
_player->SetTemporaryUnsummonedPetNumber(0);
}
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
void WorldSession::HandleBuyStableSlot(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_BUY_STABLE_SLOT.");
#endif
uint64 npcGUID;
recvData >> npcGUID;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
{
StableSlotPricesEntry const* SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
if (_player->HasEnoughMoney(SlotPrice->Price))
{
++GetPlayer()->m_stableSlots;
_player->ModifyMoney(-int32(SlotPrice->Price));
SendStableResult(STABLE_SUCCESS_BUY_SLOT);
}
else
SendStableResult(STABLE_ERR_MONEY);
}
else
SendStableResult(STABLE_ERR_STABLE);
}
void WorldSession::HandleStableRevivePet(WorldPacket &/* recvData */)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleStableRevivePet: Not implemented");
#endif
}
void WorldSession::HandleStableSwapPet(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recv CMSG_STABLE_SWAP_PET.");
#endif
uint64 npcGUID;
uint32 petId;
recvData >> npcGUID >> petId;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
if (pet)
{
if (pet->getPetType() != HUNTER_PET)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
{
// try to find if pet is actually temporary unsummoned and alive
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
if (loadResult != SPELL_CAST_OK)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
// Find swapped pet slot in stable
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
stmt->setUInt32(0, _player->GetGUIDLow());
stmt->setUInt32(1, petId);
_stableSwapCallback.SetParam(petId);
_stableSwapCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
}
void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint32 petId)
{
if (!GetPlayer())
return;
if (!result)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
Field* fields = result->Fetch();
uint32 slot = fields[0].GetUInt8();
uint32 petEntry = fields[1].GetUInt32();
if (!petEntry)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
{
// if problem in exotic pet
if (creatureInfo && creatureInfo->IsTameable(true))
SendStableResult(STABLE_ERR_EXOTIC);
else
SendStableResult(STABLE_ERR_STABLE);
return;
}
Pet* pet = _player->GetPet();
// move alive pet to slot or delete dead pet
if (pet)
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
{
// change pet slot directly in database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, slot);
stmt->setUInt32(1, _player->GetGUIDLow());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
_player->SetTemporaryUnsummonedPetNumber(0);
}
// summon unstabled pet
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
{
SendStableResult(STABLE_ERR_STABLE);
}
else
SendStableResult(STABLE_SUCCESS_UNSTABLE);
}
void WorldSession::HandleRepairItemOpcode(WorldPacket & recvData)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_REPAIR_ITEM");
#endif
uint64 npcGUID, itemGUID;
uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
recvData >> npcGUID >> itemGUID >> guildBank;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR);
if (!unit)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
#endif
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// reputation discount
float discountMod = _player->GetReputationPriceDiscount(unit);
if (itemGUID)
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
#endif
Item* item = _player->GetItemByGuid(itemGUID);
if (item)
_player->DurabilityRepair(item->GetPos(), true, discountMod, guildBank);
}
else
{
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
sLog->outDebug(LOG_FILTER_NETWORKIO, "ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
#endif
_player->DurabilityRepairAll(true, discountMod, guildBank);
}
}