diff --git a/conf/mod_ahbot.conf.dist b/conf/mod_ahbot.conf.dist index f79faca..3df71be 100644 --- a/conf/mod_ahbot.conf.dist +++ b/conf/mod_ahbot.conf.dist @@ -85,6 +85,15 @@ # If set to zero then no limits are set in place. # Default 0 # +# AuctionHouseBot.RestockBatchSize +# The maximum amount of stacks of the same item the seller queues in a single +# cycle while restocking it up to its target count. The target count is taken +# from the "mod_auctionhousebot_countOverride" table, or from +# AuctionHouseBot.DuplicatesCount when the item has no row there. +# Without this, a sold out item comes back one stack per cycle only. +# Set to 1 to restore that behaviour. +# Default 5 +# # AuctionHouseBot.DivisibleStacks # Sell items in stack sizes which depends on the maximum amount per stack. # For example, an item with max stack size 20 will be sold in 5, 10 15 and 20 stacks, and not at random. @@ -121,6 +130,7 @@ AuctionHouseBot.ItemsPerCycle = 200 AuctionHouseBot.ConsiderOnlyBotAuctions = 0 AuctionHouseBot.DuplicatesCount = 0 +AuctionHouseBot.RestockBatchSize = 5 AuctionHouseBot.DivisibleStacks = 0 AuctionHouseBot.ElapsingTimeClass = 1 AuctionHouseBot.MinItems = 10000 diff --git a/src/AuctionHouseBot.cpp b/src/AuctionHouseBot.cpp index 4f729ca..602346c 100644 --- a/src/AuctionHouseBot.cpp +++ b/src/AuctionHouseBot.cpp @@ -821,7 +821,7 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config) std::vector itemCounts(14, 0); // Get prioritized item IDs - std::vector itemsToSell = GetItemsToSell(config, AHBplayer->GetGUID(), itemsInAH); + std::vector itemsToSell = GetItemsToSell(config, AHBplayer->GetGUID(), itemsInAH, botItemCounts); // Loop variables uint32 nbSold = 0; // Tracing counter @@ -1247,7 +1247,7 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config) // Get Prioritized ItemIDs // ============================================================================= -std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGuid /* botGuid */, const std::unordered_set& itemsInAH) +std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGuid /* botGuid */, const std::unordered_set& itemsInAH, const std::unordered_map& botItemCounts) { //std::vector prioritizedItemIDs; std::vector allItemIDs; @@ -1286,17 +1286,74 @@ std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGui std::vector yellowItemsBin(config->YellowItemsBin.begin(), config->YellowItemsBin.end()); - // 1. Items with price overrides that are not listed by the bot yet (randomized order) + // How many stacks of an item are missing from its target count. The target is + // the per-item countOverride, else the global DuplicatesCount, else 1 stack - + // the same precedence the listing cap in Sell() applies. Capped by + // RestockBatchSize so a single item cannot eat the whole ItemsPerCycle budget. + auto missingStacks = [&](uint32 itemID) -> uint32 + { + uint32 target = config->GetCountOverrideForItem(itemID); + + if (target == 0) + { + target = config->DuplicatesCount; + } + + if (target == 0) + { + target = 1; + } + + auto listed = botItemCounts.find(itemID); + uint32 current = (listed != botItemCounts.end()) ? listed->second : 0; + + if (current >= target) + { + return 0; + } + + uint32 batch = config->RestockBatchSize > 0 ? config->RestockBatchSize : 1; + + return std::min(target - current, batch); + }; + + // Queue every stack an item is short of, keeping the copies adjacent so a + // sold out item is restocked as a batch instead of one stack per cycle. + auto appendMissingStacks = [&](const std::vector& items) + { + for (auto const& itemID : items) + { + allItemIDs.insert(allItemIDs.end(), missingStacks(itemID), itemID); + } + }; + + // 1. Items with price overrides below their target count (randomized order), + // sold out ones first, then the ones still partially stocked std::vector itemsWithOverridesNotListed; + std::vector itemsWithOverridesBelowTarget; + for (const auto& [itemID, _] : config->itemPriceOverrides) { + if (missingStacks(itemID) == 0) + { + continue; + } + if (itemsInAH.find(itemID) == itemsInAH.end()) { itemsWithOverridesNotListed.push_back(itemID); } + else + { + itemsWithOverridesBelowTarget.push_back(itemID); + } } + std::shuffle(itemsWithOverridesNotListed.begin(), itemsWithOverridesNotListed.end(), std::mt19937(std::random_device()())); - allItemIDs.insert(allItemIDs.end(), itemsWithOverridesNotListed.begin(), itemsWithOverridesNotListed.end()); + std::shuffle(itemsWithOverridesBelowTarget.begin(), itemsWithOverridesBelowTarget.end(), std::mt19937(std::random_device()())); + + appendMissingStacks(itemsWithOverridesNotListed); + appendMissingStacks(itemsWithOverridesBelowTarget); // 2. Items for which price overrides do exist (randomized order) std::vector itemsWithOverrides; @@ -1339,6 +1396,7 @@ std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGui // Log the number of items to sell if(config->TraceSeller) { + LOG_INFO("module", "AHBot [{}]: restocking {} sold out and {} understocked price override items", _id, itemsWithOverridesNotListed.size(), itemsWithOverridesBelowTarget.size()); LOG_INFO("module", "AHBot [{}]: GetItemsToSell returning {} items", _id, allItemIDs.size()); } diff --git a/src/AuctionHouseBot.h b/src/AuctionHouseBot.h index 3dec118..0364762 100644 --- a/src/AuctionHouseBot.h +++ b/src/AuctionHouseBot.h @@ -102,7 +102,7 @@ class AuctionHouseBot ObjectGuid::LowType GetAHBplayerGUID() { return _id; }; - std::vector GetItemsToSell(AHBConfig* config, ObjectGuid botGuid, const std::unordered_set& itemsInAH); + std::vector GetItemsToSell(AHBConfig* config, ObjectGuid botGuid, const std::unordered_set& itemsInAH, const std::unordered_map& botItemCounts); bool IsItemListedByBot(uint32 itemID, uint32 ahID, ObjectGuid botGuid); bool IsItemInAuctionHouse(uint32 itemID, uint32 ahID); std::vector GetAllItemIDs(uint32 ahID); diff --git a/src/AuctionHouseBotConfig.cpp b/src/AuctionHouseBotConfig.cpp index 480f8cc..e70c97b 100644 --- a/src/AuctionHouseBotConfig.cpp +++ b/src/AuctionHouseBotConfig.cpp @@ -222,6 +222,7 @@ AHBConfig::AHBConfig(uint32 ahid, AHBConfig* conf) Bind_When_Use = conf->Bind_When_Use; Bind_Quest_Item = conf->Bind_Quest_Item; DuplicatesCount = conf->DuplicatesCount; + RestockBatchSize = conf->RestockBatchSize; ElapsingTimeClass = conf->ElapsingTimeClass; DivisibleStacks = conf->DivisibleStacks; DisablePermEnchant = conf->DisablePermEnchant; @@ -532,6 +533,7 @@ void AHBConfig::Reset() Bind_When_Use = false; Bind_Quest_Item = false; DuplicatesCount = 0; + RestockBatchSize = 5; ElapsingTimeClass = 1; DivisibleStacks = false; @@ -2069,6 +2071,7 @@ void AHBConfig::InitializeFromFile() SellAtMarketPrice = sConfigMgr->GetOption ("AuctionHouseBot.UseMarketPriceForSeller", false); MarketResetThreshold = sConfigMgr->GetOption("AuctionHouseBot.MarketResetThreshold" , 25); DuplicatesCount = sConfigMgr->GetOption("AuctionHouseBot.DuplicatesCount" , 0); + RestockBatchSize = sConfigMgr->GetOption("AuctionHouseBot.RestockBatchSize" , 5); DivisibleStacks = sConfigMgr->GetOption ("AuctionHouseBot.DivisibleStacks" , false); ElapsingTimeClass = sConfigMgr->GetOption("AuctionHouseBot.ElapsingTimeClass" , 1); ConsiderOnlyBotAuctions = sConfigMgr->GetOption ("AuctionHouseBot.ConsiderOnlyBotAuctions", false); diff --git a/src/AuctionHouseBotConfig.h b/src/AuctionHouseBotConfig.h index de55433..ff97546 100644 --- a/src/AuctionHouseBotConfig.h +++ b/src/AuctionHouseBotConfig.h @@ -203,6 +203,7 @@ class AHBConfig bool Bind_Quest_Item; uint32 DuplicatesCount; + uint32 RestockBatchSize; uint32 ElapsingTimeClass; bool DivisibleStacks;