From 29ae6b0ff2f2748a30564cbc3bbec90e486db057 Mon Sep 17 00:00:00 2001 From: Kewin Rausch Date: Sat, 14 Sep 2024 11:18:34 +0200 Subject: [PATCH] Change of the selling priority, following item quality instead of a random pick (#104) * Initialization fixed to be performed at script startup; included copyright in the new source files * Handle of the configuration reloading in the correct way from the command line (cleanup and initialization of new elements) * Improved the help text and descriptions * Introduced a mechanism that allows the bot to adapt to the market prices (right now is a plain average which resets to allow quick convergence with the markets sentiments) * Threshold for market per-item price reset now implemented as a configurable value * Adjusted the license on the source files * Change in item selection for the selling operations; instead of using a random pickup, which can always result in bad rolls and waste of cycles, the selling priority now follows the items rarity. First the bot will try to fill up poor items, and then when quotas are reached it will move to more rare ones --- src/AuctionHouseBot.cpp | 674 +++++++++++++++++----------------------- src/AuctionHouseBot.h | 7 +- 2 files changed, 284 insertions(+), 397 deletions(-) diff --git a/src/AuctionHouseBot.cpp b/src/AuctionHouseBot.cpp index dc14815..e74d674 100644 --- a/src/AuctionHouseBot.cpp +++ b/src/AuctionHouseBot.cpp @@ -49,11 +49,34 @@ AuctionHouseBot::~AuctionHouseBot() // Nothing } -uint32 AuctionHouseBot::getElement(std::set set, int index) +uint32 AuctionHouseBot::getElement(std::set set, int index, uint32 botId, uint32 maxDup, AuctionHouseObject* auctionHouse) { std::set::iterator it = set.begin(); std::advance(it, index); + if (maxDup > 0) + { + uint32 noStacks = 0; + + for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin(); itr != auctionHouse->GetAuctionsEnd(); ++itr) + { + AuctionEntry* Aentry = itr->second; + + if (Aentry->owner.GetCounter() == botId) + { + if (*it == Aentry->item_template) + { + noStacks++; + } + } + } + + if (noStacks >= maxDup) + { + return 0; + } + } + return *it; } @@ -623,501 +646,366 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config) uint32 yellowItems = config->GetItemCounts(AHB_YELLOW_I); // - // Only insert a few at a time, so they dont appear all at once + // Loop variables // - uint32 noSold = 0; // Tracing counter - uint32 binEmpty = 0; // Tracing counter - uint32 noNeed = 0; // Tracing counter - uint32 tooMany = 0; // Tracing counter - uint32 loopBrk = 0; // Tracing counter - uint32 err = 0; // Tracing counter + uint32 noSold = 0; // Tracing counter + uint32 binEmpty = 0; // Tracing counter + uint32 noNeed = 0; // Tracing counter + uint32 tooMany = 0; // Tracing counter + uint32 loopBrk = 0; // Tracing counter + uint32 err = 0; // Tracing counter for (uint32 cnt = 1; cnt <= items; cnt++) { + uint32 choice = 0; uint32 itemID = 0; uint32 loopbreaker = 0; - // - // Attempts for some times to insert a single item stack as an auction. - // The attempt can be stopped by several internal checks. + // + // Select, in rarity order, a new random item // - while (itemID == 0 && loopbreaker <= 32) + while (itemID == 0 && loopbreaker <= AUCTION_HOUSE_BOT_LOOP_BREAKER) { - ++loopbreaker; + loopbreaker++; - // - // Get a random item from the bins - // + // Poor - uint32 choice = urand(0, 13); - - switch (choice) + if ((config->GreyItemsBin.size() > 0) && (greyItems < greyIcount)) { - case AHB_GREY_I: - if ((config->GreyItemsBin.size() > 0) && (greyItems < greyIcount)) - { - itemID = getElement(config->GreyItemsBin, urand(0, config->GreyItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_WHITE_I: - if ((config->WhiteItemsBin.size() > 0) && (whiteItems < whiteIcount)) - { - itemID = getElement(config->WhiteItemsBin, urand(0, config->WhiteItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_GREEN_I: - if ((config->GreenItemsBin.size() > 0) && (greenItems < greenIcount)) - { - itemID = getElement(config->GreenItemsBin, urand(0, config->GreenItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_BLUE_I: - if ((config->BlueItemsBin.size() > 0) && (blueItems < blueIcount)) - { - itemID = getElement(config->BlueItemsBin, urand(0, config->BlueItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_PURPLE_I: - if ((config->PurpleItemsBin.size() > 0) && (purpleItems < purpleIcount)) - { - itemID = getElement(config->PurpleItemsBin, urand(0, config->PurpleItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_ORANGE_I: - if ((config->OrangeItemsBin.size() > 0) && (orangeItems < orangeIcount)) - { - itemID = getElement(config->OrangeItemsBin, urand(0, config->OrangeItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_YELLOW_I: - if ((config->YellowItemsBin.size() > 0) && (yellowItems < yellowIcount)) - { - itemID = getElement(config->YellowItemsBin, urand(0, config->YellowItemsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_GREY_TG: - if ((config->GreyTradeGoodsBin.size() > 0) && (greyTGoods < greyTGcount)) - { - itemID = getElement(config->GreyTradeGoodsBin, urand(0, config->GreyTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_WHITE_TG: - if ((config->WhiteTradeGoodsBin.size() > 0) && (whiteTGoods < whiteTGcount)) - { - itemID = getElement(config->WhiteTradeGoodsBin, urand(0, config->WhiteTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; - - case AHB_GREEN_TG: - if ((config->GreenTradeGoodsBin.size() > 0) && (greenTGoods < greenTGcount)) - { - itemID = getElement(config->GreenTradeGoodsBin, urand(0, config->GreenTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; + choice = 0; + itemID = getElement(config->GreyItemsBin, urand(0, config->GreyItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - case AHB_BLUE_TG: - if ((config->BlueTradeGoodsBin.size() > 0) && (blueTGoods < blueTGcount)) - { - itemID = getElement(config->BlueTradeGoodsBin, urand(0, config->BlueTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } + if (itemID == 0 && (config->GreyTradeGoodsBin.size() > 0) && (greyTGoods < greyTGcount)) + { + choice = 7; + itemID = getElement(config->GreyTradeGoodsBin, urand(0, config->GreyTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - break; + // Normal - case AHB_PURPLE_TG: - if ((config->PurpleTradeGoodsBin.size() > 0) && (purpleTGoods < purpleTGcount)) - { - itemID = getElement(config->PurpleTradeGoodsBin, urand(0, config->PurpleTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } - - break; + if (itemID == 0 && (config->WhiteItemsBin.size() > 0) && (whiteItems < whiteIcount)) + { + choice = 1; + itemID = getElement(config->WhiteItemsBin, urand(0, config->WhiteItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - case AHB_ORANGE_TG: - if ((config->OrangeTradeGoodsBin.size() > 0) && (orangeTGoods < orangeTGcount)) - { - itemID = getElement(config->OrangeTradeGoodsBin, urand(0, config->OrangeTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } + if (itemID == 0 && (config->WhiteTradeGoodsBin.size() > 0) && (whiteTGoods < whiteTGcount)) + { + choice = 8; + itemID = getElement(config->WhiteTradeGoodsBin, urand(0, config->WhiteTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - break; + // Uncommon - case AHB_YELLOW_TG: - if ((config->YellowTradeGoodsBin.size() > 0) && (yellowTGoods < yellowTGcount)) - { - itemID = getElement(config->YellowTradeGoodsBin, urand(0, config->YellowTradeGoodsBin.size() - 1)); - } - else - { - noNeed++; - continue; - } + if (itemID == 0 && (config->GreenItemsBin.size() > 0) && (greenItems < greenIcount)) + { + choice = 2; + itemID = getElement(config->GreenItemsBin, urand(0, config->GreenItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - break; + if (itemID == 0 && (config->GreenTradeGoodsBin.size() > 0) && (greenTGoods < greenTGcount)) + { + choice = 9; + itemID = getElement(config->GreenTradeGoodsBin, urand(0, config->GreenTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - default: - err++; + // Rare - if (config->DebugOutSeller) - { - LOG_ERROR("module", "AHBot [{}]: itemID Switch - Default Reached", _id); - } + if (itemID == 0 && (config->BlueItemsBin.size() > 0) && (blueItems < blueIcount)) + { + choice = 3; + itemID = getElement(config->BlueItemsBin, urand(0, config->BlueItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - break; + if (itemID == 0 && (config->BlueTradeGoodsBin.size() > 0) && (blueTGoods < blueTGcount)) + { + choice = 10; + itemID = getElement(config->BlueTradeGoodsBin, urand(0, config->BlueTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); } - // - // Check if it's a valid selection - // + // Epic - if (itemID == 0) + if (itemID == 0 && (config->PurpleItemsBin.size() > 0) && (purpleItems < purpleIcount)) { - binEmpty++; - - if (config->DebugOutSeller) - { - LOG_ERROR("module", "AHBot [{}]: No item could be selected in the bin {}", _id, choice); - } + choice = 4; + itemID = getElement(config->PurpleItemsBin, urand(0, config->PurpleItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - continue; + if (itemID == 0 && (config->PurpleTradeGoodsBin.size() > 0) && (purpleTGoods < purpleTGcount)) + { + choice = 11; + itemID = getElement(config->PurpleTradeGoodsBin, urand(0, config->PurpleTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); } - // - // Check how many stacks of this items are sold by the bot. - // This avoid unfortunate rolls to overwhelm the market with the same products on low population, especially with whitelists. - // + // Legendary - if (config->DuplicatesCount > 0) + if (itemID == 0 && (config->OrangeItemsBin.size() > 0) && (orangeItems < orangeIcount)) { - uint32 noStacks = 0; - - for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin(); itr != auctionHouse->GetAuctionsEnd(); ++itr) - { - AuctionEntry* Aentry = itr->second; - - if (AHBplayer->GetGUID() == Aentry->owner) - { - if (itemID == Aentry->item_template) - { - noStacks++; - } - } - } + choice = 5; + itemID = getElement(config->OrangeItemsBin, urand(0, config->OrangeItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - if (noStacks >= config->DuplicatesCount) - { - tooMany++; - continue; - } + if (itemID == 0 && (config->OrangeTradeGoodsBin.size() > 0) && (orangeTGoods < orangeTGcount)) + { + choice = 12; + itemID = getElement(config->OrangeTradeGoodsBin, urand(0, config->OrangeTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); } - // - // Retrieve information about the selected item - // + // Artifact - ItemTemplate const* prototype = sObjectMgr->GetItemTemplate(itemID); + if (itemID == 0 && (config->YellowItemsBin.size() > 0) && (yellowItems < yellowIcount)) + { + choice = 6; + itemID = getElement(config->YellowItemsBin, urand(0, config->YellowItemsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } - if (prototype == NULL) + if (itemID == 0 && (config->YellowTradeGoodsBin.size() > 0) && (yellowTGoods < yellowTGcount)) { - err++; + choice = 13; + itemID = getElement(config->YellowTradeGoodsBin, urand(0, config->YellowTradeGoodsBin.size() - 1), _id, config->DuplicatesCount, auctionHouse); + } + if (itemID == 0) + { + binEmpty++; + if (config->DebugOutSeller) { - LOG_ERROR("module", "AHBot [{}]: could not get prototype of item {}", _id, itemID); + LOG_ERROR("module", "AHBot [{}]: No item could be selected from the bins", _id); } - - continue; + + break; } + } - Item* item = Item::CreateItem(itemID, 1, AHBplayer); + if (itemID == 0 || loopbreaker > AUCTION_HOUSE_BOT_LOOP_BREAKER) + { + loopBrk++; + continue; + } - if (item == NULL) - { - err++; + // + // Retrieve information about the selected item + // - if (config->DebugOutSeller) - { - LOG_ERROR("module", "AHBot [{}]: could not create item from prototype {}", _id, itemID); - } + ItemTemplate const* prototype = sObjectMgr->GetItemTemplate(itemID); - break; + if (prototype == NULL) + { + err++; + + if (config->DebugOutSeller) + { + LOG_ERROR("module", "AHBot [{}]: could not get prototype of item {}", _id, itemID); } - // - // Start interacting with the item by adding a random property - // + continue; + } - item->AddToUpdateQueueOf(AHBplayer); + Item* item = Item::CreateItem(itemID, 1, AHBplayer); - uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID); + if (item == NULL) + { + err++; - if (randomPropertyId != 0) + if (config->DebugOutSeller) { - item->SetItemRandomProperties(randomPropertyId); + LOG_ERROR("module", "AHBot [{}]: could not create item from prototype {}", _id, itemID); } - if (prototype->Quality > AHB_MAX_QUALITY) - { - err++; + continue; + } - if (config->DebugOutSeller) - { - LOG_ERROR("module", "AHBot [{}]: Quality {} TOO HIGH for item {}", _id, prototype->Quality, itemID); - } + // + // Start interacting with the item by adding a random property + // - item->RemoveFromUpdateQueueOf(AHBplayer); - continue; - } + item->AddToUpdateQueueOf(AHBplayer); - // - // Determine the price - // + uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID); - uint64 buyoutPrice = 0; - uint64 bidPrice = 0; - uint32 stackCount = 1; + if (randomPropertyId != 0) + { + item->SetItemRandomProperties(randomPropertyId); + } - if (config->SellAtMarketPrice) - { - buyoutPrice = config->GetItemPrice(itemID); - } + if (prototype->Quality > AHB_MAX_QUALITY) + { + err++; - if (buyoutPrice == 0) + if (config->DebugOutSeller) { - if (config->SellMethod) - { - buyoutPrice = prototype->BuyPrice; - } - else - { - buyoutPrice = prototype->SellPrice; - } + LOG_ERROR("module", "AHBot [{}]: Quality {} TOO HIGH for item {}", _id, prototype->Quality, itemID); } - buyoutPrice = buyoutPrice * urand(config->GetMinPrice(prototype->Quality), config->GetMaxPrice(prototype->Quality)); - buyoutPrice = buyoutPrice / 100; + item->RemoveFromUpdateQueueOf(AHBplayer); + continue; + } - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(prototype->Quality), config->GetMaxBidPrice(prototype->Quality)); - bidPrice = bidPrice / 100; + // + // Determine the price + // - // - // Determine the stack size - // + uint64 buyoutPrice = 0; + uint64 bidPrice = 0; + uint32 stackCount = 1; - if (config->GetMaxStack(prototype->Quality) > 1 && item->GetMaxStackCount() > 1) - { - stackCount = minValue(getStackCount(config, item->GetMaxStackCount()), config->GetMaxStack(prototype->Quality)); - } - else if (config->GetMaxStack(prototype->Quality) == 0 && item->GetMaxStackCount() > 1) + if (config->SellAtMarketPrice) + { + buyoutPrice = config->GetItemPrice(itemID); + } + + if (buyoutPrice == 0) + { + if (config->SellMethod) { - stackCount = getStackCount(config, item->GetMaxStackCount()); + buyoutPrice = prototype->BuyPrice; } else { - stackCount = 1; + buyoutPrice = prototype->SellPrice; } + } - item->SetCount(stackCount); + buyoutPrice = buyoutPrice * urand(config->GetMinPrice(prototype->Quality), config->GetMaxPrice(prototype->Quality)); + buyoutPrice = buyoutPrice / 100; - // - // Determine the auction time - // + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(prototype->Quality), config->GetMaxBidPrice(prototype->Quality)); + bidPrice = bidPrice / 100; - uint32 etime = getElapsedTime(config->ElapsingTimeClass); + // + // Determine the stack size + // - // - // Determine the deposit - // + if (config->GetMaxStack(prototype->Quality) > 1 && item->GetMaxStackCount() > 1) + { + stackCount = minValue(getStackCount(config, item->GetMaxStackCount()), config->GetMaxStack(prototype->Quality)); + } + else if (config->GetMaxStack(prototype->Quality) == 0 && item->GetMaxStackCount() > 1) + { + stackCount = getStackCount(config, item->GetMaxStackCount()); + } + else + { + stackCount = 1; + } - uint32 dep = sAuctionMgr->GetAuctionDeposit(ahEntry, etime, item, stackCount); + item->SetCount(stackCount); - // - // Perform the auction - // + // + // Determine the auction time + // - auto trans = CharacterDatabase.BeginTransaction(); + uint32 etime = getElapsedTime(config->ElapsingTimeClass); - AuctionEntry* auctionEntry = new AuctionEntry(); - auctionEntry->Id = sObjectMgr->GenerateAuctionID(); - auctionEntry->houseId = config->GetAHID(); - auctionEntry->item_guid = item->GetGUID(); - auctionEntry->item_template = item->GetEntry(); - auctionEntry->itemCount = item->GetCount(); - auctionEntry->owner = AHBplayer->GetGUID(); - auctionEntry->startbid = bidPrice * stackCount; - auctionEntry->buyout = buyoutPrice * stackCount; - auctionEntry->bid = 0; - auctionEntry->deposit = dep; - auctionEntry->expire_time = (time_t)etime + time(NULL); - auctionEntry->auctionHouseEntry = ahEntry; - - item->SaveToDB(trans); - item->RemoveFromUpdateQueueOf(AHBplayer); - sAuctionMgr->AddAItem(item); - auctionHouse->AddAuction(auctionEntry); - auctionEntry->SaveToDB(trans); + // + // Determine the deposit + // - CharacterDatabase.CommitTransaction(trans); + uint32 dep = sAuctionMgr->GetAuctionDeposit(ahEntry, etime, item, stackCount); - // - // Increments the number of items presents in the auction - // + // + // Perform the auction + // - switch (choice) - { - case 0: - ++greyItems; - break; + auto trans = CharacterDatabase.BeginTransaction(); + + AuctionEntry* auctionEntry = new AuctionEntry(); + auctionEntry->Id = sObjectMgr->GenerateAuctionID(); + auctionEntry->houseId = config->GetAHID(); + auctionEntry->item_guid = item->GetGUID(); + auctionEntry->item_template = item->GetEntry(); + auctionEntry->itemCount = item->GetCount(); + auctionEntry->owner = AHBplayer->GetGUID(); + auctionEntry->startbid = bidPrice * stackCount; + auctionEntry->buyout = buyoutPrice * stackCount; + auctionEntry->bid = 0; + auctionEntry->deposit = dep; + auctionEntry->expire_time = (time_t)etime + time(NULL); + auctionEntry->auctionHouseEntry = ahEntry; + + item->SaveToDB(trans); + item->RemoveFromUpdateQueueOf(AHBplayer); + sAuctionMgr->AddAItem(item); + auctionHouse->AddAuction(auctionEntry); + auctionEntry->SaveToDB(trans); + + CharacterDatabase.CommitTransaction(trans); - case 1: - ++whiteItems; - break; + // + // Increments the number of items presents in the auction + // - case 2: - ++greenItems; - break; + switch (choice) + { + case 0: + ++greyItems; + break; - case 3: - ++blueItems; - break; + case 1: + ++whiteItems; + break; - case 4: - ++purpleItems; - break; + case 2: + ++greenItems; + break; - case 5: - ++orangeItems; - break; + case 3: + ++blueItems; + break; - case 6: - ++yellowItems; - break; + case 4: + ++purpleItems; + break; - case 7: - ++greyTGoods; - break; + case 5: + ++orangeItems; + break; - case 8: - ++whiteTGoods; - break; + case 6: + ++yellowItems; + break; - case 9: - ++greenTGoods; - break; + case 7: + ++greyTGoods; + break; - case 10: - ++blueTGoods; - break; + case 8: + ++whiteTGoods; + break; - case 11: - ++purpleTGoods; - break; + case 9: + ++greenTGoods; + break; - case 12: - ++orangeTGoods; - break; + case 10: + ++blueTGoods; + break; - case 13: - ++yellowTGoods; - break; + case 11: + ++purpleTGoods; + break; - default: - break; - } + case 12: + ++orangeTGoods; + break; - noSold++; + case 13: + ++yellowTGoods; + break; - if (config->TraceSeller) - { - LOG_INFO("module", "AHBot [{}]: New stack ah={}, id={}, stack={}, bid={}, buyout={}", _id, config->GetAHID(), itemID, stackCount, auctionEntry->startbid, auctionEntry->buyout); - } + default: + break; } - if (itemID == 0 || loopbreaker > 50) + noSold++; + + if (config->TraceSeller) { - loopBrk++; + LOG_INFO("module", "AHBot [{}]: New stack ah={}, id={}, stack={}, bid={}, buyout={}", _id, config->GetAHID(), itemID, stackCount, auctionEntry->startbid, auctionEntry->buyout); } } diff --git a/src/AuctionHouseBot.h b/src/AuctionHouseBot.h index 9f6b38d..fffbe1d 100644 --- a/src/AuctionHouseBot.h +++ b/src/AuctionHouseBot.h @@ -31,6 +31,8 @@ struct AuctionEntry; class Player; class WorldSession; +#define AUCTION_HOUSE_BOT_LOOP_BREAKER 32 + class AuctionHouseBot { private: @@ -61,7 +63,7 @@ class AuctionHouseBot uint32 getNofAuctions(AHBConfig* config, AuctionHouseObject* auctionHouse, ObjectGuid guid); uint32 getStackCount(AHBConfig* config, uint32 max); uint32 getElapsedTime(uint32 timeClass); - uint32 getElement(std::set set, int index); + uint32 getElement(std::set set, int index, uint32 botId, uint32 maxDup, AuctionHouseObject* auctionHouse); public: AuctionHouseBot(uint32 account, uint32 id); @@ -70,9 +72,6 @@ class AuctionHouseBot void Initialize(AHBConfig* allianceConfig, AHBConfig* hordeConfig, AHBConfig* neutralConfig); void Update(); - void DecrementItemCounts(AuctionEntry* ah); - void IncrementItemCounts(AuctionEntry* ah); - void Commands(AHBotCommand command, uint32 ahMapID, uint32 col, char* args); ObjectGuid::LowType GetAHBplayerGUID() { return _id; };