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

971 lines
32 KiB
C++

/*
* This file is part of the AzerothCore 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "Battleground.h"
#include "BattlegroundMgr.h"
#include "Creature.h"
#include "DatabaseEnv.h"
#include "GameGraveyard.h"
#include "Language.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Pet.h"
#include "Player.h"
#include "ReputationMgr.h"
#include "ScriptMgr.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "UpdateMask.h"
#include "WorldPacket.h"
#include "WorldSession.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)
{
ObjectGuid guid;
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TABARDDESIGNER);
if (!unit)
{
LOG_DEBUG("network", "WORLD: HandleTabardVendorActivateOpcode - Unit (%s) not found or you can not interact with him.", guid.ToString().c_str());
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendTabardVendorActivate(guid);
}
void WorldSession::SendTabardVendorActivate(ObjectGuid guid)
{
WorldPacket data(MSG_TABARDVENDOR_ACTIVATE, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleBankerActivateOpcode(WorldPacket& recvData)
{
ObjectGuid guid;
LOG_DEBUG("network", "WORLD: Received CMSG_BANKER_ACTIVATE");
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER);
if (!unit)
{
LOG_DEBUG("network", "WORLD: HandleBankerActivateOpcode - Unit (%s) not found or you can not interact with him.", guid.ToString().c_str());
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendShowBank(guid);
}
void WorldSession::SendShowBank(ObjectGuid guid)
{
WorldPacket data(SMSG_SHOW_BANK, 8);
data << guid;
m_currentBankerGUID = guid;
SendPacket(&data);
}
void WorldSession::SendShowMailBox(ObjectGuid guid)
{
WorldPacket data(SMSG_SHOW_MAILBOX, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleTrainerListOpcode(WorldPacket& recvData)
{
ObjectGuid guid;
recvData >> guid;
SendTrainerList(guid);
}
void WorldSession::SendTrainerList(ObjectGuid guid)
{
std::string str = GetAcoreString(LANG_NPC_TAINER_HELLO);
SendTrainerList(guid, str);
}
void WorldSession::SendTrainerList(ObjectGuid guid, const std::string& strTitle)
{
LOG_DEBUG("network", "WORLD: SendTrainerList");
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
LOG_DEBUG("network", "WORLD: SendTrainerList - Unit (%s) not found or you can not interact with him.", guid.ToString().c_str());
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
CreatureTemplate const* ci = unit->GetCreatureTemplate();
if (!ci)
{
LOG_DEBUG("network", "WORLD: SendTrainerList - (%s) NO CREATUREINFO!", guid.ToString().c_str());
return;
}
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
{
LOG_DEBUG("network", "WORLD: SendTrainerList - Training spells not found for creature (%s)", guid.ToString().c_str());
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)
{
ObjectGuid guid;
uint32 spellId = 0;
recvData >> guid >> spellId;
LOG_DEBUG("network", "WORLD: Received CMSG_TRAINER_BUY_SPELL Npc %s, learn spell id is: %u", guid.ToString().c_str(), spellId);
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
LOG_DEBUG("network", "WORLD: HandleTrainerBuySpellOpcode - Unit (%s) not found or you can not interact with him.", guid.ToString().c_str());
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 << guid;
data << uint32(spellId); // should be same as in packet from client
SendPacket(&data);
}
void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
{
LOG_DEBUG("network", "WORLD: Received CMSG_GOSSIP_HELLO");
ObjectGuid guid;
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
LOG_DEBUG("network", "WORLD: HandleGossipHelloOpcode - Unit (%s) not found or you can not interact with him.", guid.ToString().c_str());
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)
{
LOG_DEBUG("network.opcode", "WORLD: CMSG_GOSSIP_SELECT_OPTION");
uint32 option;
uint32 unk;
ObjectGuid guid;
std::string code = "";
recvData >> guid >> unk >> option;
if (_player->PlayerTalkClass->GossipOptionCoded(option))
{
LOG_DEBUG("network.opcode", "reading string");
recvData >> code;
LOG_DEBUG("network.opcode", "string read: %s", code.c_str());
}
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
LOG_DEBUG("network.opcode", "WORLD: HandleGossipSelectOptionOpcode - Unit (%s) not found or you can't interact with him.", guid.ToString().c_str());
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)
{
LOG_DEBUG("network", "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
ObjectGuid guid;
recvData >> guid;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER);
if (!unit)
{
LOG_DEBUG("network", "WORLD: HandleSpiritHealerActivateOpcode - Unit (%s) not found or you can not interact with him.", guid.ToString().c_str());
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 = nullptr;
// Search for any graveyards near the player's corpse.
corpseGrave = sGraveyard->GetClosestGraveyard(_player, _player->GetTeamId(), _player->HasCorpse());
// 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, _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)
{
ObjectGuid npcGUID;
recvData >> npcGUID;
if (!GetPlayer()->IsInWorld() || !GetPlayer()->IsAlive())
return;
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_INNKEEPER);
if (!unit)
{
LOG_DEBUG("network", "WORLD: HandleBinderActivateOpcode - Unit (%s) not found or you can not interact with him.", npcGUID.ToString().c_str());
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 << npc->GetGUID();
data << uint32(bindspell);
SendPacket(&data);
_player->PlayerTalkClass->SendCloseGossip();
}
void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData)
{
LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS");
ObjectGuid 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(ObjectGuid guid)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1)));
}
void WorldSession::SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result)
{
if (!GetPlayer())
return;
LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
data << guid;
size_t wpos = data.wpos();
data << uint8(0); // place holder for slot show number
data << uint8(GetPlayer()->m_stableSlots);
Pet* pet = _player->GetPet();
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())
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
if (PreparedQueryResult _result = CharacterDatabase.Query(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)
{
LOG_DEBUG("network", "WORLD: Recv CMSG_STABLE_PET");
ObjectGuid 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;
}
}
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1)));
}
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;
}
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, freeSlot);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
CharacterDatabase.Execute(stmt);
_player->SetTemporaryUnsummonedPetNumber(0);
SendStableResult(STABLE_SUCCESS_STABLE);
return;
}
else
SendStableResult(STABLE_ERR_STABLE);
}
void WorldSession::HandleUnstablePet(WorldPacket& recvData)
{
LOG_DEBUG("network", "WORLD: Recv CMSG_UNSTABLE_PET.");
ObjectGuid 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);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, petnumber);
stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1)));
}
void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result)
{
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;
}
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, slot);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
CharacterDatabase.Execute(stmt);
_player->SetTemporaryUnsummonedPetNumber(0);
}
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
{
LOG_DEBUG("network", "WORLD: Recv CMSG_BUY_STABLE_SLOT.");
ObjectGuid 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 */)
{
LOG_DEBUG("network", "HandleStableRevivePet: Not implemented");
}
void WorldSession::HandleStableSwapPet(WorldPacket& recvData)
{
LOG_DEBUG("network", "WORLD: Recv CMSG_STABLE_SWAP_PET.");
ObjectGuid 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
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, petId);
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1)));
}
void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result)
{
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())
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, slot);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
CharacterDatabase.Execute(stmt);
_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)
{
LOG_DEBUG("network", "WORLD: CMSG_REPAIR_ITEM");
ObjectGuid 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)
{
LOG_DEBUG("network", "WORLD: HandleRepairItemOpcode - Unit (%s) not found or you can not interact with him.", npcGUID.ToString().c_str());
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// reputation discount
float discountMod = _player->GetReputationPriceDiscount(unit);
sScriptMgr->OnBeforePlayerDurabilityRepair(_player, npcGUID, itemGUID, discountMod, guildBank);
if (itemGUID)
{
LOG_DEBUG("network", "ITEM: Repair item, item %s, npc %s", itemGUID.ToString().c_str(), npcGUID.ToString().c_str());
Item* item = _player->GetItemByGuid(itemGUID);
if (item)
_player->DurabilityRepair(item->GetPos(), true, discountMod, guildBank);
}
else
{
LOG_DEBUG("network", "ITEM: Repair all items, npc %s", npcGUID.ToString().c_str());
_player->DurabilityRepairAll(true, discountMod, guildBank);
}
}