TrinityCore
Loading...
Searching...
No Matches
LootMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "LootMgr.h"
19#include "Containers.h"
20#include "DB2Stores.h"
21#include "DatabaseEnv.h"
22#include "ItemBonusMgr.h"
23#include "ItemTemplate.h"
24#include "Log.h"
25#include "Loot.h"
26#include "MapUtils.h"
27#include "ObjectMgr.h"
28#include "Player.h"
29#include "Random.h"
30#include "SpellInfo.h"
31#include "SpellMgr.h"
32#include "World.h"
33
35{
36 RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR
37 RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL
38 RATE_DROP_ITEM_UNCOMMON, // ITEM_QUALITY_UNCOMMON
39 RATE_DROP_ITEM_RARE, // ITEM_QUALITY_RARE
40 RATE_DROP_ITEM_EPIC, // ITEM_QUALITY_EPIC
41 RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY
42 RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT,
43 MAX_RATES, // ITEM_QUALITY_HEIRLOOM
44 MAX_RATES, // ITEM_QUALITY_WOW_TOKEN
45};
46
47LootStore LootTemplates_Creature("creature_loot_template", "creature entry", true);
48LootStore LootTemplates_Disenchant("disenchant_loot_template", "item disenchant id", true);
49LootStore LootTemplates_Fishing("fishing_loot_template", "area id", true);
50LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true);
51LootStore LootTemplates_Item("item_loot_template", "item entry", true);
52LootStore LootTemplates_Mail("mail_loot_template", "mail template id", false);
53LootStore LootTemplates_Milling("milling_loot_template", "item entry (herb)", true);
54LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template", "creature pickpocket lootid", true);
55LootStore LootTemplates_Prospecting("prospecting_loot_template", "item entry (ore)", true);
56LootStore LootTemplates_Reference("reference_loot_template", "reference id", false);
57LootStore LootTemplates_Skinning("skinning_loot_template", "creature skinning id", true);
58LootStore LootTemplates_Spell("spell_loot_template", "spell id (random item creating)", false);
59
60// Selects invalid loot items to be removed from group possible entries (before rolling)
62{
63 explicit LootGroupInvalidSelector(uint16 lootMode, Player const* personalLooter) : _lootMode(lootMode), _personalLooter(personalLooter) { }
64
65 bool operator()(LootStoreItem const* item) const
66 {
67 if (!(item->lootmode & _lootMode))
68 return true;
69
71 return true;
72
73 return false;
74 }
75
76private:
79};
80
81class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
82{
83 public:
84 LootGroup() = default;
85 LootGroup(LootGroup const&) = delete;
86 LootGroup(LootGroup&&) = delete;
87 LootGroup& operator=(LootGroup const&) = delete;
89 ~LootGroup() = default;
90
91 void AddEntry(LootStoreItem* item); // Adds an entry to the group (at loading stage)
92 bool HasDropForPlayer(Player const* player, bool strictUsabilityCheck) const;
93 bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry
94 bool HasQuestDropForPlayer(Player const* player) const;
95 // The same for active quests of the player
96 void Process(Loot& loot, uint16 lootMode,
97 Player const* personalLooter = nullptr) const; // Rolls an item from the group (if any) and adds the item to the loot
98 float RawTotalChance() const; // Overall chance for the group (without equal chanced items)
99 float TotalChance() const; // Overall chance for the group
100
101 void Verify(LootStore const& lootstore, uint32 id, uint8 group_id) const;
102 void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const;
105 private:
106 LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB
107 LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance
108
109 // Rolls an item from the group, returns NULL if all miss their chances
110 LootStoreItem const* Roll(uint16 lootMode, Player const* personalLooter = nullptr) const;
111};
112
113LootStore::LootStore(char const* name, char const* entryName, bool ratesAllowed)
114 : m_name(name), m_entryName(entryName), m_ratesAllowed(ratesAllowed)
115{
116}
117
118LootStore::LootStore(LootStore&&) noexcept = default;
119LootStore& LootStore::operator=(LootStore&&) noexcept = default;
120LootStore::~LootStore() = default;
121
122//Remove all data and free all memory
123void LootStore::Clear()
124{
125 m_LootTemplates.clear();
126}
127
128// Checks validity of the loot store
129// Actual checks are done within LootTemplate::Verify() which is called for every template
131{
132 for (auto const& [lootId, lootTemplate] : m_LootTemplates)
133 lootTemplate->Verify(*this, lootId);
134}
135
136// Loads a *_loot_template DB table into loot store
137// All checks of the loaded template are called from here, no error reports at loot generation required
139{
140 // Clearing store (for reloading case)
141 Clear();
142
143 // 0 1 2 3 4 5 6 7 8
144 QueryResult result = WorldDatabase.PQuery("SELECT Entry, ItemType, Item, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount FROM {}", GetName());
145
146 if (!result)
147 return 0;
148
149 uint32 count = 0;
150
151 do
152 {
153 Field* fields = result->Fetch();
154
155 uint32 entry = fields[0].GetUInt32();
156 LootStoreItem::Type type = static_cast<LootStoreItem::Type>(fields[1].GetInt8());
157 uint32 item = fields[2].GetUInt32();
158 float chance = fields[3].GetFloat();
159 bool needsquest = fields[4].GetBool();
160 uint16 lootmode = fields[5].GetUInt16();
161 uint8 groupid = fields[6].GetUInt8();
162 uint8 mincount = fields[7].GetUInt8();
163 uint8 maxcount = fields[8].GetUInt8();
164
165 LootStoreItem* storeitem = new LootStoreItem(item, type, chance, needsquest, lootmode, groupid, mincount, maxcount);
166
167 if (!storeitem->IsValid(*this, entry)) // Validity checks
168 {
169 delete storeitem;
170 continue;
171 }
172
173 // Looking for the template of the entry
174 auto [tab, isNew] = m_LootTemplates.try_emplace(entry);
175 if (isNew)
176 tab->second.reset(new LootTemplate());
177
178 // Adds current row to the template
179 tab->second->AddEntry(storeitem);
180 ++count;
181 }
182 while (result->NextRow());
183
184 Verify(); // Checks validity of the loot store
185
186 return count;
187}
188
190{
191 // scan loot for quest items
192 if (LootTemplate const* lootTemplate = Trinity::Containers::MapGetValuePtr(m_LootTemplates, loot_id))
193 return lootTemplate->HasQuestDrop();
194
195 return false;
196}
197
198bool LootStore::HaveQuestLootForPlayer(uint32 loot_id, Player const* player) const
199{
200 if (LootTemplate const* lootTemplate = Trinity::Containers::MapGetValuePtr(m_LootTemplates, loot_id))
201 if (lootTemplate->HasQuestDropForPlayer(player))
202 return true;
203
204 return false;
205}
206
211
216
218{
219 uint32 count = LoadLootTable();
220
221 std::ranges::transform(m_LootTemplates, std::inserter(lootIdSet, lootIdSet.end()), Trinity::Containers::MapKey);
222
223 return count;
224}
225
227{
228 for (auto const& [_, lootTemplate] : m_LootTemplates)
229 lootTemplate->CheckLootRefs(m_LootTemplates, ref_set);
230}
231
232void LootStore::ReportUnusedIds(LootIdSet const& lootIdSet) const
233{
234 // all still listed ids isn't referenced
235 for (uint32 lootId : lootIdSet)
236 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} isn't {} and not referenced from loot, and thus useless.", GetName(), lootId, GetEntryName());
237}
238
239void LootStore::ReportNonExistingId(uint32 lootId, char const* ownerType, uint32 ownerId) const
240{
241 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} does not exist but it is used by {} {}", GetName(), lootId, ownerType, ownerId);
242}
243
244//
245// --------- LootStoreItem ---------
246//
247
248// Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
249// RATE_DROP_ITEMS is no longer used for all types of entries
250bool LootStoreItem::Roll(bool rate) const
251{
252 if (chance >= 100.0f)
253 return true;
254
255 switch (type)
256 {
257 case Type::Item:
258 {
259 ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
260
261 float qualityModifier = pProto && rate && QualityToRate[pProto->GetQuality()] != MAX_RATES ? sWorld->getRate(QualityToRate[pProto->GetQuality()]) : 1.0f;
262
263 return roll_chance(chance * qualityModifier);
264 }
265 case Type::Reference:
266 return roll_chance(chance * (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f));
267 case Type::Currency:
268 {
269 CurrencyTypesEntry const* currency = sCurrencyTypesStore.AssertEntry(itemid);
270
271 float qualityModifier = currency && rate && QualityToRate[currency->Quality] != MAX_RATES ? sWorld->getRate(QualityToRate[currency->Quality]) : 1.0f;
272
273 return roll_chance(chance * qualityModifier);
274 }
276 return roll_chance(chance);
277 default:
278 break;
279 }
280
281 return false;
282}
283
284// Checks correctness of values
285bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
286{
287 if (mincount == 0)
288 {
289 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: wrong MinCount ({}) - skipped",
290 store.GetName(), entry, type, itemid, mincount);
291 return false;
292 }
293
294 switch (type)
295 {
296 case Type::Item:
297 {
298 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
299 if (!proto)
300 {
301 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: item does not exist - skipped",
302 store.GetName(), entry, type, itemid);
303 return false;
304 }
305
306 if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
307 {
308 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: equal-chanced grouped entry, but group not defined - skipped",
309 store.GetName(), entry, type, itemid);
310 return false;
311 }
312
313 if (chance != 0 && chance < 0.0001f) // loot with low chance
314 {
315 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: low chance ({}) - skipped",
316 store.GetName(), entry, type, itemid, chance);
317 return false;
318 }
319
320 if (maxcount < mincount) // wrong max count
321 {
322 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: MaxCount ({}) less that MinCount ({}) - skipped",
323 store.GetName(), entry, type, itemid, int32(maxcount), mincount);
324 return false;
325 }
326 break;
327 }
328 case Type::Reference:
329 if (needs_quest)
330 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: quest required will be ignored",
331 store.GetName(), entry, type, itemid);
332 else if (chance == 0) // no chance for the reference
333 {
334 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: zero chance is specified for a reference, skipped",
335 store.GetName(), entry, type, itemid);
336 return false;
337 }
338 break;
339 case Type::Currency:
340 {
341 if (!sCurrencyTypesStore.HasRecord(itemid))
342 {
343 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: currency does not exist - skipped",
344 store.GetName(), entry, type, itemid);
345 return false;
346 }
347
348 if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
349 {
350 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: equal-chanced grouped entry, but group not defined - skipped",
351 store.GetName(), entry, type, itemid);
352 return false;
353 }
354
355 if (chance != 0 && chance < 0.0001f) // loot with low chance
356 {
357 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: low chance ({}) - skipped",
358 store.GetName(), entry, type, itemid, chance);
359 return false;
360 }
361
362 if (maxcount < mincount) // wrong max count
363 {
364 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: MaxCount ({}) less that MinCount ({}) - skipped",
365 store.GetName(), entry, type, itemid, int32(maxcount), mincount);
366 return false;
367 }
368 break;
369 }
371 {
372 Quest const* quest = sObjectMgr->GetQuestTemplate(itemid);
373 if (!quest)
374 {
375 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: quest does not exist - skipped",
376 store.GetName(), entry, type, itemid);
377 return false;
378 }
379
381 {
382 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: quest is not a tracking flag - skipped",
383 store.GetName(), entry, type, itemid);
384 return false;
385 }
386
387 if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
388 {
389 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: equal-chanced grouped entry, but group not defined - skipped",
390 store.GetName(), entry, type, itemid);
391 return false;
392 }
393
394 if (chance != 0 && chance < 0.0001f) // loot with low chance
395 {
396 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: low chance ({}) - skipped",
397 store.GetName(), entry, type, itemid, chance);
398 return false;
399 }
400
401 if (mincount != 1 || maxcount) // wrong count
402 {
403 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} ItemType {} Item {}: tracking quest has count other than 1 (MinCount {} MaxCount {}) - skipped",
404 store.GetName(), entry, type, itemid, int32(maxcount), mincount);
405 return false;
406 }
407 break;
408 }
409
410 default:
411 TC_LOG_ERROR("sql.sql", "Table '{}' Entry {} Item {}: invalid ItemType {}, skipped",
412 store.GetName(), entry, itemid, type);
413 return false;
414 }
415 return true; // Referenced template existence is checked at whole store level
416}
417
418//
419// --------- LootTemplate::LootGroup ---------
420//
421
422// Adds an entry to the group (at loading stage)
424{
425 if (item->chance != 0)
426 ExplicitlyChanced.emplace_back(item);
427 else
428 EqualChanced.emplace_back(item);
429}
430
431// Rolls an item from the group, returns NULL if all miss their chances
432LootStoreItem const* LootTemplate::LootGroup::Roll(uint16 lootMode, Player const* personalLooter /*= nullptr*/) const
433{
434 auto getValidLoot = [](LootStoreItemList const& items, uint16 lootMode, Player const* personalLooter)
435 {
436 std::vector<LootStoreItem const*> possibleLoot;
437 possibleLoot.resize(items.size());
438 std::ranges::transform(items, possibleLoot.begin(), [](std::unique_ptr<LootStoreItem> const& i) { return i.get(); });
439 Trinity::Containers::EraseIf(possibleLoot, LootGroupInvalidSelector(lootMode, personalLooter));
440 return possibleLoot;
441 };
442
443 std::vector<LootStoreItem const*> possibleLoot = getValidLoot(ExplicitlyChanced, lootMode, personalLooter);
444
445 if (!possibleLoot.empty()) // First explicitly chanced entries are checked
446 {
447 float roll = rand_chance();
448
449 for (LootStoreItem const* item : possibleLoot) // check each explicitly chanced entry in the template and modify its chance based on quality.
450 {
451 if (item->chance >= 100.0f)
452 return item;
453
454 roll -= item->chance;
455 if (roll < 0)
456 return item;
457 }
458 }
459
460 possibleLoot = getValidLoot(EqualChanced, lootMode, personalLooter);
461 if (!possibleLoot.empty()) // If nothing selected yet - an item is taken from equal-chanced part
463
464 return nullptr; // Empty drop from the group
465}
466
467bool LootTemplate::LootGroup::HasDropForPlayer(Player const* player, bool strictUsabilityCheck) const
468{
469 for (std::unique_ptr<LootStoreItem> const& lootStoreItem : ExplicitlyChanced)
470 if (LootItem::AllowedForPlayer(player, *lootStoreItem, strictUsabilityCheck))
471 return true;
472
473 for (std::unique_ptr<LootStoreItem> const& lootStoreItem : EqualChanced)
474 if (LootItem::AllowedForPlayer(player, *lootStoreItem, strictUsabilityCheck))
475 return true;
476
477 return false;
478}
479
480// True if group includes at least 1 quest drop entry
482{
483 if (std::ranges::any_of(ExplicitlyChanced, &LootStoreItem::needs_quest))
484 return true;
485
486 if (std::ranges::any_of(EqualChanced, &LootStoreItem::needs_quest))
487 return true;
488
489 return false;
490}
491
492// True if group includes at least 1 quest drop entry for active quests of the player
494{
495 auto hasQuestForLootItem = [player](std::unique_ptr<LootStoreItem> const& item)
496 {
497 switch (item->type)
498 {
500 return player->HasQuestForItem(item->itemid);
502 return player->HasQuestForCurrency(item->itemid);
503 default:
504 break;
505 }
506 return false;
507 };
508
509 if (std::ranges::any_of(ExplicitlyChanced, hasQuestForLootItem))
510 return true;
511
512 if (std::ranges::any_of(EqualChanced, hasQuestForLootItem))
513 return true;
514
515 return false;
516}
517
518// Rolls an item from the group (if any takes its chance) and adds the item to the loot
519void LootTemplate::LootGroup::Process(Loot& loot, uint16 lootMode, Player const* personalLooter /*= nullptr*/) const
520{
521 if (LootStoreItem const* item = Roll(lootMode, personalLooter))
522 loot.AddItem(*item);
523}
524
525// Overall chance for the group without equal chanced items
527{
528 float result = 0;
529
530 for (std::unique_ptr<LootStoreItem> const& item : ExplicitlyChanced)
531 if (!item->needs_quest)
532 result += item->chance;
533
534 return result;
535}
536
537// Overall chance for the group
539{
540 float result = RawTotalChance();
541
542 if (!EqualChanced.empty() && result < 100.0f)
543 return 100.0f;
544
545 return result;
546}
547
548void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint8 group_id) const
549{
550 float chance = RawTotalChance();
551 if (chance > 101.0f)
552 TC_LOG_ERROR("sql.sql", "Table '{}' entry {} group {} has total chance > 100% ({})", lootstore.GetName(), id, group_id, chance);
553
554 if (chance >= 100.0f && !EqualChanced.empty())
555 TC_LOG_ERROR("sql.sql", "Table '{}' entry {} group {} has items with chance=0% but group total chance >= 100% ({})", lootstore.GetName(), id, group_id, chance);
556}
557
559{
560 for (std::unique_ptr<LootStoreItem> const& item : ExplicitlyChanced)
561 {
562 if (item->type == LootStoreItem::Type::Reference)
563 {
564 if (!LootTemplates_Reference.GetLootFor(item->itemid))
565 LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
566 else if (ref_set)
567 ref_set->erase(item->itemid);
568 }
569 }
570
571 for (std::unique_ptr<LootStoreItem> const& item : EqualChanced)
572 {
573 if (item->type == LootStoreItem::Type::Reference)
574 {
575 if (!LootTemplates_Reference.GetLootFor(item->itemid))
576 LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
577 else if (ref_set)
578 ref_set->erase(item->itemid);
579 }
580 }
581}
582
583//
584// --------- LootTemplate ---------
585//
586
588LootTemplate::LootTemplate(LootTemplate&&) noexcept = default;
589LootTemplate& LootTemplate::operator=(LootTemplate&&) noexcept = default;
590LootTemplate::~LootTemplate() = default;
591
592// Adds an entry to the group (at loading stage)
594{
595 if (item->groupid > 0 && item->type != LootStoreItem::Type::Reference) // Group
596 {
597 std::unique_ptr<LootGroup>& group = Trinity::Containers::EnsureWritableVectorIndex(Groups, item->groupid - 1);
598 if (!group)
599 group.reset(new LootGroup()); // Adds new group the the loot template if needed
600
601 group->AddEntry(item); // Adds new entry to the group
602 }
603 else // Non-grouped entries and references are stored together
604 Entries.emplace_back(item);
605}
606
608{
609 // Copies the conditions list from a template item to a LootItemData
610 for (std::unique_ptr<LootStoreItem> const& item : Entries)
611 {
612 switch (item->type)
613 {
615 if (li->type != LootItemType::Item)
616 continue;
617 break;
619 continue;
621 if (li->type != LootItemType::Currency)
622 continue;
623 break;
626 continue;
627 break;
628 default:
629 break;
630 }
631
632 if (item->itemid != li->itemid)
633 continue;
634
635 li->conditions = item->conditions;
636 break;
637 }
638}
639
640// Rolls for every item in the template and adds the rolled items the the loot
641void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId, Player const* personalLooter /*= nullptr*/) const
642{
643 if (groupId) // Group reference uses own processing of the group
644 {
645 if (groupId > Groups.size())
646 return; // Error message already printed at loading stage
647
648 if (!Groups[groupId - 1])
649 return;
650
651 Groups[groupId - 1]->Process(loot, lootMode, personalLooter);
652 return;
653 }
654
655 // Rolling non-grouped items
656 for (std::unique_ptr<LootStoreItem> const& item : Entries)
657 {
658 if (!(item->lootmode & lootMode)) // Do not add if mode mismatch
659 continue;
660
661 if (!item->Roll(rate))
662 continue; // Bad luck for the entry
663
664 switch (item->type)
665 {
669 // Plain entries (not a reference, not grouped)
670 // Chance is already checked, just add
671 if (!personalLooter || LootItem::AllowedForPlayer(personalLooter, *item, true))
672 loot.AddItem(*item);
673
674 break;
676 {
677 LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->itemid);
678 if (!Referenced)
679 continue; // Error message already printed at loading stage
680
681 uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
682 for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
683 Referenced->Process(loot, rate, lootMode, item->groupid, personalLooter);
684
685 break;
686 }
687 default:
688 break;
689 }
690 }
691
692 // Now processing groups
693 for (std::unique_ptr<LootGroup> const& group : Groups)
694 if (group)
695 group->Process(loot, lootMode, personalLooter);
696}
697
698void LootTemplate::ProcessPersonalLoot(std::unordered_map<Player*, std::unique_ptr<Loot>>& personalLoot, bool rate, uint16 lootMode) const
699{
700 auto getLootersForItem = [&personalLoot](auto&& predicate) -> std::vector<Player*>
701 {
702 std::vector<Player*> lootersForItem;
703 for (auto&& [looter, loot] : personalLoot)
704 if (predicate(looter))
705 lootersForItem.push_back(looter);
706
707 return lootersForItem;
708 };
709
710 // Rolling non-grouped items
711 for (std::unique_ptr<LootStoreItem> const& item : Entries)
712 {
713 if (!(item->lootmode & lootMode)) // Do not add if mode mismatch
714 continue;
715
716 if (!item->Roll(rate))
717 continue; // Bad luck for the entry
718
719 switch (item->type)
720 {
722 {
723 // Plain entries (not a reference, not grouped)
724 // Chance is already checked, just add
725 std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
726 {
727 return LootItem::AllowedForPlayer(looter, *item, true);
728 });
729
730 if (!lootersForItem.empty())
731 {
732 Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
733 personalLoot[chosenLooter]->AddItem(*item);
734 }
735 break;
736 }
738 {
739 LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(item->itemid);
740 if (!referenced)
741 continue; // Error message already printed at loading stage
742
743 uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
744 std::vector<Player*> gotLoot;
745 for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
746 {
747 std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
748 {
749 return referenced->HasDropForPlayer(looter, item->groupid, true);
750 });
751
752 // nobody can loot this, skip it
753 if (lootersForItem.empty())
754 break;
755
756 auto newEnd = std::remove_if(lootersForItem.begin(), lootersForItem.end(), [&](Player const* looter)
757 {
758 return advstd::ranges::contains(gotLoot, looter);
759 });
760
761 if (lootersForItem.begin() == newEnd)
762 {
763 // if we run out of looters this means that there are more items dropped than players
764 // start a new cycle adding one item to everyone
765 gotLoot.clear();
766 }
767 else
768 lootersForItem.erase(newEnd, lootersForItem.end());
769
770 Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
771 referenced->Process(*personalLoot[chosenLooter], rate, lootMode, item->groupid, chosenLooter);
772 gotLoot.push_back(chosenLooter);
773 }
774
775 break;
776 }
779 {
780 // Plain entries (not a reference, not grouped)
781 // Chance is already checked, just add
782 std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
783 {
784 return LootItem::AllowedForPlayer(looter, *item, true);
785 });
786
787 for (Player* looter : lootersForItem)
788 personalLoot[looter]->AddItem(*item);
789 break;
790 }
791 default:
792 break;
793 }
794 }
795
796 // Now processing groups
797 for (std::unique_ptr<LootGroup> const& group : Groups)
798 {
799 if (group)
800 {
801 std::vector<Player*> lootersForGroup = getLootersForItem([&](Player const* looter)
802 {
803 return group->HasDropForPlayer(looter, true);
804 });
805
806 if (!lootersForGroup.empty())
807 {
808 Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForGroup);
809 group->Process(*personalLoot[chosenLooter], lootMode);
810 }
811 }
812 }
813}
814
815// True if template includes at least 1 drop for the player
816bool LootTemplate::HasDropForPlayer(Player const* player, uint8 groupId, bool strictUsabilityCheck) const
817{
818 if (groupId) // Group reference
819 {
820 if (groupId > Groups.size())
821 return false; // Error message already printed at loading stage
822
823 if (!Groups[groupId - 1])
824 return false;
825
826 return Groups[groupId - 1]->HasDropForPlayer(player, strictUsabilityCheck);
827 }
828
829 // Checking non-grouped entries
830 for (std::unique_ptr<LootStoreItem> const& lootStoreItem : Entries)
831 {
832 switch (lootStoreItem->type)
833 {
837 if (LootItem::AllowedForPlayer(player, *lootStoreItem, strictUsabilityCheck))
838 return true; // active quest drop found
839 break;
841 {
842 LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(lootStoreItem->itemid);
843 if (!referenced)
844 continue; // Error message already printed at loading stage
845 if (referenced->HasDropForPlayer(player, lootStoreItem->groupid, strictUsabilityCheck))
846 return true;
847 break;
848 }
849 default:
850 break;
851 }
852 }
853
854 // Now checking groups
855 for (std::unique_ptr<LootGroup> const& group : Groups)
856 if (group && group->HasDropForPlayer(player, strictUsabilityCheck))
857 return true;
858
859 return false;
860}
861
862// True if template includes at least 1 quest drop entry
864{
865 if (groupId) // Group reference
866 {
867 if (groupId > Groups.size())
868 return false; // Error message [should be] already printed at loading stage
869
870 if (!Groups[groupId - 1])
871 return false;
872
873 return Groups[groupId-1]->HasQuestDrop();
874 }
875
876 for (std::unique_ptr<LootStoreItem> const& item : Entries)
877 {
878 switch (item->type)
879 {
882 if (item->needs_quest)
883 return true; // quest drop found
884 break;
886 {
887 LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->itemid);
888 if (!Referenced)
889 continue; // Error message [should be] already printed at loading stage
890 if (Referenced->HasQuestDrop(item->groupid))
891 return true;
892 break;
893 }
894 default:
895 break;
896 }
897 }
898
899 // Now processing groups
900 for (std::unique_ptr<LootGroup> const& group : Groups)
901 if (group && group->HasQuestDrop())
902 return true;
903
904 return false;
905}
906
907// True if template includes at least 1 quest drop for an active quest of the player
908bool LootTemplate::HasQuestDropForPlayer(Player const* player, uint8 groupId) const
909{
910 if (groupId) // Group reference
911 {
912 if (groupId > Groups.size())
913 return false; // Error message already printed at loading stage
914
915 if (!Groups[groupId - 1])
916 return false;
917
918 return Groups[groupId - 1]->HasQuestDropForPlayer(player);
919 }
920
921 // Checking non-grouped entries
922 for (std::unique_ptr<LootStoreItem> const& item : Entries)
923 {
924 switch (item->type)
925 {
927 if (player->HasQuestForItem(item->itemid))
928 return true; // active quest drop found
929 break;
931 {
932 LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->itemid);
933 if (!Referenced)
934 continue; // Error message already printed at loading stage
935 if (Referenced->HasQuestDropForPlayer(player, item->groupid))
936 return true;
937 break;
938 }
940 if (player->HasQuestForCurrency(item->itemid))
941 return true; // active quest drop found
942 break;
943 default:
944 break;
945 }
946 }
947
948 // Now checking groups
949 for (std::unique_ptr<LootGroup> const& group : Groups)
950 if (group && group->HasQuestDropForPlayer(player))
951 return true;
952
953 return false;
954}
955
956// Checks integrity of the template
957void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
958{
959 // Checking group chances
960 for (uint32 i = 0; i < Groups.size(); ++i)
961 if (Groups[i])
962 Groups[i]->Verify(lootstore, id, i + 1);
963
965}
966
967void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
968{
969 for (std::unique_ptr<LootStoreItem> const& item : Entries)
970 {
971 if (item->type == LootStoreItem::Type::Reference)
972 {
973 if (!LootTemplates_Reference.GetLootFor(item->itemid))
974 LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
975 else if (ref_set)
976 ref_set->erase(item->itemid);
977 }
978 }
979
980 for (std::unique_ptr<LootGroup> const& group : Groups)
981 if (group)
982 group->CheckLootRefs(store, ref_set);
983}
984
986{
987 if (!Entries.empty())
988 {
989 for (std::unique_ptr<LootStoreItem>& item : Entries)
990 {
991 if (item->itemid == uint32(id.SourceEntry))
992 {
993 item->conditions = std::move(reference);
994 return true;
995 }
996 }
997 }
998
999 if (!Groups.empty())
1000 {
1001 for (std::unique_ptr<LootGroup>& group : Groups)
1002 {
1003 if (!group)
1004 continue;
1005
1006 LootStoreItemList* itemList = group->GetExplicitlyChancedItemList();
1007 if (!itemList->empty())
1008 {
1009 for (std::unique_ptr<LootStoreItem> const& item : *itemList)
1010 {
1011 if (item->itemid == uint32(id.SourceEntry))
1012 {
1013 item->conditions = std::move(reference);
1014 return true;
1015 }
1016 }
1017 }
1018
1019 itemList = group->GetEqualChancedItemList();
1020 if (!itemList->empty())
1021 {
1022 for (std::unique_ptr<LootStoreItem> const& item : *itemList)
1023 {
1024 if (item->itemid == uint32(id.SourceEntry))
1025 {
1026 item->conditions = std::move(reference);
1027 return true;
1028 }
1029 }
1030 }
1031 }
1032 }
1033 return false;
1034}
1035
1036std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> GenerateDungeonEncounterPersonalLoot(uint32 dungeonEncounterId, uint32 lootId, LootStore const& store,
1037 LootType type, WorldObject const* lootOwner, uint32 minMoney, uint32 maxMoney, uint16 lootMode, MapDifficultyEntry const* mapDifficulty,
1038 std::vector<Player*> const& tappers)
1039{
1040 std::unordered_map<Player*, std::unique_ptr<Loot>> tempLoot;
1041
1042 for (Player* tapper : tappers)
1043 {
1044 if (tapper->IsLockedToDungeonEncounter(dungeonEncounterId))
1045 continue;
1046
1047 std::unique_ptr<Loot>& loot = tempLoot[tapper];
1048 loot.reset(new Loot(lootOwner->GetMap(), lootOwner->GetGUID(), type, nullptr));
1049 loot->SetItemContext(ItemBonusMgr::GetContextForPlayer(mapDifficulty, tapper));
1050 loot->SetDungeonEncounterId(dungeonEncounterId);
1051 loot->generateMoneyLoot(minMoney, maxMoney);
1052 }
1053
1054 if (LootTemplate const* tab = store.GetLootFor(lootId))
1055 tab->ProcessPersonalLoot(tempLoot, store.IsRatesAllowed(), lootMode);
1056
1057 std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> personalLoot;
1058 for (auto&& [looter, loot] : tempLoot)
1059 {
1060 loot->FillNotNormalLootFor(looter);
1061
1062 if (loot->isLooted())
1063 continue;
1064
1065 personalLoot[looter->GetGUID()] = std::move(loot);
1066 }
1067
1068 return personalLoot;
1069}
1070
1072{
1073 TC_LOG_INFO("server.loading", "Loading creature loot templates...");
1074
1075 uint32 oldMSTime = getMSTime();
1076
1077 LootIdSet lootIdSet, lootIdSetUsed;
1079
1080 // Remove real entries and check loot existence
1081 CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
1082 for (auto const& [creatureId, creatureTemplate] : ctc)
1083 {
1084 for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
1085 {
1086 if (uint32 lootid = creatureDifficulty.LootID)
1087 {
1088 if (!lootIdSet.contains(lootid))
1089 LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureId);
1090 else
1091 lootIdSetUsed.insert(lootid);
1092 }
1093 }
1094 }
1095
1096 for (uint32 lootId : lootIdSetUsed)
1097 lootIdSet.erase(lootId);
1098
1099 // 1 means loot for player corpse
1100 lootIdSet.erase(PLAYER_CORPSE_LOOT_ENTRY);
1101
1102 // output error for any still listed (not referenced from appropriate table) ids
1104
1105 if (count)
1106 TC_LOG_INFO("server.loading", ">> Loaded {} creature loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1107 else
1108 TC_LOG_INFO("server.loading", ">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty");
1109}
1110
1112{
1113 TC_LOG_INFO("server.loading", "Loading disenchanting loot templates...");
1114
1115 uint32 oldMSTime = getMSTime();
1116
1117 LootIdSet lootIdSet, lootIdSetUsed;
1119
1120 for (ItemDisenchantLootEntry const* disenchant : sItemDisenchantLootStore)
1121 {
1122 uint32 lootid = disenchant->ID;
1123 if (!lootIdSet.contains(lootid))
1124 LootTemplates_Disenchant.ReportNonExistingId(lootid, "ItemDisenchantLoot", lootid);
1125 else
1126 lootIdSetUsed.insert(lootid);
1127 }
1128
1129 for (ItemBonusEntry const* itemBonus : sItemBonusStore)
1130 {
1131 if (itemBonus->Type != ITEM_BONUS_DISENCHANT_LOOT_ID)
1132 continue;
1133
1134 uint32 lootid = itemBonus->Value[0];
1135 if (!lootIdSet.contains(lootid))
1136 LootTemplates_Disenchant.ReportNonExistingId(lootid, "ItemBonusList", itemBonus->ParentItemBonusListID);
1137 else
1138 lootIdSetUsed.insert(lootid);
1139 }
1140
1141 for (uint32 lootId : lootIdSetUsed)
1142 lootIdSet.erase(lootId);
1143
1144 // output error for any still listed (not referenced from appropriate table) ids
1146
1147 if (count)
1148 TC_LOG_INFO("server.loading", ">> Loaded {} disenchanting loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1149 else
1150 TC_LOG_INFO("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty");
1151}
1152
1154{
1155 TC_LOG_INFO("server.loading", "Loading fishing loot templates...");
1156
1157 uint32 oldMSTime = getMSTime();
1158
1159 LootIdSet lootIdSet;
1161
1162 // remove real entries and check existence loot
1163 for (AreaTableEntry const* areaTable : sAreaTableStore)
1164 lootIdSet.erase(areaTable->ID);
1165
1166 // output error for any still listed (not referenced from appropriate table) ids
1168
1169 if (count)
1170 TC_LOG_INFO("server.loading", ">> Loaded {} fishing loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1171 else
1172 TC_LOG_INFO("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty");
1173}
1174
1176{
1177 TC_LOG_INFO("server.loading", "Loading gameobject loot templates...");
1178
1179 uint32 oldMSTime = getMSTime();
1180
1181 LootIdSet lootIdSet, lootIdSetUsed;
1183
1184 auto checkLootId = [&](uint32 lootId, uint32 gameObjectId)
1185 {
1186 if (!lootIdSet.contains(lootId))
1187 LootTemplates_Gameobject.ReportNonExistingId(lootId, "Gameobject", gameObjectId);
1188 else
1189 lootIdSetUsed.insert(lootId);
1190 };
1191
1192 // remove real entries and check existence loot
1193 GameObjectTemplateContainer const& gotc = sObjectMgr->GetGameObjectTemplates();
1194 for (auto const& [gameObjectId, gameObjectTemplate] : gotc)
1195 {
1196 if (uint32 lootid = gameObjectTemplate.GetLootId())
1197 checkLootId(lootid, gameObjectId);
1198
1199 if (gameObjectTemplate.type == GAMEOBJECT_TYPE_CHEST)
1200 {
1201 if (gameObjectTemplate.chest.chestPersonalLoot)
1202 checkLootId(gameObjectTemplate.chest.chestPersonalLoot, gameObjectId);
1203
1204 if (gameObjectTemplate.chest.chestPushLoot)
1205 checkLootId(gameObjectTemplate.chest.chestPushLoot, gameObjectId);
1206 }
1207 }
1208
1209 for (uint32 lootId : lootIdSetUsed)
1210 lootIdSet.erase(lootId);
1211
1212 // output error for any still listed (not referenced from appropriate table) ids
1214
1215 if (count)
1216 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1217 else
1218 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty");
1219}
1220
1222{
1223 TC_LOG_INFO("server.loading", "Loading item loot templates...");
1224
1225 uint32 oldMSTime = getMSTime();
1226
1227 LootIdSet lootIdSet;
1229
1230 // remove real entries and check existence loot
1231 ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
1232 for (auto const& [itemId, itemTemplate] : its)
1233 if (itemTemplate.HasFlag(ITEM_FLAG_HAS_LOOT))
1234 if (!lootIdSet.erase(itemId))
1235 LootTemplates_Item.ReportNonExistingId(itemId, "Item", itemId);
1236
1237 // output error for any still listed (not referenced from appropriate table) ids
1239
1240 if (count)
1241 TC_LOG_INFO("server.loading", ">> Loaded {} item loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1242 else
1243 TC_LOG_INFO("server.loading", ">> Loaded 0 item loot templates. DB table `item_loot_template` is empty");
1244}
1245
1247{
1248 TC_LOG_INFO("server.loading", "Loading milling loot templates...");
1249
1250 uint32 oldMSTime = getMSTime();
1251
1252 LootIdSet lootIdSet;
1254
1255 // remove real entries and check existence loot
1256 ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
1257 for (auto const& [itemId, itemTemplate] : its)
1258 if (itemTemplate.HasFlag(ITEM_FLAG_IS_MILLABLE))
1259 if (!lootIdSet.erase(itemId))
1260 LootTemplates_Milling.ReportNonExistingId(itemId, "Item", itemId);
1261
1262 // output error for any still listed (not referenced from appropriate table) ids
1264
1265 if (count)
1266 TC_LOG_INFO("server.loading", ">> Loaded {} milling loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1267 else
1268 TC_LOG_INFO("server.loading", ">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty");
1269}
1270
1272{
1273 TC_LOG_INFO("server.loading", "Loading pickpocketing loot templates...");
1274
1275 uint32 oldMSTime = getMSTime();
1276
1277 LootIdSet lootIdSet, lootIdSetUsed;
1279
1280 // Remove real entries and check loot existence
1281 CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
1282 for (auto const& [creatureId, creatureTemplate] : ctc)
1283 {
1284 for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
1285 {
1286 if (uint32 lootid = creatureDifficulty.PickPocketLootID)
1287 {
1288 if (!lootIdSet.contains(lootid))
1289 LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureId);
1290 else
1291 lootIdSetUsed.insert(lootid);
1292 }
1293 }
1294 }
1295
1296 for (uint32 lootId : lootIdSetUsed)
1297 lootIdSet.erase(lootId);
1298
1299 // output error for any still listed (not referenced from appropriate table) ids
1301
1302 if (count)
1303 TC_LOG_INFO("server.loading", ">> Loaded {} pickpocketing loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1304 else
1305 TC_LOG_INFO("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty");
1306}
1307
1309{
1310 TC_LOG_INFO("server.loading", "Loading prospecting loot templates...");
1311
1312 uint32 oldMSTime = getMSTime();
1313
1314 LootIdSet lootIdSet;
1316
1317 // remove real entries and check existence loot
1318 ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
1319 for (auto const& [itemId, itemTemplate] : its)
1320 if (itemTemplate.HasFlag(ITEM_FLAG_IS_PROSPECTABLE))
1321 if (!lootIdSet.erase(itemId))
1322 LootTemplates_Prospecting.ReportNonExistingId(itemId, "Item", itemId);
1323
1324 // output error for any still listed (not referenced from appropriate table) ids
1326
1327 if (count)
1328 TC_LOG_INFO("server.loading", ">> Loaded {} prospecting loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1329 else
1330 TC_LOG_INFO("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty");
1331}
1332
1334{
1335 TC_LOG_INFO("server.loading", "Loading mail loot templates...");
1336
1337 uint32 oldMSTime = getMSTime();
1338
1339 LootIdSet lootIdSet;
1341
1342 // remove real entries and check existence loot
1343 for (MailTemplateEntry const* mailTemplate : sMailTemplateStore)
1344 lootIdSet.erase(mailTemplate->ID);
1345
1346 // output error for any still listed (not referenced from appropriate table) ids
1348
1349 if (count)
1350 TC_LOG_INFO("server.loading", ">> Loaded {} mail loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1351 else
1352 TC_LOG_INFO("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty");
1353}
1354
1356{
1357 TC_LOG_INFO("server.loading", "Loading skinning loot templates...");
1358
1359 uint32 oldMSTime = getMSTime();
1360
1361 LootIdSet lootIdSet, lootIdSetUsed;
1363
1364 // remove real entries and check existence loot
1365 CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
1366 for (auto const& [creatureId, creatureTemplate] : ctc)
1367 {
1368 for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
1369 {
1370 if (uint32 lootid = creatureDifficulty.SkinLootID)
1371 {
1372 if (!lootIdSet.contains(lootid))
1373 LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureId);
1374 else
1375 lootIdSetUsed.insert(lootid);
1376 }
1377 }
1378 }
1379
1380 for (uint32 lootId : lootIdSetUsed)
1381 lootIdSet.erase(lootId);
1382
1383 // output error for any still listed (not referenced from appropriate table) ids
1385
1386 if (count)
1387 TC_LOG_INFO("server.loading", ">> Loaded {} skinning loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1388 else
1389 TC_LOG_INFO("server.loading", ">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty");
1390}
1391
1393{
1394 // TODO: change this to use MiscValue from spell effect as id instead of spell id
1395 TC_LOG_INFO("server.loading", "Loading spell loot templates...");
1396
1397 uint32 oldMSTime = getMSTime();
1398
1399 LootIdSet lootIdSet, lootIdSetUsed;
1401
1402 // remove real entries and check existence loot
1403 sSpellMgr->ForEachSpellInfo([&](SpellInfo const* spellInfo)
1404 {
1405 // possible cases
1406 if (!spellInfo->IsLootCrafting())
1407 return;
1408
1409 if (!lootIdSet.contains(spellInfo->Id))
1410 {
1411 // not report about not trainable spells (optionally supported by DB)
1412 // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example
1414 LootTemplates_Spell.ReportNonExistingId(spellInfo->Id, "Spell", spellInfo->Id);
1415 }
1416 else
1417 lootIdSetUsed.insert(spellInfo->Id);
1418 });
1419
1420 for (uint32 lootId : lootIdSetUsed)
1421 lootIdSet.erase(lootId);
1422
1423 // output error for any still listed (not referenced from appropriate table) ids
1425
1426 if (count)
1427 TC_LOG_INFO("server.loading", ">> Loaded {} spell loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1428 else
1429 TC_LOG_INFO("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty");
1430}
1431
1433{
1434 TC_LOG_INFO("server.loading", "Loading reference loot templates...");
1435
1436 uint32 oldMSTime = getMSTime();
1437
1438 LootIdSet lootIdSet;
1440
1441 // check references and remove used
1453
1454 // output error for any still listed ids (not referenced from any loot table)
1456
1457 TC_LOG_INFO("server.loading", ">> Loaded reference loot templates in {} ms", GetMSTimeDiffToNow(oldMSTime));
1458}
1459
DB2Storage< ItemBonusEntry > sItemBonusStore("ItemBonus.db2", &ItemBonusLoadInfo::Instance)
DB2Storage< MailTemplateEntry > sMailTemplateStore("MailTemplate.db2", &MailTemplateLoadInfo::Instance)
DB2Storage< ItemDisenchantLootEntry > sItemDisenchantLootStore("ItemDisenchantLoot.db2", &ItemDisenchantLootLoadInfo::Instance)
DB2Storage< CurrencyTypesEntry > sCurrencyTypesStore("CurrencyTypes.db2", &CurrencyTypesLoadInfo::Instance)
DB2Storage< AreaTableEntry > sAreaTableStore("AreaTable.db2", &AreaTableLoadInfo::Instance)
@ ITEM_BONUS_DISENCHANT_LOOT_ID
Definition DBCEnums.h:1264
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
uint8_t uint8
Definition Define.h:156
int32_t int32
Definition Define.h:150
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
@ ITEM_FLAG_IS_MILLABLE
@ ITEM_FLAG_IS_PROSPECTABLE
@ ITEM_FLAG_HAS_LOOT
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:176
LootStore LootTemplates_Spell("spell_loot_template", "spell id (random item creating)", false)
LootStore LootTemplates_Skinning("skinning_loot_template", "creature skinning id", true)
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
static constexpr Rates QualityToRate[MAX_ITEM_QUALITY]
Definition LootMgr.cpp:34
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)
LootStore LootTemplates_Creature("creature_loot_template", "creature entry", true)
LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template", "creature pickpocket lootid", true)
LootStore LootTemplates_Mail("mail_loot_template", "mail template id", false)
LootStore LootTemplates_Fishing("fishing_loot_template", "area id", true)
TC_GAME_API void LoadLootTemplates_Skinning()
Definition LootMgr.cpp:1355
std::vector< std::unique_ptr< LootStoreItem > > LootStoreItemList
Definition LootMgr.h:68
TC_GAME_API LootStore LootTemplates_Reference
TC_GAME_API void LoadLootTemplates_Creature()
Definition LootMgr.cpp:1071
TC_GAME_API void LoadLootTemplates_Milling()
Definition LootMgr.cpp:1246
TC_GAME_API void LoadLootTemplates_Disenchant()
Definition LootMgr.cpp:1111
TC_GAME_API LootStore LootTemplates_Gameobject
TC_GAME_API LootStore LootTemplates_Item
TC_GAME_API LootStore LootTemplates_Prospecting
TC_GAME_API void LoadLootTemplates_Gameobject()
Definition LootMgr.cpp:1175
TC_GAME_API LootStore LootTemplates_Skinning
TC_GAME_API void LoadLootTemplates_Pickpocketing()
Definition LootMgr.cpp:1271
TC_GAME_API LootStore LootTemplates_Fishing
TC_GAME_API void LoadLootTemplates_Prospecting()
Definition LootMgr.cpp:1308
TC_GAME_API LootStore LootTemplates_Disenchant
TC_GAME_API LootStore LootTemplates_Pickpocketing
TC_GAME_API LootStore LootTemplates_Spell
std::unordered_map< uint32, std::unique_ptr< LootTemplate > > LootTemplateMap
Definition LootMgr.h:69
TC_GAME_API void LoadLootTemplates_Mail()
Definition LootMgr.cpp:1333
TC_GAME_API LootStore LootTemplates_Creature
TC_GAME_API void LoadLootTables()
Definition LootMgr.cpp:1460
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)
Definition LootMgr.cpp:1036
TC_GAME_API LootStore LootTemplates_Mail
std::set< uint32 > LootIdSet
Definition LootMgr.h:71
TC_GAME_API void LoadLootTemplates_Item()
Definition LootMgr.cpp:1221
TC_GAME_API void LoadLootTemplates_Fishing()
Definition LootMgr.cpp:1153
TC_GAME_API void LoadLootTemplates_Reference()
Definition LootMgr.cpp:1432
TC_GAME_API LootStore LootTemplates_Milling
TC_GAME_API void LoadLootTemplates_Spell()
Definition LootMgr.cpp:1392
LootType
Definition Loot.h:99
std::unordered_map< uint32, ItemTemplate > ItemTemplateContainer
Definition ObjectMgr.h:515
std::unordered_map< uint32, GameObjectTemplate > GameObjectTemplateContainer
Definition ObjectMgr.h:500
#define sObjectMgr
Definition ObjectMgr.h:1873
std::unordered_map< uint32, CreatureTemplate > CreatureTemplateContainer
Definition ObjectMgr.h:487
@ QUEST_FLAGS_TRACKING_EVENT
Definition QuestDef.h:224
float rand_chance()
Definition Random.cpp:82
bool roll_chance(float chance, PseudoRandomDistributionState &state)
Definition Random.cpp:131
#define PLAYER_CORPSE_LOOT_ENTRY
@ GAMEOBJECT_TYPE_CHEST
@ MAX_ITEM_QUALITY
@ SPELL_ATTR0_IS_TRADESKILL
@ SPELL_ATTR0_NOT_SHAPESHIFTED
#define sSpellMgr
Definition SpellMgr.h:812
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
Class used to access individual fields of database query result.
Definition Field.h:94
float GetFloat() const noexcept
Definition Field.cpp:85
bool GetBool() const noexcept
Definition Field.h:102
uint32 GetUInt32() const noexcept
Definition Field.cpp:57
uint16 GetUInt16() const noexcept
Definition Field.cpp:43
uint8 GetUInt8() const noexcept
Definition Field.cpp:29
int8 GetInt8() const noexcept
Definition Field.cpp:36
uint32 LoadLootTable()
Definition LootMgr.cpp:138
void ReportUnusedIds(LootIdSet const &lootIdSet) const
Definition LootMgr.cpp:232
LootTemplate const * GetLootFor(uint32 loot_id) const
Definition LootMgr.cpp:207
LootTemplateMap m_LootTemplates
Definition LootMgr.h:106
void ReportNonExistingId(uint32 lootId, char const *ownerType, uint32 ownerId) const
Definition LootMgr.cpp:239
void CheckLootRefs(LootIdSet *ref_set=nullptr) const
Definition LootMgr.cpp:226
char const * GetEntryName() const
Definition LootMgr.h:100
bool HaveQuestLootFor(uint32 loot_id) const
Definition LootMgr.cpp:189
LootTemplate * GetLootForConditionFill(uint32 loot_id)
Definition LootMgr.cpp:212
LootStore(char const *name, char const *entryName, bool ratesAllowed)
Definition LootMgr.cpp:113
bool IsRatesAllowed() const
Definition LootMgr.h:101
void Clear()
Definition LootMgr.cpp:123
char const * GetName() const
Definition LootMgr.h:99
uint32 LoadAndCollectLootIds(LootIdSet &lootIdSet)
Definition LootMgr.cpp:217
bool HaveQuestLootForPlayer(uint32 loot_id, Player const *player) const
Definition LootMgr.cpp:198
void Verify() const
Definition LootMgr.cpp:130
void CheckLootRefs(LootTemplateMap const &store, LootIdSet *ref_set) const
Definition LootMgr.cpp:558
LootStoreItemList ExplicitlyChanced
Definition LootMgr.cpp:106
LootGroup(LootGroup &&)=delete
LootGroup & operator=(LootGroup &&)=delete
LootStoreItemList * GetEqualChancedItemList()
Definition LootMgr.cpp:104
LootStoreItem const * Roll(uint16 lootMode, Player const *personalLooter=nullptr) const
Definition LootMgr.cpp:432
LootGroup & operator=(LootGroup const &)=delete
LootStoreItemList EqualChanced
Definition LootMgr.cpp:107
void Verify(LootStore const &lootstore, uint32 id, uint8 group_id) const
Definition LootMgr.cpp:548
LootStoreItemList * GetExplicitlyChancedItemList()
Definition LootMgr.cpp:103
float RawTotalChance() const
Definition LootMgr.cpp:526
void AddEntry(LootStoreItem *item)
Definition LootMgr.cpp:423
bool HasQuestDropForPlayer(Player const *player) const
Definition LootMgr.cpp:493
void Process(Loot &loot, uint16 lootMode, Player const *personalLooter=nullptr) const
Definition LootMgr.cpp:519
LootGroup(LootGroup const &)=delete
float TotalChance() const
Definition LootMgr.cpp:538
bool HasQuestDrop() const
Definition LootMgr.cpp:481
bool HasDropForPlayer(Player const *player, bool strictUsabilityCheck) const
Definition LootMgr.cpp:467
void ProcessPersonalLoot(std::unordered_map< Player *, std::unique_ptr< Loot > > &personalLoot, bool rate, uint16 lootMode) const
Definition LootMgr.cpp:698
bool HasQuestDropForPlayer(Player const *player, uint8 groupId=0) const
Definition LootMgr.cpp:908
bool HasQuestDrop(uint8 groupId=0) const
Definition LootMgr.cpp:863
void Process(Loot &loot, bool rate, uint16 lootMode, uint8 groupId, Player const *personalLooter=nullptr) const
Definition LootMgr.cpp:641
void CheckLootRefs(LootTemplateMap const &store, LootIdSet *ref_set) const
Definition LootMgr.cpp:967
void CopyConditions(LootItem *li) const
Definition LootMgr.cpp:607
void AddEntry(LootStoreItem *item)
Definition LootMgr.cpp:593
bool LinkConditions(ConditionId const &id, ConditionsReference reference)
Definition LootMgr.cpp:985
bool HasDropForPlayer(Player const *player, uint8 groupId=0, bool strictUsabilityCheck=false) const
Definition LootMgr.cpp:816
LootStoreItemList Entries
Definition LootMgr.h:147
void Verify(LootStore const &store, uint32 Id) const
Definition LootMgr.cpp:957
LootGroups Groups
Definition LootMgr.h:148
bool HasQuestForItem(uint32 itemId) const
Definition Player.cpp:16930
bool HasQuestForCurrency(uint32 currencyId) const
Definition Player.cpp:17004
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:615
uint32 const Id
Definition SpellInfo.h:328
bool IsLootCrafting() const
bool HasAttribute(SpellAttr0 attribute) const
Definition SpellInfo.h:456
Map * GetMap() const
Definition Object.h:411
#define sWorld
Definition World.h:919
Rates
Server rates.
Definition World.h:451
@ RATE_DROP_ITEM_REFERENCED_AMOUNT
Definition World.h:480
@ RATE_DROP_ITEM_RARE
Definition World.h:475
@ RATE_DROP_ITEM_LEGENDARY
Definition World.h:477
@ RATE_DROP_ITEM_POOR
Definition World.h:472
@ MAX_RATES
Definition World.h:532
@ RATE_DROP_ITEM_UNCOMMON
Definition World.h:474
@ RATE_DROP_ITEM_REFERENCED
Definition World.h:479
@ RATE_DROP_ITEM_EPIC
Definition World.h:476
@ RATE_DROP_ITEM_ARTIFACT
Definition World.h:478
@ RATE_DROP_ITEM_NORMAL
Definition World.h:473
ItemContext GetContextForPlayer(MapDifficultyEntry const *mapDifficulty, Player const *player)
constexpr void EraseIf(Container &c, Predicate p)
Definition Containers.h:283
constexpr auto MapKey
Definition MapUtils.h:72
auto SelectRandomContainerElement(C const &container) -> std::add_const_t< decltype(*std::ranges::begin(container))> &
Definition Containers.h:110
constexpr decltype(auto) EnsureWritableVectorIndex(std::vector< T > &vec, typename std::vector< T >::size_type i)
Definition Containers.h:299
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
uint32 GetQuality() const
LootGroupInvalidSelector(uint16 lootMode, Player const *personalLooter)
Definition LootMgr.cpp:63
bool operator()(LootStoreItem const *item) const
Definition LootMgr.cpp:65
Player const * _personalLooter
Definition LootMgr.cpp:78
uint32 itemid
Definition Loot.h:178
bool AllowedForPlayer(Player const *player, Loot const *loot) const
Definition Loot.cpp:76
ConditionsReference conditions
Definition Loot.h:183
LootItemType type
Definition Loot.h:187
float chance
Definition LootMgr.h:50
bool Roll(bool rate) const
Definition LootMgr.cpp:250
bool needs_quest
Definition LootMgr.h:52
uint32 itemid
Definition LootMgr.h:48
uint8 maxcount
Definition LootMgr.h:55
uint16 lootmode
Definition LootMgr.h:51
uint8 groupid
Definition LootMgr.h:53
uint8 mincount
Definition LootMgr.h:54
bool IsValid(LootStore const &store, uint32 entry) const
Definition LootMgr.cpp:285
Definition Loot.h:286
void AddItem(LootStoreItem const &item)
Definition Loot.cpp:929