fix(Core/Loot): properly handle referenced group loot templates (#7539)

This commit is contained in:
UltraNix
2021-09-09 11:48:17 +02:00
committed by GitHub
parent efdb64af07
commit a314d6091a
4 changed files with 211 additions and 62 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -51,20 +51,23 @@ struct LootGroupInvalidSelector : public Acore::unary_function<LootStoreItem*, b
if (!(item->lootmode & _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<LootItem>::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<LootItem>::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<LootStoreItem*>(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<LootStoreItem*>(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
}

View File

@@ -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)
{}