aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/world/master/2024_08_25_02_world.sql64
-rw-r--r--src/server/game/Loot/LootMgr.cpp555
-rw-r--r--src/server/game/Loot/LootMgr.h13
3 files changed, 364 insertions, 268 deletions
diff --git a/sql/updates/world/master/2024_08_25_02_world.sql b/sql/updates/world/master/2024_08_25_02_world.sql
new file mode 100644
index 00000000000..d11c8b98167
--- /dev/null
+++ b/sql/updates/world/master/2024_08_25_02_world.sql
@@ -0,0 +1,64 @@
+ALTER TABLE `creature_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `disenchant_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `fishing_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `gameobject_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `item_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `mail_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `milling_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `pickpocketing_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `prospecting_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `reference_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `skinning_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+ALTER TABLE `spell_loot_template` ADD `ItemType` tinyint NOT NULL DEFAULT 0 AFTER `Entry`;
+
+UPDATE `conditions` c INNER JOIN `creature_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=1 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `disenchant_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=2 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `fishing_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=3 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `gameobject_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=4 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `item_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=5 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `mail_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=6 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `milling_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=7 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `pickpocketing_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=8 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `prospecting_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=9 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `reference_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=10 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `skinning_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=11 AND lt.`Reference`<>0;
+UPDATE `conditions` c INNER JOIN `spell_loot_template` lt ON c.`SourceGroup`=lt.`Entry` AND c.`SourceEntry`=lt.`Item` SET `SourceEntry`=lt.`Reference` WHERE c.`SourceTypeOrReferenceId`=12 AND lt.`Reference`<>0;
+
+ALTER TABLE `creature_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `disenchant_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `fishing_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `gameobject_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `item_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `mail_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `milling_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `pickpocketing_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `prospecting_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `reference_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `skinning_loot_template` DROP PRIMARY KEY;
+ALTER TABLE `spell_loot_template` DROP PRIMARY KEY;
+
+UPDATE `creature_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `disenchant_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `fishing_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `gameobject_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `item_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `mail_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `milling_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `pickpocketing_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `prospecting_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `reference_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `skinning_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+UPDATE `spell_loot_template` SET `ItemType`=1,`Item`=`Reference` WHERE `Reference`<>0;
+
+ALTER TABLE `creature_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `disenchant_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `fishing_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `gameobject_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `item_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `mail_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `milling_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `pickpocketing_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `prospecting_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `reference_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `skinning_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
+ALTER TABLE `spell_loot_template` DROP `Reference`, ADD INDEX `idx_primary` (`Entry`,`ItemType`,`Item`);
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index a6ed0eb9fcb..4b8b72dc1e6 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -17,8 +17,8 @@
#include "LootMgr.h"
#include "Containers.h"
-#include "DatabaseEnv.h"
#include "DB2Stores.h"
+#include "DatabaseEnv.h"
#include "ItemBonusMgr.h"
#include "ItemTemplate.h"
#include "Log.h"
@@ -30,7 +30,7 @@
#include "SpellMgr.h"
#include "World.h"
-static Rates const qualityToRate[MAX_ITEM_QUALITY] =
+static constexpr Rates QualityToRate[MAX_ITEM_QUALITY] =
{
RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR
RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL
@@ -38,7 +38,9 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] =
RATE_DROP_ITEM_RARE, // ITEM_QUALITY_RARE
RATE_DROP_ITEM_EPIC, // ITEM_QUALITY_EPIC
RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY
- RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT
+ RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT,
+ MAX_RATES, // ITEM_QUALITY_HEIRLOOM
+ MAX_RATES, // ITEM_QUALITY_WOW_TOKEN
};
LootStore LootTemplates_Creature("creature_loot_template", "creature entry", true);
@@ -123,8 +125,8 @@ void LootStore::Clear()
// Actual checks are done within LootTemplate::Verify() which is called for every template
void LootStore::Verify() const
{
- for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i)
- i->second->Verify(*this, i->first);
+ for (auto const& [lootId, lootTemplate] : m_LootTemplates)
+ lootTemplate->Verify(*this, lootId);
}
// Loads a *_loot_template DB table into loot store
@@ -134,8 +136,8 @@ uint32 LootStore::LoadLootTable()
// Clearing store (for reloading case)
Clear();
- // 0 1 2 3 4 5 6
- QueryResult result = WorldDatabase.PQuery("SELECT Entry, Item, Reference, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount FROM {}", GetName());
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult result = WorldDatabase.PQuery("SELECT Entry, ItemType, Item, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount FROM {}", GetName());
if (!result)
return 0;
@@ -147,8 +149,8 @@ uint32 LootStore::LoadLootTable()
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
- uint32 item = fields[1].GetUInt32();
- uint32 reference = fields[2].GetUInt32();
+ LootStoreItem::Type type = static_cast<LootStoreItem::Type>(fields[1].GetInt8());
+ uint32 item = fields[2].GetUInt32();
float chance = fields[3].GetFloat();
bool needsquest = fields[4].GetBool();
uint16 lootmode = fields[5].GetUInt16();
@@ -156,7 +158,7 @@ uint32 LootStore::LoadLootTable()
uint8 mincount = fields[7].GetUInt8();
uint8 maxcount = fields[8].GetUInt8();
- LootStoreItem* storeitem = new LootStoreItem(item, reference, chance, needsquest, lootmode, groupid, mincount, maxcount);
+ LootStoreItem* storeitem = new LootStoreItem(item, type, chance, needsquest, lootmode, groupid, mincount, maxcount);
if (!storeitem->IsValid(*this, entry)) // Validity checks
{
@@ -182,19 +184,17 @@ uint32 LootStore::LoadLootTable()
bool LootStore::HaveQuestLootFor(uint32 loot_id) const
{
- LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id);
- if (itr == m_LootTemplates.end())
- return false;
-
// scan loot for quest items
- return itr->second->HasQuestDrop(m_LootTemplates);
+ if (LootTemplate const* lootTemplate = Trinity::Containers::MapGetValuePtr(m_LootTemplates, loot_id))
+ return lootTemplate->HasQuestDrop(m_LootTemplates);
+
+ return false;
}
bool LootStore::HaveQuestLootForPlayer(uint32 loot_id, Player const* player) const
{
- LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
- if (tab != m_LootTemplates.end())
- if (tab->second->HasQuestDropForPlayer(m_LootTemplates, player))
+ if (LootTemplate const* lootTemplate = Trinity::Containers::MapGetValuePtr(m_LootTemplates, loot_id))
+ if (lootTemplate->HasQuestDropForPlayer(m_LootTemplates, player))
return true;
return false;
@@ -214,23 +214,22 @@ uint32 LootStore::LoadAndCollectLootIds(LootIdSet& lootIdSet)
{
uint32 count = LoadLootTable();
- for (LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab)
- lootIdSet.insert(tab->first);
+ std::ranges::transform(m_LootTemplates, std::inserter(lootIdSet, lootIdSet.end()), &LootTemplateMap::value_type::first);
return count;
}
void LootStore::CheckLootRefs(LootIdSet* ref_set) const
{
- for (LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr)
- ltItr->second->CheckLootRefs(m_LootTemplates, ref_set);
+ for (auto const& [_, lootTemplate] : m_LootTemplates)
+ lootTemplate->CheckLootRefs(m_LootTemplates, ref_set);
}
void LootStore::ReportUnusedIds(LootIdSet const& lootIdSet) const
{
// all still listed ids isn't referenced
- for (LootIdSet::const_iterator itr = lootIdSet.begin(); itr != lootIdSet.end(); ++itr)
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} isn't {} and not referenced from loot, and thus useless.", GetName(), *itr, GetEntryName());
+ for (uint32 lootId : lootIdSet)
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} isn't {} and not referenced from loot, and thus useless.", GetName(), lootId, GetEntryName());
}
void LootStore::ReportNonExistingId(uint32 lootId) const
@@ -254,14 +253,23 @@ bool LootStoreItem::Roll(bool rate) const
if (chance >= 100.0f)
return true;
- if (reference > 0) // reference case
- return roll_chance_f(chance* (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f));
+ switch (type)
+ {
+ case Type::Item:
+ {
+ ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
- ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
+ float qualityModifier = pProto && rate && QualityToRate[pProto->GetQuality()] != MAX_RATES ? sWorld->getRate(QualityToRate[pProto->GetQuality()]) : 1.0f;
- float qualityModifier = pProto && rate ? sWorld->getRate(qualityToRate[pProto->GetQuality()]) : 1.0f;
+ return roll_chance_f(chance * qualityModifier);
+ }
+ case Type::Reference:
+ return roll_chance_f(chance * (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f));
+ default:
+ break;
+ }
- return roll_chance_f(chance * qualityModifier);
+ return false;
}
// Checks correctness of values
@@ -273,43 +281,49 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
return false;
}
- if (reference == 0) // item (quest or non-quest) entry, maybe grouped
+ switch (type)
{
- ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
- if (!proto)
+ case Type::Item:
{
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: item does not exist - skipped", store.GetName(), entry, itemid);
- return false;
- }
+ ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
+ if (!proto)
+ {
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: item does not exist - skipped", store.GetName(), entry, itemid);
+ return false;
+ }
- if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
- {
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
- return false;
- }
+ if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
+ {
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
+ return false;
+ }
- if (chance != 0 && chance < 0.000001f) // loot with low chance
- {
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: low chance ({}) - skipped",
- store.GetName(), entry, itemid, chance);
- return false;
- }
+ if (chance != 0 && chance < 0.000001f) // loot with low chance
+ {
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: low chance ({}) - skipped",
+ store.GetName(), entry, itemid, chance);
+ return false;
+ }
- if (maxcount < mincount) // wrong max count
- {
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: MaxCount ({}) less that MinCount ({}) - skipped", store.GetName(), entry, itemid, int32(maxcount), mincount);
- return false;
+ if (maxcount < mincount) // wrong max count
+ {
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: MaxCount ({}) less that MinCount ({}) - skipped", store.GetName(), entry, itemid, int32(maxcount), mincount);
+ return false;
+ }
+ break;
}
- }
- else // if reference loot
- {
- if (needs_quest)
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: quest required will be ignored", store.GetName(), entry, itemid);
- else if (chance == 0) // no chance for the reference
- {
- TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
+ case Type::Reference:
+ if (needs_quest)
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: quest required will be ignored", store.GetName(), entry, itemid);
+ else if (chance == 0) // no chance for the reference
+ {
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
+ return false;
+ }
+ break;
+ default:
+ TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: invalid ItemType {}, skipped", store.GetName(), entry, itemid, type);
return false;
- }
}
return true; // Referenced template existence is checked at whole store level
}
@@ -341,13 +355,12 @@ LootStoreItem const* LootTemplate::LootGroup::Roll(uint16 lootMode, Player const
std::vector<LootStoreItem const*> possibleLoot = getValidLoot(ExplicitlyChanced, lootMode, personalLooter);
- if (!possibleLoot.empty()) // First explicitly chanced entries are checked
+ if (!possibleLoot.empty()) // First explicitly chanced entries are checked
{
float roll = rand_chance();
- for (auto itr = possibleLoot.begin(); itr != possibleLoot.end(); ++itr) // check each explicitly chanced entry in the template and modify its chance based on quality.
+ for (LootStoreItem const* item : possibleLoot) // check each explicitly chanced entry in the template and modify its chance based on quality.
{
- LootStoreItem const* item = *itr;
if (item->chance >= 100.0f)
return item;
@@ -361,7 +374,7 @@ LootStoreItem const* LootTemplate::LootGroup::Roll(uint16 lootMode, Player const
if (!possibleLoot.empty()) // If nothing selected yet - an item is taken from equal-chanced part
return Trinity::Containers::SelectRandomContainerElement(possibleLoot);
- return nullptr; // Empty drop from the group
+ return nullptr; // Empty drop from the group
}
bool LootTemplate::LootGroup::HasDropForPlayer(Player const* player, bool strictUsabilityCheck) const
@@ -384,13 +397,11 @@ bool LootTemplate::LootGroup::HasDropForPlayer(Player const* player, bool strict
// True if group includes at least 1 quest drop entry
bool LootTemplate::LootGroup::HasQuestDrop() const
{
- for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- if ((*i)->needs_quest)
- return true;
+ if (std::ranges::any_of(ExplicitlyChanced, &LootStoreItem::needs_quest))
+ return true;
- for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
- if ((*i)->needs_quest)
- return true;
+ if (std::ranges::any_of(EqualChanced, &LootStoreItem::needs_quest))
+ return true;
return false;
}
@@ -398,13 +409,11 @@ bool LootTemplate::LootGroup::HasQuestDrop() const
// True if group includes at least 1 quest drop entry for active quests of the player
bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const* player) const
{
- for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- if (player->HasQuestForItem((*i)->itemid))
- return true;
+ if (std::ranges::any_of(ExplicitlyChanced, [player](uint32 itemId) { return player->HasQuestForItem(itemId); }, &LootStoreItem::itemid))
+ return true;
- for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
- if (player->HasQuestForItem((*i)->itemid))
- return true;
+ if (std::ranges::any_of(EqualChanced, [player](uint32 itemId) { return player->HasQuestForItem(itemId); }, &LootStoreItem::itemid))
+ return true;
return false;
}
@@ -421,9 +430,9 @@ float LootTemplate::LootGroup::RawTotalChance() const
{
float result = 0;
- for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- if (!(*i)->needs_quest)
- result += (*i)->chance;
+ for (std::unique_ptr<LootStoreItem> const& item : ExplicitlyChanced)
+ if (!item->needs_quest)
+ result += item->chance;
return result;
}
@@ -451,27 +460,25 @@ void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint
void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& /*store*/, LootIdSet* ref_set) const
{
- for (LootStoreItemList::const_iterator ieItr = ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
+ for (std::unique_ptr<LootStoreItem> const& item : ExplicitlyChanced)
{
- LootStoreItem* item = ieItr->get();
- if (item->reference > 0)
+ if (item->type == LootStoreItem::Type::Reference)
{
- if (!LootTemplates_Reference.GetLootFor(item->reference))
- LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid);
+ if (!LootTemplates_Reference.GetLootFor(item->itemid))
+ LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
else if (ref_set)
- ref_set->erase(item->reference);
+ ref_set->erase(item->itemid);
}
}
- for (LootStoreItemList::const_iterator ieItr = EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
+ for (std::unique_ptr<LootStoreItem> const& item : EqualChanced)
{
- LootStoreItem* item = ieItr->get();
- if (item->reference > 0)
+ if (item->type == LootStoreItem::Type::Reference)
{
- if (!LootTemplates_Reference.GetLootFor(item->reference))
- LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid);
+ if (!LootTemplates_Reference.GetLootFor(item->itemid))
+ LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
else if (ref_set)
- ref_set->erase(item->reference);
+ ref_set->erase(item->itemid);
}
}
}
@@ -488,7 +495,7 @@ LootTemplate::~LootTemplate() = default;
// Adds an entry to the group (at loading stage)
void LootTemplate::AddEntry(LootStoreItem* item)
{
- if (item->groupid > 0 && item->reference == 0) // Group
+ if (item->groupid > 0 && item->type != LootStoreItem::Type::Reference) // Group
{
std::unique_ptr<LootGroup>& group = Trinity::Containers::EnsureWritableVectorIndex(Groups, item->groupid - 1);
if (!group)
@@ -503,9 +510,8 @@ void LootTemplate::AddEntry(LootStoreItem* item)
void LootTemplate::CopyConditions(LootItem* li) const
{
// Copies the conditions list from a template item to a LootItemData
- for (LootStoreItemList::const_iterator _iter = Entries.begin(); _iter != Entries.end(); ++_iter)
+ for (std::unique_ptr<LootStoreItem> const& item : Entries)
{
- LootStoreItem* item = _iter->get();
if (item->itemid != li->itemid)
continue;
@@ -530,40 +536,46 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId
}
// Rolling non-grouped items
- for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i)
+ for (std::unique_ptr<LootStoreItem> const& item : Entries)
{
- LootStoreItem* item = i->get();
if (!(item->lootmode & lootMode)) // Do not add if mode mismatch
continue;
if (!item->Roll(rate))
continue; // Bad luck for the entry
- if (item->reference > 0) // References processing
+ switch (item->type)
{
- LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference);
- if (!Referenced)
- continue; // Error message already printed at loading stage
+ case LootStoreItem::Type::Item:
+ // Plain entries (not a reference, not grouped)
+ // Chance is already checked, just add
+ if (!personalLooter
+ || LootItem::AllowedForPlayer(personalLooter, nullptr, item->itemid, item->needs_quest,
+ !item->needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(item->itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
+ true, item->conditions))
+ loot.AddItem(*item);
+
+ break;
+ case LootStoreItem::Type::Reference:
+ {
+ LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->itemid);
+ if (!Referenced)
+ continue; // Error message already printed at loading stage
- 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, personalLooter);
- }
- else
- {
- // Plain entries (not a reference, not grouped)
- // Chance is already checked, just add
- if (!personalLooter
- || LootItem::AllowedForPlayer(personalLooter, nullptr, item->itemid, item->needs_quest,
- !item->needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(item->itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
- true, item->conditions))
- loot.AddItem(*item);
+ 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, personalLooter);
+
+ break;
+ }
+ default:
+ break;
}
}
// Now processing groups
- for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i)
- if (LootGroup* group = i->get())
+ for (std::unique_ptr<LootGroup> const& group : Groups)
+ if (group)
group->Process(loot, lootMode, personalLooter);
}
@@ -589,60 +601,68 @@ void LootTemplate::ProcessPersonalLoot(std::unordered_map<Player*, std::unique_p
if (!item->Roll(rate))
continue; // Bad luck for the entry
- if (item->reference > 0) // References processing
+ switch (item->type)
{
- LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(item->reference);
- if (!referenced)
- continue; // Error message already printed at loading stage
-
- uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
- std::vector<Player*> gotLoot;
- for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
+ case LootStoreItem::Type::Item:
{
+ // Plain entries (not a reference, not grouped)
+ // Chance is already checked, just add
std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
{
- return referenced->HasDropForPlayer(looter, item->groupid, true);
+ return LootItem::AllowedForPlayer(looter, nullptr, item->itemid, item->needs_quest,
+ !item->needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(item->itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
+ true, item->conditions);
});
- // nobody can loot this, skip it
- if (lootersForItem.empty())
- break;
-
- auto newEnd = std::remove_if(lootersForItem.begin(), lootersForItem.end(), [&](Player const* looter)
+ if (!lootersForItem.empty())
{
- return std::find(gotLoot.begin(), gotLoot.end(), looter) != gotLoot.end();
- });
-
- if (lootersForItem.begin() == newEnd)
- {
- // if we run out of looters this means that there are more items dropped than players
- // start a new cycle adding one item to everyone
- gotLoot.clear();
+ Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
+ personalLoot[chosenLooter]->AddItem(*item);
}
- else
- lootersForItem.erase(newEnd, lootersForItem.end());
-
- Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
- referenced->Process(*personalLoot[chosenLooter], rate, lootMode, item->groupid, chosenLooter);
- gotLoot.push_back(chosenLooter);
+ break;
}
- }
- else
- {
- // Plain entries (not a reference, not grouped)
- // Chance is already checked, just add
- std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
+ case LootStoreItem::Type::Reference:
{
- return LootItem::AllowedForPlayer(looter, nullptr, item->itemid, item->needs_quest,
- !item->needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(item->itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
- true, item->conditions);
- });
+ LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(item->itemid);
+ if (!referenced)
+ continue; // Error message already printed at loading stage
- if (!lootersForItem.empty())
- {
- Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
- personalLoot[chosenLooter]->AddItem(*item);
+ uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
+ std::vector<Player*> gotLoot;
+ for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
+ {
+ std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
+ {
+ return referenced->HasDropForPlayer(looter, item->groupid, true);
+ });
+
+ // nobody can loot this, skip it
+ if (lootersForItem.empty())
+ break;
+
+ auto newEnd = std::ranges::remove_if(lootersForItem, [&](Player const* looter)
+ {
+ return std::ranges::find(gotLoot, looter) != gotLoot.end();
+ });
+
+ if (lootersForItem.begin() == newEnd.begin())
+ {
+ // if we run out of looters this means that there are more items dropped than players
+ // start a new cycle adding one item to everyone
+ gotLoot.clear();
+ }
+ else
+ lootersForItem.erase(newEnd.begin(), newEnd.end());
+
+ Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
+ referenced->Process(*personalLoot[chosenLooter], rate, lootMode, item->groupid, chosenLooter);
+ gotLoot.push_back(chosenLooter);
+ }
+
+ break;
}
+ default:
+ break;
}
}
@@ -682,25 +702,32 @@ bool LootTemplate::HasDropForPlayer(Player const* player, uint8 groupId, bool st
// Checking non-grouped entries
for (std::unique_ptr<LootStoreItem> const& lootStoreItem : Entries)
{
- if (lootStoreItem->reference > 0) // References processing
+ switch (lootStoreItem->type)
{
- LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(lootStoreItem->reference);
- if (!referenced)
- continue; // Error message already printed at loading stage
- if (referenced->HasDropForPlayer(player, lootStoreItem->groupid, strictUsabilityCheck))
- return true;
+ case LootStoreItem::Type::Item:
+ if (LootItem::AllowedForPlayer(player, nullptr, lootStoreItem->itemid, lootStoreItem->needs_quest,
+ !lootStoreItem->needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(lootStoreItem->itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
+ strictUsabilityCheck, lootStoreItem->conditions))
+ return true; // active quest drop found
+ break;
+ case LootStoreItem::Type::Reference:
+ {
+ LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(lootStoreItem->itemid);
+ if (!referenced)
+ continue; // Error message already printed at loading stage
+ if (referenced->HasDropForPlayer(player, lootStoreItem->groupid, strictUsabilityCheck))
+ return true;
+ break;
+ }
+ default:
+ break;
}
- else if (LootItem::AllowedForPlayer(player, nullptr, lootStoreItem->itemid, lootStoreItem->needs_quest,
- !lootStoreItem->needs_quest || ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(lootStoreItem->itemid))->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES),
- strictUsabilityCheck, lootStoreItem->conditions))
- return true; // active quest drop found
}
// Now checking groups
for (std::unique_ptr<LootGroup> const& group : Groups)
- if (group)
- if (group->HasDropForPlayer(player, strictUsabilityCheck))
- return true;
+ if (group && group->HasDropForPlayer(player, strictUsabilityCheck))
+ return true;
return false;
}
@@ -719,26 +746,32 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con
return Groups[groupId-1]->HasQuestDrop();
}
- for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i)
+ for (std::unique_ptr<LootStoreItem> const& item : Entries)
{
- LootStoreItem* item = i->get();
- if (item->reference > 0) // References
+ switch (item->type)
{
- LootTemplateMap::const_iterator Referenced = store.find(item->reference);
- if (Referenced == store.end())
- continue; // Error message [should be] already printed at loading stage
- if (Referenced->second->HasQuestDrop(store, item->groupid))
- return true;
+ case LootStoreItem::Type::Item:
+ if (item->needs_quest)
+ return true; // quest drop found
+ break;
+ case LootStoreItem::Type::Reference:
+ {
+ LootTemplateMap::const_iterator Referenced = store.find(item->itemid);
+ if (Referenced == store.end())
+ continue; // Error message [should be] already printed at loading stage
+ if (Referenced->second->HasQuestDrop(store, item->groupid))
+ return true;
+ break;
+ }
+ default:
+ break;
}
- else if (item->needs_quest)
- return true; // quest drop found
}
// Now processing groups
- for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i)
- if (LootGroup* group = i->get())
- if (group->HasQuestDrop())
- return true;
+ for (std::unique_ptr<LootGroup> const& group : Groups)
+ if (group && group->HasQuestDrop())
+ return true;
return false;
}
@@ -758,26 +791,32 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
}
// Checking non-grouped entries
- for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i)
+ for (std::unique_ptr<LootStoreItem> const& item : Entries)
{
- LootStoreItem* item = i->get();
- if (item->reference > 0) // References processing
+ switch (item->type)
{
- LootTemplateMap::const_iterator Referenced = store.find(item->reference);
- if (Referenced == store.end())
- continue; // Error message already printed at loading stage
- if (Referenced->second->HasQuestDropForPlayer(store, player, item->groupid))
- return true;
+ case LootStoreItem::Type::Item:
+ if (player->HasQuestForItem(item->itemid))
+ return true; // active quest drop found
+ break;
+ case LootStoreItem::Type::Reference:
+ {
+ LootTemplateMap::const_iterator Referenced = store.find(item->itemid);
+ if (Referenced == store.end())
+ continue; // Error message already printed at loading stage
+ if (Referenced->second->HasQuestDropForPlayer(store, player, item->groupid))
+ return true;
+ break;
+ }
+ default:
+ break;
}
- else if (player->HasQuestForItem(item->itemid))
- return true; // active quest drop found
}
// Now checking groups
- for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i)
- if (LootGroup* group = i->get())
- if (group->HasQuestDropForPlayer(player))
- return true;
+ for (std::unique_ptr<LootGroup> const& group : Groups)
+ if (group && group->HasQuestDropForPlayer(player))
+ return true;
return false;
}
@@ -795,20 +834,19 @@ void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
{
- for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
+ for (std::unique_ptr<LootStoreItem> const& item : Entries)
{
- LootStoreItem* item = ieItr->get();
- if (item->reference > 0)
+ if (item->type == LootStoreItem::Type::Reference)
{
- if (!LootTemplates_Reference.GetLootFor(item->reference))
- LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid);
+ if (!LootTemplates_Reference.GetLootFor(item->itemid))
+ LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
else if (ref_set)
- ref_set->erase(item->reference);
+ ref_set->erase(item->itemid);
}
}
- for (LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr)
- if (LootGroup* group = grItr->get())
+ for (std::unique_ptr<LootGroup> const& group : Groups)
+ if (group)
group->CheckLootRefs(store, ref_set);
}
@@ -909,22 +947,22 @@ void LoadLootTemplates_Creature()
// Remove real entries and check loot existence
CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
- for (auto const& creatureTemplatePair : ctc)
+ for (auto const& [creatureId, creatureTemplate] : ctc)
{
- for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
+ for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
{
if (uint32 lootid = creatureDifficulty.LootID)
{
- if (!lootIdSet.count(lootid))
- LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
+ if (!lootIdSet.contains(lootid))
+ LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureId);
else
lootIdSetUsed.insert(lootid);
}
}
}
- for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
- lootIdSet.erase(*itr);
+ for (uint32 lootId : lootIdSetUsed)
+ lootIdSet.erase(lootId);
// 1 means loot for player corpse
lootIdSet.erase(PLAYER_CORPSE_LOOT_ENTRY);
@@ -950,14 +988,14 @@ void LoadLootTemplates_Disenchant()
for (ItemDisenchantLootEntry const* disenchant : sItemDisenchantLootStore)
{
uint32 lootid = disenchant->ID;
- if (lootIdSet.find(lootid) == lootIdSet.end())
+ if (!lootIdSet.contains(lootid))
LootTemplates_Disenchant.ReportNonExistingId(lootid);
else
lootIdSetUsed.insert(lootid);
}
- for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
- lootIdSet.erase(*itr);
+ for (uint32 lootId : lootIdSetUsed)
+ lootIdSet.erase(lootId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Disenchant.ReportUnusedIds(lootIdSet);
@@ -979,8 +1017,7 @@ void LoadLootTemplates_Fishing()
// remove real entries and check existence loot
for (AreaTableEntry const* areaTable : sAreaTableStore)
- if (lootIdSet.find(areaTable->ID) != lootIdSet.end())
- lootIdSet.erase(areaTable->ID);
+ lootIdSet.erase(areaTable->ID);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Fishing.ReportUnusedIds(lootIdSet);
@@ -1002,7 +1039,7 @@ void LoadLootTemplates_Gameobject()
auto checkLootId = [&](uint32 lootId, uint32 gameObjectId)
{
- if (!lootIdSet.count(lootId))
+ if (!lootIdSet.contains(lootId))
LootTemplates_Gameobject.ReportNonExistingId(lootId, "Gameobject", gameObjectId);
else
lootIdSetUsed.insert(lootId);
@@ -1025,8 +1062,8 @@ void LoadLootTemplates_Gameobject()
}
}
- for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
- lootIdSet.erase(*itr);
+ for (uint32 lootId : lootIdSetUsed)
+ lootIdSet.erase(lootId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Gameobject.ReportUnusedIds(lootIdSet);
@@ -1048,9 +1085,10 @@ void LoadLootTemplates_Item()
// remove real entries and check existence loot
ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
- for (auto const& itemTemplatePair : its)
- if (lootIdSet.count(itemTemplatePair.first) > 0 && itemTemplatePair.second.HasFlag(ITEM_FLAG_HAS_LOOT))
- lootIdSet.erase(itemTemplatePair.first);
+ for (auto const& [itemId, itemTemplate] : its)
+ if (itemTemplate.HasFlag(ITEM_FLAG_HAS_LOOT))
+ if (!lootIdSet.erase(itemId))
+ LootTemplates_Item.ReportNonExistingId(itemId, "Item", itemId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Item.ReportUnusedIds(lootIdSet);
@@ -1072,14 +1110,10 @@ void LoadLootTemplates_Milling()
// remove real entries and check existence loot
ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
- for (auto const& itemTemplatePair : its)
- {
- if (!itemTemplatePair.second.HasFlag(ITEM_FLAG_IS_MILLABLE))
- continue;
-
- if (lootIdSet.count(itemTemplatePair.first) > 0)
- lootIdSet.erase(itemTemplatePair.first);
- }
+ for (auto const& [itemId, itemTemplate] : its)
+ if (itemTemplate.HasFlag(ITEM_FLAG_IS_MILLABLE))
+ if (!lootIdSet.erase(itemId))
+ LootTemplates_Milling.ReportNonExistingId(itemId, "Item", itemId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Milling.ReportUnusedIds(lootIdSet);
@@ -1101,22 +1135,22 @@ void LoadLootTemplates_Pickpocketing()
// Remove real entries and check loot existence
CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
- for (auto const& creatureTemplatePair : ctc)
+ for (auto const& [creatureId, creatureTemplate] : ctc)
{
- for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
+ for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
{
if (uint32 lootid = creatureDifficulty.PickPocketLootID)
{
- if (!lootIdSet.count(lootid))
- LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
+ if (!lootIdSet.contains(lootid))
+ LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureId);
else
lootIdSetUsed.insert(lootid);
}
}
}
- for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
- lootIdSet.erase(*itr);
+ for (uint32 lootId : lootIdSetUsed)
+ lootIdSet.erase(lootId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Pickpocketing.ReportUnusedIds(lootIdSet);
@@ -1138,14 +1172,10 @@ void LoadLootTemplates_Prospecting()
// remove real entries and check existence loot
ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
- for (auto const& itemTemplatePair : its)
- {
- if (!itemTemplatePair.second.HasFlag(ITEM_FLAG_IS_PROSPECTABLE))
- continue;
-
- if (lootIdSet.count(itemTemplatePair.first) > 0)
- lootIdSet.erase(itemTemplatePair.first);
- }
+ for (auto const& [itemId, itemTemplate] : its)
+ if (itemTemplate.HasFlag(ITEM_FLAG_IS_PROSPECTABLE))
+ if (!lootIdSet.erase(itemId))
+ LootTemplates_Prospecting.ReportNonExistingId(itemId, "Item", itemId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Prospecting.ReportUnusedIds(lootIdSet);
@@ -1166,10 +1196,8 @@ void LoadLootTemplates_Mail()
uint32 count = LootTemplates_Mail.LoadAndCollectLootIds(lootIdSet);
// remove real entries and check existence loot
- for (uint32 i = 1; i < sMailTemplateStore.GetNumRows(); ++i)
- if (sMailTemplateStore.LookupEntry(i))
- if (lootIdSet.find(i) != lootIdSet.end())
- lootIdSet.erase(i);
+ for (MailTemplateEntry const* mailTemplate : sMailTemplateStore)
+ lootIdSet.erase(mailTemplate->ID);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Mail.ReportUnusedIds(lootIdSet);
@@ -1191,22 +1219,22 @@ void LoadLootTemplates_Skinning()
// remove real entries and check existence loot
CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
- for (auto const& creatureTemplatePair : ctc)
+ for (auto const& [creatureId, creatureTemplate] : ctc)
{
- for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
+ for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
{
if (uint32 lootid = creatureDifficulty.SkinLootID)
{
- if (!lootIdSet.count(lootid))
- LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
+ if (!lootIdSet.contains(lootid))
+ LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureId);
else
lootIdSetUsed.insert(lootid);
}
}
}
- for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
- lootIdSet.erase(*itr);
+ for (uint32 lootId : lootIdSetUsed)
+ lootIdSet.erase(lootId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Skinning.ReportUnusedIds(lootIdSet);
@@ -1224,21 +1252,17 @@ void LoadLootTemplates_Spell()
uint32 oldMSTime = getMSTime();
- LootIdSet lootIdSet;
+ LootIdSet lootIdSet, lootIdSetUsed;
uint32 count = LootTemplates_Spell.LoadAndCollectLootIds(lootIdSet);
// remove real entries and check existence loot
- for (SpellNameEntry const* spellNameEntry : sSpellNameStore)
+ sSpellMgr->ForEachSpellInfo([&](SpellInfo const* spellInfo)
{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellNameEntry->ID, DIFFICULTY_NONE);
- if (!spellInfo)
- continue;
-
// possible cases
if (!spellInfo->IsLootCrafting())
- continue;
+ return;
- if (lootIdSet.find(spellInfo->Id) == lootIdSet.end())
+ if (!lootIdSet.contains(spellInfo->Id))
{
// not report about not trainable spells (optionally supported by DB)
// ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example
@@ -1246,8 +1270,11 @@ void LoadLootTemplates_Spell()
LootTemplates_Spell.ReportNonExistingId(spellInfo->Id, "Spell", spellInfo->Id);
}
else
- lootIdSet.erase(spellInfo->Id);
- }
+ lootIdSetUsed.insert(spellInfo->Id);
+ });
+
+ for (uint32 lootId : lootIdSetUsed)
+ lootIdSet.erase(lootId);
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Spell.ReportUnusedIds(lootIdSet);
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index 74397527c86..1237347714f 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -37,8 +37,14 @@ enum class ItemContext : uint8;
struct TC_GAME_API LootStoreItem
{
+ enum class Type : int8
+ {
+ Item = 0,
+ Reference = 1,
+ };
+
uint32 itemid; // id of the item
- uint32 reference; // referenced TemplateleId
+ Type type;
float chance; // chance to drop for both quest and non-quest items, chance to be used for refs
uint16 lootmode;
bool needs_quest; // quest drop (quest is required for item to drop)
@@ -48,9 +54,8 @@ struct TC_GAME_API LootStoreItem
ConditionsReference conditions; // additional loot condition
// Constructor
- // displayid is filled in IsValid() which must be called after
- LootStoreItem(uint32 _itemid, uint32 _reference, float _chance, bool _needs_quest, uint16 _lootmode, uint8 _groupid, uint8 _mincount, uint8 _maxcount)
- : itemid(_itemid), reference(_reference), chance(_chance), lootmode(_lootmode),
+ LootStoreItem(uint32 _itemid, Type _type, float _chance, bool _needs_quest, uint16 _lootmode, uint8 _groupid, uint8 _mincount, uint8 _maxcount)
+ : itemid(_itemid), type(_type), chance(_chance), lootmode(_lootmode),
needs_quest(_needs_quest), groupid(_groupid), mincount(_mincount), maxcount(_maxcount)
{ }