[7608] Implement ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT.
[AHbot.git] / src / game / SpellHandler.cpp
blobb6122c7c82e42b41dc667b21928c47c5599d4dd9
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "DBCStores.h"
21 #include "WorldPacket.h"
22 #include "WorldSession.h"
23 #include "ObjectMgr.h"
24 #include "SpellMgr.h"
25 #include "Log.h"
26 #include "Opcodes.h"
27 #include "Spell.h"
28 #include "ScriptCalls.h"
29 #include "Totem.h"
31 void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
33 // TODO: add targets.read() check
34 CHECK_PACKET_SIZE(recvPacket,1+1+1+4+8+4+1);
36 Player* pUser = _player;
37 uint8 bagIndex, slot;
38 uint8 unk_flags; // flags (if 0x02 - some additional data are received)
39 uint8 cast_count; // next cast if exists (single or not)
40 uint64 item_guid;
41 uint32 glyphIndex; // something to do with glyphs?
42 uint32 spellid; // casted spell id
44 recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags;
46 Item *pItem = pUser->GetItemByPos(bagIndex, slot);
47 if(!pItem)
49 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
50 return;
53 if(pItem->GetGUID() != item_guid)
55 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
56 return;
59 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());
61 ItemPrototype const *proto = pItem->GetProto();
62 if(!proto)
64 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
65 return;
68 // some item classes can be used only in equipped state
69 if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
71 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
72 return;
75 uint8 msg = pUser->CanUseItem(pItem);
76 if( msg != EQUIP_ERR_OK )
78 pUser->SendEquipError( msg, pItem, NULL );
79 return;
82 // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
83 if( proto->Class == ITEM_CLASS_CONSUMABLE &&
84 !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
85 pUser->InArena())
87 pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
88 return;
91 if (pUser->isInCombat())
93 for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
95 if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
97 if (IsNonCombatSpell(spellInfo))
99 pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
100 return;
106 // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
107 if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
109 if (!pItem->IsSoulBound())
111 pItem->SetState(ITEM_CHANGED, pUser);
112 pItem->SetBinding( true );
116 SpellCastTargets targets;
117 if(!targets.read(&recvPacket, pUser))
118 return;
120 //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
121 if(!Script->ItemUse(pUser,pItem,targets))
123 // no script or script not process request by self
124 pUser->CastItemUseSpell(pItem,targets,cast_count,glyphIndex);
128 #define OPEN_CHEST 11437
129 #define OPEN_SAFE 11535
130 #define OPEN_CAGE 11792
131 #define OPEN_BOOTY_CHEST 5107
132 #define OPEN_STRONGBOX 8517
134 void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
136 CHECK_PACKET_SIZE(recvPacket,1+1);
138 sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",(uint32)recvPacket.size());
140 Player* pUser = _player;
141 uint8 bagIndex, slot;
143 recvPacket >> bagIndex >> slot;
145 sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
147 Item *pItem = pUser->GetItemByPos(bagIndex, slot);
148 if(!pItem)
150 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
151 return;
154 ItemPrototype const *proto = pItem->GetProto();
155 if(!proto)
157 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
158 return;
161 // locked item
162 uint32 lockId = proto->LockID;
163 if(lockId)
165 LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
167 if (!lockInfo)
169 pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
170 sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
171 return;
174 // required picklocking
175 if(lockInfo->Skill[1] || lockInfo->Skill[0])
177 pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
178 return;
182 if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
184 QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
185 if (result)
187 Field *fields = result->Fetch();
188 uint32 entry = fields[0].GetUInt32();
189 uint32 flags = fields[1].GetUInt32();
191 pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
192 pItem->SetEntry(entry);
193 pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
194 pItem->SetState(ITEM_CHANGED, pUser);
195 delete result;
197 else
199 sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
200 pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
201 return;
203 CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
205 else
206 pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
209 void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
211 CHECK_PACKET_SIZE(recv_data, 8);
213 uint64 guid;
215 recv_data >> guid;
217 sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
218 GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid);
220 if(!obj)
221 return;
223 if (Script->GOHello(_player, obj))
224 return;
226 obj->Use(_player);
229 void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
231 CHECK_PACKET_SIZE(recvPacket,8);
233 uint64 guid;
234 recvPacket >> guid;
236 sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid));
238 GameObject* go = ObjectAccessor::GetGameObject(*_player,guid);
239 if(!go)
240 return;
242 if(!go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
243 return;
245 _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
248 void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
250 CHECK_PACKET_SIZE(recvPacket,1+4+1);
252 uint32 spellId;
253 uint8 cast_count, unk_flags;
254 recvPacket >> cast_count;
255 recvPacket >> spellId;
256 recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received)
258 sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i",
259 spellId, cast_count, unk_flags, (uint32)recvPacket.size());
261 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
263 if(!spellInfo)
265 sLog.outError("WORLD: unknown spell id %u", spellId);
266 return;
269 // not have spell in spellbook or spell passive and not casted by client
270 if ( !_player->HasActiveSpell (spellId) || IsPassiveSpell(spellId) )
272 //cheater? kick? ban?
273 return;
276 // client provided targets
277 SpellCastTargets targets;
278 if(!targets.read(&recvPacket,_player))
279 return;
281 // auto-selection buff level base at target level (in spellInfo)
282 if(targets.getUnitTarget())
284 SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
286 // 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
287 if(actualSpellInfo)
288 spellInfo = actualSpellInfo;
291 Spell *spell = new Spell(_player, spellInfo, false);
292 spell->m_cast_count = cast_count; // set count of casts
293 spell->prepare(&targets);
296 void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
298 CHECK_PACKET_SIZE(recvPacket,5);
300 // increments with every CANCEL packet, don't use for now
301 uint8 counter;
302 uint32 spellId;
303 recvPacket >> counter;
304 recvPacket >> spellId;
306 //FIXME: hack, ignore unexpected client cancel Deadly Throw cast
307 if(spellId==26679)
308 return;
310 if(_player->IsNonMeleeSpellCasted(false))
311 _player->InterruptNonMeleeSpells(false,spellId);
314 void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
316 CHECK_PACKET_SIZE(recvPacket,4);
318 uint32 spellId;
319 recvPacket >> spellId;
321 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
322 if (!spellInfo)
323 return;
325 // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
326 if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
327 return;
329 // channeled spell case (it currently casted then)
330 if(IsChanneledSpell(spellInfo))
332 if(Spell* spell = _player->m_currentSpells[CURRENT_CHANNELED_SPELL])
334 if(spell->m_spellInfo->Id==spellId)
336 spell->cancel();
337 spell->SetReferencedFromCurrent(false);
338 _player->m_currentSpells[CURRENT_CHANNELED_SPELL] = NULL;
341 return;
344 // non channeled case
345 _player->RemoveAurasDueToSpellByCancel(spellId);
348 void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
350 CHECK_PACKET_SIZE(recvPacket, 8+4);
352 uint64 guid;
353 uint32 spellId;
355 recvPacket >> guid;
356 recvPacket >> spellId;
358 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
359 if(!spellInfo)
361 sLog.outError("WORLD: unknown PET spell id %u", spellId);
362 return;
365 Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid);
367 if(!pet)
369 sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
370 return;
373 if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
375 sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
376 return;
379 if(!pet->isAlive())
381 pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
382 return;
385 pet->RemoveAurasDueToSpell(spellId);
387 pet->AddCreatureSpellCooldown(spellId);
390 void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
392 // nothing do
395 void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
397 // may be better send SMSG_CANCEL_AUTO_REPEAT?
398 // cancel and prepare for deleting
399 _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
402 /// \todo Complete HandleCancelChanneling function
403 void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
406 CHECK_PACKET_SIZE(recv_data, 4);
408 uint32 spellid;
409 recv_data >> spellid;
413 void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket)
415 CHECK_PACKET_SIZE(recvPacket, 1);
417 uint8 slotId;
419 recvPacket >> slotId;
421 if (slotId >= MAX_TOTEM)
422 return;
424 if(!_player->m_TotemSlot[slotId])
425 return;
427 Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]);
428 if(totem && totem->isTotem())
429 ((Totem*)totem)->UnSummon();
432 void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
434 sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode
436 if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
438 SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
439 if(spellInfo)
440 _player->CastSpell(_player,spellInfo,false,0);
442 _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);