Merge pull request #34 from zeb139/use-db-drop-rates

WIP: Feature: Use DB drop rates to determine rare/epic weapons/armor/recipes likeliness in AH
This commit is contained in:
Nathan Handley
2025-10-11 15:54:41 -05:00
committed by GitHub
4 changed files with 458 additions and 18 deletions

View File

@@ -81,6 +81,37 @@ AuctionHouseBot.ItemsPerCycle = 150
AuctionHouseBot.ListingExpireTimeInSecondsMin = 900 AuctionHouseBot.ListingExpireTimeInSecondsMin = 900
AuctionHouseBot.ListingExpireTimeInSecondsMax = 86400 AuctionHouseBot.ListingExpireTimeInSecondsMax = 86400
###############################################################################
# AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled
# Enable/Disable the Seller using items' in-game drop rates from Enemies
# and GameObjects to determine the probability of listing them on the AH.
# Attempts to simulate "live" AHs by making very powerful items appear less often.
# This setting respects ListProportion config. It can also result in more duplicate
# items appearing on the AH due to higher-drop-rate items being selected more often.
# Crafted items may also appear more often since their drop rate is effectively 100%.
# Default: false (disabled)
#
# AuctionHouseBot.AdvancedListingRules.UseDropRates.MinQuality
# The minimum quality that should be included by AdvancedListingRules.UseDropRates.
# Value should be the integer corresponding to the desired minimum rarity.
# Examples: Setting to 3 will apply to Rare, Epic, Legendary, and Heirloom.
# Setting to 1 will apply to Common, Uncommon, Rare, Epic, Legendary, and Heirloom.
# (0) Poor, (1) Common, (2) Uncommon, (3) Rare, (4) Epic, (5) Legendary, (6) Heirloom
# Default: 3
#
# AuctionHouseBot.Seller.AdvancedListingRules.UseDropRates.<Category>
# Toggle AdvancedListingRules.UseDropRates behavior for individual category.
# Only applied if AdvancedListingRules.UseDropRates.Enabled is true.
# Default: true (enabled)
###############################################################################
AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled = false
AuctionHouseBot.AdvancedListingRules.UseDropRates.MinQuality = 2
AuctionHouseBot.AdvancedListingRules.UseDropRates.Weapon = true
AuctionHouseBot.AdvancedListingRules.UseDropRates.Armor = true
AuctionHouseBot.AdvancedListingRules.UseDropRates.Recipe = true
############################################################################### ###############################################################################
# AuctionHouseBot.MaxBuyoutPriceInCopper # AuctionHouseBot.MaxBuyoutPriceInCopper
# Maximum amount that a buyout on a listing can be in copper. Prevents # Maximum amount that a buyout on a listing can be in copper. Prevents

View File

@@ -25,7 +25,6 @@
#include "Config.h" #include "Config.h"
#include "Player.h" #include "Player.h"
#include "WorldSession.h" #include "WorldSession.h"
#include "GameTime.h"
#include "DatabaseEnv.h" #include "DatabaseEnv.h"
#include "ItemTemplate.h" #include "ItemTemplate.h"
#include "SharedDefines.h" #include "SharedDefines.h"
@@ -33,6 +32,7 @@
#include <cmath> #include <cmath>
#include <set> #include <set>
#include <unordered_map>
using namespace std; using namespace std;
@@ -157,6 +157,10 @@ AuctionHouseBot::AuctionHouseBot() :
ListedItemIDRestrictedEnabled(false), ListedItemIDRestrictedEnabled(false),
ListedItemIDMin(0), ListedItemIDMin(0),
ListedItemIDMax(200000), ListedItemIDMax(200000),
AdvancedListingRuleUseDropRatesEnabled(false),
AdvancedListingRuleUseDropRatesWeaponEnabled(true),
AdvancedListingRuleUseDropRatesArmorEnabled(true),
AdvancedListingRuleUseDropRatesRecipeEnabled(true),
LastBuyCycleCount(0), LastBuyCycleCount(0),
LastSellCycleCount(0), LastSellCycleCount(0),
ActiveListMultipleItemID(0), ActiveListMultipleItemID(0),
@@ -971,6 +975,7 @@ void AuctionHouseBot::AddNewAuctions(std::vector<Player*> AHBPlayers, FactionSpe
for (uint32 cnt = 1; cnt <= newItemsToListCount; cnt++) for (uint32 cnt = 1; cnt <= newItemsToListCount; cnt++)
{ {
auto trans = CharacterDatabase.BeginTransaction(); auto trans = CharacterDatabase.BeginTransaction();
ItemTemplate const* prototype = nullptr;
uint32 batchCount = 0; uint32 batchCount = 0;
while (batchCount < 500 && itemsGenerated < newItemsToListCount) while (batchCount < 500 && itemsGenerated < newItemsToListCount)
@@ -980,6 +985,15 @@ void AuctionHouseBot::AddNewAuctions(std::vector<Player*> AHBPlayers, FactionSpe
if (ActiveListMultipleItemID != 0) if (ActiveListMultipleItemID != 0)
{ {
itemID = ActiveListMultipleItemID; itemID = ActiveListMultipleItemID;
prototype = sObjectMgr->GetItemTemplate(itemID);
if (!prototype)
{
if (debug_Out)
LOG_ERROR("module", "AHSeller: prototype == NULL");
continue;
}
RemainingListMultipleCount--; RemainingListMultipleCount--;
if (RemainingListMultipleCount <= 0) if (RemainingListMultipleCount <= 0)
ActiveListMultipleItemID = 0; ActiveListMultipleItemID = 0;
@@ -987,7 +1001,59 @@ void AuctionHouseBot::AddNewAuctions(std::vector<Player*> AHBPlayers, FactionSpe
else else
{ {
itemID = GetRandomItemIDForListing(); itemID = GetRandomItemIDForListing();
if (itemID != 0 && ItemListProportionMultipliedItemIDs.find(itemID) != ItemListProportionMultipliedItemIDs.end() && if (itemID == 0)
{
if (debug_Out)
LOG_ERROR("module", "AHSeller: Item::CreateItem() failed as the ItemID is 0");
continue;
}
prototype = sObjectMgr->GetItemTemplate(itemID);
if (!prototype)
{
if (debug_Out)
LOG_ERROR("module", "AHSeller: prototype == NULL");
continue;
}
// If current item is crafted, ineligible, or quest reward, ignore drop rates and continue to list it
if (AdvancedListingRuleUseDropRatesEnabled &&
IsItemEligibleForDBDropRates(prototype) &&
!IsItemCrafted(itemID) &&
!IsItemQuestReward(itemID))
{
// The AHBot has chosen a rare/epic armor/weapon/recipe, so select another item
// of that type based on drop rates. This way ListProportions are respected.
// Roll for rarity tier
double r = 100.0 * (urand(0, INT32_MAX) / static_cast<double>(INT32_MAX));
int tier = GetItemDropChanceTier(r);
// If chosen tier is empty, search rarer tiers until not empty
auto& tierBuckets = ItemTiersByClassAndQuality[prototype->Class][prototype->Quality];
while (tierBuckets[tier].empty() && tier < 10) {
if (debug_Out)
LOG_INFO("module", "Bucket is empty for class {} quality {} tier {}", prototype->Class, prototype->Quality, tier);
tier++;
}
// Pull a random item from selected rarity tier
auto& bucket = tierBuckets[tier];
if (!bucket.empty())
{
itemID = bucket[rand() % bucket.size()];
prototype = sObjectMgr->GetItemTemplate(itemID);
if (!prototype)
{
if (debug_Out)
LOG_ERROR("module", "AHSeller: prototype == NULL");
continue;
}
}
else continue;
}
if (ItemListProportionMultipliedItemIDs.find(itemID) != ItemListProportionMultipliedItemIDs.end() &&
ItemListProportionMultipliedItemIDs[itemID] > 1) ItemListProportionMultipliedItemIDs[itemID] > 1)
{ {
ActiveListMultipleItemID = itemID; ActiveListMultipleItemID = itemID;
@@ -997,22 +1063,6 @@ void AuctionHouseBot::AddNewAuctions(std::vector<Player*> AHBPlayers, FactionSpe
} }
} }
// Prevent invalid IDs
if (itemID == 0)
{
if (debug_Out)
LOG_ERROR("module", "AHSeller: Item::CreateItem() failed as the ItemID is 0");
continue;
}
ItemTemplate const* prototype = sObjectMgr->GetItemTemplate(itemID);
if (prototype == NULL)
{
if (debug_Out)
LOG_ERROR("module", "AHSeller: prototype == NULL");
continue;
}
Player* AHBplayer = AHBPlayers[urand(0, AHBPlayers.size() - 1)]; Player* AHBplayer = AHBPlayers[urand(0, AHBPlayers.size() - 1)];
Item* item = Item::CreateItem(itemID, 1, AHBplayer); Item* item = Item::CreateItem(itemID, 1, AHBplayer);
@@ -1070,6 +1120,303 @@ void AuctionHouseBot::AddNewAuctions(std::vector<Player*> AHBPlayers, FactionSpe
LOG_INFO("module", "AHSeller: Added {} items", itemsGenerated); LOG_INFO("module", "AHSeller: Added {} items", itemsGenerated);
} }
std::string AuctionHouseBot::GetAdvancedListingRuleUseDropRatesEnabledCategoriesString()
{
std::vector<uint32> enabledCategories;
if (AdvancedListingRuleUseDropRatesWeaponEnabled)
enabledCategories.push_back(ITEM_CLASS_WEAPON);
if (AdvancedListingRuleUseDropRatesArmorEnabled)
enabledCategories.push_back(ITEM_CLASS_ARMOR);
if (AdvancedListingRuleUseDropRatesRecipeEnabled)
enabledCategories.push_back(ITEM_CLASS_RECIPE);
std::ostringstream oss;
for (size_t i = 0; i < enabledCategories.size(); ++i)
{
if (i > 0)
oss << ",";
oss << enabledCategories[i];
}
return oss.str();
}
void AuctionHouseBot::PopulateItemDropChances()
{
// Search creature loot templates, referenced loot_loot_template, group_loot tables, and object_loot tables for items' drop rates
std::string directDropString = R"SQL(
SELECT it.entry AS itemID,
clt.Chance AS direct_chance,
0 AS reference_chance
FROM creature_template ct
JOIN creature_loot_template clt ON clt.Entry = ct.lootid
JOIN item_template it ON it.entry = clt.Item
WHERE clt.Reference = 0 AND clt.GroupId = 0 AND it.class IN ({}) AND it.quality >= {}
)SQL";
std::string referenceDropString = R"SQL(
WITH reference_group_counts AS (
SELECT entry AS referenceID, COUNT(*) AS groupCount
FROM reference_loot_template
GROUP BY entry
),
all_references AS (
SELECT
rlt.Entry AS referenceID,
rlt.Item AS itemID,
rlt.Chance AS referenceChance,
rgc.groupCount,
MIN(clt.Chance) AS creatureChance,
CASE WHEN COUNT(clt.Entry) > 0 THEN 1 ELSE 0 END AS hasCreature
FROM reference_loot_template rlt
JOIN reference_group_counts rgc ON rlt.Entry = rgc.referenceID
LEFT JOIN creature_loot_template clt ON clt.Reference = rlt.Entry
WHERE clt.`Comment` NOT LIKE '%Placeholder%'
GROUP BY rlt.Entry, rlt.Item, rlt.Chance, rgc.groupCount
)
SELECT
ar.itemID AS itemID,
0 AS direct_chance,
CASE
WHEN ar.hasCreature = 1
THEN (1.0 / case when ar.groupCount < 6 then 6 ELSE ar.groupCount end) *
COALESCE(NULLIF(ar.referenceChance,0),1) *
COALESCE(NULLIF(ar.creatureChance,0),1)
ELSE
(1.0 / ar.groupCount) *
COALESCE(NULLIF(ar.referenceChance,0),1)
END AS reference_chance
FROM all_references ar
JOIN item_template it ON it.entry = ar.itemID
WHERE it.class IN ({}) AND it.quality >= {}
)SQL";
// This will lookup items in referenced_loot_template whose Reference entry is not associated with a creature_loot_template
std::string danglingReferenceDropString = R"SQL(
WITH reference_group_counts AS (
SELECT entry AS referenceID, COUNT(*) AS groupCount
FROM reference_loot_template
GROUP BY entry
),
creature_references AS (
SELECT DISTINCT Reference AS referenceID FROM creature_loot_template WHERE REFERENCE != 0
),
reference_data AS (
SELECT
rlt.Entry AS referenceID,
rlt.Item AS itemID,
rlt.Chance AS referenceChance,
rgc.groupCount
FROM reference_loot_template rlt
JOIN reference_group_counts rgc ON rlt.Entry = rgc.referenceID
)
SELECT
rd.itemID AS itemID,
0 AS direct_chance,
(1.0 / rd.groupCount) * COALESCE(NULLIF(rd.referenceChance, 0), 1) AS reference_chance
FROM reference_data rd
JOIN item_template it ON it.entry = rd.itemID
WHERE it.class IN ({})
AND it.quality >= {}
)SQL";
std::string groupDropString = R"SQL(
WITH group_tables AS (
SELECT clt.Entry AS loot_entry, clt.GroupId AS group_id, clt.Chance AS chance, clt.Item AS item_id, it.`name` AS itemName, it.class AS itemClass, it.Quality AS itemQuality
FROM creature_loot_template clt
JOIN item_template it ON clt.Item = it.entry
WHERE clt.groupid != 0 AND clt.REFERENCE = 0
)
SELECT item_id,
0 AS direct_chance,
CASE
WHEN chance = 0 THEN (1.0 / item_count) * (1 - POWER(1 - (1.0 / item_count), item_count)) * 100
WHEN chance != 0 THEN ((1.0 / item_count) * (1 - POWER(1 - (1.0 / item_count), item_count)) * 100) * chance/100
END AS reference_chance
FROM (
SELECT group_tables.*, COUNT(*) OVER (PARTITION BY loot_entry, group_id) AS item_count
FROM group_tables
) compute_item_count
WHERE itemClass IN ({}) AND itemQuality >= {}
)SQL";
std::string objectsDropString = R"SQL(
SELECT it.entry AS itemID,
ilt.Chance AS direct_chance,
0 AS reference_chance
FROM item_loot_template ilt
JOIN item_template it ON it.entry = ilt.Item
WHERE it.class IN ({}) AND it.quality >= {} AND chance != 0
UNION ALL
SELECT it.entry AS itemID,
golt.Chance AS direct_chance,
0 AS reference_chance
FROM gameobject_loot_template golt
JOIN item_template it ON it.entry = golt.Item
WHERE it.class IN ({}) AND it.quality >= {} AND chance != 0
)SQL";
std::string enabledCategories = GetAdvancedListingRuleUseDropRatesEnabledCategoriesString();
if (enabledCategories.empty())
{
LOG_ERROR("module", "AuctionHouseBot: No categories are enabled for AuctionHouseBot.Seller.AdvancedListingRules.UseDropRates");
return;
}
QueryResult directResult = WorldDatabase.Query(directDropString, enabledCategories, AdvancedListingRuleUseDropRatesMinQuality);
QueryResult referenceResult = WorldDatabase.Query(referenceDropString, enabledCategories, AdvancedListingRuleUseDropRatesMinQuality);
QueryResult danglingReferenceResult = WorldDatabase.Query(danglingReferenceDropString, enabledCategories, AdvancedListingRuleUseDropRatesMinQuality);
QueryResult groupResult = WorldDatabase.Query(groupDropString, enabledCategories, AdvancedListingRuleUseDropRatesMinQuality);
QueryResult objectsDropResult = WorldDatabase.Query(objectsDropString,
enabledCategories, AdvancedListingRuleUseDropRatesMinQuality,
enabledCategories, AdvancedListingRuleUseDropRatesMinQuality);
if (!directResult || !referenceResult || !danglingReferenceResult || !groupResult || !objectsDropResult)
{
LOG_ERROR("module", "AuctionHouseBot: PopulateItemDropChances() failed to query items' drop rates.");
return;
}
// Add drop rate of all results to CachedItemDropRates
auto parseResults = [this](QueryResult result)
{
do {
Field* fields = result->Fetch();
double directDropChance = 0.0;
double referenceDropChance = 0.0;
uint32 itemID = fields[0].Get<uint32>();
// Ignore quest rewards and crafted items, they have "100%" drop rate
if (IsItemQuestReward(itemID) || IsItemCrafted(itemID))
continue;
if (!fields[1].IsNull())
directDropChance = fields[1].Get<double>();
if (!fields[2].IsNull())
referenceDropChance = fields[2].Get<double>();
double higherDropChance = (directDropChance > referenceDropChance) ? directDropChance : referenceDropChance;
if (CachedItemDropRates[itemID] < higherDropChance)
CachedItemDropRates[itemID] = higherDropChance;
} while (result->NextRow());
};
parseResults(directResult);
parseResults(referenceResult);
parseResults(danglingReferenceResult);
parseResults(groupResult);
parseResults(objectsDropResult);
// Process item candidates: filter invalid entries and group by drop rate tier
for (auto& [classID, qualityGroups] : ItemCandidatesByItemClassAndQuality)
{
for (auto& [qualityID, candidates] : qualityGroups)
{
// Erase items that are not crafted, not quest rewards, and missing DB drop rate
candidates.erase(
std::remove_if(
candidates.begin(),
candidates.end(),
[&](uint32 id)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(id);
if (!IsItemEligibleForDBDropRates(proto))
return false;
bool shouldErase = !IsItemCrafted(id)
&& !IsItemQuestReward(id)
&& !CachedItemDropRates.contains(id);
return shouldErase;
}),
candidates.end()
);
// Now process the remaining valid items
for (uint32 id : candidates)
{
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(id);
if (!IsItemEligibleForDBDropRates(proto))
continue;
double rate = CachedItemDropRates[id];
int tier = GetItemDropChanceTier(rate);
ItemTiersByClassAndQuality[proto->Class][proto->Quality][tier].push_back(id);
}
}
}
if (debug_Out)
{
// Show number of items in each tier
for (int i = 0; i < 17; i++)
for (int j = 0; j < 7; j++)
for (int k = 0; k < 10; k++)
{
if (i == 2)
LOG_INFO("module", "Armor Count: Rarity {} Tier {} has {} items", j, k, ItemTiersByClassAndQuality[i][j][k].size());
if (i == 4)
LOG_INFO("module", "Weapon Count: Rarity {} Tier {} has {} items", j, k, ItemTiersByClassAndQuality[i][j][k].size());
if (i == 9)
LOG_INFO("module", "Recipe Count: Rarity {} Tier {} has {} items", j, k, ItemTiersByClassAndQuality[i][j][k].size());
}
}
}
void AuctionHouseBot::PopulateQuestRewardItemIDs()
{
string questRewardsString = R"SQL(
SELECT DISTINCT item_id
FROM (
SELECT RewardItem1 AS item_id FROM quest_template UNION ALL
SELECT RewardItem2 AS item_id FROM quest_template UNION ALL
SELECT RewardItem3 AS item_id FROM quest_template UNION ALL
SELECT RewardItem4 AS item_id FROM quest_template UNION ALL
SELECT ItemDrop1 AS item_id FROM quest_template UNION ALL
SELECT ItemDrop2 AS item_id FROM quest_template UNION ALL
SELECT ItemDrop3 AS item_id FROM quest_template UNION ALL
SELECT ItemDrop4 AS item_id FROM quest_template UNION ALL
SELECT RewardChoiceItemID1 AS item_id FROM quest_template UNION ALL
SELECT RewardChoiceItemID2 AS item_id FROM quest_template UNION ALL
SELECT RewardChoiceItemID3 AS item_id FROM quest_template UNION ALL
SELECT RewardChoiceItemID4 AS item_id FROM quest_template UNION ALL
SELECT RewardChoiceItemID5 AS item_id FROM quest_template UNION ALL
SELECT RewardChoiceItemID6 AS item_id FROM quest_template
) AS quest_rewards
WHERE item_id != 0
)SQL";
QueryResult questRewardsResult = WorldDatabase.Query(questRewardsString);
if (!questRewardsResult)
{
LOG_ERROR("module", "AuctionHouseBot: Quest Rewards lookup failed.");
return;
}
do
{
uint32 id = questRewardsResult->Fetch()->Get<uint32>();
QuestRewardItemIDs.insert(id);
} while (questRewardsResult->NextRow());
}
int AuctionHouseBot::GetItemDropChanceTier(double dropRate)
{
if (dropRate > 10) return 0;
else if (dropRate > 5) return 1;
else if (dropRate > 2) return 2;
else if (dropRate > 1) return 3;
else if (dropRate > 0.5) return 4;
else if (dropRate > 0.2) return 5;
else if (dropRate > 0.1) return 6;
else if (dropRate > 0.05) return 7;
else if (dropRate > 0.02) return 8;
else if (dropRate > 0.01) return 9;
else return 10;
}
void AuctionHouseBot::AddNewAuctionBuyerBotBid(std::vector<Player*> AHBPlayers, FactionSpecificAuctionHouseConfig *config) void AuctionHouseBot::AddNewAuctionBuyerBotBid(std::vector<Player*> AHBPlayers, FactionSpecificAuctionHouseConfig *config)
{ {
if (!BuyingBotEnabled) if (!BuyingBotEnabled)
@@ -1375,6 +1722,11 @@ void AuctionHouseBot::InitializeConfiguration()
SetCyclesBetweenBuyOrSell(); SetCyclesBetweenBuyOrSell();
ReturnExpiredAuctionItemsToBot = sConfigMgr->GetOption<bool>("AuctionHouseBot.ReturnExpiredAuctionItemsToBot", false); ReturnExpiredAuctionItemsToBot = sConfigMgr->GetOption<bool>("AuctionHouseBot.ReturnExpiredAuctionItemsToBot", false);
ItemsPerCycle = sConfigMgr->GetOption<uint32>("AuctionHouseBot.ItemsPerCycle", 75); ItemsPerCycle = sConfigMgr->GetOption<uint32>("AuctionHouseBot.ItemsPerCycle", 75);
AdvancedListingRuleUseDropRatesEnabled = sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled", false);
AdvancedListingRuleUseDropRatesWeaponEnabled = sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Weapon", true);
AdvancedListingRuleUseDropRatesArmorEnabled = sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Armor", true);
AdvancedListingRuleUseDropRatesRecipeEnabled = sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Recipe", true);
AdvancedListingRuleUseDropRatesMinQuality = sConfigMgr->GetOption<int>("AuctionHouseBot.AdvancedListingRules.UseDropRates.MinQuality", 3);
MaxBuyoutPriceInCopper = sConfigMgr->GetOption<uint32>("AuctionHouseBot.MaxBuyoutPriceInCopper", 1000000000); MaxBuyoutPriceInCopper = sConfigMgr->GetOption<uint32>("AuctionHouseBot.MaxBuyoutPriceInCopper", 1000000000);
BuyoutVariationReducePercent = sConfigMgr->GetOption<float>("AuctionHouseBot.BuyoutVariationReducePercent", 0.15f); BuyoutVariationReducePercent = sConfigMgr->GetOption<float>("AuctionHouseBot.BuyoutVariationReducePercent", 0.15f);
BuyoutVariationAddPercent = sConfigMgr->GetOption<float>("AuctionHouseBot.BuyoutVariationAddPercent", 0.25f); BuyoutVariationAddPercent = sConfigMgr->GetOption<float>("AuctionHouseBot.BuyoutVariationAddPercent", 0.25f);
@@ -1984,3 +2336,34 @@ void AuctionHouseBot::CleanupExpiredAuctionItems()
CharacterDatabase.CommitTransaction(trans); CharacterDatabase.CommitTransaction(trans);
} }
bool AuctionHouseBot::IsItemQuestReward(uint32 itemID)
{
return (QuestRewardItemIDs.find(itemID) != QuestRewardItemIDs.end());
}
bool AuctionHouseBot::IsItemCrafted(uint32 itemID)
{
return (ItemIDsProducedByRecipes.find(itemID) != ItemIDsProducedByRecipes.end());
}
bool AuctionHouseBot::IsItemEligibleForDBDropRates(ItemTemplate const* proto)
{
if (!AdvancedListingRuleUseDropRatesEnabled)
return false;
if (!proto || proto->Quality < AdvancedListingRuleUseDropRatesMinQuality)
return false;
switch (proto->Class)
{
case ITEM_CLASS_WEAPON:
return AdvancedListingRuleUseDropRatesWeaponEnabled;
case ITEM_CLASS_ARMOR:
return AdvancedListingRuleUseDropRatesArmorEnabled;
case ITEM_CLASS_RECIPE:
return AdvancedListingRuleUseDropRatesRecipeEnabled;
default:
return false;
}
}

View File

@@ -278,6 +278,14 @@ private:
uint32 ListedItemIDMax; uint32 ListedItemIDMax;
std::set<uint32> ListedItemIDExceptionItems; std::set<uint32> ListedItemIDExceptionItems;
bool PreventOverpayingForVendorItems; bool PreventOverpayingForVendorItems;
std::unordered_map<uint32, double> CachedItemDropRates;
std::vector<uint32> ItemTiersByClassAndQuality[17][7][11]; // [Classes][Qualities][Tiers]
bool AdvancedListingRuleUseDropRatesEnabled;
bool AdvancedListingRuleUseDropRatesWeaponEnabled;
bool AdvancedListingRuleUseDropRatesArmorEnabled;
bool AdvancedListingRuleUseDropRatesRecipeEnabled;
int AdvancedListingRuleUseDropRatesMinQuality;
std::unordered_set<uint32> QuestRewardItemIDs;
FactionSpecificAuctionHouseConfig AllianceConfig; FactionSpecificAuctionHouseConfig AllianceConfig;
FactionSpecificAuctionHouseConfig HordeConfig; FactionSpecificAuctionHouseConfig HordeConfig;
@@ -314,6 +322,13 @@ public:
const char* GetCategoryName(ItemClass category); const char* GetCategoryName(ItemClass category);
uint32 GetStackSizeForItem(ItemTemplate const* itemProto) const; uint32 GetStackSizeForItem(ItemTemplate const* itemProto) const;
void CalculateItemValue(ItemTemplate const* itemProto, uint64& outBidPrice, uint64& outBuyoutPrice); void CalculateItemValue(ItemTemplate const* itemProto, uint64& outBidPrice, uint64& outBuyoutPrice);
void PopulateItemDropChances();
std::string GetAdvancedListingRuleUseDropRatesEnabledCategoriesString();
void PopulateQuestRewardItemIDs();
bool IsItemQuestReward(uint32 itemID);
bool IsItemCrafted(uint32 itemID);
bool IsItemEligibleForDBDropRates(ItemTemplate const* proto);
int GetItemDropChanceTier(double dropRate);
float GetAdvancedPricingMultiplier(ItemTemplate const* itemProto); float GetAdvancedPricingMultiplier(ItemTemplate const* itemProto);
ItemTemplate const* GetProducedItemFromRecipe(ItemTemplate const* recipeItemTemplate); ItemTemplate const* GetProducedItemFromRecipe(ItemTemplate const* recipeItemTemplate);
std::unordered_set<uint32> GetItemIDsProducedByRecipes(); std::unordered_set<uint32> GetItemIDsProducedByRecipes();

View File

@@ -25,6 +25,12 @@ public:
{ {
LOG_INFO("server.loading", "AuctionHouseBot: (Re)populating item candidate lists ..."); LOG_INFO("server.loading", "AuctionHouseBot: (Re)populating item candidate lists ...");
auctionbot->PopulateItemCandidatesAndProportions(); auctionbot->PopulateItemCandidatesAndProportions();
if (sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled", true))
{
auctionbot->PopulateQuestRewardItemIDs();
auctionbot->PopulateItemDropChances();
}
} }
} }
@@ -32,6 +38,11 @@ public:
{ {
LOG_INFO("server.loading", "AuctionHouseBot: (Re)populating item candidate lists ..."); LOG_INFO("server.loading", "AuctionHouseBot: (Re)populating item candidate lists ...");
auctionbot->PopulateItemCandidatesAndProportions(); auctionbot->PopulateItemCandidatesAndProportions();
if (sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled", true))
{
auctionbot->PopulateQuestRewardItemIDs();
auctionbot->PopulateItemDropChances();
}
HasPerformedStartup = true; HasPerformedStartup = true;
} }
}; };