diff --git a/data/sql/updates/pending_db_world/rev_1629826555036082700.sql b/data/sql/updates/pending_db_world/rev_1629826555036082700.sql new file mode 100644 index 000000000..046e1449e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1629826555036082700.sql @@ -0,0 +1,14 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1629826555036082700'); + +ALTER TABLE `creature_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `disenchant_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `fishing_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `gameobject_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `item_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `mail_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `milling_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `pickpocketing_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `prospecting_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `reference_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `skinning_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; +ALTER TABLE `spell_loot_template` CHANGE `Reference` `Reference` MEDIUMINT DEFAULT 0 NOT NULL; diff --git a/data/sql/updates/pending_db_world/rev_1630484852047598500.sql b/data/sql/updates/pending_db_world/rev_1630484852047598500.sql new file mode 100644 index 000000000..7e56616a0 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1630484852047598500.sql @@ -0,0 +1,5 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1630484852047598500'); + +SET @THORIUM_ORE := 10620; +UPDATE `prospecting_loot_template` SET `GroupId`=1 WHERE `entry` = @THORIUM_ORE; +UPDATE `prospecting_loot_template` SET `Reference`=-13001 WHERE `entry` = @THORIUM_ORE AND `item`=1; diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 16c331ddb..7b463be83 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -51,20 +51,23 @@ struct LootGroupInvalidSelector : public Acore::unary_functionlootmode & _lootMode)) return true; - ItemTemplate const* _proto = sObjectMgr->GetItemTemplate(item->itemid); - if (!_proto) - return true; + if (!item->reference) + { + ItemTemplate const* _proto = sObjectMgr->GetItemTemplate(item->itemid); + if (!_proto) + return true; - uint8 foundDuplicates = 0; - for (std::vector::const_iterator itr = _loot.items.begin(); itr != _loot.items.end(); ++itr) - if (itr->itemid == item->itemid) - { - ++foundDuplicates; - if (_proto->InventoryType == 0 && foundDuplicates == 3 && _proto->ItemId != 47242 /*Trophy of the Crusade*/) // Non-equippable items are limited to 3 drops - return true; - else if (_proto->InventoryType != 0 && foundDuplicates == 1) // Equippable item are limited to 1 drop - return true; - } + uint8 foundDuplicates = 0; + for (std::vector::const_iterator itr = _loot.items.begin(); itr != _loot.items.end(); ++itr) + if (itr->itemid == item->itemid) + { + ++foundDuplicates; + if (_proto->InventoryType == 0 && foundDuplicates == 3 && _proto->ItemId != 47242 /*Trophy of the Crusade*/) // Non-equippable items are limited to 3 drops + return true; + else if (_proto->InventoryType != 0 && foundDuplicates == 1) // Equippable item are limited to 1 drop + return true; + } + } return false; } @@ -80,9 +83,9 @@ public: LootGroup() { } ~LootGroup(); - void AddEntry(LootStoreItem* item); // Adds an entry to the group (at loading stage) - bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry - bool HasQuestDropForPlayer(Player const* player) const; + void AddEntry(LootStoreItem* item); // Adds an entry to the group (at loading stage) + bool HasQuestDrop(LootTemplateMap const& store) const; // True if group includes at least 1 quest drop entry + bool HasQuestDropForPlayer(Player const* player, LootTemplateMap const& store) const; // The same for active quests of the player void Process(Loot& loot, Player const* player, LootStore const& lootstore, uint16 lootMode) const; // Rolls an item from the group (if any) and adds the item to the loot float RawTotalChance() const; // Overall chance for the group (without equal chanced items) @@ -144,7 +147,7 @@ uint32 LootStore::LoadLootTable() uint32 entry = fields[0].GetUInt32(); uint32 item = fields[1].GetUInt32(); - uint32 reference = fields[2].GetUInt32(); + int32 reference = fields[2].GetInt32(); float chance = fields[3].GetFloat(); bool needsquest = fields[4].GetBool(); uint16 lootmode = fields[5].GetUInt16(); @@ -295,7 +298,7 @@ bool LootStoreItem::Roll(bool rate, Player const* player, Loot& loot, LootStore if (_chance >= 100.0f) return true; - if (reference > 0) // reference case + if (reference) // reference case return roll_chance_f(_chance * (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); @@ -320,7 +323,7 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const return false; } - if (reference == 0) // item (quest or non-quest) entry, maybe grouped + if (!reference) // item (quest or non-quest) entry, maybe grouped { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); if (!proto) @@ -1263,29 +1266,101 @@ LootStoreItem const* LootTemplate::LootGroup::Roll(Loot& loot, Player const* pla } // True if group includes at least 1 quest drop entry -bool LootTemplate::LootGroup::HasQuestDrop() const +bool LootTemplate::LootGroup::HasQuestDrop(LootTemplateMap const& store) const { for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) - if ((*i)->needs_quest) + { + LootStoreItem* item = *i; + if (item->reference) // References + { + LootTemplateMap::const_iterator Referenced = store.find(std::abs(item->reference)); + if (Referenced == store.end()) + { + continue; // Error message [should be] already printed at loading stage + } + + if (Referenced->second->HasQuestDrop(store, item->groupid)) + { + return true; + } + } + else if (item->needs_quest) + { return true; + } + } for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i) - if ((*i)->needs_quest) + { + LootStoreItem* item = *i; + if (item->reference) // References + { + LootTemplateMap::const_iterator Referenced = store.find(std::abs(item->reference)); + if (Referenced == store.end()) + { + continue; // Error message [should be] already printed at loading stage + } + + if (Referenced->second->HasQuestDrop(store, item->groupid)) + { + return true; + } + } + else if (item->needs_quest) + { return true; + } + } return false; } // True if group includes at least 1 quest drop entry for active quests of the player -bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const* player) const +bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const* player, LootTemplateMap const& store) const { for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) - if (player->HasQuestForItem((*i)->itemid)) - return true; + { + LootStoreItem* item = *i; + if (item->reference) // References processing + { + LootTemplateMap::const_iterator Referenced = store.find(std::abs(item->reference)); + if (Referenced == store.end()) + { + continue; // Error message already printed at loading stage + } + + if (Referenced->second->HasQuestDropForPlayer(store, player, item->groupid)) + { + return true; + } + } + else if (player->HasQuestForItem(item->itemid)) + { + return true; // active quest drop found + } + } for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i) - if (player->HasQuestForItem((*i)->itemid)) - return true; + { + LootStoreItem* item = *i; + if (item->reference) // References processing + { + LootTemplateMap::const_iterator Referenced = store.find(std::abs(item->reference)); + if (Referenced == store.end()) + { + continue; // Error message already printed at loading stage + } + + if (Referenced->second->HasQuestDropForPlayer(store, player, item->groupid)) + { + return true; + } + } + else if (player->HasQuestForItem(item->itemid)) + { + return true; // active quest drop found + } + } return false; } @@ -1303,7 +1378,26 @@ void LootTemplate::LootGroup::CopyConditions(ConditionList /*conditions*/) void LootTemplate::LootGroup::Process(Loot& loot, Player const* player, LootStore const& store, uint16 lootMode) const { if (LootStoreItem const* item = Roll(loot, player, store, lootMode)) - loot.AddItem(*item); + { + bool rate = store.IsRatesAllowed(); + + if (item->reference) // References processing + { + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(std::abs(item->reference))) + { + uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT)); + sScriptMgr->OnAfterRefCount(player, loot, rate, lootMode, const_cast(item), maxcount, store); + for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator + Referenced->Process(loot, store, lootMode, player, item->groupid); + } + } + else + { + // Plain entries (not a reference, not grouped) + sScriptMgr->OnBeforeDropAddItem(player, loot, rate, lootMode, const_cast(item), store); + loot.AddItem(*item); // Chance is already checked, just add + } + } } // Overall chance for the group without equal chanced items @@ -1348,24 +1442,32 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& /*store*/, Lo for (LootStoreItemList::const_iterator ieItr = ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr) { LootStoreItem* item = *ieItr; - if (item->reference > 0) + if (item->reference) { - if (!LootTemplates_Reference.GetLootFor(item->reference)) - LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid); + if (!LootTemplates_Reference.GetLootFor(std::abs(item->reference))) + { + LootTemplates_Reference.ReportNonExistingId(std::abs(item->reference), "Reference", item->itemid); + } else if (ref_set) - ref_set->erase(item->reference); + { + ref_set->erase(std::abs(item->reference)); + } } } for (LootStoreItemList::const_iterator ieItr = EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr) { LootStoreItem* item = *ieItr; - if (item->reference > 0) + if (item->reference) { - if (!LootTemplates_Reference.GetLootFor(item->reference)) - LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid); + if (!LootTemplates_Reference.GetLootFor(std::abs(item->reference))) + { + LootTemplates_Reference.ReportNonExistingId(std::abs(item->reference), "Reference", item->itemid); + } else if (ref_set) - ref_set->erase(item->reference); + { + ref_set->erase(std::abs(item->reference)); + } } } } @@ -1390,16 +1492,23 @@ LootTemplate::~LootTemplate() // Adds an entry to the group (at loading stage) void LootTemplate::AddEntry(LootStoreItem* item) { - if (item->groupid > 0 && item->reference == 0) // Group + // `item->reference` > 0 --> Reference is counted as a normal and non grouped entry + // `item->reference` < 0 --> Reference is counted as grouped entry within shared groupid + if (item->groupid > 0 && item->reference <= 0) // Group and grouped reference { if (item->groupid >= Groups.size()) - Groups.resize(item->groupid, nullptr); // Adds new group the the loot template if needed - if (!Groups[item->groupid - 1]) - Groups[item->groupid - 1] = new LootGroup(); + { + Groups.resize(item->groupid, nullptr); // Adds new group the the loot template if needed + } - Groups[item->groupid - 1]->AddEntry(item); // Adds new entry to the group + if (!Groups[item->groupid - 1]) + { + Groups[item->groupid - 1] = new LootGroup(); + } + + Groups[item->groupid - 1]->AddEntry(item); // Adds new entry to the group } - else // Non-grouped entries and references are stored together + else // Non-grouped entries Entries.push_back(item); } @@ -1420,7 +1529,7 @@ bool LootTemplate::CopyConditions(LootItem* li, uint32 conditionLootId) const LootStoreItem* item = *_iter; if (item->reference) { - if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference)) + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(std::abs(item->reference))) { if (Referenced->CopyConditions(li, conditionLootId)) { @@ -1457,7 +1566,7 @@ bool LootTemplate::CopyConditions(LootItem* li, uint32 conditionLootId) const LootStoreItem* item = *i; if (item->reference) { - if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference)) + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(std::abs(item->reference))) { if (Referenced->CopyConditions(li, conditionLootId)) { @@ -1488,7 +1597,7 @@ bool LootTemplate::CopyConditions(LootItem* li, uint32 conditionLootId) const LootStoreItem* item = *i; if (item->reference) { - if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference)) + if (LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(std::abs(item->reference))) { if (Referenced->CopyConditions(li, conditionLootId)) { @@ -1544,9 +1653,9 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, uint16 lootMode, if (!item->Roll(rate, player, loot, store)) continue; // Bad luck for the entry - if (item->reference > 0) // References processing + if (item->reference) // References processing { - LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(item->reference); + LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(std::abs(item->reference)); if (!Referenced) continue; // Error message already printed at loading stage @@ -1580,17 +1689,18 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con if (!Groups[groupId - 1]) return false; - return Groups[groupId - 1]->HasQuestDrop(); + return Groups[groupId - 1]->HasQuestDrop(store); } for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i) { LootStoreItem* item = *i; - if (item->reference > 0) // References + if (item->reference) // References { - LootTemplateMap::const_iterator Referenced = store.find(item->reference); + LootTemplateMap::const_iterator Referenced = store.find(std::abs(item->reference)); if (Referenced == store.end()) continue; // Error message [should be] already printed at loading stage + if (Referenced->second->HasQuestDrop(store, item->groupid)) return true; } @@ -1600,9 +1710,15 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con // Now processing groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i) + { if (LootGroup* group = *i) - if (group->HasQuestDrop()) + { + if (group->HasQuestDrop(store)) + { return true; + } + } + } return false; } @@ -1618,16 +1734,16 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co if (!Groups[groupId - 1]) return false; - return Groups[groupId - 1]->HasQuestDropForPlayer(player); + return Groups[groupId - 1]->HasQuestDropForPlayer(player, store); } // Checking non-grouped entries for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i) { LootStoreItem* item = *i; - if (item->reference > 0) // References processing + if (item->reference) // References processing { - LootTemplateMap::const_iterator Referenced = store.find(item->reference); + LootTemplateMap::const_iterator Referenced = store.find(std::abs(item->reference)); if (Referenced == store.end()) continue; // Error message already printed at loading stage if (Referenced->second->HasQuestDropForPlayer(store, player, item->groupid)) @@ -1639,9 +1755,15 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co // Now checking groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i) + { if (LootGroup* group = *i) - if (group->HasQuestDropForPlayer(player)) + { + if (group->HasQuestDropForPlayer(player, store)) + { return true; + } + } + } return false; } @@ -1662,12 +1784,16 @@ void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_se for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr) { LootStoreItem* item = *ieItr; - if (item->reference > 0) + if (item->reference) { - if (!LootTemplates_Reference.GetLootFor(item->reference)) - LootTemplates_Reference.ReportNonExistingId(item->reference, "Reference", item->itemid); + if (!LootTemplates_Reference.GetLootFor(std::abs(item->reference))) + { + LootTemplates_Reference.ReportNonExistingId(std::abs(item->reference), "Reference", item->itemid); + } else if (ref_set) - ref_set->erase(item->reference); + { + ref_set->erase(std::abs(item->reference)); + } } } @@ -1737,8 +1863,12 @@ bool LootTemplate::addConditionItem(Condition* cond) bool LootTemplate::isReference(uint32 id) const { for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr) - if ((*ieItr)->itemid == id && (*ieItr)->reference > 0) + { + if ((*ieItr)->itemid == id && (*ieItr)->reference) + { return true; + } + } return false;//not found or not reference } diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index afafae172..05ff318e0 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -117,7 +117,7 @@ struct Loot; struct LootStoreItem { uint32 itemid; // id of the item - uint32 reference; // referenced TemplateleId + int32 reference; // referenced TemplateleId float chance; // chance to drop for both quest and non-quest items, chance to be used for refs bool needs_quest : 1; // quest drop (quest is required for item to drop) uint16 lootmode; @@ -128,7 +128,7 @@ struct LootStoreItem // Constructor // displayid is filled in IsValid() which must be called after - LootStoreItem(uint32 _itemid, uint32 _reference, float _chance, bool _needs_quest, uint16 _lootmode, uint8 _groupid, int32 _mincount, uint8 _maxcount) + LootStoreItem(uint32 _itemid, int32 _reference, float _chance, bool _needs_quest, uint16 _lootmode, uint8 _groupid, int32 _mincount, uint8 _maxcount) : itemid(_itemid), reference(_reference), chance(_chance), needs_quest(_needs_quest), lootmode(_lootmode), groupid(_groupid), mincount(_mincount), maxcount(_maxcount) {}