port over some changes from Trinity version including one possible memory leak fix
[AHbot.git] / src / game / SpellHandler.cpp
bloba38e7eec0e832f0ae75d29bca53cd28ca21dd2c3
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;
38 // ignore for remote control state
39 if(pUser->m_mover != pUser)
40 return;
42 uint8 bagIndex, slot;
43 uint8 unk_flags; // flags (if 0x02 - some additional data are received)
44 uint8 cast_count; // next cast if exists (single or not)
45 uint64 item_guid;
46 uint32 glyphIndex; // something to do with glyphs?
47 uint32 spellid; // casted spell id
49 recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags;
51 Item *pItem = pUser->GetItemByPos(bagIndex, slot);
52 if(!pItem)
54 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
55 return;
58 if(pItem->GetGUID() != item_guid)
60 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
61 return;
64 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());
66 ItemPrototype const *proto = pItem->GetProto();
67 if(!proto)
69 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
70 return;
73 // some item classes can be used only in equipped state
74 if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
76 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
77 return;
80 uint8 msg = pUser->CanUseItem(pItem);
81 if( msg != EQUIP_ERR_OK )
83 pUser->SendEquipError( msg, pItem, NULL );
84 return;
87 // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
88 if( proto->Class == ITEM_CLASS_CONSUMABLE &&
89 !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
90 pUser->InArena())
92 pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
93 return;
96 if (pUser->isInCombat())
98 for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
100 if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
102 if (IsNonCombatSpell(spellInfo))
104 pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
105 return;
111 // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
112 if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
114 if (!pItem->IsSoulBound())
116 pItem->SetState(ITEM_CHANGED, pUser);
117 pItem->SetBinding( true );
121 SpellCastTargets targets;
122 if(!targets.read(&recvPacket, pUser))
123 return;
125 //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
126 if(!Script->ItemUse(pUser,pItem,targets))
128 // no script or script not process request by self
129 pUser->CastItemUseSpell(pItem,targets,cast_count,glyphIndex);
133 #define OPEN_CHEST 11437
134 #define OPEN_SAFE 11535
135 #define OPEN_CAGE 11792
136 #define OPEN_BOOTY_CHEST 5107
137 #define OPEN_STRONGBOX 8517
139 void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
141 CHECK_PACKET_SIZE(recvPacket,1+1);
143 sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",(uint32)recvPacket.size());
145 Player* pUser = _player;
147 // ignore for remote control state
148 if(pUser->m_mover != pUser)
149 return;
151 uint8 bagIndex, slot;
153 recvPacket >> bagIndex >> slot;
155 sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
157 Item *pItem = pUser->GetItemByPos(bagIndex, slot);
158 if(!pItem)
160 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
161 return;
164 ItemPrototype const *proto = pItem->GetProto();
165 if(!proto)
167 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
168 return;
171 // locked item
172 uint32 lockId = proto->LockID;
173 if(lockId)
175 LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
177 if (!lockInfo)
179 pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
180 sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
181 return;
184 // required picklocking
185 if(lockInfo->Skill[1] || lockInfo->Skill[0])
187 pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
188 return;
192 if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
194 QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
195 if (result)
197 Field *fields = result->Fetch();
198 uint32 entry = fields[0].GetUInt32();
199 uint32 flags = fields[1].GetUInt32();
201 pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
202 pItem->SetEntry(entry);
203 pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
204 pItem->SetState(ITEM_CHANGED, pUser);
205 delete result;
207 else
209 sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
210 pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
211 return;
213 CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
215 else
216 pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
219 void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
221 CHECK_PACKET_SIZE(recv_data, 8);
223 uint64 guid;
225 recv_data >> guid;
227 sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
229 // ignore for remote control state
230 if(_player->m_mover != _player)
231 return;
233 GameObject *obj = GetPlayer()->GetMap()->GetGameObject(guid);
235 if(!obj)
236 return;
238 if (Script->GOHello(_player, obj))
239 return;
241 obj->Use(_player);
244 void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
246 CHECK_PACKET_SIZE(recvPacket,8);
248 uint64 guid;
249 recvPacket >> guid;
251 sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid));
253 // ignore for remote control state
254 if(_player->m_mover != _player)
255 return;
257 GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
258 if(!go)
259 return;
261 if(!go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
262 return;
264 _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
267 void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
269 CHECK_PACKET_SIZE(recvPacket,1+4+1);
271 uint32 spellId;
272 uint8 cast_count, unk_flags;
273 recvPacket >> cast_count;
274 recvPacket >> spellId;
275 recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received)
277 // ignore for remote control state (for player case)
278 Unit* mover = _player->m_mover;
279 if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER)
280 return;
282 sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i",
283 spellId, cast_count, unk_flags, (uint32)recvPacket.size());
285 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
287 if(!spellInfo)
289 sLog.outError("WORLD: unknown spell id %u", spellId);
290 return;
293 if(mover->GetTypeId()==TYPEID_PLAYER)
295 // not have spell in spellbook or spell passive and not casted by client
296 if (!((Player*)mover)->HasActiveSpell (spellId) || IsPassiveSpell(spellId) )
298 //cheater? kick? ban?
299 return;
302 else
304 // not have spell in spellbook or spell passive and not casted by client
305 if (!((Creature*)mover)->HasSpell(spellId) || IsPassiveSpell(spellId) )
307 //cheater? kick? ban?
308 return;
312 // client provided targets
313 SpellCastTargets targets;
314 if(!targets.read(&recvPacket,mover))
315 return;
317 // auto-selection buff level base at target level (in spellInfo)
318 if(targets.getUnitTarget())
320 SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
322 // 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
323 if(actualSpellInfo)
324 spellInfo = actualSpellInfo;
327 Spell *spell = new Spell(mover, spellInfo, false);
328 spell->m_cast_count = cast_count; // set count of casts
329 spell->prepare(&targets);
332 void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
334 CHECK_PACKET_SIZE(recvPacket,5);
336 // ignore for remote control state (for player case)
337 Unit* mover = _player->m_mover;
338 if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER)
339 return;
341 // increments with every CANCEL packet, don't use for now
342 uint8 counter;
343 uint32 spellId;
344 recvPacket >> counter;
345 recvPacket >> spellId;
347 //FIXME: hack, ignore unexpected client cancel Deadly Throw cast
348 if(spellId==26679)
349 return;
351 if(mover->IsNonMeleeSpellCasted(false))
352 mover->InterruptNonMeleeSpells(false,spellId);
355 void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
357 CHECK_PACKET_SIZE(recvPacket,4);
359 // ignore for remote control state
360 if(_player->m_mover != _player)
361 return;
363 uint32 spellId;
364 recvPacket >> spellId;
366 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
367 if (!spellInfo)
368 return;
370 // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
371 if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
372 return;
374 // channeled spell case (it currently casted then)
375 if (IsChanneledSpell(spellInfo))
377 if (_player->m_currentSpells[CURRENT_CHANNELED_SPELL] &&
378 _player->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spellId)
379 _player->InterruptSpell(CURRENT_CHANNELED_SPELL);
380 return;
383 // non channeled case
384 _player->RemoveAurasDueToSpellByCancel(spellId);
387 void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
389 CHECK_PACKET_SIZE(recvPacket, 8+4);
391 // ignore for remote control state
392 if(_player->m_mover != _player)
393 return;
395 uint64 guid;
396 uint32 spellId;
398 recvPacket >> guid;
399 recvPacket >> spellId;
401 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
402 if(!spellInfo)
404 sLog.outError("WORLD: unknown PET spell id %u", spellId);
405 return;
408 Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid);
410 if(!pet)
412 sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
413 return;
416 if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
418 sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
419 return;
422 if(!pet->isAlive())
424 pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
425 return;
428 pet->RemoveAurasDueToSpell(spellId);
430 pet->AddCreatureSpellCooldown(spellId);
433 void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
435 // nothing do
438 void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
440 // may be better send SMSG_CANCEL_AUTO_REPEAT?
441 // cancel and prepare for deleting
442 _player->m_mover->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
445 /// \todo Complete HandleCancelChanneling function
446 void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
449 CHECK_PACKET_SIZE(recv_data, 4);
451 uint32 spellid;
452 recv_data >> spellid;
456 void WorldSession::HandleTotemDestroyed( WorldPacket& recvPacket)
458 CHECK_PACKET_SIZE(recvPacket, 1);
460 // ignore for remote control state
461 if(_player->m_mover != _player)
462 return;
464 uint8 slotId;
466 recvPacket >> slotId;
468 if (slotId >= MAX_TOTEM)
469 return;
471 if(!_player->m_TotemSlot[slotId])
472 return;
474 Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_TotemSlot[slotId]);
475 if(totem && totem->isTotem())
476 ((Totem*)totem)->UnSummon();
479 void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
481 sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode
483 if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
485 SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
486 if(spellInfo)
487 _player->CastSpell(_player,spellInfo,false,0);
489 _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
493 void WorldSession::HandleSpellClick( WorldPacket & recv_data )
495 CHECK_PACKET_SIZE(recv_data, 8);
497 uint64 guid;
498 recv_data >> guid;
500 Creature *unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
502 if(!unit)
503 return;
505 SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap;
506 for(SpellClickInfoMap::const_iterator itr = map.lower_bound(unit->GetEntry()); itr != map.upper_bound(unit->GetEntry()); ++itr)
508 if(itr->second.questId == 0 || _player->GetQuestStatus(itr->second.questId) == QUEST_STATUS_INCOMPLETE)
510 Unit *caster = (itr->second.castFlags & 0x1) ? (Unit*)_player : (Unit*)unit;
511 Unit *target = (itr->second.castFlags & 0x2) ? (Unit*)_player : (Unit*)unit;
513 caster->CastSpell(target, itr->second.spellId, true);