From 8e76a71a53cc8eb03da1620120813d807a92fb62 Mon Sep 17 00:00:00 2001 From: Cyberium Date: Wed, 31 Jan 2024 11:47:50 +0100 Subject: [PATCH] Add stats support for 2 same item id in same loot entry. --- src/game/Chat/Level2.cpp | 6 ++++-- src/game/Loot/LootMgr.cpp | 28 ++++++++++++++++++---------- src/game/Loot/LootMgr.h | 14 ++++++++------ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/game/Chat/Level2.cpp b/src/game/Chat/Level2.cpp index 5411c6cd71a..8a407c5696a 100644 --- a/src/game/Chat/Level2.cpp +++ b/src/game/Chat/Level2.cpp @@ -5547,7 +5547,7 @@ bool ChatHandler::LootStatsHelper(char* args, bool full) " -> '.loot stats [#amountOfDropCheck]'\n" " else you have to provide loot type and loot entry\n" " -> '.loot stats lootType #lootEntry [#amountOfDropCheck]\n'" - " -> lootType can be 'creature', 'gameobject', 'fishing', 'item', 'pickpocketing', 'skinning', 'disenchanting', 'prospecting', 'mail', 'reference'\n" + " -> lootType can be 'creature', 'gameobject', 'fishing', 'item', 'pickpocketing', 'skinning', 'disenchanting', 'prospecting', 'milling', 'mail', 'spell', 'reference'\n" " -> ex: '.loot stats c 448' will show Hogger loot table"; auto showError = [&]() @@ -5591,8 +5591,10 @@ bool ChatHandler::LootStatsHelper(char* args, bool full) lootStore = "item"; else if (lootType.rfind("pi", 0) == 0) lootStore = "pickpocketing"; - else if (lootType.rfind("s", 0) == 0) + else if (lootType.rfind("sk", 0) == 0) lootStore = "skinning"; + else if (lootType.rfind("sp", 0) == 0) + lootStore = "spell"; else if (lootType.rfind("dis", 0) == 0) lootStore = "disenchanting"; else if (lootType.rfind("pr", 0) == 0) diff --git a/src/game/Loot/LootMgr.cpp b/src/game/Loot/LootMgr.cpp index 458b2004a9d..9090829555a 100644 --- a/src/game/Loot/LootMgr.cpp +++ b/src/game/Loot/LootMgr.cpp @@ -128,6 +128,7 @@ void LootStore::LoadLootTable() { LootTemplateMap::const_iterator tplEntriesItr; uint32 count = 0; + std::map validItems; // Clearing store (for reloading case) Clear(); @@ -179,7 +180,8 @@ void LootStore::LoadLootTable() continue; // Add the item to the loot store - m_LootTemplates[entry].AddEntry(LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount)); + ++validItems[entry]; + m_LootTemplates[entry].AddEntry(LootStoreItem(validItems[entry], item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount)); ++count; } while (queryResult->NextRow()); @@ -343,7 +345,7 @@ bool LootStore::IsValidItemTemplate(uint32 entry, uint32 itemId, uint32 group, i return false; } - if (maxCount < mincountOrRef) // wrong max count + if (maxCount < uint32(mincountOrRef)) // wrong max count { sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", GetName(), entry, itemId, uint32(maxCount), mincountOrRef); return false; @@ -2603,7 +2605,7 @@ void LootTemplate::LootGroup::Process(Loot& loot, Player const* lootOwner, bool loot.AddItem(*item); // only used if we want some stats if (groupStats) - groupStats->IncItemCount(item->group, item->itemid); + groupStats->IncItemCount(item->group, std::make_pair(item->itemid, item->itemIndex)); } else { @@ -2619,7 +2621,7 @@ void LootTemplate::LootGroup::Process(Loot& loot, Player const* lootOwner, bool lsData = std::make_unique(item->mincountOrRef, lootStatsData->stats); // no need to check groupStats here, if we have a lootStatsPair->first, we have a lootStatsPair->second - groupStats->IncItemCount(item->group, item->mincountOrRef); // register the reference as a loot + groupStats->IncItemCount(item->group, std::make_pair(item->mincountOrRef, item->itemIndex)); // register the reference as a loot } for (uint32 loop = 0; loop < item->maxcount; ++loop) @@ -2734,7 +2736,7 @@ void LootTemplate::Process(Loot& loot, Player const* lootOwner, bool rate, LootS lsData = std::make_unique(Entrie.mincountOrRef, lootStatsData->stats); // no need to check groupStats here, if we have a lootStatsPair->first, we have a lootStatsPair->second - groupStats->IncItemCount(0, Entrie.mincountOrRef); // register the reference as a loot + groupStats->IncItemCount(0, std::make_pair(Entrie.mincountOrRef, Entrie.itemIndex)); // register the reference as a loot } for (uint32 loop = 0; loop < Entrie.maxcount; ++loop) // Ref multiplicator @@ -2745,7 +2747,7 @@ void LootTemplate::Process(Loot& loot, Player const* lootOwner, bool rate, LootS loot.AddItem(Entrie); // Chance is already checked, just add // only used if we want some stats if (groupStats) - groupStats->IncItemCount(0, Entrie.itemid); + groupStats->IncItemCount(0, std::make_pair(Entrie.itemid, Entrie.itemIndex)); } } @@ -3361,17 +3363,23 @@ void LootMgr::CheckDropStats(ChatHandler& chat, uint32 amountOfCheck, uint32 loo lootStatsInfo->lootIdOrRef = lootIdOrRef; - // for each group, set the items stats and sort them + // set the items stats and sort them for each group for (auto& group : lootRef.second.groups) { uint32 groupId = group.first; - auto& groupStats = lootStatsInfo->groupStats[groupId]; + auto& groupStats = group.second; + auto& fullGroupStats = lootStatsInfo->groupStats[groupId]; // fill the group stats with the items stats - groupStats.insert(groupStats.end(), group.second.begin(), group.second.end()); + for (const auto& kv : groupStats) + { + const auto& itemId = kv.first; + const auto& count = kv.second; + fullGroupStats.emplace_back(itemId.first, count); + } // sort the items stats - groupStats.sort( + fullGroupStats.sort( [](LootStatsInfo::ItemStats const& a, LootStatsInfo::ItemStats const& b) { return a.second > b.second; } ); diff --git a/src/game/Loot/LootMgr.h b/src/game/Loot/LootMgr.h index 952327f97b5..463f6319c0e 100644 --- a/src/game/Loot/LootMgr.h +++ b/src/game/Loot/LootMgr.h @@ -129,14 +129,15 @@ struct LootStats // itemId and count struct GroupStats { - using ItemStatsPair = std::map; - using GroupStatsMap = std::map; + using ItemIndex = std::pair; + using ItemStatsMap = std::map; + using GroupStatsMap = std::map; GroupStatsMap groups; - void IncItemCount(uint32 group, int32 itemId) + void IncItemCount(uint32 group, ItemIndex itemIdx) { - ++groups[group][itemId]; + ++groups[group][itemIdx]; } }; @@ -201,6 +202,7 @@ typedef std::unordered_map GroupLootRollMap; struct LootStoreItem { + uint32 itemIndex; // index in loot store uint32 itemid; // id of the item float chance; // always positive, chance to drop for both quest and non-quest items, chance to be used for refs int32 mincountOrRef; // mincount for drop items (positive) or minus referenced TemplateleId (negative) @@ -211,8 +213,8 @@ struct LootStoreItem // Constructor, converting ChanceOrQuestChance -> (chance, needs_quest) // displayid is filled in IsValid() which must be called after - LootStoreItem(uint32 _itemid, float _chanceOrQuestChance, int8 _group, uint16 _conditionId, int32 _mincountOrRef, uint8 _maxcount) - : itemid(_itemid), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef), + LootStoreItem(uint32 _itemIndex, uint32 _itemid, float _chanceOrQuestChance, int8 _group, uint16 _conditionId, int32 _mincountOrRef, uint8 _maxcount) + : itemIndex(_itemIndex), itemid(_itemid), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef), group(_group), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount), conditionId(_conditionId) {}