2 * Copyright (C) 2005-2010 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 "WorldPacket.h"
23 #include "GameObject.h"
25 #include "ObjectAccessor.h"
26 #include "ObjectGuid.h"
27 #include "WorldSession.h"
34 void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket
& recv_data
)
36 sLog
.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
37 Player
*player
= GetPlayer();
38 ObjectGuid lguid
= player
->GetLootGUID();
42 recv_data
>> lootSlot
;
44 switch( lguid
.GetHigh())
46 case HIGHGUID_GAMEOBJECT
:
48 GameObject
*go
= player
->GetMap()->GetGameObject(lguid
);
50 // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
51 if (!go
|| ((go
->GetOwnerGUID() != _player
->GetGUID() && go
->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE
) && !go
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
)))
53 player
->SendLootRelease(lguid
);
62 Item
*pItem
= player
->GetItemByGuid( lguid
);
66 player
->SendLootRelease(lguid
);
75 Corpse
*bones
= player
->GetMap()->GetCorpse(lguid
);
78 player
->SendLootRelease(lguid
);
86 Creature
* pCreature
= GetPlayer()->GetMap()->GetCreature(lguid
);
88 bool ok_loot
= pCreature
&& pCreature
->isAlive() == (player
->getClass()==CLASS_ROGUE
&& pCreature
->lootForPickPocketed
);
90 if( !ok_loot
|| !pCreature
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
92 player
->SendLootRelease(lguid
);
96 loot
= &pCreature
->loot
;
101 sLog
.outError("%s is unsupported for looting.",lguid
.GetString().c_str());
106 QuestItem
*qitem
= NULL
;
107 QuestItem
*ffaitem
= NULL
;
108 QuestItem
*conditem
= NULL
;
110 LootItem
*item
= loot
->LootItemInSlot(lootSlot
,player
,&qitem
,&ffaitem
,&conditem
);
114 player
->SendEquipError( EQUIP_ERR_ALREADY_LOOTED
, NULL
, NULL
);
118 // questitems use the blocked field for other purposes
119 if (!qitem
&& item
->is_blocked
)
121 player
->SendLootRelease(lguid
);
125 ItemPosCountVec dest
;
126 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, item
->itemid
, item
->count
);
127 if ( msg
== EQUIP_ERR_OK
)
129 Item
* newitem
= player
->StoreNewItem( dest
, item
->itemid
, true, item
->randomPropertyId
);
133 qitem
->is_looted
= true;
134 //freeforall is 1 if everyone's supposed to get the quest item.
135 if (item
->freeforall
|| loot
->GetPlayerQuestItems().size() == 1)
136 player
->SendNotifyLootItemRemoved(lootSlot
);
138 loot
->NotifyQuestItemRemoved(qitem
->index
);
144 //freeforall case, notify only one player of the removal
145 ffaitem
->is_looted
=true;
146 player
->SendNotifyLootItemRemoved(lootSlot
);
150 //not freeforall, notify everyone
152 conditem
->is_looted
=true;
153 loot
->NotifyItemRemoved(lootSlot
);
157 //if only one person is supposed to loot the item, then set it to looted
158 if (!item
->freeforall
)
159 item
->is_looted
= true;
161 --loot
->unlootedCount
;
163 player
->SendNewItem(newitem
, uint32(item
->count
), false, false, true);
164 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
, item
->itemid
, item
->count
);
165 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
, loot
->loot_type
, item
->count
);
166 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM
, item
->itemid
, item
->count
);
169 player
->SendEquipError( msg
, NULL
, NULL
, item
->itemid
);
172 void WorldSession::HandleLootMoneyOpcode( WorldPacket
& /*recv_data*/ )
174 sLog
.outDebug("WORLD: CMSG_LOOT_MONEY");
176 Player
*player
= GetPlayer();
177 ObjectGuid guid
= player
->GetLootGUID();
183 switch(guid
.GetHigh())
185 case HIGHGUID_GAMEOBJECT
:
187 GameObject
*pGameObject
= GetPlayer()->GetMap()->GetGameObject(guid
);
189 // not check distance for GO in case owned GO (fishing bobber case, for example)
190 if( pGameObject
&& (pGameObject
->GetOwnerGUID()==_player
->GetGUID() || pGameObject
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
)) )
191 pLoot
= &pGameObject
->loot
;
195 case HIGHGUID_CORPSE
: // remove insignia ONLY in BG
197 Corpse
*bones
= _player
->GetMap()->GetCorpse(guid
);
199 if (bones
&& bones
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
200 pLoot
= &bones
->loot
;
206 if(Item
*item
= GetPlayer()->GetItemByGuid(guid
))
212 Creature
* pCreature
= GetPlayer()->GetMap()->GetCreature(guid
);
214 bool ok_loot
= pCreature
&& pCreature
->isAlive() == (player
->getClass()==CLASS_ROGUE
&& pCreature
->lootForPickPocketed
);
216 if ( ok_loot
&& pCreature
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
217 pLoot
= &pCreature
->loot
;
222 return; // unlootable type
227 if (!guid
.IsItem() && player
->GetGroup()) //item can be looted only single player
229 Group
*group
= player
->GetGroup();
231 std::vector
<Player
*> playersNear
;
232 for(GroupReference
*itr
= group
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
234 Player
* playerGroup
= itr
->getSource();
237 if (player
->IsWithinDistInMap(playerGroup
,sWorld
.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE
),false))
238 playersNear
.push_back(playerGroup
);
241 uint32 money_per_player
= uint32((pLoot
->gold
)/(playersNear
.size()));
243 for (std::vector
<Player
*>::const_iterator i
= playersNear
.begin(); i
!= playersNear
.end(); ++i
)
245 (*i
)->ModifyMoney( money_per_player
);
246 (*i
)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY
, money_per_player
);
247 //Offset surely incorrect, but works
248 WorldPacket
data( SMSG_LOOT_MONEY_NOTIFY
, 4 );
249 data
<< uint32(money_per_player
);
250 (*i
)->GetSession()->SendPacket( &data
);
255 player
->ModifyMoney( pLoot
->gold
);
256 player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY
, pLoot
->gold
);
259 pLoot
->NotifyMoneyRemoved();
263 void WorldSession::HandleLootOpcode( WorldPacket
& recv_data
)
265 sLog
.outDebug("WORLD: CMSG_LOOT");
270 // Check possible cheat
271 if(!_player
->isAlive())
274 GetPlayer()->SendLoot(guid
, LOOT_CORPSE
);
277 void WorldSession::HandleLootReleaseOpcode( WorldPacket
& recv_data
)
279 sLog
.outDebug("WORLD: CMSG_LOOT_RELEASE");
281 // cheaters can modify lguid to prevent correct apply loot release code and re-loot
282 // use internal stored guid
283 recv_data
.read_skip
<uint64
>(); // guid;
285 if(uint64 lguid
= GetPlayer()->GetLootGUID())
286 DoLootRelease(lguid
);
289 void WorldSession::DoLootRelease(ObjectGuid lguid
)
291 Player
*player
= GetPlayer();
294 player
->SetLootGUID(0);
295 player
->SendLootRelease(lguid
);
297 player
->RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_LOOTING
);
299 if(!player
->IsInWorld())
302 switch(lguid
.GetHigh())
304 case HIGHGUID_GAMEOBJECT
:
306 GameObject
*go
= GetPlayer()->GetMap()->GetGameObject(lguid
);
308 // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
309 if (!go
|| ((go
->GetOwnerGUID() != _player
->GetGUID() && go
->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE
) && !go
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
)))
314 if (go
->GetGoType() == GAMEOBJECT_TYPE_DOOR
)
316 // locked doors are opened with spelleffect openlock, prevent remove its as looted
317 go
->UseDoorOrButton();
319 else if (loot
->isLooted() || go
->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE
)
321 // GO is mineral vein? so it is not removed after its looted
322 if(go
->GetGoType() == GAMEOBJECT_TYPE_CHEST
)
324 uint32 go_min
= go
->GetGOInfo()->chest
.minSuccessOpens
;
325 uint32 go_max
= go
->GetGOInfo()->chest
.maxSuccessOpens
;
327 // only vein pass this check
328 if(go_min
!= 0 && go_max
> go_min
)
330 float amount_rate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_MINING_AMOUNT
);
331 float min_amount
= go_min
*amount_rate
;
332 float max_amount
= go_max
*amount_rate
;
335 float uses
= float(go
->GetUseCount());
337 if(uses
< max_amount
)
339 if(uses
>= min_amount
)
341 float chance_rate
= sWorld
.getConfig(CONFIG_FLOAT_RATE_MINING_NEXT
);
343 int32 ReqValue
= 175;
344 LockEntry
const *lockInfo
= sLockStore
.LookupEntry(go
->GetGOInfo()->chest
.lockId
);
346 ReqValue
= lockInfo
->Skill
[0];
347 float skill
= float(player
->GetSkillValue(SKILL_MINING
))/(ReqValue
+25);
348 double chance
= pow(0.8*chance_rate
,4*(1/double(max_amount
))*double(uses
));
349 if(roll_chance_f(float(100.0f
*chance
+skill
)))
351 go
->SetLootState(GO_READY
);
353 else // not have more uses
354 go
->SetLootState(GO_JUST_DEACTIVATED
);
356 else // 100% chance until min uses
357 go
->SetLootState(GO_READY
);
359 else // max uses already
360 go
->SetLootState(GO_JUST_DEACTIVATED
);
363 go
->SetLootState(GO_JUST_DEACTIVATED
);
365 else if (go
->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE
)
366 { // The fishing hole used once more
367 go
->AddUse(); // if the max usage is reached, will be despawned in next tick
368 if (go
->GetUseCount() >= urand(go
->GetGOInfo()->fishinghole
.minSuccessOpens
,go
->GetGOInfo()->fishinghole
.maxSuccessOpens
))
370 go
->SetLootState(GO_JUST_DEACTIVATED
);
373 go
->SetLootState(GO_READY
);
375 else // not chest (or vein/herb/etc)
376 go
->SetLootState(GO_JUST_DEACTIVATED
);
381 // not fully looted object
382 go
->SetLootState(GO_ACTIVATED
);
385 case HIGHGUID_CORPSE
: // ONLY remove insignia at BG
387 Corpse
*corpse
= _player
->GetMap()->GetCorpse(lguid
);
388 if (!corpse
|| !corpse
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
391 loot
= &corpse
->loot
;
393 if (loot
->isLooted())
396 corpse
->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS
, CORPSE_DYNFLAG_LOOTABLE
);
402 Item
*pItem
= player
->GetItemByGuid(lguid
);
406 ItemPrototype
const* proto
= pItem
->GetProto();
408 // destroy only 5 items from stack in case prospecting and milling
409 if( (proto
->BagFamily
& (BAG_FAMILY_MASK_MINING_SUPP
|BAG_FAMILY_MASK_HERBS
)) &&
410 proto
->Class
== ITEM_CLASS_TRADE_GOODS
)
412 pItem
->m_lootGenerated
= false;
415 uint32 count
= pItem
->GetCount();
417 // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
421 player
->DestroyItemCount(pItem
, count
, true);
424 // FIXME: item don't must be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or checting possible.
425 player
->DestroyItem( pItem
->GetBagSlot(),pItem
->GetSlot(), true);
426 return; // item can be looted only single player
430 Creature
* pCreature
= GetPlayer()->GetMap()->GetCreature(lguid
);
432 bool ok_loot
= pCreature
&& pCreature
->isAlive() == (player
->getClass()==CLASS_ROGUE
&& pCreature
->lootForPickPocketed
);
433 if ( !ok_loot
|| !pCreature
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
436 loot
= &pCreature
->loot
;
438 // update next looter
439 if(Player
*recipient
= pCreature
->GetLootRecipient())
440 if(Group
* group
= recipient
->GetGroup())
441 if (group
->GetLooterGuid() == player
->GetGUID())
442 group
->UpdateLooterGuid(pCreature
);
444 if (loot
->isLooted())
446 // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact
447 if(!pCreature
->isAlive())
448 pCreature
->AllLootRemovedFromCorpse();
450 pCreature
->RemoveFlag(UNIT_DYNAMIC_FLAGS
, UNIT_DYNFLAG_LOOTABLE
);
457 sLog
.outError("%s is unsupported for looting.", lguid
.GetString().c_str());
462 //Player is not looking at loot list, he doesn't need to see updates on the loot list
463 loot
->RemoveLooter(player
->GetGUID());
466 void WorldSession::HandleLootMasterGiveOpcode( WorldPacket
& recv_data
)
470 ObjectGuid target_playerguid
;
472 recv_data
>> lootguid
>> slotid
>> target_playerguid
;
474 if(!_player
->GetGroup() || _player
->GetGroup()->GetLooterGuid() != _player
->GetGUID())
476 _player
->SendLootRelease(GetPlayer()->GetLootGUID());
480 Player
*target
= ObjectAccessor::FindPlayer(target_playerguid
);
484 sLog
.outDebug("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = %s [%s].", target_playerguid
.GetString().c_str(), target
->GetName());
486 if(_player
->GetLootGUID() != lootguid
.GetRawValue())
491 if(lootguid
.IsCreature())
493 Creature
*pCreature
= GetPlayer()->GetMap()->GetCreature(lootguid
);
497 pLoot
= &pCreature
->loot
;
499 else if(lootguid
.IsGameobject())
501 GameObject
*pGO
= GetPlayer()->GetMap()->GetGameObject(lootguid
);
510 if (slotid
> pLoot
->items
.size())
512 sLog
.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %lu)",GetPlayer()->GetName(), slotid
, (unsigned long)pLoot
->items
.size());
516 LootItem
& item
= pLoot
->items
[slotid
];
518 ItemPosCountVec dest
;
519 uint8 msg
= target
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, item
.itemid
, item
.count
);
520 if ( msg
!= EQUIP_ERR_OK
)
522 target
->SendEquipError( msg
, NULL
, NULL
, item
.itemid
);
524 // send duplicate of error massage to master looter
525 _player
->SendEquipError( msg
, NULL
, NULL
, item
.itemid
);
529 // now move item from loot to target inventory
530 Item
* newitem
= target
->StoreNewItem( dest
, item
.itemid
, true, item
.randomPropertyId
);
531 target
->SendNewItem(newitem
, uint32(item
.count
), false, false, true );
532 target
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
, item
.itemid
, item
.count
);
533 target
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE
, pLoot
->loot_type
, item
.count
);
534 target
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM
, item
.itemid
, item
.count
);
540 pLoot
->NotifyItemRemoved(slotid
);
541 --pLoot
->unlootedCount
;