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

Revision 283, 16.7 kB (checked in by yumileroy, 17 years ago)

*Alterac Valley. By Bogie and Balrok. Note: some core contents are modified. Will fix them later. Some sql are disabled because of possible conflict with offical DB. Use them at your own risk.

Original author: megamage
Date: 2008-11-21 19:45:49-06:00

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