diff options
Diffstat (limited to 'src/game/SpellHandler.cpp')
-rw-r--r-- | src/game/SpellHandler.cpp | 111 |
1 files changed, 107 insertions, 4 deletions
diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 31eb05ae802..b8aa4469472 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "DBCStores.h" #include "WorldPacket.h" @@ -31,50 +32,61 @@ #include "TemporarySummon.h" #include "SpellAuras.h" #include "CreatureAI.h" + void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) { // TODO: add targets.read() check Player* pUser = _player; + // ignore for remote control state if(pUser->m_mover != pUser) return; + uint8 bagIndex, slot; uint8 unk_flags; // flags (if 0x02 - some additional data are received) uint8 cast_count; // next cast if exists (single or not) uint64 item_guid; uint32 glyphIndex; // something to do with glyphs? uint32 spellid; // casted spell id + recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags; + Item *pItem = pUser->GetUseableItemByPos(bagIndex, slot); if(!pItem) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); return; } + if(pItem->GetGUID() != item_guid) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); return; } + sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, cast_count: %u, spellid: %u, Item: %u, glyphIndex: %u, unk_flags: %u, data length = %i", bagIndex, slot, cast_count, spellid, pItem->GetEntry(), glyphIndex, unk_flags, (uint32)recvPacket.size()); + ItemPrototype const *proto = pItem->GetProto(); if(!proto) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } + // some item classes can be used only in equipped state if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped()) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } + uint8 msg = pUser->CanUseItem(pItem); if( msg != EQUIP_ERR_OK ) { pUser->SendEquipError( msg, pItem, NULL ); return; } + // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB) if( proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) && @@ -83,6 +95,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL); return; } + if (pUser->isInCombat()) { for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) @@ -97,6 +110,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) } } } + // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM ) { @@ -106,26 +120,31 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) pItem->SetBinding( true ); } } + SpellCastTargets targets; if (!targets.read(&recvPacket, pUser)) return; + targets.Update(pUser); + if (!pItem->IsTargetValidForItemUse(targets.getUnitTarget())) { // free gray item after use fail pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + // send spell error if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid)) { - // for implicit area/coord target spells + // for implicit area/coord target spells if(!targets.getUnitTarget()) Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_NO_VALID_TARGETS); - // for explicit target spells + // for explicit target spells else - Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_BAD_TARGETS); + Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_BAD_TARGETS); } return; } + //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state. if(!Script->ItemUse(pUser,pItem,targets)) { @@ -133,46 +152,59 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) pUser->CastItemUseSpell(pItem,targets,cast_count,glyphIndex); } } + #define OPEN_CHEST 11437 #define OPEN_SAFE 11535 #define OPEN_CAGE 11792 #define OPEN_BOOTY_CHEST 5107 #define OPEN_STRONGBOX 8517 + void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) { sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",(uint32)recvPacket.size()); + Player* pUser = _player; + // ignore for remote control state if(pUser->m_mover != pUser) return; + uint8 bagIndex, slot; + recvPacket >> bagIndex >> slot; + sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot); + Item *pItem = pUser->GetItemByPos(bagIndex, slot); if(!pItem) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); return; } + ItemPrototype const *proto = pItem->GetProto(); if(!proto) { pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); return; } + if(!pUser->GetSession()->HandleOnItemOpen(pItem)) return; + // locked item uint32 lockId = proto->LockID; if(lockId) { LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + if (!lockInfo) { pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL ); sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId); return; } + // required picklocking if(lockInfo->Skill[1] || lockInfo->Skill[0]) { @@ -180,6 +212,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) return; } } + if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped? { QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow()); @@ -188,6 +221,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) Field *fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); uint32 flags = fields[1].GetUInt32(); + pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0); pItem->SetEntry(entry); pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags); @@ -205,36 +239,51 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) else pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE); } + void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) { uint64 guid; + recv_data >> guid; + sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid)); + // ignore for remote control state if(_player->m_mover != _player) return; + GameObject *obj = GetPlayer()->GetMap()->GetGameObject(guid); + if(!obj) return; + if (Script->GOHello(_player, obj)) return; + obj->Use(_player); } + void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) { uint64 guid; recvPacket >> guid; + sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid)); + // ignore for remote control state if(_player->m_mover != _player) return; + GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); if(!go) return; + if(!go->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) return; + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); } + void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) { uint32 spellId; @@ -242,6 +291,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) recvPacket >> cast_count; recvPacket >> spellId; recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received) + // ignore for remote control state (for player case) Unit* mover = _player->m_mover; if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER) @@ -249,15 +299,19 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } + sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i", spellId, cast_count, unk_flags, (uint32)recvPacket.size()); + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); + if(!spellInfo) { sLog.outError("WORLD: unknown spell id %u", spellId); recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } + if(mover->GetTypeId()==TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client @@ -278,14 +332,17 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) return; } } + // Client is resending autoshot cast opcode when other spell is casted during shoot rotation // Skip it to prevent "interrupt" message if (IsAutoRepeatRangedSpell(spellInfo) && _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL) && _player->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; if(!targets.read(&recvPacket,mover)) @@ -293,6 +350,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } + // some spell cast packet including more data (for projectiles?) if (unk_flags & 0x02) { @@ -303,36 +361,46 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) MovementInfo movementInfo; ReadMovementInfo(recvPacket, &movementInfo); } + // auto-selection buff level base at target level (in spellInfo) if(targets.getUnitTarget()) { SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel()); + // if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message if(actualSpellInfo) spellInfo = actualSpellInfo; } + Spell *spell = new Spell(mover, spellInfo, false); spell->m_cast_count = cast_count; // set count of casts spell->prepare(&targets); } + void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket) { uint32 spellId; + recvPacket.read_skip<uint8>(); // counter, increments with every CANCEL packet, don't use for now recvPacket >> spellId; + if(_player->IsNonMeleeSpellCasted(false)) _player->InterruptNonMeleeSpells(false,spellId,false); } + void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) { uint32 spellId; recvPacket >> spellId; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); if (!spellInfo) return; + // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL)) return; + // channeled spell case (it currently casted then) if (IsChanneledSpell(spellInfo)) { @@ -341,96 +409,124 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) _player->InterruptSpell(CURRENT_CHANNELED_SPELL); return; } + // non channeled case // maybe should only remove one buff when there are multiple? _player->RemoveAurasDueToSpell(spellId, 0, AURA_REMOVE_BY_CANCEL); } + void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket) { uint64 guid; uint32 spellId; + recvPacket >> guid; recvPacket >> spellId; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); if(!spellInfo) { sLog.outError("WORLD: unknown PET spell id %u", spellId); return; } + Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); + if(!pet) { sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) ); return; } + if(pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharm()) { sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } + if(!pet->isAlive()) { pet->SendPetActionFeedback(FEEDBACK_PET_DEAD); return; } + pet->RemoveAurasDueToSpell(spellId); + pet->AddCreatureSpellCooldown(spellId); } + void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/) { // nothing do } + 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 & recv_data) { recv_data.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; + recvPacket >> slotId; + ++slotId; if (slotId >= MAX_TOTEM_SLOT) return; + if(!_player->m_SummonSlot[slotId]) return; + Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_SummonSlot[slotId]); // Don't unsummon sentry totem if(totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY) ((Totem*)totem)->UnSummon(); } + void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ ) { sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode + if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL)) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL)); if(spellInfo) _player->CastSpell(_player,spellInfo,false,0); + _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0); } } + void WorldSession::HandleSpellClick( WorldPacket & recv_data ) { uint64 guid; recv_data >> 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()) { @@ -438,6 +534,7 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data ) assert(false); return; } + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(unit->GetEntry()); for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) { @@ -449,15 +546,19 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data ) caster->CastSpell(target, itr->second.spellId, true, NULL, NULL, origCasterGUID); } } + if(unit->IsVehicle()) _player->EnterVehicle(unit); + unit->AI()->DoAction(EVENT_SPELLCLICK); } + void WorldSession::HandleMirrrorImageDataRequest( WorldPacket & recv_data ) { sLog.outDebug("WORLD: CMSG_GET_MIRRORIMAGE_DATA"); uint64 guid; recv_data >> guid; + // Get unit for which data is needed by client Unit *unit = ObjectAccessor::GetObjectInWorld(guid, (Unit*)NULL); if(!unit) @@ -476,12 +577,14 @@ void WorldSession::HandleMirrrorImageDataRequest( WorldPacket & recv_data ) data << (uint8)pCreator->getGender(); data << (uint8)pCreator->getClass(); data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 0); // skin + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 1); // face data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 2); // hair data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 3); // haircolor data << (uint8)pCreator->GetByteValue(PLAYER_BYTES_2, 0); // facialhair + data << (uint32)pCreator->GetGuildId(); // unk - static const EquipmentSlots ItemSlots[] = + static const EquipmentSlots ItemSlots[] = { EQUIPMENT_SLOT_HEAD, EQUIPMENT_SLOT_SHOULDERS, |