diff options
author | Shauren <shauren.trinity@gmail.com> | 2022-09-17 20:58:24 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-09-17 20:58:24 +0200 |
commit | c00e2e4851498aa04ac1d8ff13a0759b245df72b (patch) | |
tree | 8745da084a4787c222cc3441ae0b3703793d2e03 | |
parent | 3ef5079feeedfdafc9d3c1d9f865e96dbc77ecc8 (diff) |
Core/Loot: Simplify loot containers
* Unify items and quest_items
* Drop PlayerQuestItems and PlayerNonQuestNonFFAConditionalItems
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 150 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Handlers/LootHandler.cpp | 15 | ||||
-rw-r--r-- | src/server/game/Loot/Loot.cpp | 666 | ||||
-rw-r--r-- | src/server/game/Loot/Loot.h | 48 | ||||
-rw-r--r-- | src/server/game/Loot/LootMgr.cpp | 16 | ||||
-rw-r--r-- | src/server/game/Loot/LootMgr.h | 2 | ||||
-rw-r--r-- | src/server/game/Mails/Mail.cpp | 3 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_npc.cpp | 19 |
9 files changed, 203 insertions, 718 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 58bbc90a40c..b56b414981d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8794,7 +8794,6 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa m_session->DoLootReleaseAll(); Loot* loot; - PermissionTypes permission = ALL_PERMISSION; TC_LOG_DEBUG("loot", "Player::SendLoot: Player: '%s' (%s), Loot: %s", GetName().c_str(), GetGUID().ToString().c_str(), guid.ToString().c_str()); @@ -8884,30 +8883,6 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa go->SetLootState(GO_ACTIVATED, this); } - - if (go->getLootState() == GO_ACTIVATED) - { - if (Group* group = GetGroup()) - { - switch (loot->GetLootMethod()) - { - case MASTER_LOOT: - permission = group->GetMasterLooterGuid() == GetGUID() ? MASTER_PERMISSION : RESTRICTED_PERMISSION; - break; - case FREE_FOR_ALL: - permission = ALL_PERMISSION; - break; - case ROUND_ROBIN: - permission = ROUND_ROBIN_PERMISSION; - break; - default: - permission = GROUP_PERMISSION; - break; - } - } - else - permission = ALL_PERMISSION; - } } else if (guid.IsItem()) { @@ -8919,8 +8894,6 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa return; } - permission = OWNER_PERMISSION; - loot = item->GetLootForPlayer(this); // If item doesn't already have loot, attempt to load it. If that @@ -8966,11 +8939,6 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa } loot = bones->GetLootForPlayer(this); - - if (bones->lootRecipient && bones->lootRecipient != this) - permission = NONE_PERMISSION; - else - permission = OWNER_PERMISSION; } else { @@ -9008,7 +8976,6 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa const uint32 a = urand(0, creature->GetLevel() / 2); const uint32 b = urand(0, GetLevel() / 2); loot->gold = uint32(10 * (a + b) * sWorld->getRate(RATE_DROP_MONEY)); - permission = OWNER_PERMISSION; } else { @@ -9039,76 +9006,37 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type, bool aeLooting/* = fa if (loot->loot_type == LOOT_SKINNING) { loot_type = LOOT_SKINNING; - permission = creature->GetLootRecipientGUID() == GetGUID() ? OWNER_PERMISSION : NONE_PERMISSION; } else if (loot_type == LOOT_SKINNING) { loot->clear(); loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); - permission = OWNER_PERMISSION; // Set new loot recipient creature->SetLootRecipient(this, false); } - // set group rights only for loot_type != LOOT_SKINNING - else - { - if (creature->GetLootRecipientGroup()) - { - Group* group = GetGroup(); - if (group == creature->GetLootRecipientGroup()) - { - switch (loot->GetLootMethod()) - { - case MASTER_LOOT: - permission = group->GetMasterLooterGuid() == GetGUID() ? MASTER_PERMISSION : RESTRICTED_PERMISSION; - break; - case FREE_FOR_ALL: - permission = ALL_PERMISSION; - break; - case ROUND_ROBIN: - permission = ROUND_ROBIN_PERMISSION; - break; - default: - permission = GROUP_PERMISSION; - break; - } - } - else - permission = NONE_PERMISSION; - } - else if (creature->GetLootRecipient() == this) - permission = OWNER_PERMISSION; - else - permission = NONE_PERMISSION; - } } } - if (permission != NONE_PERMISSION) - { - if (!guid.IsItem() && !aeLooting) - SetLootGUID(guid); + if (!guid.IsItem() && !aeLooting) + SetLootGUID(guid); - WorldPackets::Loot::LootResponse packet; - packet.Owner = guid; - packet.LootObj = loot->GetGUID(); - packet._LootMethod = loot->GetLootMethod(); - packet.AcquireReason = GetLootTypeForClient(loot_type); - packet.Acquired = true; // false == No Loot (this too^^) - packet.AELooting = aeLooting; - loot->BuildLootResponse(packet, this, permission); - SendDirectMessage(packet.Write()); + WorldPackets::Loot::LootResponse packet; + packet.Owner = guid; + packet.LootObj = loot->GetGUID(); + packet._LootMethod = loot->GetLootMethod(); + packet.AcquireReason = GetLootTypeForClient(loot_type); + packet.Acquired = true; // false == No Loot (this too^^) + packet.AELooting = aeLooting; + loot->BuildLootResponse(packet, this); + SendDirectMessage(packet.Write()); - // add 'this' player as one of the players that are looting 'loot' - loot->OnLootOpened(GetMap(), GetGUID()); - m_AELootView[loot->GetGUID()] = loot; + // add 'this' player as one of the players that are looting 'loot' + loot->OnLootOpened(GetMap(), GetGUID()); + m_AELootView[loot->GetGUID()] = loot; - if (loot_type == LOOT_CORPSE && !guid.IsItem()) - SetUnitFlag(UNIT_FLAG_LOOTING); - } - else - SendLootError(loot ? loot->GetGUID() : ObjectGuid::Empty, guid, LOOT_ERROR_DIDNT_KILL); + if (loot_type == LOOT_CORPSE && !guid.IsItem()) + SetUnitFlag(UNIT_FLAG_LOOTING); } void Player::SendLootError(ObjectGuid const& lootObj, ObjectGuid const& owner, LootError error) const @@ -9128,12 +9056,12 @@ void Player::SendNotifyLootMoneyRemoved(ObjectGuid lootObj) const SendDirectMessage(packet.Write()); } -void Player::SendNotifyLootItemRemoved(ObjectGuid lootObj, ObjectGuid owner, uint8 lootSlot) const +void Player::SendNotifyLootItemRemoved(ObjectGuid lootObj, ObjectGuid owner, uint8 lootListId) const { WorldPackets::Loot::LootRemoved packet; packet.LootObj = lootObj; packet.Owner = owner; - packet.LootListID = lootSlot + 1; + packet.LootListID = lootListId; SendDirectMessage(packet.Write()); } @@ -18179,7 +18107,6 @@ bool Player::isAllowedToLoot(const Creature* creature) const { case PERSONAL_LOOT: /// @todo implement personal loot (http://wow.gamepedia.com/Loot#Personal_Loot) return false; - case MASTER_LOOT: case FREE_FOR_ALL: return true; case ROUND_ROBIN: @@ -18189,10 +18116,11 @@ bool Player::isAllowedToLoot(const Creature* creature) const return true; return loot->hasItemFor(this); + case MASTER_LOOT: case GROUP_LOOT: case NEED_BEFORE_GREED: // may only loot if the player is the loot roundrobin player - // or item over threshold (so roll(s) can be launched) + // or item over threshold (so roll(s) can be launched or to preview master looted items) // or if there are free/quest/conditional item for the player if (loot->roundRobinPlayer.IsEmpty() || loot->roundRobinPlayer == GetGUID()) return true; @@ -26025,11 +25953,9 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot* loot, AELootResult* aeResult/* = nullptr*/) { - NotNormalLootItem* qitem = nullptr; - NotNormalLootItem* ffaitem = nullptr; - NotNormalLootItem* conditem = nullptr; + NotNormalLootItem* ffaItem = nullptr; - LootItem* item = loot->LootItemInSlot(lootSlot, this, &qitem, &ffaitem, &conditem); + LootItem* item = loot->LootItemInSlot(lootSlot, this, &ffaItem); if (!item || item->is_looted) { @@ -26043,8 +25969,7 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot* return; } - // questitems use the blocked field for other purposes - if (!qitem && item->is_blocked) + if (item->is_blocked) { SendLootReleaseAll(); return; @@ -26063,31 +25988,14 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot* { Item* newitem = StoreNewItem(dest, item->itemid, true, item->randomBonusListId, item->GetAllowedLooters(), item->context, item->BonusListIDs); - if (qitem) - { - qitem->is_looted = true; - //freeforall is 1 if everyone's supposed to get the quest item. - if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) - SendNotifyLootItemRemoved(loot->GetGUID(), loot->GetOwnerGUID(), lootSlot); - else - loot->NotifyQuestItemRemoved(qitem->index, GetMap()); - } - else + if (ffaItem) { - if (ffaitem) - { - //freeforall case, notify only one player of the removal - ffaitem->is_looted = true; - SendNotifyLootItemRemoved(loot->GetGUID(), loot->GetOwnerGUID(), lootSlot); - } - else - { - //not freeforall, notify everyone - if (conditem) - conditem->is_looted = true; - loot->NotifyItemRemoved(lootSlot, GetMap()); - } + //freeforall case, notify only one player of the removal + ffaItem->is_looted = true; + SendNotifyLootItemRemoved(loot->GetGUID(), loot->GetOwnerGUID(), lootSlot); } + else //not freeforall, notify everyone + loot->NotifyItemRemoved(lootSlot, GetMap()); //if only one person is supposed to loot the item, then set it to looted if (!item->freeforall) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index ce1d2c586f5..4ad0f6af647 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2355,7 +2355,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SendLootError(ObjectGuid const& lootObj, ObjectGuid const& owner, LootError error) const; void SendLootRelease(ObjectGuid guid) const; void SendLootReleaseAll() const; - void SendNotifyLootItemRemoved(ObjectGuid lootObj, ObjectGuid owner, uint8 lootSlot) const; + void SendNotifyLootItemRemoved(ObjectGuid lootObj, ObjectGuid owner, uint8 lootListId) const; void SendNotifyLootMoneyRemoved(ObjectGuid lootObj) const; /*********************************************************/ diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index d14810c2b32..a367e18da8e 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -107,7 +107,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPackets::Loot::LootItem& p } } - player->StoreLootItem(lguid, req.LootListID - 1, loot, aeResultPtr); + player->StoreLootItem(lguid, req.LootListID, loot, aeResultPtr); // If player is removing the last LootItem, delete the empty container. if (loot->isLooted() && lguid.IsItem()) @@ -413,15 +413,14 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem if (!loot || loot->GetLootMethod() != MASTER_LOOT) return; - uint8 slotid = req.LootListID - 1; - if (slotid >= loot->items.size() + loot->quest_items.size()) + if (req.LootListID >= loot->items.size()) { - TC_LOG_DEBUG("loot", "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", - GetPlayer()->GetName().c_str(), slotid, (unsigned long)loot->items.size()); + TC_LOG_DEBUG("loot", "MasterLootItem: Player %s might be using a hack! (slot %d, size " SZFMTD ")", + GetPlayer()->GetName().c_str(), req.LootListID, loot->items.size()); return; } - LootItem& item = slotid >= loot->items.size() ? loot->quest_items[slotid - loot->items.size()] : loot->items[slotid]; + LootItem& item = loot->items[req.LootListID]; ItemPosCountVec dest; InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count); @@ -446,7 +445,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem item.count = 0; item.is_looted = true; - loot->NotifyItemRemoved(slotid, GetPlayer()->GetMap()); + loot->NotifyItemRemoved(req.LootListID, GetPlayer()->GetMap()); --loot->unlootedCount; } @@ -461,7 +460,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem void WorldSession::HandleLootRoll(WorldPackets::Loot::LootRoll& packet) { - LootRoll* lootRoll = GetPlayer()->GetLootRoll(packet.LootObj, packet.LootListID - 1); + LootRoll* lootRoll = GetPlayer()->GetLootRoll(packet.LootObj, packet.LootListID); if (!lootRoll) return; diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index 8f086facb80..3c5e976245e 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -126,6 +126,75 @@ void LootItem::AddAllowedLooter(const Player* player) allowedGUIDs.insert(player->GetGUID()); } +Optional<LootSlotType> LootItem::GetUiTypeForPlayer(Player const* player, Loot const& loot) const +{ + if (is_looted) + return {}; + + if (allowedGUIDs.find(player->GetGUID()) == allowedGUIDs.end()) + return {}; + + if (freeforall) + { + if (std::unique_ptr<NotNormalLootItemList> const* ffaItems = Trinity::Containers::MapGetValuePtr(loot.GetPlayerFFAItems(), player->GetGUID())) + { + auto ffaItemItr = std::find_if(ffaItems->get()->begin(), ffaItems->get()->end(), [&](NotNormalLootItem const& ffaItem) + { + return ffaItem.LootListId == LootListId; + }); + if (ffaItemItr != ffaItems->get()->end() && !ffaItemItr->is_looted) + return loot.GetLootMethod() == FREE_FOR_ALL ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; + } + return {}; + } + + if (needs_quest && !follow_loot_rules) + return loot.GetLootMethod() == FREE_FOR_ALL ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; + + switch (loot.GetLootMethod()) + { + case FREE_FOR_ALL: + return LOOT_SLOT_TYPE_OWNER; + case ROUND_ROBIN: + if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player->GetGUID()) + return {}; + + return LOOT_SLOT_TYPE_ALLOW_LOOT; + case MASTER_LOOT: + if (is_underthreshold) + { + if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player->GetGUID()) + return {}; + + return LOOT_SLOT_TYPE_ALLOW_LOOT; + } + + return loot.GetLootMasterGUID() == player->GetGUID() ? LOOT_SLOT_TYPE_MASTER : LOOT_SLOT_TYPE_LOCKED; + case GROUP_LOOT: + case NEED_BEFORE_GREED: + if (is_underthreshold) + if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player->GetGUID()) + return {}; + + if (is_blocked) + return LOOT_SLOT_TYPE_ROLL_ONGOING; + + if (rollWinnerGUID.IsEmpty()) // all passed + return LOOT_SLOT_TYPE_ALLOW_LOOT; + + if (rollWinnerGUID == player->GetGUID()) + return LOOT_SLOT_TYPE_OWNER; + + return {}; + case PERSONAL_LOOT: + return LOOT_SLOT_TYPE_OWNER; + default: + break; + } + + return {}; +} + // // ------- Loot Roll ------- // @@ -286,7 +355,7 @@ void LootRoll::SendLootRollWon(ObjectGuid const& targetGuid, int32 rollNumber, R void LootRoll::FillPacket(WorldPackets::Loot::LootItemData& lootItem) const { lootItem.Quantity = m_lootItem->count; - lootItem.LootListID = m_lootListId + 1; + lootItem.LootListID = m_lootItem->LootListId; lootItem.CanTradeToTapList = m_lootItem->allowedGUIDs.size() > 1; lootItem.Loot.Initialize(*m_lootItem); } @@ -324,7 +393,6 @@ bool LootRoll::TryToStart(Map* map, Loot& loot, uint32 lootListId, uint16 enchan m_lootItem = &loot.items[lootListId]; m_loot = &loot; - m_lootListId = lootListId; m_lootItem->is_blocked = true; // block the item while rolling uint32 playerCount = 0; @@ -426,7 +494,7 @@ bool LootRoll::UpdateRoll() bool LootRoll::IsLootItem(ObjectGuid const& lootObject, uint32 lootListId) const { - return m_loot->GetGUID() == lootObject && m_lootListId == lootListId; + return m_loot->GetGUID() == lootObject && m_lootItem->LootListId == lootListId; } /** @@ -518,8 +586,7 @@ void LootRoll::Finish(RollVoteMap::const_iterator winnerItr) loot.FillLoot(disenchant->ID, LootTemplates_Disenchant, player, true, false, LOOT_MODE_DEFAULT, ItemContext::NONE); if (!loot.AutoStore(player, NULL_BAG, NULL_SLOT, true)) { - uint32 maxSlot = loot.GetMaxSlotInLootFor(player); - for (uint32 i = 0; i < maxSlot; ++i) + for (uint32 i = 0; i < loot.items.size(); ++i) if (LootItem* disenchantLoot = loot.LootItemInSlot(i, player)) player->SendItemRetrievalMail(disenchantLoot->itemid, disenchantLoot->count, disenchantLoot->context); } @@ -527,7 +594,7 @@ void LootRoll::Finish(RollVoteMap::const_iterator winnerItr) m_loot->NotifyItemRemoved(m_lootItem->LootListId, m_map); } else - player->StoreLootItem(m_loot->GetOwnerGUID(), m_lootListId, m_loot); + player->StoreLootItem(m_loot->GetOwnerGUID(), m_lootItem->LootListId, m_loot); } } m_isStarted = false; @@ -551,25 +618,14 @@ Loot::~Loot() void Loot::clear() { - for (NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) - delete itr->second; - PlayerQuestItems.clear(); - - for (NotNormalLootItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) - delete itr->second; PlayerFFAItems.clear(); - for (NotNormalLootItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) - delete itr->second; - PlayerNonQuestNonFFAConditionalItems.clear(); - for (ObjectGuid playerGuid : PlayersLooting) if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGuid)) player->GetSession()->DoLootRelease(this); PlayersLooting.clear(); items.clear(); - quest_items.clear(); gold = 0; unlootedCount = 0; roundRobinPlayer.Clear(); @@ -598,47 +654,19 @@ void Loot::NotifyLootList(Map const* map) const allowedLooter->SendDirectMessage(lootList.GetRawPacket()); } -void Loot::NotifyItemRemoved(uint8 lootIndex, Map const* map) +void Loot::NotifyItemRemoved(uint8 lootListId, Map const* map) { // notify all players that are looting this that the item was removed // convert the index to the slot the player sees for (auto itr = PlayersLooting.begin(); itr != PlayersLooting.end();) { - if (Player* player = ObjectAccessor::GetPlayer(map, *itr)) - { - player->SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), lootIndex); - ++itr; - } - else - itr = PlayersLooting.erase(itr); - } -} + LootItem const& item = items[lootListId]; + if (item.GetAllowedLooters().find(*itr) == item.GetAllowedLooters().end()) + continue; -void Loot::NotifyQuestItemRemoved(uint8 questIndex, Map const* map) -{ - // when a free for all questitem is looted - // all players will get notified of it being removed - // (other questitems can be looted by each group member) - // bit inefficient but isn't called often - for (auto itr = PlayersLooting.begin(); itr != PlayersLooting.end();) - { if (Player* player = ObjectAccessor::GetPlayer(map, *itr)) { - NotNormalLootItemMap::const_iterator pq = PlayerQuestItems.find(player->GetGUID()); - if (pq != PlayerQuestItems.end() && pq->second) - { - // find where/if the player has the given item in it's vector - NotNormalLootItemList& pql = *pq->second; - - uint8 j; - for (j = 0; j < pql.size(); ++j) - if (pql[j].index == questIndex) - break; - - if (j < pql.size()) - player->SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), items.size() + j); - } - + player->SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), lootListId); ++itr; } else @@ -675,8 +703,7 @@ void Loot::OnLootOpened(Map* map, ObjectGuid looter) if (Player* allowedLooter = ObjectAccessor::GetPlayer(map, allowedLooterGuid)) maxEnchantingSkill = std::max(maxEnchantingSkill, allowedLooter->GetSkillValue(SKILL_ENCHANTING)); - uint32 lootListId = 0; - for (; lootListId < items.size(); ++lootListId) + for (uint32 lootListId = 0; lootListId < items.size(); ++lootListId) { LootItem& item = items[lootListId]; if (!item.is_blocked) @@ -686,17 +713,6 @@ void Loot::OnLootOpened(Map* map, ObjectGuid looter) if (!itr->second.TryToStart(map, *this, lootListId, maxEnchantingSkill)) _rolls.erase(itr); } - - for (; lootListId - items.size() < quest_items.size(); ++lootListId) - { - LootItem& item = quest_items[lootListId - items.size()]; - if (!item.is_blocked) - continue; - - auto&& [itr, inserted] = _rolls.try_emplace(lootListId); - if (!itr->second.TryToStart(map, *this, lootListId, maxEnchantingSkill)) - _rolls.erase(itr); - } } else if (_lootMethod == MASTER_LOOT) { @@ -746,9 +762,8 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo _itemContext = context; items.reserve(MAX_NR_LOOT_ITEMS); - quest_items.reserve(MAX_NR_QUEST_ITEMS); - tab->Process(*this, store.IsRatesAllowed(), lootMode, 0, lootOwner); // Processing is done there, callback via Loot::AddItem() + tab->Process(*this, store.IsRatesAllowed(), lootMode, 0); // Processing is done there, callback via Loot::AddItem() // Setting access rights for group loot case Group const* group = lootOwner->GetGroup(); @@ -761,8 +776,11 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo if (player->IsAtGroupRewardDistance(lootOwner)) FillNotNormalLootFor(player); - auto processLootItem = [&](LootItem& item) + for (LootItem& item : items) { + if (!item.follow_loot_rules || item.freeforall) + continue; + if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid)) { if (proto->GetQuality() < uint32(group->GetLootThreshold())) @@ -783,22 +801,6 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo } } } - }; - - for (LootItem& item : items) - { - if (item.freeforall) - continue; - - processLootItem(item); - } - - for (LootItem& item : quest_items) - { - if (!item.follow_loot_rules) - continue; - - processLootItem(item); } } // ... for personal loot @@ -809,7 +811,7 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo } // Inserts the item into the loot (called by LootTemplate processors) -void Loot::AddItem(LootStoreItem const& item, Player const* player) +void Loot::AddItem(LootStoreItem const& item) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid); if (!proto) @@ -818,65 +820,38 @@ void Loot::AddItem(LootStoreItem const& item, Player const* player) uint32 count = urand(item.mincount, item.maxcount); uint32 stacks = count / proto->GetMaxStackSize() + ((count % proto->GetMaxStackSize()) ? 1 : 0); - std::vector<LootItem>& lootItems = item.needs_quest ? quest_items : items; - uint32 limit = item.needs_quest ? MAX_NR_QUEST_ITEMS : MAX_NR_LOOT_ITEMS; - - for (uint32 i = 0; i < stacks && lootItems.size() < limit; ++i) + for (uint32 i = 0; i < stacks && items.size() < MAX_NR_LOOT_ITEMS; ++i) { LootItem generatedLoot(item); generatedLoot.context = _itemContext; generatedLoot.count = std::min(count, proto->GetMaxStackSize()); - generatedLoot.LootListId = lootItems.size(); + generatedLoot.LootListId = items.size(); if (_itemContext != ItemContext::NONE) { std::set<uint32> bonusListIDs = sDB2Manager.GetDefaultItemBonusTree(generatedLoot.itemid, _itemContext); generatedLoot.BonusListIDs.insert(generatedLoot.BonusListIDs.end(), bonusListIDs.begin(), bonusListIDs.end()); } - lootItems.push_back(generatedLoot); + items.push_back(generatedLoot); count -= proto->GetMaxStackSize(); - - // In some cases, a dropped item should be visible/lootable only for some players in group - bool canSeeItemInLootWindow = false; - if (Group const* group = player->GetGroup()) - { - for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - if (Player const* member = itr->GetSource()) - if (generatedLoot.AllowedForPlayer(member)) - canSeeItemInLootWindow = true; - } - else if (generatedLoot.AllowedForPlayer(player)) - canSeeItemInLootWindow = true; - - if (!canSeeItemInLootWindow) - continue; - - // non-conditional one-player only items are counted here, - // free for all items are counted in FillFFALoot(), - // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() - if (!item.needs_quest && item.conditions.empty() && !proto->HasFlag(ITEM_FLAG_MULTI_DROP)) - ++unlootedCount; } } bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool createdByPlayer) { bool allLooted = true; - uint32 max_slot = GetMaxSlotInLootFor(player); - for (uint32 i = 0; i < max_slot; ++i) + for (uint32 i = 0; i < items.size(); ++i) { - NotNormalLootItem* qitem = nullptr; NotNormalLootItem* ffaitem = nullptr; - NotNormalLootItem* conditem = nullptr; - LootItem* lootItem = LootItemInSlot(i, player, &qitem, &ffaitem, &conditem); + LootItem* lootItem = LootItemInSlot(i, player, &ffaitem); if (!lootItem || lootItem->is_looted) continue; if (!lootItem->AllowedForPlayer(player)) continue; - if (!qitem && lootItem->is_blocked) + if (lootItem->is_blocked) continue; // dont allow protected item to be looted by someone else @@ -896,12 +871,8 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool continue; } - if (qitem) - qitem->is_looted = true; - else if (ffaitem) + if (ffaitem) ffaitem->is_looted = true; - else if (conditem) - conditem->is_looted = true; if (!lootItem->freeforall) lootItem->is_looted = true; @@ -916,70 +887,33 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool return allLooted; } -LootItem const* Loot::GetItemInSlot(uint32 lootSlot) const +LootItem const* Loot::GetItemInSlot(uint32 lootListId) const { - if (lootSlot < items.size()) - return &items[lootSlot]; - - lootSlot -= uint32(items.size()); - if (lootSlot < quest_items.size()) - return &quest_items[lootSlot]; + if (lootListId < items.size()) + return &items[lootListId]; return nullptr; } -LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, NotNormalLootItem* *qitem, NotNormalLootItem* *ffaitem, NotNormalLootItem* *conditem) +LootItem* Loot::LootItemInSlot(uint32 lootListId, Player const* player, NotNormalLootItem** ffaItem) { - LootItem* item = nullptr; - bool is_looted = true; - if (lootSlot >= items.size()) - { - uint32 questSlot = lootSlot - items.size(); - NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID()); - if (itr != PlayerQuestItems.end() && questSlot < itr->second->size()) - { - NotNormalLootItem* qitem2 = &(*itr->second)[questSlot]; - if (qitem) - *qitem = qitem2; - item = &quest_items[qitem2->index]; - is_looted = qitem2->is_looted; - } - } - else + LootItem* item = &items[lootListId]; + bool is_looted = item->is_looted; + + if (item->freeforall) { - item = &items[lootSlot]; - is_looted = item->is_looted; - if (item->freeforall) + auto itr = PlayerFFAItems.find(player->GetGUID()); + if (itr != PlayerFFAItems.end()) { - NotNormalLootItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUID()); - if (itr != PlayerFFAItems.end()) + for (NotNormalLootItem& notNormalLootItem : *itr->second) { - for (NotNormalLootItemList::const_iterator iter = itr->second->begin(); iter != itr->second->end(); ++iter) - if (iter->index == lootSlot) - { - NotNormalLootItem* ffaitem2 = (NotNormalLootItem*)&(*iter); - if (ffaitem) - *ffaitem = ffaitem2; - is_looted = ffaitem2->is_looted; - break; - } - } - } - else if (!item->conditions.empty()) - { - NotNormalLootItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUID()); - if (itr != PlayerNonQuestNonFFAConditionalItems.end()) - { - for (NotNormalLootItemList::const_iterator iter = itr->second->begin(); iter != itr->second->end(); ++iter) + if (notNormalLootItem.LootListId == lootListId) { - if (iter->index == lootSlot) - { - NotNormalLootItem* conditem2 = (NotNormalLootItem*)&(*iter); - if (conditem) - *conditem = conditem2; - is_looted = conditem2->is_looted; - break; - } + is_looted = notNormalLootItem.is_looted; + if (ffaItem) + *ffaItem = ¬NormalLootItem; + + break; } } } @@ -991,12 +925,6 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, NotNormalLootIte return item; } -uint32 Loot::GetMaxSlotInLootFor(Player* player) const -{ - NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID()); - return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); -} - // return true if there is any item that is lootable for any player (not quest item, FFA or conditional) bool Loot::hasItemForAll() const { @@ -1005,7 +933,7 @@ bool Loot::hasItemForAll() const return true; for (LootItem const& item : items) - if (!item.is_looted && !item.freeforall && item.conditions.empty()) + if (!item.is_looted && item.follow_loot_rules && !item.freeforall && item.conditions.empty()) return true; return false; } @@ -1013,43 +941,19 @@ bool Loot::hasItemForAll() const // return true if there is any FFA, quest or conditional item for the player. bool Loot::hasItemFor(Player const* player) const { - NotNormalLootItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); - NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUID()); - if (q_itr != lootPlayerQuestItems.end()) - { - NotNormalLootItemList* q_list = q_itr->second; - for (NotNormalLootItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) - { - const LootItem &item = quest_items[qi->index]; - if (!qi->is_looted && !item.is_looted) - return true; - } - } - - NotNormalLootItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); - NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUID()); - if (ffa_itr != lootPlayerFFAItems.end()) - { - NotNormalLootItemList* ffa_list = ffa_itr->second; - for (NotNormalLootItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) - { - const LootItem &item = items[fi->index]; - if (!fi->is_looted && !item.is_looted) - return true; - } - } + // quest items + for (LootItem const& lootItem : items) + if (!lootItem.is_looted && !lootItem.follow_loot_rules && lootItem.GetAllowedLooters().find(player->GetGUID()) != lootItem.GetAllowedLooters().end()) + return true; - NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUID()); - if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) + if (std::unique_ptr<NotNormalLootItemList> const* ffaItems = Trinity::Containers::MapGetValuePtr(GetPlayerFFAItems(), player->GetGUID())) { - NotNormalLootItemList* conditional_list = nn_itr->second; - for (NotNormalLootItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) + bool hasFfaItem = std::any_of(ffaItems->get()->begin(), ffaItems->get()->end(), [&](NotNormalLootItem const& ffaItem) { - const LootItem &item = items[ci->index]; - if (!ci->is_looted && !item.is_looted) - return true; - } + return !ffaItem.is_looted; + }); + if (hasFfaItem) + return true; } return false; @@ -1067,223 +971,21 @@ bool Loot::hasOverThresholdItem() const return false; } -void Loot::BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* viewer, PermissionTypes permission) const +void Loot::BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player const* viewer) const { - if (permission == NONE_PERMISSION) - return; - packet.Coins = gold; - switch (permission) - { - case GROUP_PERMISSION: - case MASTER_PERMISSION: - case RESTRICTED_PERMISSION: - { - // if you are not the round-robin group looter, you can only see - // blocked rolled items and quest items, and !ffa items - for (uint8 i = 0; i < items.size(); ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) - { - uint8 slot_type; - - if (items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold - { - switch (permission) - { - case GROUP_PERMISSION: - slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING; - break; - case MASTER_PERMISSION: - { - if (viewer->GetGroup() && viewer->GetGroup()->GetMasterLooterGuid() == viewer->GetGUID()) - slot_type = LOOT_SLOT_TYPE_MASTER; - else - slot_type = LOOT_SLOT_TYPE_LOCKED; - break; - } - case RESTRICTED_PERMISSION: - slot_type = LOOT_SLOT_TYPE_LOCKED; - break; - default: - continue; - } - } - else if (roundRobinPlayer.IsEmpty() || viewer->GetGUID() == roundRobinPlayer || !items[i].is_underthreshold) - { - // no round robin owner or he has released the loot - // or it IS the round robin group owner - // => item is lootable - slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT; - } - else if (!items[i].rollWinnerGUID.IsEmpty()) - { - if (items[i].rollWinnerGUID == viewer->GetGUID()) - slot_type = LOOT_SLOT_TYPE_OWNER; - else - continue; - } - else - // item shall not be displayed. - continue; - - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = i + 1; - lootItem.UIType = slot_type; - lootItem.Quantity = items[i].count; - lootItem.Loot.Initialize(items[i]); - packet.Items.push_back(lootItem); - } - } - break; - } - case ROUND_ROBIN_PERMISSION: - { - for (uint8 i = 0; i < items.size(); ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) - { - if (!roundRobinPlayer.IsEmpty() && viewer->GetGUID() != roundRobinPlayer) - // item shall not be displayed. - continue; - - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = i + 1; - lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; - lootItem.Quantity = items[i].count; - lootItem.Loot.Initialize(items[i]); - packet.Items.push_back(lootItem); - } - } - break; - } - case ALL_PERMISSION: - case OWNER_PERMISSION: - { - for (uint8 i = 0; i < items.size(); ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = i + 1; - lootItem.UIType = permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; - lootItem.Quantity = items[i].count; - lootItem.Loot.Initialize(items[i]); - packet.Items.push_back(lootItem); - } - } - break; - } - default: - return; - } - - LootSlotType slotType = permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; - NotNormalLootItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); - NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(viewer->GetGUID()); - if (q_itr != lootPlayerQuestItems.end()) - { - NotNormalLootItemList const& q_list = *q_itr->second; - for (std::size_t i = 0; i < q_list.size(); ++i) - { - NotNormalLootItem const& qi = q_list[i]; - LootItem const& item = quest_items[qi.index]; - if (!qi.is_looted && !item.is_looted) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = items.size() + i + 1; - lootItem.Quantity = item.count; - lootItem.Loot.Initialize(item); - - if (item.follow_loot_rules) - { - switch (permission) - { - case MASTER_PERMISSION: - lootItem.UIType = LOOT_SLOT_TYPE_MASTER; - break; - case RESTRICTED_PERMISSION: - lootItem.UIType = item.is_blocked ? LOOT_SLOT_TYPE_LOCKED : LOOT_SLOT_TYPE_ALLOW_LOOT; - break; - case GROUP_PERMISSION: - case ROUND_ROBIN_PERMISSION: - if (!item.is_blocked) - lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; - else - lootItem.UIType = LOOT_SLOT_TYPE_ROLL_ONGOING; - break; - default: - lootItem.UIType = slotType; - break; - } - } - else - lootItem.UIType = slotType; - - packet.Items.push_back(lootItem); - } - } - } - - NotNormalLootItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); - NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(viewer->GetGUID()); - if (ffa_itr != lootPlayerFFAItems.end()) - { - NotNormalLootItemList* ffa_list = ffa_itr->second; - for (NotNormalLootItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) - { - LootItem const& item = items[fi->index]; - if (!fi->is_looted && !item.is_looted) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = fi->index + 1; - lootItem.UIType = slotType; - lootItem.Quantity = item.count; - lootItem.Loot.Initialize(item); - packet.Items.push_back(lootItem); - } - } - } - - NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(viewer->GetGUID()); - if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) + for (LootItem const& item : items) { - NotNormalLootItemList* conditional_list = nn_itr->second; - for (NotNormalLootItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) - { - LootItem const& item = items[ci->index]; - if (!ci->is_looted && !item.is_looted) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = ci->index + 1; - lootItem.Quantity = item.count; - lootItem.Loot.Initialize(item); - - switch (permission) - { - case MASTER_PERMISSION: - lootItem.UIType = LOOT_SLOT_TYPE_MASTER; - break; - case RESTRICTED_PERMISSION: - lootItem.UIType = item.is_blocked ? LOOT_SLOT_TYPE_LOCKED : LOOT_SLOT_TYPE_ALLOW_LOOT; - break; - case GROUP_PERMISSION: - case ROUND_ROBIN_PERMISSION: - if (!item.is_blocked) - lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; - else - lootItem.UIType = LOOT_SLOT_TYPE_ROLL_ONGOING; - break; - default: - lootItem.UIType = slotType; - break; - } + Optional<LootSlotType> uiType = item.GetUiTypeForPlayer(viewer, *this); + if (!uiType) + continue; - packet.Items.push_back(lootItem); - } - } + WorldPackets::Loot::LootItemData& lootItem = packet.Items.emplace_back(); + lootItem.LootListID = item.LootListId; + lootItem.UIType = *uiType; + lootItem.Quantity = item.count; + lootItem.Loot.Initialize(item); } } @@ -1303,111 +1005,29 @@ void Loot::FillNotNormalLootFor(Player const* player) ObjectGuid plguid = player->GetGUID(); _allowedLooters.insert(plguid); - NotNormalLootItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid); - if (qmapitr == PlayerQuestItems.end()) - FillQuestLoot(player); - - qmapitr = PlayerFFAItems.find(plguid); - if (qmapitr == PlayerFFAItems.end()) - FillFFALoot(player); - - qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); - if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) - FillNonQuestNonFFAConditionalLoot(player); -} - -NotNormalLootItemList* Loot::FillFFALoot(Player const* player) -{ - NotNormalLootItemList* ql = new NotNormalLootItemList(); + std::unique_ptr<NotNormalLootItemList> ffaItems = std::make_unique<NotNormalLootItemList>(); - for (uint8 i = 0; i < items.size(); ++i) - { - LootItem &item = items[i]; - if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) - { - ql->push_back(NotNormalLootItem(i)); - ++unlootedCount; - } - } - if (ql->empty()) + for (LootItem& item : items) { - delete ql; - return nullptr; - } - - PlayerFFAItems[player->GetGUID()] = ql; - return ql; -} - -NotNormalLootItemList* Loot::FillQuestLoot(Player const* player) -{ - if (items.size() == MAX_NR_LOOT_ITEMS) - return nullptr; - - NotNormalLootItemList* ql = new NotNormalLootItemList(); + if (!item.AllowedForPlayer(player)) + continue; - for (uint8 i = 0; i < quest_items.size(); ++i) - { - LootItem &item = quest_items[i]; + item.AddAllowedLooter(player); - if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || GetLootMethod() != MASTER_LOOT)))) + if (item.freeforall) { - item.AddAllowedLooter(player); - - ql->push_back(NotNormalLootItem(i)); - - // quest items get blocked when they first appear in a - // player's quest vector - // - // increase once if one looter only, looter-times if free for all - if (item.freeforall || !item.is_blocked) - ++unlootedCount; - if (!player->GetGroup() || (GetLootMethod() != GROUP_LOOT && GetLootMethod() != ROUND_ROBIN)) - item.is_blocked = true; - - if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) - break; + ffaItems->emplace_back(item.LootListId); + ++unlootedCount; } - } - if (ql->empty()) - { - delete ql; - return nullptr; - } - - PlayerQuestItems[player->GetGUID()] = ql; - return ql; -} - -NotNormalLootItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player const* player) -{ - NotNormalLootItemList* ql = new NotNormalLootItemList(); - - for (uint8 i = 0; i < items.size(); ++i) - { - LootItem &item = items[i]; - if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player))) + else if (!item.is_counted) { - item.AddAllowedLooter(player); - if (!item.conditions.empty()) - { - ql->push_back(NotNormalLootItem(i)); - if (!item.is_counted) - { - ++unlootedCount; - item.is_counted = true; - } - } + item.is_counted = true; + ++unlootedCount; } } - if (ql->empty()) - { - delete ql; - return nullptr; - } - PlayerNonQuestNonFFAConditionalItems[player->GetGUID()] = ql; - return ql; + if (!ffaItems->empty()) + PlayerFFAItems[player->GetGUID()] = std::move(ffaItems); } // diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h index a75862f05dc..a966a4df4dd 100644 --- a/src/server/game/Loot/Loot.h +++ b/src/server/game/Loot/Loot.h @@ -26,6 +26,7 @@ #include "ObjectGuid.h" #include "Optional.h" #include "SharedDefines.h" +#include <memory> #include <unordered_map> #include <vector> @@ -80,9 +81,6 @@ enum RollMask }; #define MAX_NR_LOOT_ITEMS 18 -// note: the client cannot show more than 16 items total -#define MAX_NR_QUEST_ITEMS 32 -// unrelated to the number of quest items shown, just for reserve enum LootMethod : uint8 { @@ -94,17 +92,6 @@ enum LootMethod : uint8 PERSONAL_LOOT = 5 }; -enum PermissionTypes -{ - ALL_PERMISSION = 0, - GROUP_PERMISSION = 1, - MASTER_PERMISSION = 2, - RESTRICTED_PERMISSION = 3, - ROUND_ROBIN_PERMISSION = 4, - OWNER_PERMISSION = 5, - NONE_PERMISSION = 6 -}; - enum LootType : uint8 { LOOT_NONE = 0, @@ -203,23 +190,24 @@ struct TC_GAME_API LootItem bool AllowedForPlayer(Player const* player, bool isGivenByMasterLooter = false) const; void AddAllowedLooter(Player const* player); GuidSet const& GetAllowedLooters() const { return allowedGUIDs; } + Optional<LootSlotType> GetUiTypeForPlayer(Player const* player, Loot const& loot) const; }; struct NotNormalLootItem { - uint8 index; // position in quest_items or items; + uint8 LootListId; bool is_looted; NotNormalLootItem() - : index(0), is_looted(false) { } + : LootListId(0), is_looted(false) { } NotNormalLootItem(uint8 _index, bool _islooted = false) - : index(_index), is_looted(_islooted) { } + : LootListId(_index), is_looted(_islooted) { } }; typedef std::vector<NotNormalLootItem> NotNormalLootItemList; typedef std::vector<LootItem> LootItemList; -typedef std::unordered_map<ObjectGuid, NotNormalLootItemList*> NotNormalLootItemMap; +typedef std::unordered_map<ObjectGuid, std::unique_ptr<NotNormalLootItemList>> NotNormalLootItemMap; //===================================================== @@ -235,7 +223,7 @@ class LootRoll public: using RollVoteMap = std::unordered_map<ObjectGuid, PlayerRollVote>; - LootRoll() : m_map(nullptr), m_isStarted(false), m_lootItem(nullptr), m_loot(nullptr), m_lootListId(0), m_voteMask(), m_endTime(TimePoint::min()) { } + LootRoll() : m_map(nullptr), m_isStarted(false), m_lootItem(nullptr), m_loot(nullptr), m_voteMask(), m_endTime(TimePoint::min()) { } ~LootRoll(); LootRoll(LootRoll const&) = delete; @@ -263,19 +251,15 @@ private: bool m_isStarted; LootItem* m_lootItem; Loot* m_loot; - uint32 m_lootListId; RollMask m_voteMask; TimePoint m_endTime; }; struct TC_GAME_API Loot { - NotNormalLootItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } NotNormalLootItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } - NotNormalLootItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } std::vector<LootItem> items; - std::vector<LootItem> quest_items; uint32 gold; uint8 unlootedCount; ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released. @@ -301,8 +285,7 @@ struct TC_GAME_API Loot bool isLooted() const { return gold == 0 && unlootedCount == 0; } void NotifyLootList(Map const* map) const; - void NotifyItemRemoved(uint8 lootIndex, Map const* map); - void NotifyQuestItemRemoved(uint8 questIndex, Map const* map); + void NotifyItemRemoved(uint8 lootListId, Map const* map); void NotifyMoneyRemoved(Map const* map); void OnLootOpened(Map* map, ObjectGuid looter); void AddLooter(ObjectGuid GUID) { PlayersLooting.insert(GUID); } @@ -312,33 +295,26 @@ struct TC_GAME_API Loot bool FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError = false, uint16 lootMode = LOOT_MODE_DEFAULT, ItemContext context = ItemContext::NONE); // Inserts the item into the loot (called by LootTemplate processors) - void AddItem(LootStoreItem const& item, Player const* player); + void AddItem(LootStoreItem const& item); bool AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast = false, bool createdByPlayer = false); - LootItem const* GetItemInSlot(uint32 lootSlot) const; - LootItem* LootItemInSlot(uint32 lootslot, Player* player, NotNormalLootItem** qitem = nullptr, NotNormalLootItem** ffaitem = nullptr, NotNormalLootItem** conditem = nullptr); - uint32 GetMaxSlotInLootFor(Player* player) const; + LootItem const* GetItemInSlot(uint32 lootListId) const; + LootItem* LootItemInSlot(uint32 lootListId, Player const* player, NotNormalLootItem** ffaItem = nullptr); bool hasItemForAll() const; bool hasItemFor(Player const* player) const; bool hasOverThresholdItem() const; // Builds data for SMSG_LOOT_RESPONSE - void BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* viewer, PermissionTypes permission = ALL_PERMISSION) const; + void BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player const* viewer) const; void Update(); private: - void FillNotNormalLootFor(Player const* player); - NotNormalLootItemList* FillFFALoot(Player const* player); - NotNormalLootItemList* FillQuestLoot(Player const* player); - NotNormalLootItemList* FillNonQuestNonFFAConditionalLoot(Player const* player); GuidSet PlayersLooting; - NotNormalLootItemMap PlayerQuestItems; NotNormalLootItemMap PlayerFFAItems; - NotNormalLootItemMap PlayerNonQuestNonFFAConditionalItems; // Loot GUID ObjectGuid _guid; diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index b37d22f10eb..506b26b59ef 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -87,7 +87,7 @@ class LootTemplate::LootGroup // A set of loot def bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry bool HasQuestDropForPlayer(Player const* player) const; // The same for active quests of the player - void Process(Loot& loot, uint16 lootMode, Player const* player) const; // Rolls an item from the group (if any) and adds the item to the loot + void Process(Loot& loot, uint16 lootMode) const; // Rolls an item from the group (if any) and adds the item to the loot float RawTotalChance() const; // Overall chance for the group (without equal chanced items) float TotalChance() const; // Overall chance for the group @@ -443,10 +443,10 @@ void LootTemplate::LootGroup::CopyConditions(ConditionContainer /*conditions*/) } // Rolls an item from the group (if any takes its chance) and adds the item to the loot -void LootTemplate::LootGroup::Process(Loot& loot, uint16 lootMode, Player const* player) const +void LootTemplate::LootGroup::Process(Loot& loot, uint16 lootMode) const { if (LootStoreItem const* item = Roll(loot, lootMode)) - loot.AddItem(*item, player); + loot.AddItem(*item); } // Overall chance for the group without equal chanced items @@ -563,7 +563,7 @@ void LootTemplate::CopyConditions(LootItem* li) const } // Rolls for every item in the template and adds the rolled items the the loot -void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId, Player const* player) const +void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId) const { if (groupId) // Group reference uses own processing of the group { @@ -573,7 +573,7 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId if (!Groups[groupId - 1]) return; - Groups[groupId - 1]->Process(loot, lootMode, player); + Groups[groupId - 1]->Process(loot, lootMode); return; } @@ -595,16 +595,16 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT)); for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator - Referenced->Process(loot, rate, lootMode, item->groupid, player); + Referenced->Process(loot, rate, lootMode, item->groupid); } else // Plain entries (not a reference, not grouped) - loot.AddItem(*item, player); // Chance is already checked, just add + loot.AddItem(*item); // Chance is already checked, just add } // Now processing groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i) if (LootGroup* group = *i) - group->Process(loot, lootMode, player); + group->Process(loot, lootMode); } // True if template includes at least 1 quest drop entry diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index b6161aed051..5fcd3f28ed6 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -108,7 +108,7 @@ class TC_GAME_API LootTemplate // Adds an entry to the group (at loading stage) void AddEntry(LootStoreItem* item); // Rolls for every item in the template and adds the rolled items the the loot - void Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId, Player const* player) const; + void Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId) const; void CopyConditions(ConditionContainer const& conditions); void CopyConditions(LootItem* li) const; diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index fecbd220ce0..c8b10f410f0 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -118,8 +118,7 @@ void MailDraft::prepareItems(Player* receiver, CharacterDatabaseTransaction tran // can be empty mailLoot.FillLoot(m_mailTemplateId, LootTemplates_Mail, receiver, true, true, LOOT_MODE_DEFAULT, ItemContext::NONE); - uint32 max_slot = mailLoot.GetMaxSlotInLootFor(receiver); - for (uint32 i = 0; m_items.size() < MAX_MAIL_ITEMS && i < max_slot; ++i) + for (uint32 i = 0; m_items.size() < MAX_MAIL_ITEMS && i < mailLoot.items.size(); ++i) { if (LootItem* lootitem = mailLoot.LootItemInSlot(i, receiver)) { diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 9fcf544b0ec..9340b0f292f 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -1183,7 +1183,7 @@ public: for (auto it = pair.second->cbegin(); it != pair.second->cend(); ++it) { - LootItem const& item = items[it->index]; + LootItem const& item = items[it->LootListId]; if (!(it->is_looted) && !item.is_looted) _ShowLootEntry(handler, item.itemid, item.count, true); } @@ -1216,11 +1216,6 @@ public: for (LootItem const& item : loot->items) if (!item.is_looted) _ShowLootEntry(handler, item.itemid, item.count); - - handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL, "Quest items", loot->quest_items.size()); - for (LootItem const& item : loot->quest_items) - if (!item.is_looted) - _ShowLootEntry(handler, item.itemid, item.count); } else { @@ -1229,23 +1224,11 @@ public: if (!item.is_looted && !item.freeforall && item.conditions.empty()) _ShowLootEntry(handler, item.itemid, item.count); - if (!loot->GetPlayerQuestItems().empty()) - { - handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player quest items"); - _IterateNotNormalLootMap(handler, loot->GetPlayerQuestItems(), loot->quest_items); - } - if (!loot->GetPlayerFFAItems().empty()) { handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "FFA items per allowed player"); _IterateNotNormalLootMap(handler, loot->GetPlayerFFAItems(), loot->items); } - - if (!loot->GetPlayerNonQuestNonFFAConditionalItems().empty()) - { - handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player conditional items"); - _IterateNotNormalLootMap(handler, loot->GetPlayerNonQuestNonFFAConditionalItems(), loot->items); - } } return true; |