Files
TCPlayerbotCore/src/server/game/Handlers/SpellHandler.cpp
T
et65 c6ab951025 Core/PacketsIO: Implemented or updated most of party related packets.
Packets updated or implemented :
- SMSG_INSTANCE_INFO : updated

- CMSG_SAVE_CUF_PROFILES : updated
- SMSG_LOAD_CUF_PROFILES : updated

- SMSG_PARTY_COMMAND_RESULT : updated

- CMSG_PARTY_INVITE : updated
- SMSG_PARTY_INVITE : updated
- CMSG_PARTY_INVITE_RESPONSE : updated
- CMSG_PARTY_UNINVITE : updated
- SMSG_GROUP_UNINVITE : updated
- CMSG_LEAVE_GROUP : updated
- SMSG_GROUP_DECLINE : updated

- SMSG_GROUP_DESTROYED : updated

- CMSG_MINIMAP_PING : updated
- SMSG_MINIMAP_PING : updated

- CMSG_CONVERT_RAID : updated
- CMSG_SET_EVERYONE_IS_ASSISTANT

- CMSG_DO_READY_CHECK : updated
- CMSG_READY_CHECK_RESPONSE : updated
- SMSG_READY_CHECK_COMPLETED : updated
- SMSG_READY_CHECK_RESPONSE : updated
- SMSG_READY_CHECK_STARTED : updated

- CMSG_REQUEST_PARTY_JOIN_UPDATES : implemented (source : sniffs)
- CMSG_REQUEST_PARTY_MEMBER_STATE : updated
- SMSG_PARTY_MEMBER_STATE : updated
- SMSG_PARTY_UPDATE : updated

- CMSG_REQUEST_RAID_INFO : updated

- CMSG_INITIATE_ROLE_POLL : updated
- SMSG_ROLE_POLL_INFORM : updated
- CMSG_SET_ROLE : updated
- SMSG_ROLE_CHANGED_INFORM : updated

- CMSG_CHANGE_SUB_GROUP : updated
- CMSG_SWAP_SUB_GROUPS : implemented

- CMSG_SET_ASSISTANT_LEADER : updated
- CMSG_SET_PARTY_LEADER : updated
- SMSG_GROUP_NEW_LEADER : updated

- CMSG_CLEAR_RAID_MARKER : implemented
- SMSG_RAID_MARKERS_CHANGED : implemented

- CMSG_UPDATE_RAID_TARGET : updated
- SMSG_SEND_RAID_TARGET_UPDATE_ALL : updated
- SMSG_SEND_RAID_TARGET_UPDATE_SINGLE : updated

- CMSG_OPT_OUT_OF_LOOT : updated
- CMSG_SET_LOOT_METHOD : updated

About group update flags:
- Not sure they are use for now.
- Pets now have their group update flags.
- Pets'power is no send to client anymore.
- Changes about them are inspired from SMSG_PARTY_MEMBER_STATS parsing of WowPacketParser, but it seems this packet is not use anymore.

CHAT_MSG_RAID has been fixed.

About Ready check:
- Correctly implemented this function.
- An update function has been added to Group class, and to GroupMgr class in order to manage the ready check expiration (when 35 seconds were gone, players who have not answered must be reported as AFK)

About Raid markers:
- Old spell effect SPELL_EFFECT_SUMMON_OBJECT_SLOT3 has been renamed to SPELL_EFFECT_CHANGE_RAID_MARKER and implemented. I'm sure about that because raid markers spells are the only spells that have this effect type.

Source: WowPacketParser, and sniffs from official.
2015-05-19 13:10:29 +02:00

635 lines
21 KiB
C++

/*
* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Common.h"
#include "DBCStores.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectMgr.h"
#include "GuildMgr.h"
#include "SpellMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Spell.h"
#include "Totem.h"
#include "TemporarySummon.h"
#include "SpellAuras.h"
#include "CreatureAI.h"
#include "ScriptMgr.h"
#include "GameObjectAI.h"
#include "SpellAuraEffects.h"
#include "Player.h"
#include "Config.h"
#include "SpellPackets.h"
#include "GameObjectPackets.h"
void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet)
{
Player* user = _player;
// ignore for remote control state
if (user->m_mover != user)
return;
Item* item = user->GetUseableItemByPos(packet.PackSlot, packet.Slot);
if (!item)
{
user->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
if (item->GetGUID() != packet.CastItem)
{
user->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
ItemTemplate const* proto = item->GetTemplate();
if (!proto)
{
user->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
return;
}
// some item classes can be used only in equipped state
if (proto->GetInventoryType() != INVTYPE_NON_EQUIP && !item->IsEquipped())
{
user->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
return;
}
InventoryResult msg = user->CanUseItem(item);
if (msg != EQUIP_ERR_OK)
{
user->SendEquipError(msg, item, NULL);
return;
}
// only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
if (proto->GetClass() == ITEM_CLASS_CONSUMABLE && !(proto->GetFlags() & ITEM_FLAG_USEABLE_IN_ARENA) && user->InArena())
{
user->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, item, NULL);
return;
}
// don't allow items banned in arena
if (proto->GetFlags() & ITEM_FLAG_NOT_USEABLE_IN_ARENA && user->InArena())
{
user->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, item, NULL);
return;
}
if (user->IsInCombat())
{
for (uint32 i = 0; i < proto->Effects.size(); ++i)
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Effects[i]->SpellID))
{
if (!spellInfo->CanBeUsedInCombat())
{
user->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT, item, NULL);
return;
}
}
}
}
// check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
if (item->GetTemplate()->GetBonding() == BIND_WHEN_USE || item->GetTemplate()->GetBonding() == BIND_WHEN_PICKED_UP || item->GetTemplate()->GetBonding() == BIND_QUEST_ITEM)
{
if (!item->IsSoulBound())
{
item->SetState(ITEM_CHANGED, user);
item->SetBinding(true);
}
}
SpellCastTargets targets(user, packet.Cast);
// Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
if (!sScriptMgr->OnItemUse(user, item, targets))
{
// no script or script not process request by self
user->CastItemUseSpell(item, targets, packet.Cast.CastID, packet.Cast.Misc);
}
}
void WorldSession::HandleOpenItemOpcode(WorldPackets::Spells::OpenItem& packet)
{
Player* player = _player;
// ignore for remote control state
if (player->m_mover != player)
return;
TC_LOG_INFO("network", "bagIndex: %u, slot: %u", packet.Slot, packet.PackSlot);
Item* item = player->GetItemByPos(packet.Slot, packet.PackSlot);
if (!item)
{
player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
return;
}
ItemTemplate const* proto = item->GetTemplate();
if (!proto)
{
player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
return;
}
// Verify that the bag is an actual bag or wrapped item that can be used "normally"
if (!(proto->GetFlags() & ITEM_FLAG_OPENABLE) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))
{
player->SendEquipError(EQUIP_ERR_CLIENT_LOCKED_OUT, item, NULL);
TC_LOG_ERROR("network", "Possible hacking attempt: Player %s [%s] tried to open item [%s, entry: %u] which is not openable!",
player->GetName().c_str(), player->GetGUID().ToString().c_str(), item->GetGUID().ToString().c_str(), proto->GetId());
return;
}
// locked item
uint32 lockId = proto->GetLockID();
if (lockId)
{
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
if (!lockInfo)
{
player->SendEquipError(EQUIP_ERR_ITEM_LOCKED, item, NULL);
TC_LOG_ERROR("network", "WORLD::OpenItem: item [%s] has an unknown lockId: %u!", item->GetGUID().ToString().c_str(), lockId);
return;
}
// was not unlocked yet
if (item->IsLocked())
{
player->SendEquipError(EQUIP_ERR_ITEM_LOCKED, item, NULL);
return;
}
}
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))// wrapped?
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM);
stmt->setUInt64(0, item->GetGUID().GetCounter());
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (result)
{
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 flags = fields[1].GetUInt32();
item->SetGuidValue(ITEM_FIELD_GIFTCREATOR, ObjectGuid::Empty);
item->SetEntry(entry);
item->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
item->SetState(ITEM_CHANGED, player);
}
else
{
TC_LOG_ERROR("network", "Wrapped item %s don't have record in character_gifts table and will deleted", item->GetGUID().ToString().c_str());
player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
return;
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->setUInt64(0, item->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
else
player->SendLoot(item->GetGUID(), LOOT_CORPSE);
}
void WorldSession::HandleGameObjectUseOpcode(WorldPackets::GameObject::GameObjUse& packet)
{
if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(packet.Guid))
{
if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance()))
return;
// ignore for remote control state
if (GetPlayer()->m_mover != GetPlayer())
if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_mover) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted())
return;
obj->Use(GetPlayer());
}
}
void WorldSession::HandleGameobjectReportUse(WorldPackets::GameObject::GameObjReportUse& packet)
{
// ignore for remote control state
if (_player->m_mover != _player)
return;
GameObject* go = GetPlayer()->GetMap()->GetGameObject(packet.Guid);
if (!go)
return;
if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
if (go->AI()->GossipHello(_player))
return;
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
}
void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& cast)
{
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
return;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(cast.Cast.SpellID);
if (!spellInfo)
{
TC_LOG_ERROR("network", "WORLD: unknown spell id %u", cast.Cast.SpellID);
return;
}
if (spellInfo->IsPassive())
return;
Unit* caster = mover;
if (caster->GetTypeId() == TYPEID_UNIT && !caster->ToCreature()->HasSpell(spellInfo->Id))
{
// If the vehicle creature does not have the spell but it allows the passenger to cast own spells
// change caster to player and let him cast
if (!_player->IsOnVehicle(caster) || spellInfo->CheckVehicle(_player) != SPELL_CAST_OK)
return;
caster = _player;
}
// check known spell or raid marker spell (which not requires player to know it)
if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->HasEffect(SPELL_EFFECT_CHANGE_RAID_MARKER))
return;
// Check possible spell cast overrides
spellInfo = caster->GetCastSpellInfo(spellInfo);
// Client is resending autoshot cast opcode when other spell is cast during shoot rotation
// Skip it to prevent "interrupt" message
if (spellInfo->IsAutoRepeatRangedSpell() && caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)
&& caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo)
return;
// can't use our own spells when we're in possession of another unit,
if (_player->isPossessing())
return;
// client provided targets
SpellCastTargets targets(caster, cast.Cast);
// auto-selection buff level base at target level (in spellInfo)
if (targets.GetUnitTarget())
{
SpellInfo const* actualSpellInfo = spellInfo->GetAuraRankForLevel(targets.GetUnitTarget()->getLevel());
// if rank not found then function return NULL but in explicit cast case original spell can be cast and later failed with appropriate error message
if (actualSpellInfo)
spellInfo = actualSpellInfo;
}
Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE, ObjectGuid::Empty, false);
spell->m_cast_count = cast.Cast.CastID; // set count of casts
spell->m_misc.Data = cast.Cast.Misc; // 6.x Misc is just a guess
spell->prepare(&targets);
}
void WorldSession::HandleCancelCastOpcode(WorldPackets::Spells::CancelCast& packet)
{
if (_player->IsNonMeleeSpellCast(false))
_player->InterruptNonMeleeSpells(false, packet.SpellID, false);
}
void WorldSession::HandleCancelAuraOpcode(WorldPackets::Spells::CancelAura& cancelAura)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(cancelAura.SpellID);
if (!spellInfo)
return;
// not allow remove spells with attr SPELL_ATTR0_CANT_CANCEL
if (spellInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL))
return;
// channeled spell case (it currently cast then)
if (spellInfo->IsChanneled())
{
if (Spell* curSpell = _player->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (curSpell->GetSpellInfo()->Id == uint32(cancelAura.SpellID))
_player->InterruptSpell(CURRENT_CHANNELED_SPELL);
return;
}
// non channeled case:
// don't allow remove non positive spells
// don't allow cancelling passive auras (some of them are visible)
if (!spellInfo->IsPositive() || spellInfo->IsPassive())
return;
_player->RemoveOwnedAura(cancelAura.SpellID, cancelAura.CasterGUID, 0, AURA_REMOVE_BY_CANCEL);
// If spell being removed is a resource tracker, see if player was tracking both (herbs / minerals) and remove the other
if (sWorld->getBoolConfig(CONFIG_ALLOW_TRACK_BOTH_RESOURCES) && spellInfo->HasAura(DIFFICULTY_NONE, SPELL_AURA_TRACK_RESOURCES))
{
Unit::AuraEffectList const& auraEffects = _player->GetAuraEffectsByType(SPELL_AURA_TRACK_RESOURCES);
if (!auraEffects.empty())
{
// Build list of spell IDs to cancel. Trying to cancel the aura while iterating
// over AuraEffectList caused "incompatible iterator" errors on second pass
std::list<uint32> spellIDs;
for (Unit::AuraEffectList::const_iterator auraEffect = auraEffects.begin(); auraEffect != auraEffects.end(); ++auraEffect)
spellIDs.push_back((*auraEffect)->GetId());
// Remove all auras related to resource tracking (only Herbs and Minerals in 3.3.5a)
for (std::list<uint32>::iterator it = spellIDs.begin(); it != spellIDs.end(); ++it)
_player->RemoveOwnedAura(*it, cancelAura.CasterGUID, 0, AURA_REMOVE_BY_CANCEL);
}
}
}
void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket)
{
ObjectGuid guid;
uint32 spellId;
recvPacket >> guid;
recvPacket >> spellId;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
TC_LOG_ERROR("network", "WORLD: unknown PET spell id %u", spellId);
return;
}
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet)
{
TC_LOG_ERROR("network", "HandlePetCancelAura: Attempt to cancel an aura for non-existant %s by player '%s'", guid.ToString().c_str(), GetPlayer()->GetName().c_str());
return;
}
if (pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm())
{
TC_LOG_ERROR("network", "HandlePetCancelAura: %s is not a pet of player '%s'", guid.ToString().c_str(), GetPlayer()->GetName().c_str());
return;
}
if (!pet->IsAlive())
{
pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
return;
}
pet->RemoveOwnedAura(spellId, ObjectGuid::Empty, 0, AURA_REMOVE_BY_CANCEL);
}
void WorldSession::HandleCancelGrowthAuraOpcode(WorldPackets::Spells::CancelGrowthAura& /*cancelGrowthAura*/)
{
_player->RemoveAurasByType(SPELL_AURA_MOD_SCALE, [](AuraApplication const* aurApp)
{
SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo();
return !spellInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL) && spellInfo->IsPositive() && !spellInfo->IsPassive();
});
}
void WorldSession::HandleCancelMountAuraOpcode(WorldPackets::Spells::CancelMountAura& /*cancelMountAura*/)
{
_player->RemoveAurasByType(SPELL_AURA_MOUNTED, [](AuraApplication const* aurApp)
{
SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo();
return !spellInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL) && spellInfo->IsPositive() && !spellInfo->IsPassive();
});
}
void WorldSession::HandleCancelAutoRepeatSpellOpcode(WorldPacket& /*recvPacket*/)
{
// may be better send SMSG_CANCEL_AUTO_REPEAT?
// cancel and prepare for deleting
_player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
}
void WorldSession::HandleCancelChanneling(WorldPacket& recvData)
{
recvData.read_skip<uint32>(); // spellid, not used
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)
return;
mover->InterruptSpell(CURRENT_CHANNELED_SPELL);
}
void WorldSession::HandleTotemDestroyed(WorldPacket& recvPacket)
{
// ignore for remote control state
if (_player->m_mover != _player)
return;
uint8 slotId;
ObjectGuid guid;
recvPacket >> slotId;
recvPacket >> guid;
++slotId;
if (slotId >= MAX_TOTEM_SLOT)
return;
if (!_player->m_SummonSlot[slotId])
return;
Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_SummonSlot[slotId]);
if (totem && totem->IsTotem() && totem->GetGUID() == guid)
totem->ToTotem()->UnSummon();
}
void WorldSession::HandleSelfResOpcode(WorldPackets::Spells::SelfRes& /*packet*/)
{
if (_player->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
return; // silent return, client should display error by itself and not send this opcode
if (_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
if (spellInfo)
_player->CastSpell(_player, spellInfo, false, nullptr);
_player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
}
}
void WorldSession::HandleSpellClick(WorldPacket& recvData)
{
ObjectGuid guid;
recvData >> guid;
// this will get something not in world. crash
Creature* unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!unit)
return;
/// @todo Unit::SetCharmedBy: 28782 is not in world but 0 is trying to charm it! -> crash
if (!unit->IsInWorld())
return;
unit->HandleSpellClick(_player);
}
void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
{
ObjectGuid guid;
recvData >> guid;
recvData.read_skip<uint32>(); // DisplayId ?
// Get unit for which data is needed by client
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
if (!unit)
return;
if (!unit->HasAuraType(SPELL_AURA_CLONE_CASTER))
return;
// Get creator of the unit (SPELL_AURA_CLONE_CASTER does not stack)
Unit* creator = unit->GetAuraEffectsByType(SPELL_AURA_CLONE_CASTER).front()->GetCaster();
if (!creator)
return;
WorldPacket data(SMSG_MIRROR_IMAGE_COMPONENTED_DATA, 68);
data << guid;
data << uint32(creator->GetDisplayId());
data << uint8(creator->getRace());
data << uint8(creator->getGender());
data << uint8(creator->getClass());
if (creator->GetTypeId() == TYPEID_PLAYER)
{
Player* player = creator->ToPlayer();
Guild* guild = NULL;
if (ObjectGuid::LowType guildId = player->GetGuildId())
guild = sGuildMgr->GetGuildById(guildId);
data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
data << uint8(player->GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
data << (guild ? guild->GetGUID() : ObjectGuid::Empty);
static EquipmentSlots const itemSlots[] =
{
EQUIPMENT_SLOT_HEAD,
EQUIPMENT_SLOT_SHOULDERS,
EQUIPMENT_SLOT_BODY,
EQUIPMENT_SLOT_CHEST,
EQUIPMENT_SLOT_WAIST,
EQUIPMENT_SLOT_LEGS,
EQUIPMENT_SLOT_FEET,
EQUIPMENT_SLOT_WRISTS,
EQUIPMENT_SLOT_HANDS,
EQUIPMENT_SLOT_BACK,
EQUIPMENT_SLOT_TABARD,
EQUIPMENT_SLOT_END
};
// Display items in visible slots
for (EquipmentSlots const* itr = &itemSlots[0]; *itr != EQUIPMENT_SLOT_END; ++itr)
{
if (*itr == EQUIPMENT_SLOT_HEAD && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
data << uint32(0);
else if (*itr == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
data << uint32(0);
else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr))
data << uint32(item->GetDisplayId());
else
data << uint32(0);
}
}
else
{
// Skip player data for creatures
data << uint8(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(0);
}
SendPacket(&data);
}
void WorldSession::HandleUpdateProjectilePosition(WorldPacket& recvPacket)
{
ObjectGuid casterGuid;
uint32 spellId;
uint8 castCount;
float x, y, z; // Position of missile hit
recvPacket >> casterGuid;
recvPacket >> spellId;
recvPacket >> castCount;
recvPacket >> x;
recvPacket >> y;
recvPacket >> z;
Unit* caster = ObjectAccessor::GetUnit(*_player, casterGuid);
if (!caster)
return;
Spell* spell = caster->FindCurrentSpellBySpellId(spellId);
if (!spell || !spell->m_targets.HasDst())
return;
Position pos = *spell->m_targets.GetDstPos();
pos.Relocate(x, y, z);
spell->m_targets.ModDst(pos);
WorldPacket data(SMSG_NOTIFY_MISSILE_TRAJECTORY_COLLISION, 21);
data << casterGuid;
data << uint8(castCount);
data << float(x);
data << float(y);
data << float(z);
caster->SendMessageToSet(&data, true);
}
void WorldSession::HandleRequestCategoryCooldowns(WorldPackets::Spells::RequestCategoryCooldowns& /*requestCategoryCooldowns*/)
{
_player->SendSpellCategoryCooldowns();
}