Allow cancel mind control spells while control target.
[getmangos.git] / src / game / SpellHandler.cpp
blob373f764899616b7f4628f43bd0c9c642cd7197b1
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"
30 #include "SpellAuras.h"
32 void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
34 // TODO: add targets.read() check
35 CHECK_PACKET_SIZE(recvPacket,1+1+1+4+8+4+1);
37 Player* pUser = _player;
39 // ignore for remote control state
40 if(pUser->m_mover != pUser)
41 return;
43 uint8 bagIndex, slot;
44 uint8 unk_flags; // flags (if 0x02 - some additional data are received)
45 uint8 cast_count; // next cast if exists (single or not)
46 uint64 item_guid;
47 uint32 glyphIndex; // something to do with glyphs?
48 uint32 spellid; // casted spell id
50 recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags;
52 Item *pItem = pUser->GetItemByPos(bagIndex, slot);
53 if(!pItem)
55 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
56 return;
59 if(pItem->GetGUID() != item_guid)
61 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
62 return;
65 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());
67 ItemPrototype const *proto = pItem->GetProto();
68 if(!proto)
70 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
71 return;
74 // some item classes can be used only in equipped state
75 if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
77 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
78 return;
81 uint8 msg = pUser->CanUseItem(pItem);
82 if( msg != EQUIP_ERR_OK )
84 pUser->SendEquipError( msg, pItem, NULL );
85 return;
88 // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
89 if( proto->Class == ITEM_CLASS_CONSUMABLE &&
90 !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
91 pUser->InArena())
93 pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
94 return;
97 if (pUser->isInCombat())
99 for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
101 if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
103 if (IsNonCombatSpell(spellInfo))
105 pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
106 return;
112 // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
113 if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
115 if (!pItem->IsSoulBound())
117 pItem->SetState(ITEM_CHANGED, pUser);
118 pItem->SetBinding( true );
122 SpellCastTargets targets;
123 if (!targets.read(&recvPacket, pUser))
124 return;
126 targets.Update(pUser);
128 if (!pItem->IsTargetValidForItemUse(targets.getUnitTarget()))
130 // free gray item after use fail
131 pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
133 // send spell error
134 if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid))
136 // for implicit area/coord target spells
137 if (IsPointEffectTarget(Targets(spellInfo->EffectImplicitTargetA[0])) ||
138 IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[0])))
139 Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_NO_VALID_TARGETS);
140 // for explicit target spells
141 else
142 Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_BAD_TARGETS);
144 return;
147 //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
148 if(!Script->ItemUse(pUser,pItem,targets))
150 // no script or script not process request by self
151 pUser->CastItemUseSpell(pItem,targets,cast_count,glyphIndex);
155 #define OPEN_CHEST 11437
156 #define OPEN_SAFE 11535
157 #define OPEN_CAGE 11792
158 #define OPEN_BOOTY_CHEST 5107
159 #define OPEN_STRONGBOX 8517
161 void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
163 CHECK_PACKET_SIZE(recvPacket,1+1);
165 sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",(uint32)recvPacket.size());
167 Player* pUser = _player;
169 // ignore for remote control state
170 if(pUser->m_mover != pUser)
171 return;
173 uint8 bagIndex, slot;
175 recvPacket >> bagIndex >> slot;
177 sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
179 Item *pItem = pUser->GetItemByPos(bagIndex, slot);
180 if(!pItem)
182 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
183 return;
186 ItemPrototype const *proto = pItem->GetProto();
187 if(!proto)
189 pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
190 return;
193 // locked item
194 uint32 lockId = proto->LockID;
195 if(lockId)
197 LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
199 if (!lockInfo)
201 pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
202 sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
203 return;
206 // required picklocking
207 if(lockInfo->Skill[1] || lockInfo->Skill[0])
209 pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
210 return;
214 if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
216 QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
217 if (result)
219 Field *fields = result->Fetch();
220 uint32 entry = fields[0].GetUInt32();
221 uint32 flags = fields[1].GetUInt32();
223 pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
224 pItem->SetEntry(entry);
225 pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
226 pItem->SetState(ITEM_CHANGED, pUser);
227 delete result;
229 else
231 sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
232 pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
233 return;
235 CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
237 else
238 pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
241 void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
243 CHECK_PACKET_SIZE(recv_data, 8);
245 uint64 guid;
247 recv_data >> guid;
249 sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
251 // ignore for remote control state
252 if(_player->m_mover != _player)
253 return;
255 GameObject *obj = GetPlayer()->GetMap()->GetGameObject(guid);
257 if(!obj)
258 return;
260 if (Script->GOHello(_player, obj))
261 return;
263 obj->Use(_player);
266 void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
268 CHECK_PACKET_SIZE(recvPacket,8);
270 uint64 guid;
271 recvPacket >> guid;
273 sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid));
275 // ignore for remote control state
276 if(_player->m_mover != _player)
277 return;
279 GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
280 if(!go)
281 return;
283 if(!go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
284 return;
286 _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
289 void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
291 CHECK_PACKET_SIZE(recvPacket,1+4+1);
293 uint32 spellId;
294 uint8 cast_count, unk_flags;
295 recvPacket >> cast_count;
296 recvPacket >> spellId;
297 recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received)
299 // ignore for remote control state (for player case)
300 Unit* mover = _player->m_mover;
301 if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER)
302 return;
304 sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i",
305 spellId, cast_count, unk_flags, (uint32)recvPacket.size());
307 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
309 if(!spellInfo)
311 sLog.outError("WORLD: unknown spell id %u", spellId);
312 return;
315 if(mover->GetTypeId()==TYPEID_PLAYER)
317 // not have spell in spellbook or spell passive and not casted by client
318 if (!((Player*)mover)->HasActiveSpell (spellId) || IsPassiveSpell(spellId) )
320 //cheater? kick? ban?
321 return;
324 else
326 // not have spell in spellbook or spell passive and not casted by client
327 if (!((Creature*)mover)->HasSpell(spellId) || IsPassiveSpell(spellId) )
329 //cheater? kick? ban?
330 return;
334 // client provided targets
335 SpellCastTargets targets;
336 if(!targets.read(&recvPacket,mover))
337 return;
339 // auto-selection buff level base at target level (in spellInfo)
340 if(targets.getUnitTarget())
342 SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
344 // 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
345 if(actualSpellInfo)
346 spellInfo = actualSpellInfo;
349 Spell *spell = new Spell(mover, spellInfo, false);
350 spell->m_cast_count = cast_count; // set count of casts
351 spell->prepare(&targets);
354 void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
356 CHECK_PACKET_SIZE(recvPacket,5);
358 // ignore for remote control state (for player case)
359 Unit* mover = _player->m_mover;
360 if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER)
361 return;
363 // increments with every CANCEL packet, don't use for now
364 uint8 counter;
365 uint32 spellId;
366 recvPacket >> counter;
367 recvPacket >> spellId;
369 //FIXME: hack, ignore unexpected client cancel Deadly Throw cast
370 if(spellId==26679)
371 return;
373 if(mover->IsNonMeleeSpellCasted(false))
374 mover->InterruptNonMeleeSpells(false,spellId);
377 void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
379 CHECK_PACKET_SIZE(recvPacket,4);
381 uint32 spellId;
382 recvPacket >> spellId;
384 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
385 if (!spellInfo)
386 return;
388 if (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL)
389 return;
391 if(!IsPositiveSpell(spellId))
393 // ignore for remote control state
394 if (_player->m_mover != _player)
396 // except own aura spells
397 bool allow = false;
398 for(int k = 0; k < 3; ++k)
400 if (spellInfo->EffectApplyAuraName[k] == SPELL_AURA_MOD_POSSESS ||
401 spellInfo->EffectApplyAuraName[k] == SPELL_AURA_MOD_POSSESS_PET)
403 allow = true;
404 break;
408 // this also include case when aura not found
409 if(!allow)
410 return;
412 else
413 return;
416 // channeled spell case (it currently casted then)
417 if (IsChanneledSpell(spellInfo))
419 if (_player->m_currentSpells[CURRENT_CHANNELED_SPELL] &&
420 _player->m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spellId)
421 _player->InterruptSpell(CURRENT_CHANNELED_SPELL);
422 return;
425 // non channeled case
426 _player->RemoveAurasDueToSpellByCancel(spellId);
429 void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
431 CHECK_PACKET_SIZE(recvPacket, 8+4);
433 // ignore for remote control state
434 if(_player->m_mover != _player)
435 return;
437 uint64 guid;
438 uint32 spellId;
440 recvPacket >> guid;
441 recvPacket >> spellId;
443 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
444 if(!spellInfo)
446 sLog.outError("WORLD: unknown PET spell id %u", spellId);
447 return;
450 Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid);
452 if(!pet)
454 sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
455 return;
458 if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
460 sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
461 return;
464 if(!pet->isAlive())
466 pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
467 return;
470 pet->RemoveAurasDueToSpell(spellId);
472 pet->AddCreatureSpellCooldown(spellId);
475 void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
477 // nothing do
480 void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
482 // may be better send SMSG_CANCEL_AUTO_REPEAT?
483 // cancel and prepare for deleting
484 _player->m_mover->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
487 /// \todo Complete HandleCancelChanneling function
488 void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
491 CHECK_PACKET_SIZE(recv_data, 4);
493 uint32 spellid;
494 recv_data >> spellid;
498 void WorldSession::HandleTotemDestroyed( WorldPacket& recvPacket)
500 CHECK_PACKET_SIZE(recvPacket, 1);
502 // ignore for remote control state
503 if(_player->m_mover != _player)
504 return;
506 uint8 slotId;
508 recvPacket >> slotId;
510 if (slotId >= MAX_TOTEM)
511 return;
513 if(!_player->m_TotemSlot[slotId])
514 return;
516 Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_TotemSlot[slotId]);
517 if(totem && totem->isTotem())
518 ((Totem*)totem)->UnSummon();
521 void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
523 sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode
525 if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
527 SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
528 if(spellInfo)
529 _player->CastSpell(_player,spellInfo,false,0);
531 _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
535 void WorldSession::HandleSpellClick( WorldPacket & recv_data )
537 CHECK_PACKET_SIZE(recv_data, 8);
539 uint64 guid;
540 recv_data >> guid;
542 if (_player->isInCombat()) // client prevent click and set different icon at combat state
543 return;
545 Creature *unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
546 if (!unit || unit->isInCombat()) // client prevent click and set different icon at combat state
547 return;
549 SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(unit->GetEntry());
550 for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
552 if (itr->second.IsFitToRequirements(_player))
554 Unit *caster = (itr->second.castFlags & 0x1) ? (Unit*)_player : (Unit*)unit;
555 Unit *target = (itr->second.castFlags & 0x2) ? (Unit*)_player : (Unit*)unit;
557 caster->CastSpell(target, itr->second.spellId, true);