/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2 * Copyright (C) 2008-2016 TrinityCore * Copyright (C) 2005-2009 MaNGOS */ #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(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(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); } }