// Collects information about item counts and minimum prices to SameItemInfo and updates EligibleItems - a list with new items eligible for bot to buy and bid // Returns count of items in AH that were eligible for being bought or bidded on by ahbot buyer (EligibleItems size) uint32 AuctionBotBuyer::GetItemInformation(BuyerConfiguration& config) { config.SameItemInfo.clear(); time_t now = time(nullptr); uint32 count = 0; AuctionHouseObject* house = sAuctionMgr->GetAuctionsMap(config.GetHouseType()); for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = house->GetAuctionsBegin(); itr != house->GetAuctionsEnd(); ++itr) { AuctionEntry* entry = itr->second; Item* item = sAuctionMgr->GetAItem(entry->itemGUIDLow); if (!item) continue; BuyerItemInfo& itemInfo = config.SameItemInfo[item->GetEntry()]; // Update item entry's count and total bid prices // This can be used later to determine the prices and chances to bid uint32 itemBidPrice = entry->startbid / item->GetCount(); itemInfo.TotalBidPrice = itemInfo.TotalBidPrice + itemBidPrice; itemInfo.BidItemCount++; // Set minimum bid price if (!itemInfo.MinBidPrice) itemInfo.MinBidPrice = itemBidPrice; else itemBidPrice = std::min(itemInfo.MinBidPrice, itemBidPrice); // Set minimum buyout price if item has buyout if (entry->buyout) { // Update item entry's count and total buyout prices // This can be used later to determine the prices and chances to buyout uint32 itemBuyPrice = entry->buyout / item->GetCount(); itemInfo.TotalBuyPrice = itemInfo.TotalBuyPrice + itemBuyPrice; itemInfo.BuyItemCount++; if (!itemInfo.MinBuyPrice) itemInfo.MinBuyPrice = itemBuyPrice; else itemInfo.MinBuyPrice = std::min(itemInfo.MinBuyPrice, itemBuyPrice); } // Add/update to EligibleItems if: // has a bid by player or // has no bids and not owned by bot if ((entry->bid && entry->bidder) || (entry->owner && !entry->bid)) { config.EligibleItems[entry->Id].LastExist = now; config.EligibleItems[entry->Id].AuctionId = entry->Id; ++count; } } TC_LOG_DEBUG("ahbot", "AHBot: %u items added to buyable/biddable vector for ah type: %u", count, config.GetHouseType()); TC_LOG_DEBUG("ahbot", "AHBot: SameItemInfo size = %u", (uint32)config.SameItemInfo.size()); return count; }
void AuctionBotBuyer::LoadBuyerValues(BuyerConfiguration& config) { uint32 factionChance; switch (config.GetHouseType()) { case AUCTION_HOUSE_ALLIANCE: config.BuyerPriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ALLIANCE_PRICE_RATIO) + 50; factionChance = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_RATIO_ALLIANCE); break; case AUCTION_HOUSE_HORDE: config.BuyerPriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_HORDE_PRICE_RATIO) + 50; factionChance = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_RATIO_HORDE); break; default: config.BuyerPriceRatio = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_NEUTRAL_PRICE_RATIO) + 50; factionChance = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_RATIO_NEUTRAL); break; } config.FactionChance = 5000 * factionChance; }
// Tries to bid and buy items based on their prices and chances set in configs void AuctionBotBuyer::BuyAndBidItems(BuyerConfiguration& config) { time_t now = time(nullptr); AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(config.GetHouseType()); CheckEntryMap& items = config.EligibleItems; // Max amount of items to buy or bid uint32 cycles = sAuctionBotConfig->GetItemPerCycleNormal(); if (items.size() > sAuctionBotConfig->GetItemPerCycleBoost()) { // set more cycles if there is a huge influx of items cycles = sAuctionBotConfig->GetItemPerCycleBoost(); TC_LOG_DEBUG("ahbot", "AHBot: Boost value used for Buyer! (if this happens often adjust both ItemsPerCycle in worldserver.conf)"); } // Process items eligible to be bidded or bought CheckEntryMap::iterator itr = items.begin(); while (cycles && itr != items.end()) { AuctionEntry* auction = auctionHouse->GetAuction(itr->second.AuctionId); if (!auction) { TC_LOG_DEBUG("ahbot", "AHBot: Entry %u doesn't exists, perhaps bought already?", itr->second.AuctionId); items.erase(itr++); continue; } // Check if the item has been checked once before // If it has been checked and it was recently, skip it if (itr->second.LastChecked && (now - itr->second.LastChecked) <= _checkInterval) { TC_LOG_DEBUG("ahbot", "AHBot: In time interval wait for entry %u!", auction->Id); ++itr; continue; } Item* item = sAuctionMgr->GetAItem(auction->itemGUIDLow); if (!item) { // auction item not accessible, possible auction in payment pending mode items.erase(itr++); continue; } // price to bid if bidding uint32 bidPrice; if (auction->bid >= auction->startbid) { // get bid price to outbid previous bidder bidPrice = auction->bid + auction->GetAuctionOutBid(); } else { // no previous bidders - use starting bid bidPrice = auction->startbid; } const BuyerItemInfo* ahInfo = nullptr; BuyerItemInfoMap::const_iterator sameItemItr = config.SameItemInfo.find(item->GetEntry()); if (sameItemItr != config.SameItemInfo.end()) ahInfo = &sameItemItr->second; TC_LOG_DEBUG("ahbot", "AHBot: Rolling for AHentry %u:", auction->Id); // Roll buy and bid chances bool successBuy = RollBuyChance(ahInfo, item, auction, bidPrice); bool successBid = RollBidChance(ahInfo, item, auction, bidPrice); // If roll bidding succesfully and bid price is above buyout -> buyout // If roll for buying was successful but not for bid, buyout directly // If roll bidding was also successful, buy the entry with 20% chance // - Better bid than buy since the item is bought by bot if no player bids after // Otherwise bid if roll for bid was successful if ((auction->buyout && successBid && bidPrice >= auction->buyout) || (successBuy && (!successBid || urand(1, 5) == 1))) BuyEntry(auction, auctionHouse); // buyout else if (successBid) PlaceBidToEntry(auction, bidPrice); // bid itr->second.LastChecked = now; --cycles; ++itr; } // Clear not needed entries config.SameItemInfo.clear(); }
uint32 AuctionBotBuyer::GetBuyableEntry(BuyerConfiguration& config) { config.SameItemInfo.clear(); uint32 count = 0; time_t now = time(nullptr); AuctionHouseObject* house = sAuctionMgr->GetAuctionsMap(config.GetHouseType()); for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = house->GetAuctionsBegin(); itr != house->GetAuctionsEnd(); ++itr) { AuctionEntry* entry = itr->second; Item* item = sAuctionMgr->GetAItem(entry->itemGUIDLow); if (item) { ItemTemplate const * prototype = item->GetTemplate(); if (prototype) { ++config.SameItemInfo[item->GetEntry()].ItemCount; // Structure constructor will make sure Element are correctly initialized if entry is created here. config.SameItemInfo[item->GetEntry()].BuyPrice = config.SameItemInfo[item->GetEntry()].BuyPrice + (itr->second->buyout / item->GetCount()); config.SameItemInfo[item->GetEntry()].BidPrice = config.SameItemInfo[item->GetEntry()].BidPrice + (itr->second->startbid / item->GetCount()); if (itr->second->buyout != 0) { if (itr->second->buyout / item->GetCount() < config.SameItemInfo[item->GetEntry()].MinBuyPrice) config.SameItemInfo[item->GetEntry()].MinBuyPrice = itr->second->buyout / item->GetCount(); else if (config.SameItemInfo[item->GetEntry()].MinBuyPrice == 0) config.SameItemInfo[item->GetEntry()].MinBuyPrice = itr->second->buyout / item->GetCount(); } if (itr->second->startbid / item->GetCount() < config.SameItemInfo[item->GetEntry()].MinBidPrice) config.SameItemInfo[item->GetEntry()].MinBidPrice = itr->second->startbid / item->GetCount(); else if (config.SameItemInfo[item->GetEntry()].MinBidPrice == 0) config.SameItemInfo[item->GetEntry()].MinBidPrice = itr->second->startbid / item->GetCount(); if (!entry->owner) { if (entry->bid != 0 && entry->bidder) // Add bid by player { config.CheckedEntry[entry->Id].LastExist = now; config.CheckedEntry[entry->Id].AuctionId = entry->Id; ++count; } } else { if (entry->bid != 0) { if (entry->bidder) { config.CheckedEntry[entry->Id].LastExist = now; config.CheckedEntry[entry->Id].AuctionId = entry->Id; ++count; } } else { config.CheckedEntry[entry->Id].LastExist = now; config.CheckedEntry[entry->Id].AuctionId = entry->Id; ++count; } } } } } TC_LOG_DEBUG("ahbot", "AHBot: %u items added to buyable vector for ah type: %u", count, config.GetHouseType()); TC_LOG_DEBUG("ahbot", "AHBot: SameItemInfo size = %u", (uint32)config.SameItemInfo.size()); return count; }
void AuctionBotBuyer::AddNewAuctionBuyerBotBid(BuyerConfiguration& config) { AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(config.GetHouseType()); PrepareListOfEntry(config); time_t now = time(nullptr); uint32 buyCycles; if (config.CheckedEntry.size() > sAuctionBotConfig->GetItemPerCycleBoost()) { buyCycles = sAuctionBotConfig->GetItemPerCycleBoost(); TC_LOG_DEBUG("ahbot", "AHBot: Boost value used for Buyer! (if this happens often adjust both ItemsPerCycle in worldserver.conf)"); } else buyCycles = sAuctionBotConfig->GetItemPerCycleNormal(); for (CheckEntryMap::iterator itr = config.CheckedEntry.begin(); itr != config.CheckedEntry.end();) { AuctionEntry* auction = auctionHouse->GetAuction(itr->second.AuctionId); if (!auction) // is auction not active now { TC_LOG_DEBUG("ahbot", "AHBot: Entry %u doesn't exists, perhaps bought already?", itr->second.AuctionId); config.CheckedEntry.erase(itr++); continue; } if (itr->second.LastChecked != 0 && (now - itr->second.LastChecked) <= _checkInterval) { TC_LOG_DEBUG("ahbot", "AHBot: In time interval wait for entry %u!", auction->Id); ++itr; continue; } if (buyCycles == 0) break; uint32 maxChance = 5000; Item* item = sAuctionMgr->GetAItem(auction->itemGUIDLow); if (!item) // auction item not accessible, possible auction in payment pending mode { config.CheckedEntry.erase(itr++); continue; } ItemTemplate const* prototype = item->GetTemplate(); uint32 basePrice = sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYPRICE_BUYER) ? prototype->BuyPrice : prototype->SellPrice; basePrice *= item->GetCount(); uint32 maxBuyablePrice = (basePrice * config.BuyerPriceRatio) / 100; BuyerItemInfoMap::iterator sameItemItr = config.SameItemInfo.find(item->GetEntry()); uint32 buyoutPrice = auction->buyout / item->GetCount(); uint32 bidPrice; uint32 bidPriceByItem; uint32 minBidPrice; uint32 minBuyPrice; if (auction->bid >= auction->startbid) { bidPrice = auction->GetAuctionOutBid(); bidPriceByItem = auction->bid / item->GetCount(); } else { bidPrice = auction->startbid; bidPriceByItem = auction->startbid / item->GetCount(); } double inGameBuyPrice; double inGameBidPrice; if (sameItemItr == config.SameItemInfo.end()) { inGameBuyPrice = 0; inGameBidPrice = 0; minBidPrice = 0; minBuyPrice = 0; } else { if (sameItemItr->second.ItemCount == 1) maxBuyablePrice = maxBuyablePrice * 5; // if only one item exist can be bought if the price is high too. inGameBuyPrice = sameItemItr->second.BuyPrice / sameItemItr->second.ItemCount; inGameBidPrice = sameItemItr->second.BidPrice / sameItemItr->second.ItemCount; minBidPrice = sameItemItr->second.MinBidPrice; minBuyPrice = sameItemItr->second.MinBuyPrice; } uint32 maxBidablePrice = maxBuyablePrice - (maxBuyablePrice / 30); // Max Bidable price defined to 70% of max buyable price TC_LOG_DEBUG("ahbot", "AHBot: Auction added with data:"); TC_LOG_DEBUG("ahbot", "AHBot: MaxPrice of Entry %u is %.1fg.", itr->second.AuctionId, double(maxBuyablePrice) / 10000.0); TC_LOG_DEBUG("ahbot", "AHBot: GamePrice buy=%.1fg, bid=%.1fg.", inGameBuyPrice / 10000, inGameBidPrice / 10000); TC_LOG_DEBUG("ahbot", "AHBot: Minimal price see in AH Buy=%ug, Bid=%ug.", minBuyPrice / 10000, minBidPrice / 10000); TC_LOG_DEBUG("ahbot", "AHBot: Actual Entry price, Buy=%ug, Bid=%ug.", buyoutPrice / 10000, bidPrice / 10000); if (!auction->owner) // Original auction owner maxChance = maxChance / 5; // if Owner is AHBot this mean player placed bid on this auction. We divide by 5 chance for AhBuyer to place bid on it. (This make more challenge than ignore entry) if (auction->buyout != 0) // Is the item directly buyable? { if (IsBuyableEntry(buyoutPrice, inGameBuyPrice, maxBuyablePrice, minBuyPrice, maxChance, config.FactionChance)) { if (IsBidableEntry(bidPriceByItem, inGameBuyPrice, maxBidablePrice, minBidPrice, maxChance / 2, config.FactionChance)) { if (urand(0, 5) == 0) PlaceBidToEntry(auction, bidPrice); else BuyEntry(auction); } else BuyEntry(auction); } else if (IsBidableEntry(bidPriceByItem, inGameBuyPrice, maxBidablePrice, minBidPrice, maxChance / 2, config.FactionChance)) PlaceBidToEntry(auction, bidPrice); } else if (IsBidableEntry(bidPriceByItem, inGameBuyPrice, maxBidablePrice, minBidPrice, maxChance, config.FactionChance)) PlaceBidToEntry(auction, bidPrice); itr->second.LastChecked = now; --buyCycles; ++itr; } }