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
20 #include "DBCStores.h"
21 #include "WorldPacket.h"
22 #include "WorldSession.h"
23 #include "ObjectMgr.h"
28 #include "ScriptCalls.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
)
43 uint8 unk_flags
; // flags (if 0x02 - some additional data are received)
44 uint8 cast_count
; // next cast if exists (single or not)
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
);
54 pUser
->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
58 if(pItem
->GetGUID() != item_guid
)
60 pUser
->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
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();
69 pUser
->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, pItem
, NULL
);
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
);
80 uint8 msg
= pUser
->CanUseItem(pItem
);
81 if( msg
!= EQUIP_ERR_OK
)
83 pUser
->SendEquipError( msg
, pItem
, NULL
);
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
) &&
92 pUser
->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH
,pItem
,NULL
);
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
);
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
))
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
)
151 uint8 bagIndex
, slot
;
153 recvPacket
>> bagIndex
>> slot
;
155 sLog
.outDetail("bagIndex: %u, slot: %u",bagIndex
,slot
);
157 Item
*pItem
= pUser
->GetItemByPos(bagIndex
, slot
);
160 pUser
->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
164 ItemPrototype
const *proto
= pItem
->GetProto();
167 pUser
->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND
, pItem
, NULL
);
172 uint32 lockId
= proto
->LockID
;
175 LockEntry
const *lockInfo
= sLockStore
.LookupEntry(lockId
);
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
);
184 // required picklocking
185 if(lockInfo
->Skill
[1] || lockInfo
->Skill
[0])
187 pUser
->SendEquipError(EQUIP_ERR_ITEM_LOCKED
, pItem
, NULL
);
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());
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
);
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);
213 CharacterDatabase
.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem
->GetGUIDLow());
216 pUser
->SendLoot(pItem
->GetGUID(),LOOT_CORPSE
);
219 void WorldSession::HandleGameObjectUseOpcode( WorldPacket
& recv_data
)
221 CHECK_PACKET_SIZE(recv_data
, 8);
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
)
233 GameObject
*obj
= GetPlayer()->GetMap()->GetGameObject(guid
);
238 if (Script
->GOHello(_player
, obj
))
244 void WorldSession::HandleGameobjectReportUse(WorldPacket
& recvPacket
)
246 CHECK_PACKET_SIZE(recvPacket
,8);
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
)
257 GameObject
* go
= GetPlayer()->GetMap()->GetGameObject(guid
);
261 if(!go
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
))
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);
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
)
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
);
289 sLog
.outError("WORLD: unknown spell id %u", spellId
);
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?
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?
312 // client provided targets
313 SpellCastTargets targets
;
314 if(!targets
.read(&recvPacket
,mover
))
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
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
)
341 // increments with every CANCEL packet, don't use for now
344 recvPacket
>> counter
;
345 recvPacket
>> spellId
;
347 //FIXME: hack, ignore unexpected client cancel Deadly Throw cast
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
)
364 recvPacket
>> spellId
;
366 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellId
);
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
))
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
);
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
)
399 recvPacket
>> spellId
;
401 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellId
);
404 sLog
.outError("WORLD: unknown PET spell id %u", spellId
);
408 Creature
* pet
=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player
,guid
);
412 sLog
.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid
)) );
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() );
424 pet
->SendPetActionFeedback(FEEDBACK_PET_DEAD
);
428 pet
->RemoveAurasDueToSpell(spellId
);
430 pet
->AddCreatureSpellCooldown(spellId
);
433 void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket
& /*recvPacket*/)
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);
452 recv_data >> spellid;
456 void WorldSession::HandleTotemDestroy( WorldPacket
& recvPacket
)
458 CHECK_PACKET_SIZE(recvPacket
, 1);
460 // ignore for remote control state
461 if(_player
->m_mover
!= _player
)
466 recvPacket
>> slotId
;
468 if (slotId
>= MAX_TOTEM
)
471 if(!_player
->m_TotemSlot
[slotId
])
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
));
487 _player
->CastSpell(_player
,spellInfo
,false,0);
489 _player
->SetUInt32Value(PLAYER_SELF_RES_SPELL
, 0);