96 Player const* personalLooter =
nullptr)
const;
125 i->second->Verify(*
this, i->first);
132 LootTemplateMap::const_iterator tab;
138 QueryResult result =
WorldDatabase.PQuery(
"SELECT Entry, Item, Reference, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount FROM {}",
GetName());
147 Field* fields = result->Fetch();
152 float chance = fields[3].
GetFloat();
153 bool needsquest = fields[4].
GetBool();
159 if (groupid >= 1 << 7)
161 TC_LOG_ERROR(
"sql.sql",
"Table '{}' Entry {} Item {}: GroupId ({}) must be less {} - skipped",
GetName(), entry, item, groupid, 1 << 7);
167 if (!storeitem->
IsValid(*
this, entry))
189 tab->second->AddEntry(storeitem);
192 while (result->NextRow());
244 lootIdSet.insert(tab->first);
258 for (LootIdSet::const_iterator itr = lootIdSet.begin(); itr != lootIdSet.end(); ++itr)
269 TC_LOG_ERROR(
"sql.sql",
"Table '{}' Entry {} does not exist but it is used by {} {}",
GetName(), lootId, ownerType, ownerId);
313 TC_LOG_ERROR(
"sql.sql",
"Table '{}' Entry {} Item {}: equal-chanced grouped entry, but group not defined - skipped", store.
GetName(), entry,
itemid);
319 TC_LOG_ERROR(
"sql.sql",
"Table '{}' Entry {} Item {}: low chance ({}) - skipped",
336 TC_LOG_ERROR(
"sql.sql",
"Table '{}' Entry {} Item {}: zero chance is specified for a reference, skipped", store.
GetName(), entry,
itemid);
366 ExplicitlyChanced.push_back(item);
368 EqualChanced.push_back(item);
377 if (!possibleLoot.empty())
381 for (LootStoreItemList::const_iterator itr = possibleLoot.begin(); itr != possibleLoot.end(); ++itr)
384 if (item->
chance >= 100.0f)
393 possibleLoot = EqualChanced;
395 if (!possibleLoot.empty())
406 strictUsabilityCheck, lootStoreItem->conditions))
412 strictUsabilityCheck, lootStoreItem->conditions))
421 for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
422 if ((*i)->needs_quest)
425 for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
426 if ((*i)->needs_quest)
435 for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
439 for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
449 if (
LootStoreItem const* item = Roll(lootMode, personalLooter))
458 for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
459 if (!(*i)->needs_quest)
460 result += (*i)->chance;
468 float result = RawTotalChance();
470 if (!EqualChanced.empty() && result < 100.0f)
478 float chance = RawTotalChance();
480 TC_LOG_ERROR(
"sql.sql",
"Table '{}' entry {} group {} has total chance > 100% ({})", lootstore.
GetName(),
id, group_id, chance);
482 if (chance >= 100.0f && !EqualChanced.empty())
483 TC_LOG_ERROR(
"sql.sql",
"Table '{}' entry {} group {} has items with chance=0% but group total chance >= 100% ({})", lootstore.
GetName(),
id, group_id, chance);
488 for (LootStoreItemList::const_iterator ieItr = ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
500 for (LootStoreItemList::const_iterator ieItr = EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
519 for (LootStoreItemList::iterator i =
Entries.begin(); i !=
Entries.end(); ++i)
522 for (
size_t i = 0; i <
Groups.size(); ++i)
545 for (LootStoreItemList::const_iterator _iter =
Entries.begin(); _iter !=
Entries.end(); ++_iter)
561 if (groupId >
Groups.size())
567 Groups[groupId - 1]->Process(loot, lootMode, personalLooter);
572 for (LootStoreItemList::const_iterator i =
Entries.begin(); i !=
Entries.end(); ++i)
578 if (!item->
Roll(rate))
588 for (
uint32 loop = 0; loop < maxcount; ++loop)
589 Referenced->
Process(loot, rate, lootMode, item->
groupid, personalLooter);
604 for (LootGroups::const_iterator i =
Groups.begin(); i !=
Groups.end(); ++i)
606 group->Process(loot, lootMode, personalLooter);
611 auto getLootersForItem = [&personalLoot](
auto&& predicate)
613 std::vector<Player*> lootersForItem;
614 for (
auto&& [looter, loot] : personalLoot)
616 if (predicate(looter))
617 lootersForItem.push_back(looter);
619 return lootersForItem;
625 if (!(item->lootmode & lootMode))
628 if (!item->Roll(rate))
631 if (item->reference > 0)
638 std::vector<Player*> gotLoot;
639 for (
uint32 loop = 0; loop < maxcount; ++loop)
641 std::vector<Player*> lootersForItem = getLootersForItem([&](
Player const* looter)
647 if (lootersForItem.empty())
650 auto newEnd = std::remove_if(lootersForItem.begin(), lootersForItem.end(), [&](
Player const* looter)
652 return std::find(gotLoot.begin(), gotLoot.end(), looter) != gotLoot.end();
655 if (lootersForItem.begin() == newEnd)
662 lootersForItem.erase(newEnd, lootersForItem.end());
665 referenced->
Process(*personalLoot[chosenLooter], rate, lootMode, item->groupid, chosenLooter);
666 gotLoot.push_back(chosenLooter);
673 std::vector<Player*> lootersForItem = getLootersForItem([&](
Player const* looter)
677 true, item->conditions);
680 if (!lootersForItem.empty())
683 personalLoot[chosenLooter]->AddItem(*item);
693 std::vector<Player*> lootersForGroup = getLootersForItem([&](
Player const* looter)
695 return group->HasDropForPlayer(looter,
true);
698 if (!lootersForGroup.empty())
701 group->Process(*personalLoot[chosenLooter], lootMode);
712 if (groupId >
Groups.size())
718 return Groups[groupId - 1]->HasDropForPlayer(player, strictUsabilityCheck);
724 if (lootStoreItem->reference > 0)
729 if (referenced->
HasDropForPlayer(player, lootStoreItem->groupid, strictUsabilityCheck))
734 strictUsabilityCheck, lootStoreItem->conditions))
741 if (group->HasDropForPlayer(player, strictUsabilityCheck))
752 if (groupId >
Groups.size())
758 return Groups[groupId-1]->HasQuestDrop();
761 for (LootStoreItemList::const_iterator i =
Entries.begin(); i !=
Entries.end(); ++i)
766 LootTemplateMap::const_iterator Referenced = store.find(item->
reference);
767 if (Referenced == store.end())
769 if (Referenced->second->HasQuestDrop(store, item->
groupid))
777 for (LootGroups::const_iterator i =
Groups.begin(); i !=
Groups.end(); ++i)
779 if (group->HasQuestDrop())
790 if (groupId >
Groups.size())
796 return Groups[groupId - 1]->HasQuestDropForPlayer(player);
800 for (LootStoreItemList::const_iterator i =
Entries.begin(); i !=
Entries.end(); ++i)
805 LootTemplateMap::const_iterator Referenced = store.find(item->
reference);
806 if (Referenced == store.end())
808 if (Referenced->second->HasQuestDropForPlayer(store, player, item->
groupid))
816 for (LootGroups::const_iterator i =
Groups.begin(); i !=
Groups.end(); ++i)
818 if (group->HasQuestDropForPlayer(player))
830 Groups[i]->Verify(lootstore,
id, i + 1);
837 for (LootStoreItemList::const_iterator ieItr =
Entries.begin(); ieItr !=
Entries.end(); ++ieItr)
849 for (LootGroups::const_iterator grItr =
Groups.begin(); grItr !=
Groups.end(); ++grItr)
851 group->CheckLootRefs(store, ref_set);
860 if (item->itemid ==
uint32(
id.SourceEntry))
862 item->conditions = std::move(reference);
876 if (!itemList->empty())
880 if (item->itemid ==
uint32(
id.SourceEntry))
882 item->conditions = std::move(reference);
888 itemList = group->GetEqualChancedItemList();
889 if (!itemList->empty())
893 if (item->itemid ==
uint32(
id.SourceEntry))
895 item->conditions = std::move(reference);
907 for (LootStoreItemList::const_iterator ieItr =
Entries.begin(); ieItr !=
Entries.end(); ++ieItr)
908 if ((*ieItr)->itemid ==
id && (*ieItr)->reference > 0)
916 std::vector<Player*>
const& tappers)
918 std::unordered_map<Player*, std::unique_ptr<Loot>> tempLoot;
920 for (
Player* tapper : tappers)
922 if (tapper->IsLockedToDungeonEncounter(dungeonEncounterId))
925 std::unique_ptr<Loot>& loot = tempLoot[tapper];
928 loot->SetDungeonEncounterId(dungeonEncounterId);
929 loot->generateMoneyLoot(minMoney, maxMoney);
933 tab->ProcessPersonalLoot(tempLoot, store.
IsRatesAllowed(), lootMode);
935 std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> personalLoot;
936 for (
auto&& [looter, loot] : tempLoot)
938 loot->FillNotNormalLootFor(looter);
940 if (loot->isLooted())
943 personalLoot[looter->GetGUID()] = std::move(loot);
951 TC_LOG_INFO(
"server.loading",
"Loading creature loot templates...");
960 for (
auto const& creatureTemplatePair : ctc)
962 for (
auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
964 if (
uint32 lootid = creatureDifficulty.LootID)
966 if (!lootIdSet.count(lootid))
969 lootIdSetUsed.insert(lootid);
974 for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
975 lootIdSet.erase(*itr);
986 TC_LOG_INFO(
"server.loading",
">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty");
991 TC_LOG_INFO(
"server.loading",
"Loading disenchanting loot templates...");
1000 uint32 lootid = disenchant->ID;
1001 if (lootIdSet.find(lootid) == lootIdSet.end())
1004 lootIdSetUsed.insert(lootid);
1007 for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
1008 lootIdSet.erase(*itr);
1016 TC_LOG_INFO(
"server.loading",
">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty");
1021 TC_LOG_INFO(
"server.loading",
"Loading fishing loot templates...");
1030 if (lootIdSet.find(areaTable->ID) != lootIdSet.end())
1031 lootIdSet.erase(areaTable->ID);
1039 TC_LOG_INFO(
"server.loading",
">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty");
1044 TC_LOG_INFO(
"server.loading",
"Loading gameobject loot templates...");
1051 auto checkLootId = [&](
uint32 lootId,
uint32 gameObjectId)
1053 if (!lootIdSet.count(lootId))
1056 lootIdSetUsed.insert(lootId);
1061 for (
auto const& [gameObjectId, gameObjectTemplate] : gotc)
1063 if (
uint32 lootid = gameObjectTemplate.GetLootId())
1064 checkLootId(lootid, gameObjectId);
1068 if (gameObjectTemplate.chest.chestPersonalLoot)
1069 checkLootId(gameObjectTemplate.chest.chestPersonalLoot, gameObjectId);
1071 if (gameObjectTemplate.chest.chestPushLoot)
1072 checkLootId(gameObjectTemplate.chest.chestPushLoot, gameObjectId);
1076 for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
1077 lootIdSet.erase(*itr);
1085 TC_LOG_INFO(
"server.loading",
">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty");
1090 TC_LOG_INFO(
"server.loading",
"Loading item loot templates...");
1099 for (
auto const& itemTemplatePair : its)
1100 if (lootIdSet.count(itemTemplatePair.first) > 0 && itemTemplatePair.second.HasFlag(
ITEM_FLAG_HAS_LOOT))
1101 lootIdSet.erase(itemTemplatePair.first);
1109 TC_LOG_INFO(
"server.loading",
">> Loaded 0 item loot templates. DB table `item_loot_template` is empty");
1114 TC_LOG_INFO(
"server.loading",
"Loading milling loot templates...");
1123 for (
auto const& itemTemplatePair : its)
1128 if (lootIdSet.count(itemTemplatePair.first) > 0)
1129 lootIdSet.erase(itemTemplatePair.first);
1138 TC_LOG_INFO(
"server.loading",
">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty");
1143 TC_LOG_INFO(
"server.loading",
"Loading pickpocketing loot templates...");
1152 for (
auto const& creatureTemplatePair : ctc)
1154 for (
auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
1156 if (
uint32 lootid = creatureDifficulty.PickPocketLootID)
1158 if (!lootIdSet.count(lootid))
1161 lootIdSetUsed.insert(lootid);
1166 for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
1167 lootIdSet.erase(*itr);
1175 TC_LOG_INFO(
"server.loading",
">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty");
1180 TC_LOG_INFO(
"server.loading",
"Loading prospecting loot templates...");
1189 for (
auto const& itemTemplatePair : its)
1194 if (lootIdSet.count(itemTemplatePair.first) > 0)
1195 lootIdSet.erase(itemTemplatePair.first);
1204 TC_LOG_INFO(
"server.loading",
">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty");
1209 TC_LOG_INFO(
"server.loading",
"Loading mail loot templates...");
1219 if (lootIdSet.find(i) != lootIdSet.end())
1228 TC_LOG_INFO(
"server.loading",
">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty");
1233 TC_LOG_INFO(
"server.loading",
"Loading skinning loot templates...");
1242 for (
auto const& creatureTemplatePair : ctc)
1244 for (
auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
1246 if (
uint32 lootid = creatureDifficulty.SkinLootID)
1248 if (!lootIdSet.count(lootid))
1251 lootIdSetUsed.insert(lootid);
1256 for (LootIdSet::const_iterator itr = lootIdSetUsed.begin(); itr != lootIdSetUsed.end(); ++itr)
1257 lootIdSet.erase(*itr);
1265 TC_LOG_INFO(
"server.loading",
">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty");
1271 TC_LOG_INFO(
"server.loading",
"Loading spell loot templates...");
1289 if (lootIdSet.find(spellInfo->
Id) == lootIdSet.end())
1297 lootIdSet.erase(spellInfo->
Id);
1306 TC_LOG_INFO(
"server.loading",
">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty");
1311 TC_LOG_INFO(
"server.loading",
"Loading reference loot templates...");
DB2Storage< SpellNameEntry > sSpellNameStore("SpellName.db2", &SpellNameLoadInfo::Instance)
DB2Storage< MailTemplateEntry > sMailTemplateStore("MailTemplate.db2", &MailTemplateLoadInfo::Instance)
DB2Storage< ItemDisenchantLootEntry > sItemDisenchantLootStore("ItemDisenchantLoot.db2", &ItemDisenchantLootLoadInfo::Instance)
DB2Storage< AreaTableEntry > sAreaTableStore("AreaTable.db2", &AreaTableLoadInfo::Instance)
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
#define ASSERT_NOTNULL(pointer)
@ ITEM_FLAG_IS_PROSPECTABLE
@ ITEM_FLAGS_CU_FOLLOW_LOOT_RULES
#define TC_LOG_ERROR(filterType__,...)
#define TC_LOG_INFO(filterType__,...)
void LoadLootTemplates_Pickpocketing()
LootStore LootTemplates_Spell("spell_loot_template", "spell id (random item creating)", false)
LootStore LootTemplates_Skinning("skinning_loot_template", "creature skinning id", true)
void LoadLootTemplates_Spell()
void LoadLootTemplates_Milling()
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
void LoadLootTemplates_Gameobject()
void LoadLootTemplates_Creature()
LootStore LootTemplates_Item("item_loot_template", "item entry", true)
LootStore LootTemplates_Milling("milling_loot_template", "item entry (herb)", true)
LootStore LootTemplates_Reference("reference_loot_template", "reference id", false)
LootStore LootTemplates_Disenchant("disenchant_loot_template", "item disenchant id", true)
LootStore LootTemplates_Prospecting("prospecting_loot_template", "item entry (ore)", true)
void LoadLootTemplates_Fishing()
static Rates const qualityToRate[MAX_ITEM_QUALITY]
void LoadLootTemplates_Reference()
LootStore LootTemplates_Creature("creature_loot_template", "creature entry", true)
void LoadLootTemplates_Prospecting()
LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template", "creature pickpocket lootid", true)
void LoadLootTemplates_Mail()
void LoadLootTemplates_Disenchant()
void LoadLootTemplates_Item()
std::unordered_map< ObjectGuid, std::unique_ptr< Loot > > GenerateDungeonEncounterPersonalLoot(uint32 dungeonEncounterId, uint32 lootId, LootStore const &store, LootType type, WorldObject const *lootOwner, uint32 minMoney, uint32 maxMoney, uint16 lootMode, MapDifficultyEntry const *mapDifficulty, std::vector< Player * > const &tappers)
LootStore LootTemplates_Mail("mail_loot_template", "mail template id", false)
void LoadLootTemplates_Skinning()
LootStore LootTemplates_Fishing("fishing_loot_template", "area id", true)
std::unordered_map< uint32, LootTemplate * > LootTemplateMap
std::set< uint32 > LootIdSet
std::list< LootStoreItem * > LootStoreItemList
std::unordered_map< uint32, ItemTemplate > ItemTemplateContainer
std::unordered_map< uint32, GameObjectTemplate > GameObjectTemplateContainer
std::unordered_map< uint32, CreatureTemplate > CreatureTemplateContainer
bool roll_chance_f(float chance)
#define PLAYER_CORPSE_LOOT_ENTRY
@ SPELL_ATTR0_IS_TRADESKILL
@ SPELL_ATTR0_NOT_SHAPESHIFTED
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Class used to access individual fields of database query result.
uint32 LoadAndCollectLootIds(LootIdSet &ids_set)
LootTemplate const * GetLootFor(uint32 loot_id) const
LootTemplateMap m_LootTemplates
void CheckLootRefs(LootIdSet *ref_set=nullptr) const
char const * GetEntryName() const
bool HaveQuestLootFor(uint32 loot_id) const
LootTemplate * GetLootForConditionFill(uint32 loot_id)
bool IsRatesAllowed() const
void ReportNonExistingId(uint32 lootId) const
char const * GetName() const
bool HaveQuestLootForPlayer(uint32 loot_id, Player const *player) const
void ReportUnusedIds(LootIdSet const &ids_set) const
void CheckLootRefs(LootTemplateMap const &store, LootIdSet *ref_set) const
LootStoreItemList ExplicitlyChanced
LootGroup(LootGroup &&)=delete
LootGroup & operator=(LootGroup &&)=delete
LootStoreItemList * GetEqualChancedItemList()
LootStoreItem const * Roll(uint16 lootMode, Player const *personalLooter=nullptr) const
LootGroup & operator=(LootGroup const &)=delete
LootStoreItemList EqualChanced
void Verify(LootStore const &lootstore, uint32 id, uint8 group_id) const
LootStoreItemList * GetExplicitlyChancedItemList()
float RawTotalChance() const
void AddEntry(LootStoreItem *item)
bool HasQuestDropForPlayer(Player const *player) const
void Process(Loot &loot, uint16 lootMode, Player const *personalLooter=nullptr) const
LootGroup(LootGroup const &)=delete
float TotalChance() const
bool HasQuestDrop() const
bool HasDropForPlayer(Player const *player, bool strictUsabilityCheck) const
void ProcessPersonalLoot(std::unordered_map< Player *, std::unique_ptr< Loot > > &personalLoot, bool rate, uint16 lootMode) const
void Process(Loot &loot, bool rate, uint16 lootMode, uint8 groupId, Player const *personalLooter=nullptr) const
void CheckLootRefs(LootTemplateMap const &store, LootIdSet *ref_set) const
void CopyConditions(LootItem *li) const
bool isReference(uint32 id)
void AddEntry(LootStoreItem *item)
bool LinkConditions(ConditionId const &id, ConditionsReference reference)
bool HasQuestDropForPlayer(LootTemplateMap const &store, Player const *player, uint8 groupId=0) const
bool HasDropForPlayer(Player const *player, uint8 groupId=0, bool strictUsabilityCheck=false) const
void Verify(LootStore const &store, uint32 Id) const
bool HasQuestDrop(LootTemplateMap const &store, uint8 groupId=0) const
static ObjectGuid GetGUID(Object const *o)
bool HasQuestForItem(uint32 itemId) const
bool IsLootCrafting() const
bool HasAttribute(SpellAttr0 attribute) const
@ RATE_DROP_ITEM_REFERENCED_AMOUNT
@ RATE_DROP_ITEM_LEGENDARY
@ RATE_DROP_ITEM_UNCOMMON
@ RATE_DROP_ITEM_REFERENCED
@ RATE_DROP_ITEM_ARTIFACT
ItemContext GetContextForPlayer(MapDifficultyEntry const *mapDifficulty, Player const *player)
auto SelectRandomContainerElement(C const &container) -> typename std::add_const< decltype(*std::begin(container))>::type &
uint32 GetQuality() const
LootGroupInvalidSelector(uint16 lootMode, Player const *personalLooter)
bool operator()(LootStoreItem const *item) const
Player const * _personalLooter
bool AllowedForPlayer(Player const *player, Loot const *loot) const
ConditionsReference conditions
bool Roll(bool rate) const
ConditionsReference conditions
bool IsValid(LootStore const &store, uint32 entry) const
void AddItem(LootStoreItem const &item)