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

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