mirror of
https://github.com/NathanHandley/mod-ah-bot-plus.git
synced 2026-02-07 12:51:10 +00:00
Exposed additional config, added .conf.dist readme
This commit is contained in:
@@ -46,11 +46,6 @@
|
|||||||
# acore_characters.item_instance database table to grow over time.
|
# acore_characters.item_instance database table to grow over time.
|
||||||
# Default: false (disabled)
|
# Default: false (disabled)
|
||||||
#
|
#
|
||||||
# AuctionHouseBot.Seller.UseDBDropRates.Enabled
|
|
||||||
# Enable/Disable the Seller using items' in-game drop rates from enemies
|
|
||||||
# and gameobjects to determine probability of listing the items on the AH.
|
|
||||||
# Default: false (disabled)
|
|
||||||
#
|
|
||||||
# AuctionHouseBot.GUIDs
|
# AuctionHouseBot.GUIDs
|
||||||
# These are the character GUIDS (from characters->characters table) that
|
# These are the character GUIDS (from characters->characters table) that
|
||||||
# will be used to create auctions and otherwise interact with auctions.
|
# will be used to create auctions and otherwise interact with auctions.
|
||||||
@@ -81,12 +76,42 @@ AuctionHouseBot.MinutesBetweenBuyCycle = 1
|
|||||||
AuctionHouseBot.MinutesBetweenSellCycle = 1
|
AuctionHouseBot.MinutesBetweenSellCycle = 1
|
||||||
AuctionHouseBot.EnableSeller = false
|
AuctionHouseBot.EnableSeller = false
|
||||||
AuctionHouseBot.ReturnExpiredAuctionItemsToBot = false
|
AuctionHouseBot.ReturnExpiredAuctionItemsToBot = false
|
||||||
AuctionHouseBot.Seller.UseDBDropRates.Enabled = false
|
|
||||||
AuctionHouseBot.GUIDs = 0
|
AuctionHouseBot.GUIDs = 0
|
||||||
AuctionHouseBot.ItemsPerCycle = 150
|
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
|
||||||
|
|||||||
@@ -157,7 +157,10 @@ AuctionHouseBot::AuctionHouseBot() :
|
|||||||
ListedItemIDRestrictedEnabled(false),
|
ListedItemIDRestrictedEnabled(false),
|
||||||
ListedItemIDMin(0),
|
ListedItemIDMin(0),
|
||||||
ListedItemIDMax(200000),
|
ListedItemIDMax(200000),
|
||||||
SellerUseDBDropRates(false),
|
AdvancedListingRuleUseDropRatesEnabled(false),
|
||||||
|
AdvancedListingRuleUseDropRatesWeaponEnabled(true),
|
||||||
|
AdvancedListingRuleUseDropRatesArmorEnabled(true),
|
||||||
|
AdvancedListingRuleUseDropRatesRecipeEnabled(true),
|
||||||
LastBuyCycleCount(0),
|
LastBuyCycleCount(0),
|
||||||
LastSellCycleCount(0),
|
LastSellCycleCount(0),
|
||||||
ActiveListMultipleItemID(0),
|
ActiveListMultipleItemID(0),
|
||||||
@@ -1014,7 +1017,7 @@ void AuctionHouseBot::AddNewAuctions(std::vector<Player*> AHBPlayers, FactionSpe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If current item is crafted, ineligible, or quest reward, ignore drop rates and continue to list it
|
// If current item is crafted, ineligible, or quest reward, ignore drop rates and continue to list it
|
||||||
if (SellerUseDBDropRates &&
|
if (AdvancedListingRuleUseDropRatesEnabled &&
|
||||||
IsItemEligibleForDBDropRates(prototype) &&
|
IsItemEligibleForDBDropRates(prototype) &&
|
||||||
!IsItemCrafted(itemID) &&
|
!IsItemCrafted(itemID) &&
|
||||||
!IsItemQuestReward(itemID))
|
!IsItemQuestReward(itemID))
|
||||||
@@ -1117,6 +1120,28 @@ 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()
|
void AuctionHouseBot::PopulateItemDropChances()
|
||||||
{
|
{
|
||||||
// Search creature loot templates, referenced loot_loot_template, group_loot tables, and object_loot tables for items' drop rates
|
// Search creature loot templates, referenced loot_loot_template, group_loot tables, and object_loot tables for items' drop rates
|
||||||
@@ -1127,7 +1152,7 @@ void AuctionHouseBot::PopulateItemDropChances()
|
|||||||
FROM creature_template ct
|
FROM creature_template ct
|
||||||
JOIN creature_loot_template clt ON clt.Entry = ct.lootid
|
JOIN creature_loot_template clt ON clt.Entry = ct.lootid
|
||||||
JOIN item_template it ON it.entry = clt.Item
|
JOIN item_template it ON it.entry = clt.Item
|
||||||
WHERE clt.Reference = 0 AND clt.GroupId = 0 AND it.class IN (2,4,9) AND it.quality > 2
|
WHERE clt.Reference = 0 AND clt.GroupId = 0 AND it.class IN ({}) AND it.quality >= {}
|
||||||
)SQL";
|
)SQL";
|
||||||
|
|
||||||
std::string referenceDropString = R"SQL(
|
std::string referenceDropString = R"SQL(
|
||||||
@@ -1164,7 +1189,7 @@ void AuctionHouseBot::PopulateItemDropChances()
|
|||||||
END AS reference_chance
|
END AS reference_chance
|
||||||
FROM all_references ar
|
FROM all_references ar
|
||||||
JOIN item_template it ON it.entry = ar.itemID
|
JOIN item_template it ON it.entry = ar.itemID
|
||||||
WHERE it.class IN (2,4,9) AND it.quality > 2
|
WHERE it.class IN ({}) AND it.quality >= {}
|
||||||
)SQL";
|
)SQL";
|
||||||
|
|
||||||
// This will lookup items in referenced_loot_template whose Reference entry is not associated with a creature_loot_template
|
// This will lookup items in referenced_loot_template whose Reference entry is not associated with a creature_loot_template
|
||||||
@@ -1192,8 +1217,8 @@ void AuctionHouseBot::PopulateItemDropChances()
|
|||||||
(1.0 / rd.groupCount) * COALESCE(NULLIF(rd.referenceChance, 0), 1) AS reference_chance
|
(1.0 / rd.groupCount) * COALESCE(NULLIF(rd.referenceChance, 0), 1) AS reference_chance
|
||||||
FROM reference_data rd
|
FROM reference_data rd
|
||||||
JOIN item_template it ON it.entry = rd.itemID
|
JOIN item_template it ON it.entry = rd.itemID
|
||||||
WHERE it.class IN (2, 4, 9)
|
WHERE it.class IN ({})
|
||||||
AND it.quality > 2
|
AND it.quality >= {}
|
||||||
)SQL";
|
)SQL";
|
||||||
|
|
||||||
std::string groupDropString = R"SQL(
|
std::string groupDropString = R"SQL(
|
||||||
@@ -1213,7 +1238,7 @@ void AuctionHouseBot::PopulateItemDropChances()
|
|||||||
SELECT group_tables.*, COUNT(*) OVER (PARTITION BY loot_entry, group_id) AS item_count
|
SELECT group_tables.*, COUNT(*) OVER (PARTITION BY loot_entry, group_id) AS item_count
|
||||||
FROM group_tables
|
FROM group_tables
|
||||||
) compute_item_count
|
) compute_item_count
|
||||||
WHERE itemClass IN (2,4,9) AND itemQuality > 2
|
WHERE itemClass IN ({}) AND itemQuality >= {}
|
||||||
)SQL";
|
)SQL";
|
||||||
|
|
||||||
std::string objectsDropString = R"SQL(
|
std::string objectsDropString = R"SQL(
|
||||||
@@ -1222,24 +1247,33 @@ void AuctionHouseBot::PopulateItemDropChances()
|
|||||||
0 AS reference_chance
|
0 AS reference_chance
|
||||||
FROM item_loot_template ilt
|
FROM item_loot_template ilt
|
||||||
JOIN item_template it ON it.entry = ilt.Item
|
JOIN item_template it ON it.entry = ilt.Item
|
||||||
WHERE it.class IN (2,4,9) AND it.quality > 2 AND chance != 0
|
WHERE it.class IN ({}) AND it.quality >= {} AND chance != 0
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT it.entry AS itemID,
|
SELECT it.entry AS itemID,
|
||||||
golt.Chance AS direct_chance,
|
golt.Chance AS direct_chance,
|
||||||
0 AS reference_chance
|
0 AS reference_chance
|
||||||
FROM gameobject_loot_template golt
|
FROM gameobject_loot_template golt
|
||||||
JOIN item_template it ON it.entry = golt.Item
|
JOIN item_template it ON it.entry = golt.Item
|
||||||
WHERE it.class IN (2,4,9) AND it.quality > 2 AND chance != 0
|
WHERE it.class IN ({}) AND it.quality >= {} AND chance != 0
|
||||||
)SQL";
|
)SQL";
|
||||||
|
|
||||||
QueryResult directResult = WorldDatabase.Query(directDropString);
|
std::string enabledCategories = GetAdvancedListingRuleUseDropRatesEnabledCategoriesString();
|
||||||
QueryResult referenceResult = WorldDatabase.Query(referenceDropString);
|
if (enabledCategories.empty())
|
||||||
QueryResult danglingReferenceResult = WorldDatabase.Query(danglingReferenceDropString);
|
{
|
||||||
QueryResult groupResult = WorldDatabase.Query(groupDropString);
|
LOG_ERROR("module", "AuctionHouseBot: No categories are enabled for AuctionHouseBot.Seller.AdvancedListingRules.UseDropRates");
|
||||||
QueryResult objectsDropResult = WorldDatabase.Query(objectsDropString);
|
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)
|
if (!directResult || !referenceResult || !danglingReferenceResult || !groupResult || !objectsDropResult)
|
||||||
{
|
{
|
||||||
LOG_ERROR("module", "AuctionHouseBot: PopulateItemDropChances() failed!");
|
LOG_ERROR("module", "AuctionHouseBot: PopulateItemDropChances() failed to query items' drop rates.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1688,7 +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);
|
||||||
SellerUseDBDropRates = sConfigMgr->GetOption<bool>("AuctionHouseBot.Seller.UseDBDropRates.Enabled", false);
|
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);
|
||||||
@@ -2311,8 +2349,21 @@ bool AuctionHouseBot::IsItemCrafted(uint32 itemID)
|
|||||||
|
|
||||||
bool AuctionHouseBot::IsItemEligibleForDBDropRates(ItemTemplate const* proto)
|
bool AuctionHouseBot::IsItemEligibleForDBDropRates(ItemTemplate const* proto)
|
||||||
{
|
{
|
||||||
return (proto->Quality >= ITEM_QUALITY_RARE &&
|
if (!AdvancedListingRuleUseDropRatesEnabled)
|
||||||
(proto->Class == ITEM_CLASS_WEAPON ||
|
return false;
|
||||||
proto->Class == ITEM_CLASS_ARMOR ||
|
|
||||||
proto->Class == ITEM_CLASS_RECIPE));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,7 +280,11 @@ private:
|
|||||||
bool PreventOverpayingForVendorItems;
|
bool PreventOverpayingForVendorItems;
|
||||||
std::unordered_map<uint32, double> CachedItemDropRates;
|
std::unordered_map<uint32, double> CachedItemDropRates;
|
||||||
std::vector<uint32> ItemTiersByClassAndQuality[17][7][11]; // [Classes][Qualities][Tiers]
|
std::vector<uint32> ItemTiersByClassAndQuality[17][7][11]; // [Classes][Qualities][Tiers]
|
||||||
bool SellerUseDBDropRates;
|
bool AdvancedListingRuleUseDropRatesEnabled;
|
||||||
|
bool AdvancedListingRuleUseDropRatesWeaponEnabled;
|
||||||
|
bool AdvancedListingRuleUseDropRatesArmorEnabled;
|
||||||
|
bool AdvancedListingRuleUseDropRatesRecipeEnabled;
|
||||||
|
int AdvancedListingRuleUseDropRatesMinQuality;
|
||||||
std::unordered_set<uint32> QuestRewardItemIDs;
|
std::unordered_set<uint32> QuestRewardItemIDs;
|
||||||
|
|
||||||
FactionSpecificAuctionHouseConfig AllianceConfig;
|
FactionSpecificAuctionHouseConfig AllianceConfig;
|
||||||
@@ -319,6 +323,7 @@ public:
|
|||||||
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();
|
void PopulateItemDropChances();
|
||||||
|
std::string GetAdvancedListingRuleUseDropRatesEnabledCategoriesString();
|
||||||
void PopulateQuestRewardItemIDs();
|
void PopulateQuestRewardItemIDs();
|
||||||
bool IsItemQuestReward(uint32 itemID);
|
bool IsItemQuestReward(uint32 itemID);
|
||||||
bool IsItemCrafted(uint32 itemID);
|
bool IsItemCrafted(uint32 itemID);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ 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.Seller.UseDBDropRates.Enabled", true))
|
if (sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled", true))
|
||||||
{
|
{
|
||||||
auctionbot->PopulateQuestRewardItemIDs();
|
auctionbot->PopulateQuestRewardItemIDs();
|
||||||
auctionbot->PopulateItemDropChances();
|
auctionbot->PopulateItemDropChances();
|
||||||
@@ -38,7 +38,7 @@ 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.Seller.UseDBDropRates.Enabled", true))
|
if (sConfigMgr->GetOption<bool>("AuctionHouseBot.AdvancedListingRules.UseDropRates.Enabled", true))
|
||||||
{
|
{
|
||||||
auctionbot->PopulateQuestRewardItemIDs();
|
auctionbot->PopulateQuestRewardItemIDs();
|
||||||
auctionbot->PopulateItemDropChances();
|
auctionbot->PopulateItemDropChances();
|
||||||
|
|||||||
Reference in New Issue
Block a user