2 * Copyright (C) 2005-2008 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 "WorldSession.h"
33 void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket
& recv_data
)
35 CHECK_PACKET_SIZE(recv_data
,1);
37 sLog
.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
38 Player
*player
= GetPlayer();
39 uint64 lguid
= player
->GetLootGUID();
43 recv_data
>> lootSlot
;
45 if (IS_GAMEOBJECT_GUID(lguid
))
48 ObjectAccessor::GetGameObject(*player
, 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
);
59 else if (IS_ITEM_GUID(lguid
))
61 Item
*pItem
= player
->GetItemByGuid( lguid
);
65 player
->SendLootRelease(lguid
);
74 ObjectAccessor::GetCreature(*player
, lguid
);
76 bool ok_loot
= pCreature
&& pCreature
->isAlive() == (player
->getClass()==CLASS_ROGUE
&& pCreature
->lootForPickPocketed
);
78 if( !ok_loot
|| !pCreature
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
80 player
->SendLootRelease(lguid
);
84 loot
= &pCreature
->loot
;
87 QuestItem
*qitem
= NULL
;
88 QuestItem
*ffaitem
= NULL
;
89 QuestItem
*conditem
= NULL
;
91 LootItem
*item
= loot
->LootItemInSlot(lootSlot
,player
,&qitem
,&ffaitem
,&conditem
);
95 player
->SendEquipError( EQUIP_ERR_ALREADY_LOOTED
, NULL
, NULL
);
99 // questitems use the blocked field for other purposes
100 if (!qitem
&& item
->is_blocked
)
102 player
->SendLootRelease(lguid
);
106 ItemPosCountVec dest
;
107 uint8 msg
= player
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, item
->itemid
, item
->count
);
108 if ( msg
== EQUIP_ERR_OK
)
110 Item
* newitem
= player
->StoreNewItem( dest
, item
->itemid
, true, item
->randomPropertyId
);
114 qitem
->is_looted
= true;
115 //freeforall is 1 if everyone's supposed to get the quest item.
116 if (item
->freeforall
|| loot
->GetPlayerQuestItems().size() == 1)
117 player
->SendNotifyLootItemRemoved(lootSlot
);
119 loot
->NotifyQuestItemRemoved(qitem
->index
);
125 //freeforall case, notify only one player of the removal
126 ffaitem
->is_looted
=true;
127 player
->SendNotifyLootItemRemoved(lootSlot
);
131 //not freeforall, notify everyone
133 conditem
->is_looted
=true;
134 loot
->NotifyItemRemoved(lootSlot
);
138 //if only one person is supposed to loot the item, then set it to looted
139 if (!item
->freeforall
)
140 item
->is_looted
= true;
142 --loot
->unlootedCount
;
144 player
->SendNewItem(newitem
, uint32(item
->count
), false, false, true);
147 player
->SendEquipError( msg
, NULL
, NULL
);
150 void WorldSession::HandleLootMoneyOpcode( WorldPacket
& /*recv_data*/ )
152 sLog
.outDebug("WORLD: CMSG_LOOT_MONEY");
154 Player
*player
= GetPlayer();
155 uint64 guid
= player
->GetLootGUID();
161 switch(GUID_HIPART(guid
))
163 case HIGHGUID_GAMEOBJECT
:
165 GameObject
*pGameObject
= ObjectAccessor::GetGameObject(*GetPlayer(), guid
);
167 // not check distance for GO in case owned GO (fishing bobber case, for example)
168 if( pGameObject
&& (pGameObject
->GetOwnerGUID()==_player
->GetGUID() || pGameObject
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
)) )
169 pLoot
= &pGameObject
->loot
;
173 case HIGHGUID_CORPSE
: // remove insignia ONLY in BG
175 Corpse
*bones
= ObjectAccessor::GetCorpse(*GetPlayer(), guid
);
177 if (bones
&& bones
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
178 pLoot
= &bones
->loot
;
184 if(Item
*item
= GetPlayer()->GetItemByGuid(guid
))
190 Creature
* pCreature
= ObjectAccessor::GetCreature(*GetPlayer(), guid
);
192 bool ok_loot
= pCreature
&& pCreature
->isAlive() == (player
->getClass()==CLASS_ROGUE
&& pCreature
->lootForPickPocketed
);
194 if ( ok_loot
&& pCreature
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
195 pLoot
= &pCreature
->loot
;
200 return; // unlootable type
205 if (!IS_ITEM_GUID(guid
) && player
->GetGroup()) //item can be looted only single player
207 Group
*group
= player
->GetGroup();
209 std::vector
<Player
*> playersNear
;
210 for(GroupReference
*itr
= group
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
212 Player
* playerGroup
= itr
->getSource();
215 if (player
->GetDistance2d(playerGroup
) < sWorld
.getConfig(CONFIG_GROUP_XP_DISTANCE
))
216 playersNear
.push_back(playerGroup
);
219 uint32 money_per_player
= uint32((pLoot
->gold
)/(playersNear
.size()));
221 for (std::vector
<Player
*>::iterator i
= playersNear
.begin(); i
!= playersNear
.end(); ++i
)
223 (*i
)->ModifyMoney( money_per_player
);
224 //Offset surely incorrect, but works
225 WorldPacket
data( SMSG_LOOT_MONEY_NOTIFY
, 4 );
226 data
<< uint32(money_per_player
);
227 (*i
)->GetSession()->SendPacket( &data
);
231 player
->ModifyMoney( pLoot
->gold
);
233 pLoot
->NotifyMoneyRemoved();
237 void WorldSession::HandleLootOpcode( WorldPacket
& recv_data
)
239 CHECK_PACKET_SIZE(recv_data
,8);
241 sLog
.outDebug("WORLD: CMSG_LOOT");
246 GetPlayer()->SendLoot(guid
, LOOT_CORPSE
);
249 void WorldSession::HandleLootReleaseOpcode( WorldPacket
& recv_data
)
251 CHECK_PACKET_SIZE(recv_data
,8);
253 sLog
.outDebug("WORLD: CMSG_LOOT_RELEASE");
255 // cheaters can modify lguid to prevent correct apply loot release code and re-loot
256 // use internal stored guid
258 //recv_data >> lguid;
260 if(uint64 lguid
= GetPlayer()->GetLootGUID())
261 DoLootRelease(lguid
);
264 void WorldSession::DoLootRelease( uint64 lguid
)
266 Player
*player
= GetPlayer();
269 player
->SetLootGUID(0);
270 player
->SendLootRelease(lguid
);
272 player
->RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_LOOTING
);
274 if (IS_GAMEOBJECT_GUID(lguid
))
277 ObjectAccessor::GetGameObject(*player
, lguid
);
279 // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
280 if (!go
|| (go
->GetOwnerGUID() != _player
->GetGUID() && go
->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE
) && !go
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
))
285 if (go
->GetGoType() == GAMEOBJECT_TYPE_DOOR
)
287 // locked doors are opened with spelleffect openlock, prevent remove its as looted
288 go
->UseDoorOrButton();
290 else if (loot
->isLooted() || go
->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE
)
292 // GO is mineral vein? so it is not removed after its looted
293 if(go
->GetGoType() == GAMEOBJECT_TYPE_CHEST
)
295 uint32 go_min
= go
->GetGOInfo()->chest
.minSuccessOpens
;
296 uint32 go_max
= go
->GetGOInfo()->chest
.maxSuccessOpens
;
298 // only vein pass this check
299 if(go_min
!= 0 && go_max
> go_min
)
301 float amount_rate
= sWorld
.getRate(RATE_MINING_AMOUNT
);
302 float min_amount
= go_min
*amount_rate
;
303 float max_amount
= go_max
*amount_rate
;
306 float uses
= float(go
->GetUseCount());
308 if(uses
< max_amount
)
310 if(uses
>= min_amount
)
312 float chance_rate
= sWorld
.getRate(RATE_MINING_NEXT
);
314 int32 ReqValue
= 175;
315 LockEntry
const *lockInfo
= sLockStore
.LookupEntry(go
->GetGOInfo()->chest
.lockId
);
317 ReqValue
= lockInfo
->requiredminingskill
;
318 float skill
= float(player
->GetSkillValue(SKILL_MINING
))/(ReqValue
+25);
319 double chance
= pow(0.8*chance_rate
,4*(1/double(max_amount
))*double(uses
));
320 if(roll_chance_f(100*chance
+skill
))
322 go
->SetLootState(GO_READY
);
324 else // not have more uses
325 go
->SetLootState(GO_JUST_DEACTIVATED
);
327 else // 100% chance until min uses
328 go
->SetLootState(GO_READY
);
330 else // max uses already
331 go
->SetLootState(GO_JUST_DEACTIVATED
);
334 go
->SetLootState(GO_JUST_DEACTIVATED
);
336 else if (go
->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE
)
337 { // The fishing hole used once more
338 go
->AddUse(); // if the max usage is reached, will be despawned in next tick
339 if (go
->GetUseCount()>=irand(go
->GetGOInfo()->fishinghole
.minSuccessOpens
,go
->GetGOInfo()->fishinghole
.maxSuccessOpens
))
341 go
->SetLootState(GO_JUST_DEACTIVATED
);
344 go
->SetLootState(GO_READY
);
346 else // not chest (or vein/herb/etc)
347 go
->SetLootState(GO_JUST_DEACTIVATED
);
352 // not fully looted object
353 go
->SetLootState(GO_ACTIVATED
);
355 else if (IS_CORPSE_GUID(lguid
)) // ONLY remove insignia at BG
357 Corpse
*corpse
= ObjectAccessor::GetCorpse(*player
, lguid
);
358 if (!corpse
|| !corpse
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
361 loot
= &corpse
->loot
;
363 if (loot
->isLooted())
366 corpse
->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS
, CORPSE_DYNFLAG_LOOTABLE
);
369 else if (IS_ITEM_GUID(lguid
))
371 Item
*pItem
= player
->GetItemByGuid(lguid
);
374 if( (pItem
->GetProto()->BagFamily
& BAG_FAMILY_MASK_MINING_SUPP
) &&
375 pItem
->GetProto()->Class
== ITEM_CLASS_TRADE_GOODS
&&
376 pItem
->GetCount() >= 5)
378 pItem
->m_lootGenerated
= false;
382 player
->DestroyItemCount(pItem
, count
, true);
385 // 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.
386 player
->DestroyItem( pItem
->GetBagSlot(),pItem
->GetSlot(), true);
387 return; // item can be looted only single player
391 Creature
* pCreature
= ObjectAccessor::GetCreature(*player
, lguid
);
393 bool ok_loot
= pCreature
&& pCreature
->isAlive() == (player
->getClass()==CLASS_ROGUE
&& pCreature
->lootForPickPocketed
);
394 if ( !ok_loot
|| !pCreature
->IsWithinDistInMap(_player
,INTERACTION_DISTANCE
) )
397 loot
= &pCreature
->loot
;
399 // update next looter
400 if(Player
*recipient
= pCreature
->GetLootRecipient())
401 if(Group
* group
= recipient
->GetGroup())
402 if (group
->GetLooterGuid() == player
->GetGUID())
403 group
->UpdateLooterGuid(pCreature
);
405 if (loot
->isLooted())
407 // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact
408 if(!pCreature
->isAlive())
409 pCreature
->AllLootRemovedFromCorpse();
411 pCreature
->RemoveFlag(UNIT_DYNAMIC_FLAGS
, UNIT_DYNFLAG_LOOTABLE
);
416 //Player is not looking at loot list, he doesn't need to see updates on the loot list
417 loot
->RemoveLooter(player
->GetGUID());
420 void WorldSession::HandleLootMasterGiveOpcode( WorldPacket
& recv_data
)
422 CHECK_PACKET_SIZE(recv_data
,8+1+8);
425 uint64 lootguid
, target_playerguid
;
427 recv_data
>> lootguid
>> slotid
>> target_playerguid
;
429 if(!_player
->GetGroup() || _player
->GetGroup()->GetLooterGuid() != _player
->GetGUID())
431 _player
->SendLootRelease(GetPlayer()->GetLootGUID());
435 Player
*target
= ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid
, 0, HIGHGUID_PLAYER
));
439 sLog
.outDebug("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target
->GetName());
441 if(_player
->GetLootGUID() != lootguid
)
446 if(IS_CREATURE_GUID(GetPlayer()->GetLootGUID()))
448 Creature
*pCreature
= ObjectAccessor::GetCreature(*GetPlayer(), lootguid
);
452 pLoot
= &pCreature
->loot
;
454 else if(IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
456 GameObject
*pGO
= ObjectAccessor::GetGameObject(*GetPlayer(), lootguid
);
466 if (slotid
> pLoot
->items
.size())
468 sLog
.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %d)",GetPlayer()->GetName(), slotid
, pLoot
->items
.size());
472 LootItem
& item
= pLoot
->items
[slotid
];
474 ItemPosCountVec dest
;
475 uint8 msg
= target
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, item
.itemid
, item
.count
);
476 if ( msg
!= EQUIP_ERR_OK
)
478 target
->SendEquipError( msg
, NULL
, NULL
);
479 _player
->SendEquipError( msg
, NULL
, NULL
); // send duplicate of error massage to master looter
483 // not move item from loot to target inventory
484 Item
* newitem
= target
->StoreNewItem( dest
, item
.itemid
, true, item
.randomPropertyId
);
485 target
->SendNewItem(newitem
, uint32(item
.count
), false, false, true );
492 pLoot
->NotifyItemRemoved(slotid
);
493 --pLoot
->unlootedCount
;