Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. /*
  2.  * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
  3.  * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License as published by the
  7.  * Free Software Foundation; either version 2 of the License, or (at your
  8.  * option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful, but WITHOUT
  11.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12.  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13.  * more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License along
  16.  * with this program. If not, see <http://www.gnu.org/licenses/>.
  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.  
  33. void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data)
  34. {
  35.     sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
  36.     Player* player = GetPlayer();
  37.     uint64 lguid = player->GetLootGUID();
  38.     Loot* loot = NULL;
  39.     uint8 lootSlot = 0;
  40.  
  41.     recv_data >> lootSlot;
  42.  
  43.     if (IS_GAMEOBJECT_GUID(lguid))
  44.     {
  45.         GameObject* go = player->GetMap()->GetGameObject(lguid);
  46.  
  47.         // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
  48.         if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))
  49.         {
  50.             player->SendLootRelease(lguid);
  51.             return;
  52.         }
  53.  
  54.         loot = &go->loot;
  55.     }
  56.     else if (IS_ITEM_GUID(lguid))
  57.     {
  58.         Item* pItem = player->GetItemByGuid(lguid);
  59.  
  60.         if (!pItem)
  61.         {
  62.             player->SendLootRelease(lguid);
  63.             return;
  64.         }
  65.  
  66.         loot = &pItem->loot;
  67.     }
  68.     else if (IS_CORPSE_GUID(lguid))
  69.     {
  70.         Corpse* bones = ObjectAccessor::GetCorpse(*player, lguid);
  71.         if (!bones)
  72.         {
  73.             player->SendLootRelease(lguid);
  74.             return;
  75.         }
  76.  
  77.         loot = &bones->loot;
  78.     }
  79.     else
  80.     {
  81.         Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
  82.  
  83.         bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
  84.  
  85.         if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
  86.         {
  87.             player->SendLootRelease(lguid);
  88.             return;
  89.         }
  90.  
  91.         loot = &creature->loot;
  92.     }
  93.  
  94.     player->StoreLootItem(lootSlot, loot);
  95. }
  96.  
  97. void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/)
  98. {
  99.     sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_MONEY");
  100.  
  101.     Player* player = GetPlayer();
  102.     uint64 guid = player->GetLootGUID();
  103.     if (!guid)
  104.         return;
  105.  
  106.     Loot* loot = NULL;
  107.     bool shareMoney = true;
  108.  
  109.     switch (GUID_HIPART(guid))
  110.     {
  111.         case HIGHGUID_GAMEOBJECT:
  112.         {
  113.             GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
  114.  
  115.             // do not check distance for GO if player is the owner of it (ex. fishing bobber)
  116.             if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE))))
  117.                 loot = &go->loot;
  118.  
  119.             break;
  120.         }
  121.         case HIGHGUID_CORPSE:                               // remove insignia ONLY in BG
  122.         {
  123.             Corpse* bones = ObjectAccessor::GetCorpse(*player, guid);
  124.  
  125.             if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE))
  126.             {
  127.                 loot = &bones->loot;
  128.                 shareMoney = false;
  129.             }
  130.  
  131.             break;
  132.         }
  133.         case HIGHGUID_ITEM:
  134.         {
  135.             if (Item* item = player->GetItemByGuid(guid))
  136.             {
  137.                 loot = &item->loot;
  138.                 shareMoney = false;
  139.             }
  140.             break;
  141.         }
  142.         case HIGHGUID_UNIT:
  143.         case HIGHGUID_VEHICLE:
  144.         {
  145.             Creature* creature = player->GetMap()->GetCreature(guid);
  146.             bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
  147.             if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
  148.             {
  149.                 loot = &creature->loot;
  150.                 if (creature->isAlive())
  151.                     shareMoney = false;
  152.             }
  153.             break;
  154.         }
  155.         default:
  156.             return;                                         // unlootable type
  157.     }
  158.  
  159.     if (loot)
  160.     {
  161.         loot->NotifyMoneyRemoved();
  162.         if (shareMoney && player->GetGroup())      //item, pickpocket and players can be looted only single player
  163.         {
  164.             Group* group = player->GetGroup();
  165.  
  166.             std::vector<Player*> playersNear;
  167.             for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  168.             {
  169.                 Player* member = itr->getSource();
  170.                 if (!member)
  171.                     continue;
  172.  
  173.                 if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
  174.                     playersNear.push_back(member);
  175.             }
  176.  
  177.             uint32 goldPerPlayer = uint32((loot->gold) / (playersNear.size()));
  178.  
  179.             for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i)
  180.             {
  181.                 (*i)->ModifyMoney(goldPerPlayer);
  182.                 (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, goldPerPlayer);
  183.  
  184.                 WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1);
  185.                 data << uint32(goldPerPlayer);
  186.                 data << uint8(playersNear.size() > 1 ? 0 : 1);     // Controls the text displayed in chat. 0 is "Your share is..." and 1 is "You loot..."
  187.                 (*i)->GetSession()->SendPacket(&data);
  188.             }
  189.         }
  190.         else
  191.         {
  192.             player->ModifyMoney(loot->gold);
  193.             player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold);
  194.  
  195.             WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4 + 1);
  196.             data << uint32(loot->gold);
  197.             data << uint8(1);   // "You loot..."
  198.             SendPacket(&data);
  199.         }
  200.  
  201.         loot->gold = 0;
  202.     }
  203. }
  204.  
  205. void WorldSession::HandleLootOpcode(WorldPacket & recv_data)
  206. {
  207.     sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT");
  208.  
  209.     uint64 guid;
  210.     recv_data >> guid;
  211.  
  212.     // Check possible cheat
  213.     if (!_player->isAlive())
  214.         return;
  215.  
  216.     GetPlayer()->SendLoot(guid, LOOT_CORPSE);
  217.  
  218.     // interrupt cast
  219.     if (GetPlayer()->IsNonMeleeSpellCasted(false))
  220.         GetPlayer()->InterruptNonMeleeSpells(false);
  221. }
  222.  
  223. void WorldSession::HandleLootReleaseOpcode(WorldPacket& recvData)
  224. {
  225.     sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_RELEASE");
  226.  
  227.     // cheaters can modify lguid to prevent correct apply loot release code and re-loot
  228.     // use internal stored guid
  229.     uint64 guid;
  230.     recvData >> guid;
  231.  
  232.     if (uint64 lguid = GetPlayer()->GetLootGUID())
  233.         if (lguid == guid)
  234.             DoLootRelease(lguid);
  235. }
  236.  
  237. void WorldSession::DoLootRelease(uint64 lguid)
  238. {
  239.     Player  *player = GetPlayer();
  240.     Loot    *loot;
  241.  
  242.     player->SetLootGUID(0);
  243.     player->SendLootRelease(lguid);
  244.  
  245.     player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
  246.  
  247.     if (!player->IsInWorld())
  248.         return;
  249.  
  250.     if (IS_GAMEOBJECT_GUID(lguid))
  251.     {
  252.         GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid);
  253.  
  254.         // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO
  255.         if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))
  256.             return;
  257.  
  258.         loot = &go->loot;
  259.  
  260.         if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR)
  261.         {
  262.             // locked doors are opened with spelleffect openlock, prevent remove its as looted
  263.             go->UseDoorOrButton();
  264.         }
  265.         else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE)
  266.         {
  267.             // GO is mineral vein? so it is not removed after its looted
  268.             if (go->GetGoType() == GAMEOBJECT_TYPE_CHEST)
  269.             {
  270.                 uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens;
  271.                 uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens;
  272.  
  273.                 // only vein pass this check
  274.                 if (go_min != 0 && go_max > go_min)
  275.                 {
  276.                     float amount_rate = sWorld->getRate(RATE_MINING_AMOUNT);
  277.                     float min_amount = go_min*amount_rate;
  278.                     float max_amount = go_max*amount_rate;
  279.  
  280.                     go->AddUse();
  281.                     float uses = float(go->GetUseCount());
  282.  
  283.                     if (uses < max_amount)
  284.                     {
  285.                         if (uses >= min_amount)
  286.                         {
  287.                             float chance_rate = sWorld->getRate(RATE_MINING_NEXT);
  288.  
  289.                             int32 ReqValue = 175;
  290.                             LockEntry const* lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId);
  291.                             if (lockInfo)
  292.                                 ReqValue = lockInfo->Skill[0];
  293.                             float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25);
  294.                             double chance = pow(0.8*chance_rate, 4*(1/double(max_amount))*double(uses));
  295.                             if (roll_chance_f((float)(100*chance+skill)))
  296.                             {
  297.                                 go->SetLootState(GO_READY);
  298.                             }
  299.                             else                            // not have more uses
  300.                                 go->SetLootState(GO_JUST_DEACTIVATED);
  301.                         }
  302.                         else                                // 100% chance until min uses
  303.                             go->SetLootState(GO_READY);
  304.                     }
  305.                     else                                    // max uses already
  306.                         go->SetLootState(GO_JUST_DEACTIVATED);
  307.                 }
  308.                 else                                        // not vein
  309.                     go->SetLootState(GO_JUST_DEACTIVATED);
  310.             }
  311.             else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
  312.             {                                               // The fishing hole used once more
  313.                 go->AddUse();                               // if the max usage is reached, will be despawned in next tick
  314.                 if (go->GetUseCount() >= urand(go->GetGOInfo()->fishinghole.minSuccessOpens, go->GetGOInfo()->fishinghole.maxSuccessOpens))
  315.                 {
  316.                     go->SetLootState(GO_JUST_DEACTIVATED);
  317.                 }
  318.                 else
  319.                     go->SetLootState(GO_READY);
  320.             }
  321.             else // not chest (or vein/herb/etc)
  322.                 go->SetLootState(GO_JUST_DEACTIVATED);
  323.  
  324.             loot->clear();
  325.         }
  326.         else
  327.         {
  328.             // not fully looted object
  329.             go->SetLootState(GO_ACTIVATED, player);
  330.  
  331.             // if the round robin player release, reset it.
  332.             if (player->GetGUID() == loot->roundRobinPlayer)
  333.             {
  334.                 if (Group* group = player->GetGroup())
  335.                 {
  336.                     if (group->GetLootMethod() != MASTER_LOOT)
  337.                     {
  338.                         loot->roundRobinPlayer = 0;
  339.                     }
  340.                 }
  341.                 else
  342.                     loot->roundRobinPlayer = 0;
  343.             }
  344.         }
  345.     }
  346.     else if (IS_CORPSE_GUID(lguid))        // ONLY remove insignia at BG
  347.     {
  348.         Corpse* corpse = ObjectAccessor::GetCorpse(*player, lguid);
  349.         if (!corpse || !corpse->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
  350.             return;
  351.  
  352.         loot = &corpse->loot;
  353.  
  354.         if (loot->isLooted())
  355.         {
  356.             loot->clear();
  357.             corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
  358.         }
  359.     }
  360.     else if (IS_ITEM_GUID(lguid))
  361.     {
  362.         Item* pItem = player->GetItemByGuid(lguid);
  363.         if (!pItem)
  364.             return;
  365.  
  366.         ItemTemplate const* proto = pItem->GetTemplate();
  367.  
  368.         // destroy only 5 items from stack in case prospecting and milling
  369.         if (proto->Flags & (ITEM_PROTO_FLAG_PROSPECTABLE | ITEM_PROTO_FLAG_MILLABLE))
  370.         {
  371.             pItem->m_lootGenerated = false;
  372.             pItem->loot.clear();
  373.  
  374.             uint32 count = pItem->GetCount();
  375.  
  376.             // >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
  377.             if (count > 5)
  378.                 count = 5;
  379.  
  380.             player->DestroyItemCount(pItem, count, true);
  381.         }
  382.         else
  383.             // FIXME: item must not be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or cheating possible.
  384.             player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
  385.         return;                                             // item can be looted only single player
  386.     }
  387.     else
  388.     {
  389.         Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
  390.  
  391.         bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
  392.         if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
  393.             return;
  394.  
  395.         loot = &creature->loot;
  396.         if (loot->isLooted())
  397.         {
  398.             // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
  399.             if (!creature->isAlive())
  400.                 creature->AllLootRemovedFromCorpse();
  401.  
  402.             creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
  403.             loot->clear();
  404.         }
  405.         else
  406.         {
  407.             // if the round robin player release, reset it.
  408.             if (player->GetGUID() == loot->roundRobinPlayer)
  409.             {
  410.                 if (Group* group = player->GetGroup())
  411.                 {
  412.                     if (group->GetLootMethod() != MASTER_LOOT)
  413.                     {
  414.                         loot->roundRobinPlayer = 0;
  415.                         group->SendLooter(creature, NULL);
  416.  
  417.                         // force update of dynamic flags, otherwise other group's players still not able to loot.
  418.                         creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
  419.                     }
  420.                 }
  421.                 else
  422.                     loot->roundRobinPlayer = 0;
  423.             }
  424.         }
  425.     }
  426.  
  427.     //Player is not looking at loot list, he doesn't need to see updates on the loot list
  428.     loot->RemoveLooter(player->GetGUID());
  429. }
  430.  
  431. void WorldSession::HandleLootMasterGiveOpcode(WorldPacket & recv_data)
  432. {
  433.     uint8 slotid;
  434.     uint64 lootguid, target_playerguid;
  435.  
  436.     recv_data >> lootguid >> slotid >> target_playerguid;
  437.  
  438.     if (!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID())
  439.     {
  440.         _player->SendLootRelease(GetPlayer()->GetLootGUID());
  441.         return;
  442.     }
  443.  
  444.     Player* target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER));
  445.     if (!target)
  446.         return;
  447.  
  448.     sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName());
  449.  
  450.     if (_player->GetLootGUID() != lootguid)
  451.         return;
  452.  
  453.     Loot* loot = NULL;
  454.  
  455.     if (IS_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID()))
  456.     {
  457.         Creature* creature = GetPlayer()->GetMap()->GetCreature(lootguid);
  458.         if (!creature)
  459.             return;
  460.  
  461.         loot = &creature->loot;
  462.     }
  463.     else if (IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
  464.     {
  465.         GameObject* pGO = GetPlayer()->GetMap()->GetGameObject(lootguid);
  466.         if (!pGO)
  467.             return;
  468.  
  469.         loot = &pGO->loot;
  470.     }
  471.  
  472.     if (!loot)
  473.         return;
  474.  
  475.     if (slotid >= loot->items.size() + loot->quest_items.size())
  476.     {
  477.         sLog->outDebug(LOG_FILTER_LOOT, "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", GetPlayer()->GetName(), slotid, (unsigned long)loot->items.size());
  478.         return;
  479.     }
  480.  
  481.     LootItem& item = slotid >= loot->items.size() ? loot->quest_items[slotid - loot->items.size()] : loot->items[slotid];
  482.  
  483.     ItemPosCountVec dest;
  484.     InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
  485.     if (item.follow_loot_rules && !item.AllowedForPlayer(target))
  486.         msg = EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
  487.     if (msg != EQUIP_ERR_OK)
  488.     {
  489.         target->SendEquipError(msg, NULL, NULL, item.itemid);
  490.         // send duplicate of error massage to master looter
  491.         _player->SendEquipError(msg, NULL, NULL, item.itemid);
  492.         return;
  493.     }
  494.  
  495.     // list of players allowed to receive this item in trade
  496.     AllowedLooterSet looters = item.GetAllowedLooters();
  497.  
  498.     // not move item from loot to target inventory
  499.     Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomPropertyId, looters);
  500.     target->SendNewItem(newitem, uint32(item.count), false, false, true);
  501.     target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count);
  502.     target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item.count);
  503.     target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item.itemid, item.count);
  504.  
  505.     // mark as looted
  506.     item.count=0;
  507.     item.is_looted=true;
  508.  
  509.     loot->NotifyItemRemoved(slotid);
  510.     --loot->unlootedCount;
  511. }