root/trunk/src/game/SpellHandler.cpp @ 9

Revision 2, 13.8 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
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.
8 *
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.
13 *
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
17 */
18
19#include "Common.h"
20#include "Database/DBCStores.h"
21#include "WorldPacket.h"
22#include "WorldSession.h"
23#include "World.h"
24#include "ObjectMgr.h"
25#include "SpellMgr.h"
26#include "Log.h"
27#include "Opcodes.h"
28#include "Spell.h"
29#include "SpellAuras.h"
30#include "BattleGround.h"
31#include "MapManager.h"
32#include "ScriptCalls.h"
33#include "Totem.h"
34
35void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
36{
37    // TODO: add targets.read() check
38    CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8);
39
40    Player* pUser = _player;
41    uint8 bagIndex, slot;
42    uint8 spell_count;                                      // number of spells at item, not used
43    uint8 cast_count;                                       // next cast if exists (single or not)
44    uint64 item_guid;
45
46    recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid;
47
48    Item *pItem = pUser->GetItemByPos(bagIndex, slot);
49    if(!pItem)
50    {
51        pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
52        return;
53    }
54
55    sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size());
56
57    ItemPrototype const *proto = pItem->GetProto();
58    if(!proto)
59    {
60        pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
61        return;
62    }
63
64    // some item classes can be used only in equipped state
65    if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped())
66    {
67        pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
68        return;
69    }
70
71    uint8 msg = pUser->CanUseItem(pItem);
72    if( msg != EQUIP_ERR_OK )
73    {
74        pUser->SendEquipError( msg, pItem, NULL );
75        return;
76    }
77
78    // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB)
79    if( proto->Class == ITEM_CLASS_CONSUMABLE &&
80        !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) &&
81        pUser->InArena())
82    {
83        pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
84        return;
85    }
86
87    if (pUser->isInCombat())
88    {
89        for(int i = 0; i < 5; ++i)
90        {
91            if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId))
92            {
93                if (IsNonCombatSpell(spellInfo))
94                {
95                    pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL);
96                    return;
97                }
98            }
99        }
100    }
101
102    // check also  BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
103    if( pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
104    {
105        if (!pItem->IsSoulBound())
106        {
107            pItem->SetState(ITEM_CHANGED, pUser);
108            pItem->SetBinding( true );
109        }
110    }
111
112    SpellCastTargets targets;
113    if(!targets.read(&recvPacket, pUser))
114        return;
115
116    //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state.
117    if(!Script->ItemUse(pUser,pItem,targets))
118    {
119        // no script or script not process request by self
120
121        // special learning case
122        if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN)
123        {
124            uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId;
125
126            SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN);
127            if(!spellInfo)
128            {
129                sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN);
130                pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL);
131                return;
132            }
133
134            Spell *spell = new Spell(pUser, spellInfo, false);
135            spell->m_CastItem = pItem;
136            spell->m_cast_count = cast_count;               //set count of casts
137            spell->m_currentBasePoints[0] = learning_spell_id;
138            spell->prepare(&targets);
139            return;
140        }
141
142        // use triggered flag only for items with many spell casts and for not first cast
143        int count = 0;
144
145        for(int i = 0; i < 5; ++i)
146        {
147            _Spell const& spellData = pItem->GetProto()->Spells[i];
148
149            // no spell
150            if(!spellData.SpellId)
151                continue;
152
153            // wrong triggering type
154            if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
155                continue;
156
157            SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
158            if(!spellInfo)
159            {
160                sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId);
161                continue;
162            }
163
164            Spell *spell = new Spell(pUser, spellInfo, (count > 0));
165            spell->m_CastItem = pItem;
166            spell->m_cast_count = cast_count;               //set count of casts
167            spell->prepare(&targets);
168
169            ++count;
170        }
171    }
172}
173
174#define OPEN_CHEST 11437
175#define OPEN_SAFE 11535
176#define OPEN_CAGE 11792
177#define OPEN_BOOTY_CHEST 5107
178#define OPEN_STRONGBOX 8517
179
180void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
181{
182    CHECK_PACKET_SIZE(recvPacket,1+1);
183
184    sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",recvPacket.size());
185
186    Player* pUser = _player;
187    uint8 bagIndex, slot;
188
189    recvPacket >> bagIndex >> slot;
190
191    sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot);
192
193    Item *pItem = pUser->GetItemByPos(bagIndex, slot);
194    if(!pItem)
195    {
196        pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
197        return;
198    }
199
200    ItemPrototype const *proto = pItem->GetProto();
201    if(!proto)
202    {
203        pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL );
204        return;
205    }
206
207    // locked item
208    uint32 lockId = proto->LockID;
209    if(lockId)
210    {
211        LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
212
213        if (!lockInfo)
214        {
215            pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
216            sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId);
217            return;
218        }
219
220        // required picklocking
221        if(lockInfo->requiredlockskill || lockInfo->requiredminingskill)
222        {
223            pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL );
224            return;
225        }
226    }
227
228    if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped?
229    {
230        QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
231        if (result)
232        {
233            Field *fields = result->Fetch();
234            uint32 entry = fields[0].GetUInt32();
235            uint32 flags = fields[1].GetUInt32();
236
237            pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
238            pItem->SetUInt32Value(OBJECT_FIELD_ENTRY, entry);
239            pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
240            pItem->SetState(ITEM_CHANGED, pUser);
241            delete result;
242        }
243        else
244        {
245            sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow());
246            pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
247            return;
248        }
249        CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
250    }
251    else
252        pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
253}
254
255void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
256{
257    CHECK_PACKET_SIZE(recv_data,8);
258
259    uint64 guid;
260    uint32 spellId = OPEN_CHEST;
261
262    recv_data >> guid;
263
264    sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", guid);
265    GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid);
266
267    if(!obj)
268        return;
269
270    if (Script->GOHello(_player, obj))
271        return;
272
273    obj->Use(_player);
274}
275
276void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
277{
278    CHECK_PACKET_SIZE(recvPacket,4+1+2);
279
280    uint32 spellId;
281    uint8  cast_count;
282    recvPacket >> spellId;
283    recvPacket >> cast_count;
284
285    sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u data length = %i",
286        spellId, cast_count, recvPacket.size());
287
288    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
289
290    if(!spellInfo)
291    {
292        sLog.outError("WORLD: unknown spell id %u", spellId);
293        return;
294    }
295
296    // not have spell or spell passive and not casted by client
297    if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) )
298    {
299        //cheater? kick? ban?
300        return;
301    }
302
303    // client provided targets
304    SpellCastTargets targets;
305    if(!targets.read(&recvPacket,_player))
306        return;
307
308    // auto-selection buff level base at target level (in spellInfo)
309    if(targets.getUnitTarget())
310    {
311        SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel());
312
313        // 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
314        if(actualSpellInfo)
315            spellInfo = actualSpellInfo;
316    }
317
318    Spell *spell = new Spell(_player, spellInfo, false);
319    spell->m_cast_count = cast_count;                       //set count of casts
320    spell->prepare(&targets);
321}
322
323void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
324{
325    CHECK_PACKET_SIZE(recvPacket,4);
326
327    uint32 spellId;
328    recvPacket >> spellId;
329
330    //FIXME: hack, ignore unexpected client cancel Deadly Throw cast
331    if(spellId==26679)
332        return;
333
334    if(_player->IsNonMeleeSpellCasted(false))
335        _player->InterruptNonMeleeSpells(false,spellId);
336}
337
338void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
339{
340    CHECK_PACKET_SIZE(recvPacket,4);
341
342    uint32 spellId;
343    recvPacket >> spellId;
344
345    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
346    if (!spellInfo)
347        return;
348
349    // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL
350    if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL))
351        return;
352
353    _player->RemoveAurasDueToSpellByCancel(spellId);
354
355    if (spellId == 2584)                                    // Waiting to resurrect spell cancel, we must remove player from resurrect queue
356    {
357        BattleGround *bg = _player->GetBattleGround();
358        if(!bg)
359            return;
360        bg->RemovePlayerFromResurrectQueue(_player->GetGUID());
361    }
362}
363
364void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
365{
366    CHECK_PACKET_SIZE(recvPacket, 8+4);
367
368    uint64 guid;
369    uint32 spellId;
370
371    recvPacket >> guid;
372    recvPacket >> spellId;
373
374    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
375    if(!spellInfo)
376    {
377        sLog.outError("WORLD: unknown PET spell id %u", spellId);
378        return;
379    }
380
381    Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid);
382
383    if(!pet)
384    {
385        sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
386        return;
387    }
388
389    if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
390    {
391        sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
392        return;
393    }
394
395    if(!pet->isAlive())
396    {
397        pet->SendPetActionFeedback(FEEDBACK_PET_DEAD);
398        return;
399    }
400
401    pet->RemoveAurasDueToSpell(spellId);
402
403    pet->AddCreatureSpellCooldown(spellId);
404}
405
406void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/)
407{
408    // nothing do
409}
410
411void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
412{
413    // may be better send SMSG_CANCEL_AUTO_REPEAT?
414    // cancel and prepare for deleting
415    _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
416}
417
418/// \todo Complete HandleCancelChanneling function
419void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */)
420{
421    /*
422        CHECK_PACKET_SIZE(recv_data, 4);
423
424        uint32 spellid;
425        recv_data >> spellid;
426    */
427}
428
429void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket)
430{
431    CHECK_PACKET_SIZE(recvPacket, 1);
432
433    uint8 slotId;
434
435    recvPacket >> slotId;
436
437    if (slotId >= MAX_TOTEM)
438        return;
439
440    if(!_player->m_TotemSlot[slotId])
441        return;
442
443    Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]);
444    if(totem && totem->isTotem())
445        ((Totem*)totem)->UnSummon();
446}
447
448void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ )
449{
450    sLog.outDebug("WORLD: CMSG_SELF_RES");                  // empty opcode
451
452    if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL))
453    {
454        SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL));
455        if(spellInfo)
456            _player->CastSpell(_player,spellInfo,false,0);
457
458        _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
459    }
460}
Note: See TracBrowser for help on using the browser.