root/trunk/src/game/LootHandler.cpp @ 111

Revision 102, 16.5 kB (checked in by yumileroy, 17 years ago)

[svn] Fixed copyright notices to comply with GPL.

Original author: w12x
Date: 2008-10-23 03:29:52-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "Common.h"
22#include "WorldPacket.h"
23#include "Log.h"
24#include "Corpse.h"
25#include "GameObject.h"
26#include "Player.h"
27#include "ObjectAccessor.h"
28#include "WorldSession.h"
29#include "LootMgr.h"
30#include "Object.h"
31#include "Group.h"
32#include "World.h"
33#include "Util.h"
34
35void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
36{
37    CHECK_PACKET_SIZE(recv_data,1);
38
39    sLog.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
40    Player  *player =   GetPlayer();
41    uint64   lguid =    player->GetLootGUID();
42    Loot    *loot;
43    uint8    lootSlot;
44
45    recv_data >> lootSlot;
46
47    if (IS_GAMEOBJECT_GUID(lguid))
48    {
49        GameObject *go =
50            ObjectAccessor::GetGameObject(*player, lguid);
51
52        // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
53        if (!go || (go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
54        {
55            player->SendLootRelease(lguid);
56            return;
57        }
58
59        loot = &go->loot;
60    }
61    else if (IS_ITEM_GUID(lguid))
62    {
63        Item *pItem = player->GetItemByGuid( lguid );
64
65        if (!pItem)
66        {
67            player->SendLootRelease(lguid);
68            return;
69        }
70
71        loot = &pItem->loot;
72    }
73    else
74    {
75        Creature* pCreature =
76            ObjectAccessor::GetCreature(*player, lguid);
77
78        bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
79
80        if( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
81        {
82            player->SendLootRelease(lguid);
83            return;
84        }
85
86        loot = &pCreature->loot;
87    }
88
89    QuestItem *qitem = NULL;
90    QuestItem *ffaitem = NULL;
91    QuestItem *conditem = NULL;
92
93    LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem);
94
95    if(!item)
96    {
97        player->SendEquipError( EQUIP_ERR_ALREADY_LOOTED, NULL, NULL );
98        return;
99    }
100
101    // questitems use the blocked field for other purposes
102    if (!qitem && item->is_blocked)
103    {
104        player->SendLootRelease(lguid);
105        return;
106    }
107
108    ItemPosCountVec dest;
109    uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count );
110    if ( msg == EQUIP_ERR_OK )
111    {
112        Item * newitem = player->StoreNewItem( dest, item->itemid, true, item->randomPropertyId);
113
114        if (qitem)
115        {
116            qitem->is_looted = true;
117            //freeforall is 1 if everyone's supposed to get the quest item.
118            if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
119                player->SendNotifyLootItemRemoved(lootSlot);
120            else
121                loot->NotifyQuestItemRemoved(qitem->index);
122        }
123        else
124        {
125            if (ffaitem)
126            {
127                //freeforall case, notify only one player of the removal
128                ffaitem->is_looted=true;
129                player->SendNotifyLootItemRemoved(lootSlot);
130            }
131            else
132            {
133                //not freeforall, notify everyone
134                if(conditem)
135                    conditem->is_looted=true;
136                loot->NotifyItemRemoved(lootSlot);
137            }
138        }
139
140        //if only one person is supposed to loot the item, then set it to looted
141        if (!item->freeforall)
142            item->is_looted = true;
143
144        --loot->unlootedCount;
145
146        player->SendNewItem(newitem, uint32(item->count), false, false, true);
147    }
148    else
149        player->SendEquipError( msg, NULL, NULL );
150}
151
152void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
153{
154    sLog.outDebug("WORLD: CMSG_LOOT_MONEY");
155
156    Player *player = GetPlayer();
157    uint64 guid = player->GetLootGUID();
158    if(!guid)
159        return;
160
161    Loot *pLoot = NULL;
162
163    switch(GUID_HIPART(guid))
164    {
165        case HIGHGUID_GAMEOBJECT:
166        {
167            GameObject *pGameObject = ObjectAccessor::GetGameObject(*GetPlayer(), guid);
168
169            // not check distance for GO in case owned GO (fishing bobber case, for example)
170            if( pGameObject && (pGameObject->GetOwnerGUID()==_player->GetGUID() || pGameObject->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) )
171                pLoot = &pGameObject->loot;
172
173            break;
174        }
175        case HIGHGUID_CORPSE:                               // remove insignia ONLY in BG
176        {
177            Corpse *bones = ObjectAccessor::GetCorpse(*GetPlayer(), guid);
178
179            if (bones && bones->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
180                pLoot = &bones->loot;
181
182            break;
183        }
184        case HIGHGUID_ITEM:
185        {
186            if(Item *item = GetPlayer()->GetItemByGuid(guid))
187                pLoot = &item->loot;
188            break;
189        }
190        case HIGHGUID_UNIT:
191        {
192            Creature* pCreature = ObjectAccessor::GetCreature(*GetPlayer(), guid);
193
194            bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
195
196            if ( ok_loot && pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
197                pLoot = &pCreature->loot ;
198
199            break;
200        }
201        default:
202            return;                                         // unlootable type
203    }
204
205    if( pLoot )
206    {
207        if (!IS_ITEM_GUID(guid) && player->GetGroup())      //item can be looted only single player
208        {
209            Group *group = player->GetGroup();
210
211            std::vector<Player*> playersNear;
212            for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
213            {
214                Player* playerGroup = itr->getSource();
215                if(!playerGroup)
216                    continue;
217                if (player->GetDistance2d(playerGroup) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
218                    playersNear.push_back(playerGroup);
219            }
220
221            uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size()));
222
223            for (std::vector<Player*>::iterator i = playersNear.begin(); i != playersNear.end(); ++i)
224            {
225                (*i)->ModifyMoney( money_per_player );
226                //Offset surely incorrect, but works
227                WorldPacket data( SMSG_LOOT_MONEY_NOTIFY, 4 );
228                data << uint32(money_per_player);
229                (*i)->GetSession()->SendPacket( &data );
230            }
231        }
232        else
233            player->ModifyMoney( pLoot->gold );
234        pLoot->gold = 0;
235        pLoot->NotifyMoneyRemoved();
236    }
237}
238
239void WorldSession::HandleLootOpcode( WorldPacket & recv_data )
240{
241    CHECK_PACKET_SIZE(recv_data,8);
242
243    sLog.outDebug("WORLD: CMSG_LOOT");
244
245    uint64 guid;
246    recv_data >> guid;
247
248    GetPlayer()->SendLoot(guid, LOOT_CORPSE);
249}
250
251void WorldSession::HandleLootReleaseOpcode( WorldPacket & recv_data )
252{
253    CHECK_PACKET_SIZE(recv_data,8);
254
255    sLog.outDebug("WORLD: CMSG_LOOT_RELEASE");
256
257    // cheaters can modify lguid to prevent correct apply loot release code and re-loot
258    // use internal stored guid
259    //uint64   lguid;
260    //recv_data >> lguid;
261
262    if(uint64 lguid = GetPlayer()->GetLootGUID())
263        DoLootRelease(lguid);
264}
265
266void WorldSession::DoLootRelease( uint64 lguid )
267{
268    Player  *player = GetPlayer();
269    Loot    *loot;
270
271    player->SetLootGUID(0);
272    player->SendLootRelease(lguid);
273
274    player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
275
276    if (IS_GAMEOBJECT_GUID(lguid))
277    {
278        GameObject *go =
279            ObjectAccessor::GetGameObject(*player, lguid);
280
281        // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
282        if (!go || (go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
283            return;
284
285        loot = &go->loot;
286
287        if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR)
288        {
289            // locked doors are opened with spelleffect openlock, prevent remove its as looted
290            go->UseDoorOrButton();
291        }
292        else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
293        {
294            // GO is mineral vein? so it is not removed after its looted
295            if(go->GetGoType() == GAMEOBJECT_TYPE_CHEST)
296            {
297                uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens;
298                uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens;
299
300                // only vein pass this check
301                if(go_min != 0 && go_max > go_min)
302                {
303                    float amount_rate = sWorld.getRate(RATE_MINING_AMOUNT);
304                    float min_amount = go_min*amount_rate;
305                    float max_amount = go_max*amount_rate;
306
307                    go->AddUse();
308                    float uses = float(go->GetUseCount());
309
310                    if(uses < max_amount)
311                    {
312                        if(uses >= min_amount)
313                        {
314                            float chance_rate = sWorld.getRate(RATE_MINING_NEXT);
315
316                            int32 ReqValue = 175;
317                            LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId);
318                            if(lockInfo)
319                                ReqValue = lockInfo->requiredminingskill;
320                            float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25);
321                            double chance = pow(0.8*chance_rate,4*(1/double(max_amount))*double(uses));
322                            if(roll_chance_f(100*chance+skill))
323                            {
324                                go->SetLootState(GO_READY);
325                            }
326                            else                            // not have more uses
327                                go->SetLootState(GO_JUST_DEACTIVATED);
328                        }
329                        else                                // 100% chance until min uses
330                            go->SetLootState(GO_READY);
331                    }
332                    else                                    // max uses already
333                        go->SetLootState(GO_JUST_DEACTIVATED);
334                }
335                else                                        // not vein
336                    go->SetLootState(GO_JUST_DEACTIVATED);
337            }
338            else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
339            {                                               // The fishing hole used once more
340                go->AddUse();                               // if the max usage is reached, will be despawned in next tick
341                if (go->GetUseCount()>=irand(go->GetGOInfo()->fishinghole.minSuccessOpens,go->GetGOInfo()->fishinghole.maxSuccessOpens))
342                {
343                    go->SetLootState(GO_JUST_DEACTIVATED);
344                }
345                else
346                    go->SetLootState(GO_READY);
347            }
348            else // not chest (or vein/herb/etc)
349                go->SetLootState(GO_JUST_DEACTIVATED);
350
351            loot->clear();
352        }
353        else
354            // not fully looted object
355            go->SetLootState(GO_ACTIVATED);
356    }
357    else if (IS_CORPSE_GUID(lguid))        // ONLY remove insignia at BG
358    {
359        Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid);
360        if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
361            return;
362
363        loot = &corpse->loot;
364
365        if (loot->isLooted())
366        {
367            loot->clear();
368            corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
369        }
370    }
371    else if (IS_ITEM_GUID(lguid))
372    {
373        Item *pItem = player->GetItemByGuid(lguid );
374        if(!pItem)
375            return;
376        if( (pItem->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) &&
377            pItem->GetProto()->Class == ITEM_CLASS_TRADE_GOODS &&
378            pItem->GetCount() >= 5)
379        {
380            pItem->m_lootGenerated = false;
381            pItem->loot.clear();
382
383            uint32 count = 5;
384            player->DestroyItemCount(pItem, count, true);
385        }
386        else
387            // 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.
388            player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true);
389        return;                                             // item can be looted only single player
390    }
391    else
392    {
393        Creature* pCreature = ObjectAccessor::GetCreature(*player, lguid);
394
395        bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed);
396        if ( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
397            return;
398
399        loot = &pCreature->loot;
400
401        // update next looter
402        if(Player *recipient = pCreature->GetLootRecipient())
403            if(Group* group = recipient->GetGroup())
404                if (group->GetLooterGuid() == player->GetGUID())
405                    group->UpdateLooterGuid(pCreature);
406
407        if (loot->isLooted())
408        {
409            // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact
410            if(!pCreature->isAlive())
411                pCreature->AllLootRemovedFromCorpse();
412
413            pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
414            loot->clear();
415        }
416    }
417
418    //Player is not looking at loot list, he doesn't need to see updates on the loot list
419    loot->RemoveLooter(player->GetGUID());
420}
421
422void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data )
423{
424    CHECK_PACKET_SIZE(recv_data,8+1+8);
425
426    uint8 slotid;
427    uint64 lootguid, target_playerguid;
428
429    recv_data >> lootguid >> slotid >> target_playerguid;
430
431    if(!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID())
432    {
433        _player->SendLootRelease(GetPlayer()->GetLootGUID());
434        return;
435    }
436
437    Player *target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER));
438    if(!target)
439        return;
440
441    sLog.outDebug("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName());
442
443    if(_player->GetLootGUID() != lootguid)
444        return;
445
446    Loot *pLoot = NULL;
447
448    if(IS_CREATURE_GUID(GetPlayer()->GetLootGUID()))
449    {
450        Creature *pCreature = ObjectAccessor::GetCreature(*GetPlayer(), lootguid);
451        if(!pCreature)
452            return;
453
454        pLoot = &pCreature->loot;
455    }
456    else if(IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
457    {
458        GameObject *pGO = ObjectAccessor::GetGameObject(*GetPlayer(), lootguid);
459        if(!pGO)
460            return;
461
462        pLoot = &pGO->loot;
463    }
464
465    if(!pLoot)
466        return;
467
468    if (slotid > pLoot->items.size())
469    {
470        sLog.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %d)",GetPlayer()->GetName(), slotid, pLoot->items.size());
471        return;
472    }
473
474    LootItem& item = pLoot->items[slotid];
475
476    ItemPosCountVec dest;
477    uint8 msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count );
478    if ( msg != EQUIP_ERR_OK )
479    {
480        target->SendEquipError( msg, NULL, NULL );
481        _player->SendEquipError( msg, NULL, NULL );         // send duplicate of error massage to master looter
482        return;
483    }
484
485    // not move item from loot to target inventory
486    Item * newitem = target->StoreNewItem( dest, item.itemid, true, item.randomPropertyId );
487    target->SendNewItem(newitem, uint32(item.count), false, false, true );
488
489    // mark as looted
490    item.count=0;
491    item.is_looted=true;
492
493
494    pLoot->NotifyItemRemoved(slotid);
495    --pLoot->unlootedCount;
496}
Note: See TracBrowser for help on using the browser.