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(m_LootTemplates);
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(m_LootTemplates, 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)
701 {
702 std::vector<Player*> lootersForItem;
703 for (auto&& [looter, loot] : personalLoot)
704 {
705 if (predicate(looter))
706 lootersForItem.push_back(looter);
707 }
708 return lootersForItem;
709 };
710
711 // Rolling non-grouped items
712 for (std::unique_ptr<LootStoreItem> const& item : Entries)
713 {
714 if (!(item->lootmode & lootMode)) // Do not add if mode mismatch
715 continue;
716
717 if (!item->Roll(rate))
718 continue; // Bad luck for the entry
719
720 switch (item->type)
721 {
723 {
724 // Plain entries (not a reference, not grouped)
725 // Chance is already checked, just add
726 std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
727 {
728 return LootItem::AllowedForPlayer(looter, *item, true);
729 });
730
731 if (!lootersForItem.empty())
732 {
733 Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
734 personalLoot[chosenLooter]->AddItem(*item);
735 }
736 break;
737 }
739 {
740 LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(item->itemid);
741 if (!referenced)
742 continue; // Error message already printed at loading stage
743
744 uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
745 std::vector<Player*> gotLoot;
746 for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
747 {
748 std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
749 {
750 return referenced->HasDropForPlayer(looter, item->groupid, true);
751 });
752
753 // nobody can loot this, skip it
754 if (lootersForItem.empty())
755 break;
756
757 auto newEnd = std::remove_if(lootersForItem.begin(), lootersForItem.end(), [&](Player const* looter)
758 {
759 return advstd::ranges::contains(gotLoot, looter);
760 });
761
762 if (lootersForItem.begin() == newEnd)
763 {
764 // if we run out of looters this means that there are more items dropped than players
765 // start a new cycle adding one item to everyone
766 gotLoot.clear();
767 }
768 else
769 lootersForItem.erase(newEnd, lootersForItem.end());
770
771 Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForItem);
772 referenced->Process(*personalLoot[chosenLooter], rate, lootMode, item->groupid, chosenLooter);
773 gotLoot.push_back(chosenLooter);
774 }
775
776 break;
777 }
780 {
781 // Plain entries (not a reference, not grouped)
782 // Chance is already checked, just add
783 std::vector<Player*> lootersForItem = getLootersForItem([&](Player const* looter)
784 {
785 return LootItem::AllowedForPlayer(looter, *item, true);
786 });
787
788 for (Player* looter : lootersForItem)
789 personalLoot[looter]->AddItem(*item);
790 break;
791 }
792 default:
793 break;
794 }
795 }
796
797 // Now processing groups
798 for (std::unique_ptr<LootGroup> const& group : Groups)
799 {
800 if (group)
801 {
802 std::vector<Player*> lootersForGroup = getLootersForItem([&](Player const* looter)
803 {
804 return group->HasDropForPlayer(looter, true);
805 });
806
807 if (!lootersForGroup.empty())
808 {
809 Player* chosenLooter = Trinity::Containers::SelectRandomContainerElement(lootersForGroup);
810 group->Process(*personalLoot[chosenLooter], lootMode);
811 }
812 }
813 }
814}
815
816// True if template includes at least 1 drop for the player
817bool LootTemplate::HasDropForPlayer(Player const* player, uint8 groupId, bool strictUsabilityCheck) const
818{
819 if (groupId) // Group reference
820 {
821 if (groupId > Groups.size())
822 return false; // Error message already printed at loading stage
823
824 if (!Groups[groupId - 1])
825 return false;
826
827 return Groups[groupId - 1]->HasDropForPlayer(player, strictUsabilityCheck);
828 }
829
830 // Checking non-grouped entries
831 for (std::unique_ptr<LootStoreItem> const& lootStoreItem : Entries)
832 {
833 switch (lootStoreItem->type)
834 {
838 if (LootItem::AllowedForPlayer(player, *lootStoreItem, strictUsabilityCheck))
839 return true; // active quest drop found
840 break;
842 {
843 LootTemplate const* referenced = LootTemplates_Reference.GetLootFor(lootStoreItem->itemid);
844 if (!referenced)
845 continue; // Error message already printed at loading stage
846 if (referenced->HasDropForPlayer(player, lootStoreItem->groupid, strictUsabilityCheck))
847 return true;
848 break;
849 }
850 default:
851 break;
852 }
853 }
854
855 // Now checking groups
856 for (std::unique_ptr<LootGroup> const& group : Groups)
857 if (group && group->HasDropForPlayer(player, strictUsabilityCheck))
858 return true;
859
860 return false;
861}
862
863// True if template includes at least 1 quest drop entry
864bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const
865{
866 if (groupId) // Group reference
867 {
868 if (groupId > Groups.size())
869 return false; // Error message [should be] already printed at loading stage
870
871 if (!Groups[groupId - 1])
872 return false;
873
874 return Groups[groupId-1]->HasQuestDrop();
875 }
876
877 for (std::unique_ptr<LootStoreItem> const& item : Entries)
878 {
879 switch (item->type)
880 {
883 if (item->needs_quest)
884 return true; // quest drop found
885 break;
887 {
888 LootTemplateMap::const_iterator Referenced = store.find(item->itemid);
889 if (Referenced == store.end())
890 continue; // Error message [should be] already printed at loading stage
891 if (Referenced->second->HasQuestDrop(store, item->groupid))
892 return true;
893 break;
894 }
895 default:
896 break;
897 }
898 }
899
900 // Now processing groups
901 for (std::unique_ptr<LootGroup> const& group : Groups)
902 if (group && group->HasQuestDrop())
903 return true;
904
905 return false;
906}
907
908// True if template includes at least 1 quest drop for an active quest of the player
909bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const
910{
911 if (groupId) // Group reference
912 {
913 if (groupId > Groups.size())
914 return false; // Error message already printed at loading stage
915
916 if (!Groups[groupId - 1])
917 return false;
918
919 return Groups[groupId - 1]->HasQuestDropForPlayer(player);
920 }
921
922 // Checking non-grouped entries
923 for (std::unique_ptr<LootStoreItem> const& item : Entries)
924 {
925 switch (item->type)
926 {
928 if (player->HasQuestForItem(item->itemid))
929 return true; // active quest drop found
930 break;
932 {
933 LootTemplateMap::const_iterator Referenced = store.find(item->itemid);
934 if (Referenced == store.end())
935 continue; // Error message already printed at loading stage
936 if (Referenced->second->HasQuestDropForPlayer(store, player, item->groupid))
937 return true;
938 break;
939 }
941 if (player->HasQuestForCurrency(item->itemid))
942 return true; // active quest drop found
943 break;
944 default:
945 break;
946 }
947 }
948
949 // Now checking groups
950 for (std::unique_ptr<LootGroup> const& group : Groups)
951 if (group && group->HasQuestDropForPlayer(player))
952 return true;
953
954 return false;
955}
956
957// Checks integrity of the template
958void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
959{
960 // Checking group chances
961 for (uint32 i = 0; i < Groups.size(); ++i)
962 if (Groups[i])
963 Groups[i]->Verify(lootstore, id, i + 1);
964
966}
967
968void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
969{
970 for (std::unique_ptr<LootStoreItem> const& item : Entries)
971 {
972 if (item->type == LootStoreItem::Type::Reference)
973 {
974 if (!LootTemplates_Reference.GetLootFor(item->itemid))
975 LootTemplates_Reference.ReportNonExistingId(item->itemid, "Reference", item->itemid);
976 else if (ref_set)
977 ref_set->erase(item->itemid);
978 }
979 }
980
981 for (std::unique_ptr<LootGroup> const& group : Groups)
982 if (group)
983 group->CheckLootRefs(store, ref_set);
984}
985
987{
988 if (!Entries.empty())
989 {
990 for (std::unique_ptr<LootStoreItem>& item : Entries)
991 {
992 if (item->itemid == uint32(id.SourceEntry))
993 {
994 item->conditions = std::move(reference);
995 return true;
996 }
997 }
998 }
999
1000 if (!Groups.empty())
1001 {
1002 for (std::unique_ptr<LootGroup>& group : Groups)
1003 {
1004 if (!group)
1005 continue;
1006
1007 LootStoreItemList* itemList = group->GetExplicitlyChancedItemList();
1008 if (!itemList->empty())
1009 {
1010 for (std::unique_ptr<LootStoreItem> const& item : *itemList)
1011 {
1012 if (item->itemid == uint32(id.SourceEntry))
1013 {
1014 item->conditions = std::move(reference);
1015 return true;
1016 }
1017 }
1018 }
1019
1020 itemList = group->GetEqualChancedItemList();
1021 if (!itemList->empty())
1022 {
1023 for (std::unique_ptr<LootStoreItem> const& item : *itemList)
1024 {
1025 if (item->itemid == uint32(id.SourceEntry))
1026 {
1027 item->conditions = std::move(reference);
1028 return true;
1029 }
1030 }
1031 }
1032 }
1033 }
1034 return false;
1035}
1036
1037std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> GenerateDungeonEncounterPersonalLoot(uint32 dungeonEncounterId, uint32 lootId, LootStore const& store,
1038 LootType type, WorldObject const* lootOwner, uint32 minMoney, uint32 maxMoney, uint16 lootMode, MapDifficultyEntry const* mapDifficulty,
1039 std::vector<Player*> const& tappers)
1040{
1041 std::unordered_map<Player*, std::unique_ptr<Loot>> tempLoot;
1042
1043 for (Player* tapper : tappers)
1044 {
1045 if (tapper->IsLockedToDungeonEncounter(dungeonEncounterId))
1046 continue;
1047
1048 std::unique_ptr<Loot>& loot = tempLoot[tapper];
1049 loot.reset(new Loot(lootOwner->GetMap(), lootOwner->GetGUID(), type, nullptr));
1050 loot->SetItemContext(ItemBonusMgr::GetContextForPlayer(mapDifficulty, tapper));
1051 loot->SetDungeonEncounterId(dungeonEncounterId);
1052 loot->generateMoneyLoot(minMoney, maxMoney);
1053 }
1054
1055 if (LootTemplate const* tab = store.GetLootFor(lootId))
1056 tab->ProcessPersonalLoot(tempLoot, store.IsRatesAllowed(), lootMode);
1057
1058 std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> personalLoot;
1059 for (auto&& [looter, loot] : tempLoot)
1060 {
1061 loot->FillNotNormalLootFor(looter);
1062
1063 if (loot->isLooted())
1064 continue;
1065
1066 personalLoot[looter->GetGUID()] = std::move(loot);
1067 }
1068
1069 return personalLoot;
1070}
1071
1073{
1074 TC_LOG_INFO("server.loading", "Loading creature loot templates...");
1075
1076 uint32 oldMSTime = getMSTime();
1077
1078 LootIdSet lootIdSet, lootIdSetUsed;
1080
1081 // Remove real entries and check loot existence
1082 CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
1083 for (auto const& [creatureId, creatureTemplate] : ctc)
1084 {
1085 for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
1086 {
1087 if (uint32 lootid = creatureDifficulty.LootID)
1088 {
1089 if (!lootIdSet.contains(lootid))
1090 LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureId);
1091 else
1092 lootIdSetUsed.insert(lootid);
1093 }
1094 }
1095 }
1096
1097 for (uint32 lootId : lootIdSetUsed)
1098 lootIdSet.erase(lootId);
1099
1100 // 1 means loot for player corpse
1101 lootIdSet.erase(PLAYER_CORPSE_LOOT_ENTRY);
1102
1103 // output error for any still listed (not referenced from appropriate table) ids
1105
1106 if (count)
1107 TC_LOG_INFO("server.loading", ">> Loaded {} creature loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1108 else
1109 TC_LOG_INFO("server.loading", ">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty");
1110}
1111
1113{
1114 TC_LOG_INFO("server.loading", "Loading disenchanting loot templates...");
1115
1116 uint32 oldMSTime = getMSTime();
1117
1118 LootIdSet lootIdSet, lootIdSetUsed;
1120
1121 for (ItemDisenchantLootEntry const* disenchant : sItemDisenchantLootStore)
1122 {
1123 uint32 lootid = disenchant->ID;
1124 if (!lootIdSet.contains(lootid))
1125 LootTemplates_Disenchant.ReportNonExistingId(lootid, "ItemDisenchantLoot", lootid);
1126 else
1127 lootIdSetUsed.insert(lootid);
1128 }
1129
1130 for (ItemBonusEntry const* itemBonus : sItemBonusStore)
1131 {
1132 if (itemBonus->Type != ITEM_BONUS_DISENCHANT_LOOT_ID)
1133 continue;
1134
1135 uint32 lootid = itemBonus->Value[0];
1136 if (!lootIdSet.contains(lootid))
1137 LootTemplates_Disenchant.ReportNonExistingId(lootid, "ItemBonusList", itemBonus->ParentItemBonusListID);
1138 else
1139 lootIdSetUsed.insert(lootid);
1140 }
1141
1142 for (uint32 lootId : lootIdSetUsed)
1143 lootIdSet.erase(lootId);
1144
1145 // output error for any still listed (not referenced from appropriate table) ids
1147
1148 if (count)
1149 TC_LOG_INFO("server.loading", ">> Loaded {} disenchanting loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1150 else
1151 TC_LOG_INFO("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty");
1152}
1153
1155{
1156 TC_LOG_INFO("server.loading", "Loading fishing loot templates...");
1157
1158 uint32 oldMSTime = getMSTime();
1159
1160 LootIdSet lootIdSet;
1162
1163 // remove real entries and check existence loot
1164 for (AreaTableEntry const* areaTable : sAreaTableStore)
1165 lootIdSet.erase(areaTable->ID);
1166
1167 // output error for any still listed (not referenced from appropriate table) ids
1169
1170 if (count)
1171 TC_LOG_INFO("server.loading", ">> Loaded {} fishing loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1172 else
1173 TC_LOG_INFO("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty");
1174}
1175
1177{
1178 TC_LOG_INFO("server.loading", "Loading gameobject loot templates...");
1179
1180 uint32 oldMSTime = getMSTime();
1181
1182 LootIdSet lootIdSet, lootIdSetUsed;
1184
1185 auto checkLootId = [&](uint32 lootId, uint32 gameObjectId)
1186 {
1187 if (!lootIdSet.contains(lootId))
1188 LootTemplates_Gameobject.ReportNonExistingId(lootId, "Gameobject", gameObjectId);
1189 else
1190 lootIdSetUsed.insert(lootId);
1191 };
1192
1193 // remove real entries and check existence loot
1194 GameObjectTemplateContainer const& gotc = sObjectMgr->GetGameObjectTemplates();
1195 for (auto const& [gameObjectId, gameObjectTemplate] : gotc)
1196 {
1197 if (uint32 lootid = gameObjectTemplate.GetLootId())
1198 checkLootId(lootid, gameObjectId);
1199
1200 if (gameObjectTemplate.type == GAMEOBJECT_TYPE_CHEST)
1201 {
1202 if (gameObjectTemplate.chest.chestPersonalLoot)
1203 checkLootId(gameObjectTemplate.chest.chestPersonalLoot, gameObjectId);
1204
1205 if (gameObjectTemplate.chest.chestPushLoot)
1206 checkLootId(gameObjectTemplate.chest.chestPushLoot, gameObjectId);
1207 }
1208 }
1209
1210 for (uint32 lootId : lootIdSetUsed)
1211 lootIdSet.erase(lootId);
1212
1213 // output error for any still listed (not referenced from appropriate table) ids
1215
1216 if (count)
1217 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1218 else
1219 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty");
1220}
1221
1223{
1224 TC_LOG_INFO("server.loading", "Loading item loot templates...");
1225
1226 uint32 oldMSTime = getMSTime();
1227
1228 LootIdSet lootIdSet;
1230
1231 // remove real entries and check existence loot
1232 ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
1233 for (auto const& [itemId, itemTemplate] : its)
1234 if (itemTemplate.HasFlag(ITEM_FLAG_HAS_LOOT))
1235 if (!lootIdSet.erase(itemId))
1236 LootTemplates_Item.ReportNonExistingId(itemId, "Item", itemId);
1237
1238 // output error for any still listed (not referenced from appropriate table) ids
1240
1241 if (count)
1242 TC_LOG_INFO("server.loading", ">> Loaded {} item loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1243 else
1244 TC_LOG_INFO("server.loading", ">> Loaded 0 item loot templates. DB table `item_loot_template` is empty");
1245}
1246
1248{
1249 TC_LOG_INFO("server.loading", "Loading milling loot templates...");
1250
1251 uint32 oldMSTime = getMSTime();
1252
1253 LootIdSet lootIdSet;
1255
1256 // remove real entries and check existence loot
1257 ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
1258 for (auto const& [itemId, itemTemplate] : its)
1259 if (itemTemplate.HasFlag(ITEM_FLAG_IS_MILLABLE))
1260 if (!lootIdSet.erase(itemId))
1261 LootTemplates_Milling.ReportNonExistingId(itemId, "Item", itemId);
1262
1263 // output error for any still listed (not referenced from appropriate table) ids
1265
1266 if (count)
1267 TC_LOG_INFO("server.loading", ">> Loaded {} milling loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1268 else
1269 TC_LOG_INFO("server.loading", ">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty");
1270}
1271
1273{
1274 TC_LOG_INFO("server.loading", "Loading pickpocketing loot templates...");
1275
1276 uint32 oldMSTime = getMSTime();
1277
1278 LootIdSet lootIdSet, lootIdSetUsed;
1280
1281 // Remove real entries and check loot existence
1282 CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
1283 for (auto const& [creatureId, creatureTemplate] : ctc)
1284 {
1285 for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
1286 {
1287 if (uint32 lootid = creatureDifficulty.PickPocketLootID)
1288 {
1289 if (!lootIdSet.contains(lootid))
1290 LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureId);
1291 else
1292 lootIdSetUsed.insert(lootid);
1293 }
1294 }
1295 }
1296
1297 for (uint32 lootId : lootIdSetUsed)
1298 lootIdSet.erase(lootId);
1299
1300 // output error for any still listed (not referenced from appropriate table) ids
1302
1303 if (count)
1304 TC_LOG_INFO("server.loading", ">> Loaded {} pickpocketing loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1305 else
1306 TC_LOG_INFO("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty");
1307}
1308
1310{
1311 TC_LOG_INFO("server.loading", "Loading prospecting loot templates...");
1312
1313 uint32 oldMSTime = getMSTime();
1314
1315 LootIdSet lootIdSet;
1317
1318 // remove real entries and check existence loot
1319 ItemTemplateContainer const& its = sObjectMgr->GetItemTemplateStore();
1320 for (auto const& [itemId, itemTemplate] : its)
1321 if (itemTemplate.HasFlag(ITEM_FLAG_IS_PROSPECTABLE))
1322 if (!lootIdSet.erase(itemId))
1323 LootTemplates_Prospecting.ReportNonExistingId(itemId, "Item", itemId);
1324
1325 // output error for any still listed (not referenced from appropriate table) ids
1327
1328 if (count)
1329 TC_LOG_INFO("server.loading", ">> Loaded {} prospecting loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1330 else
1331 TC_LOG_INFO("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty");
1332}
1333
1335{
1336 TC_LOG_INFO("server.loading", "Loading mail loot templates...");
1337
1338 uint32 oldMSTime = getMSTime();
1339
1340 LootIdSet lootIdSet;
1342
1343 // remove real entries and check existence loot
1344 for (MailTemplateEntry const* mailTemplate : sMailTemplateStore)
1345 lootIdSet.erase(mailTemplate->ID);
1346
1347 // output error for any still listed (not referenced from appropriate table) ids
1349
1350 if (count)
1351 TC_LOG_INFO("server.loading", ">> Loaded {} mail loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1352 else
1353 TC_LOG_INFO("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty");
1354}
1355
1357{
1358 TC_LOG_INFO("server.loading", "Loading skinning loot templates...");
1359
1360 uint32 oldMSTime = getMSTime();
1361
1362 LootIdSet lootIdSet, lootIdSetUsed;
1364
1365 // remove real entries and check existence loot
1366 CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
1367 for (auto const& [creatureId, creatureTemplate] : ctc)
1368 {
1369 for (auto const& [difficulty, creatureDifficulty] : creatureTemplate.difficultyStore)
1370 {
1371 if (uint32 lootid = creatureDifficulty.SkinLootID)
1372 {
1373 if (!lootIdSet.contains(lootid))
1374 LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureId);
1375 else
1376 lootIdSetUsed.insert(lootid);
1377 }
1378 }
1379 }
1380
1381 for (uint32 lootId : lootIdSetUsed)
1382 lootIdSet.erase(lootId);
1383
1384 // output error for any still listed (not referenced from appropriate table) ids
1386
1387 if (count)
1388 TC_LOG_INFO("server.loading", ">> Loaded {} skinning loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1389 else
1390 TC_LOG_INFO("server.loading", ">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty");
1391}
1392
1394{
1395 // TODO: change this to use MiscValue from spell effect as id instead of spell id
1396 TC_LOG_INFO("server.loading", "Loading spell loot templates...");
1397
1398 uint32 oldMSTime = getMSTime();
1399
1400 LootIdSet lootIdSet, lootIdSetUsed;
1402
1403 // remove real entries and check existence loot
1404 sSpellMgr->ForEachSpellInfo([&](SpellInfo const* spellInfo)
1405 {
1406 // possible cases
1407 if (!spellInfo->IsLootCrafting())
1408 return;
1409
1410 if (!lootIdSet.contains(spellInfo->Id))
1411 {
1412 // not report about not trainable spells (optionally supported by DB)
1413 // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example
1415 LootTemplates_Spell.ReportNonExistingId(spellInfo->Id, "Spell", spellInfo->Id);
1416 }
1417 else
1418 lootIdSetUsed.insert(spellInfo->Id);
1419 });
1420
1421 for (uint32 lootId : lootIdSetUsed)
1422 lootIdSet.erase(lootId);
1423
1424 // output error for any still listed (not referenced from appropriate table) ids
1426
1427 if (count)
1428 TC_LOG_INFO("server.loading", ">> Loaded {} spell loot templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1429 else
1430 TC_LOG_INFO("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty");
1431}
1432
1434{
1435 TC_LOG_INFO("server.loading", "Loading reference loot templates...");
1436
1437 uint32 oldMSTime = getMSTime();
1438
1439 LootIdSet lootIdSet;
1441
1442 // check references and remove used
1454
1455 // output error for any still listed ids (not referenced from any loot table)
1457
1458 TC_LOG_INFO("server.loading", ">> Loaded reference loot templates in {} ms", GetMSTimeDiffToNow(oldMSTime));
1459}
1460
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:1261
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:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
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:1356
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:1072
TC_GAME_API void LoadLootTemplates_Milling()
Definition LootMgr.cpp:1247
TC_GAME_API void LoadLootTemplates_Disenchant()
Definition LootMgr.cpp:1112
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:1176
TC_GAME_API LootStore LootTemplates_Skinning
TC_GAME_API void LoadLootTemplates_Pickpocketing()
Definition LootMgr.cpp:1272
TC_GAME_API LootStore LootTemplates_Fishing
TC_GAME_API void LoadLootTemplates_Prospecting()
Definition LootMgr.cpp:1309
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:1334
TC_GAME_API LootStore LootTemplates_Creature
TC_GAME_API void LoadLootTables()
Definition LootMgr.cpp:1461
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:1037
TC_GAME_API LootStore LootTemplates_Mail
std::set< uint32 > LootIdSet
Definition LootMgr.h:71
TC_GAME_API void LoadLootTemplates_Item()
Definition LootMgr.cpp:1222
TC_GAME_API void LoadLootTemplates_Fishing()
Definition LootMgr.cpp:1154
TC_GAME_API void LoadLootTemplates_Reference()
Definition LootMgr.cpp:1433
TC_GAME_API LootStore LootTemplates_Milling
TC_GAME_API void LoadLootTemplates_Spell()
Definition LootMgr.cpp:1393
LootType
Definition Loot.h:99
std::unordered_map< uint32, ItemTemplate > ItemTemplateContainer
Definition ObjectMgr.h:516
std::unordered_map< uint32, GameObjectTemplate > GameObjectTemplateContainer
Definition ObjectMgr.h:501
#define sObjectMgr
Definition ObjectMgr.h:1885
std::unordered_map< uint32, CreatureTemplate > CreatureTemplateContainer
Definition ObjectMgr.h:488
@ QUEST_FLAGS_TRACKING_EVENT
Definition QuestDef.h:228
float rand_chance()
Definition Random.cpp:81
bool roll_chance(T chance)
Definition Random.h:55
#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
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:968
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:986
bool HasQuestDropForPlayer(LootTemplateMap const &store, Player const *player, uint8 groupId=0) const
Definition LootMgr.cpp:909
bool HasDropForPlayer(Player const *player, uint8 groupId=0, bool strictUsabilityCheck=false) const
Definition LootMgr.cpp:817
LootStoreItemList Entries
Definition LootMgr.h:147
void Verify(LootStore const &store, uint32 Id) const
Definition LootMgr.cpp:958
LootGroups Groups
Definition LootMgr.h:148
bool HasQuestDrop(LootTemplateMap const &store, uint8 groupId=0) const
Definition LootMgr.cpp:864
bool HasQuestForItem(uint32 itemId) const
Definition Player.cpp:16963
bool HasQuestForCurrency(uint32 currencyId) const
Definition Player.cpp:17037
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:619
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:916
Rates
Server rates.
Definition World.h:446
@ RATE_DROP_ITEM_REFERENCED_AMOUNT
Definition World.h:475
@ RATE_DROP_ITEM_RARE
Definition World.h:470
@ RATE_DROP_ITEM_LEGENDARY
Definition World.h:472
@ RATE_DROP_ITEM_POOR
Definition World.h:467
@ MAX_RATES
Definition World.h:527
@ RATE_DROP_ITEM_UNCOMMON
Definition World.h:469
@ RATE_DROP_ITEM_REFERENCED
Definition World.h:474
@ RATE_DROP_ITEM_EPIC
Definition World.h:471
@ RATE_DROP_ITEM_ARTIFACT
Definition World.h:473
@ RATE_DROP_ITEM_NORMAL
Definition World.h:468
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