fix trading post

This commit is contained in:
luis
2026-03-15 22:17:39 -03:00
parent 9404d5afac
commit 8f73d0cdf8
3 changed files with 285 additions and 21 deletions
+235 -1
View File
@@ -10,8 +10,12 @@
#include "Containers.h"
#include "DatabaseEnv.h"
#include "ObjectMgr.h"
#include "DB2Stores.h"
#include "ItemTemplate.h"
#include <sstream>
#include "GameTime.h"
#include <random>
#include <chrono>
PerkProgramDataStoreMgr::PerkProgramDataStoreMgr() {};
@@ -33,6 +37,9 @@ void PerkProgramDataStoreMgr::Initialize()
{
LoadPerkVendorItem();
LoadActivities();
// Auto-populate missing items from DB2
PopulateFromDB2();
}
void _LoadTemplates(Field* fields)
@@ -92,7 +99,7 @@ void PerkProgramDataStoreMgr::LoadPerkVendorItem()
TC_LOG_INFO("server.loading", ">> Loaded {} PerkPrograms Vendor Items Templates in {} ms", uint64(_vendorTemplates.size()), GetMSTimeDiffToNow(oldMsTime));
}
std::unordered_map<uint32, PerkProgramData::PerkVendorItem> PerkProgramDataStoreMgr::GetVendorItems()
std::unordered_map<uint32, PerkProgramData::PerkVendorItem> PerkProgramDataStoreMgr::GetVendorItems() const
{
return _vendorTemplates;
}
@@ -193,3 +200,230 @@ void PerkProgramDataStoreMgr::GenerateRandomActivities(bool clearOld)
CharacterDatabase.Execute(stmt);
}
}
void PerkProgramDataStoreMgr::PopulateFromDB2()
{
TC_LOG_INFO("server.loading", "Auto-populating PerkProgram items from DB2 PerksVendorItem store...");
uint32 addedCount = 0;
const uint32 MAX_ITEMS = 12; // Limit to 12 items maximum
// Pre-reserve space for better performance
if (_vendorTemplates.size() < MAX_ITEMS)
_vendorTemplates.reserve(MAX_ITEMS);
// Create a vector of all DB2 items for random selection
std::vector<PerksVendorItemEntry const*> availableItems;
availableItems.reserve(sPerksVendorItemStore.GetNumRows());
for (PerksVendorItemEntry const* db2Item : sPerksVendorItemStore)
{
if (!db2Item)
continue;
// Skip if item already exists from database loading
if (_vendorTemplates.find(db2Item->ID) != _vendorTemplates.end())
continue;
// Validate DB2 item data
if (db2Item->Cost > 1000000) // Reasonable maximum cost
{
TC_LOG_WARN("server.loading", ">> DB2 PerksVendorItem {} has excessive cost {}, skipping", db2Item->ID, db2Item->Cost);
continue;
}
availableItems.push_back(db2Item);
}
// Randomly select up to MAX_ITEMS items
std::random_device rd;
std::mt19937 gen(rd());
std::shuffle(availableItems.begin(), availableItems.end(), gen);
uint32 itemsToAdd = std::min(MAX_ITEMS, static_cast<uint32>(availableItems.size()));
for (uint32 i = 0; i < itemsToAdd; ++i)
{
PerksVendorItemEntry const* db2Item = availableItems[i];
// Create vendor item from DB2 data
PerkProgramData::PerkVendorItem vendorItem;
vendorItem.Id = db2Item->ID;
vendorItem.ItemID = db2Item->ItemID;
vendorItem.MountSpellID = db2Item->Unknown010001;
vendorItem.PetSpellID = db2Item->Unknown020001;
vendorItem.TransmogSet = 0; // Not available in DB2
vendorItem.ItemModifiedAppearanceID = 0; // Not available in DB2
vendorItem.ToyItem = 0; // Not available in DB2
vendorItem.AvailableUntil = 0; // No expiration by default
vendorItem.Cost = db2Item->Cost;
vendorItem.CategoryId = db2Item->PerksVendorCategoryID;
// Auto-determine item type based on ItemID (simplified for speed)
if (db2Item->ItemID != 0)
{
if (ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(db2Item->ItemID))
{
// Quick mount detection - only check for mount subclass
if (itemProto->GetClass() == ITEM_CLASS_MISCELLANEOUS &&
itemProto->GetSubClass() == ITEM_SUBCLASS_MISCELLANEOUS_MOUNT &&
!itemProto->Effects.empty())
{
// Just take the first spell effect for speed
if (itemProto->Effects[0] && itemProto->Effects[0]->SpellID != 0)
vendorItem.MountSpellID = itemProto->Effects[0]->SpellID;
// Set correct category for mounts
vendorItem.CategoryId = PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS;
}
else
{
// Default to consumables for regular items
vendorItem.CategoryId = PerkPrograms::PERKS_VENDOR_CATEGORY_CONSUMABLES;
}
}
else
{
// If no item template found, default to consumables
vendorItem.CategoryId = PerkPrograms::PERKS_VENDOR_CATEGORY_CONSUMABLES;
}
}
else
{
// For spells/mounts without ItemID, use mount category
if (vendorItem.MountSpellID != 0)
vendorItem.CategoryId = PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS;
else if (vendorItem.PetSpellID != 0)
vendorItem.CategoryId = PerkPrograms::PERKS_VENDOR_CATEGORY_PETS;
else
vendorItem.CategoryId = PerkPrograms::PERKS_VENDOR_CATEGORY_CONSUMABLES;
}
_vendorTemplates.insert(std::make_pair(db2Item->ID, vendorItem));
addedCount++;
TC_LOG_DEBUG("server.loading", ">> Added DB2 PerkVendorItem {}: ItemID={}, Cost={}, Category={}",
db2Item->ID, db2Item->ItemID, db2Item->Cost, db2Item->PerksVendorCategoryID);
}
if (addedCount > 0)
{
TC_LOG_INFO("server.loading", ">> Auto-populated {} additional PerkProgram items from DB2 store (limited to {} max)", addedCount, MAX_ITEMS);
TC_LOG_INFO("server.loading", ">> Total PerkProgram items available: {}", uint32(_vendorTemplates.size()));
}
else
{
TC_LOG_INFO("server.loading", ">> No additional items needed from DB2 store (all items already loaded)");
}
}
void PerkProgramDataStoreMgr::RefreshFromDB2()
{
TC_LOG_INFO("server.loading", "Force refreshing PerkProgram items from DB2 PerksVendorItem store...");
uint32 addedCount = 0;
uint32 updatedCount = 0;
for (PerksVendorItemEntry const* db2Item : sPerksVendorItemStore)
{
if (!db2Item)
continue;
auto existingIt = _vendorTemplates.find(db2Item->ID);
// Validate DB2 item data
if (db2Item->Cost > 1000000) // Reasonable maximum cost
{
TC_LOG_WARN("server.loading", ">> DB2 PerksVendorItem {} has excessive cost {}, skipping", db2Item->ID, db2Item->Cost);
continue;
}
// Create vendor item from DB2 data
PerkProgramData::PerkVendorItem vendorItem;
vendorItem.Id = db2Item->ID;
vendorItem.ItemID = db2Item->ItemID;
vendorItem.MountSpellID = db2Item->Unknown010001;
vendorItem.PetSpellID = db2Item->Unknown020001;
vendorItem.TransmogSet = 0; // Not available in DB2
vendorItem.ItemModifiedAppearanceID = 0; // Not available in DB2
vendorItem.ToyItem = 0; // Not available in DB2
vendorItem.AvailableUntil = 0; // No expiration by default
vendorItem.Cost = db2Item->Cost;
vendorItem.CategoryId = db2Item->PerksVendorCategoryID;
// Auto-determine item type based on ItemID
if (db2Item->ItemID != 0)
{
if (ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(db2Item->ItemID))
{
switch (itemProto->GetClass())
{
case ITEM_CLASS_MISCELLANEOUS:
if (itemProto->GetSubClass() == ITEM_SUBCLASS_MISCELLANEOUS_MOUNT)
{
// Try to find mount spell from item effects
for (ItemEffectEntry const* effect : itemProto->Effects)
{
if (effect && effect->SpellID != 0)
{
vendorItem.MountSpellID = effect->SpellID;
break;
}
}
}
// For now, we'll skip toy detection without DB2Manager
// Toys would typically be identified via DB2Manager.IsToyItem()
break;
default:
// Regular item
break;
}
}
}
if (existingIt != _vendorTemplates.end())
{
// Update existing item
existingIt->second = vendorItem;
updatedCount++;
}
else
{
// Add new item
_vendorTemplates.insert(std::make_pair(db2Item->ID, vendorItem));
addedCount++;
}
}
TC_LOG_INFO("server.loading", ">> DB2 refresh completed: {} added, {} updated", addedCount, updatedCount);
TC_LOG_INFO("server.loading", ">> Total PerkProgram items available: {}", uint32(_vendorTemplates.size()));
}
std::vector<PerkProgramData::PerkVendorItem> PerkProgramDataStoreMgr::GetRandomVendorItems(uint32 maxCount)
{
std::vector<PerkProgramData::PerkVendorItem> randomItems;
if (_vendorTemplates.empty())
return randomItems;
// Convert map to vector for random selection
std::vector<PerkProgramData::PerkVendorItem> allItems;
allItems.reserve(_vendorTemplates.size());
for (auto const& pair : _vendorTemplates)
allItems.push_back(pair.second);
// Random shuffle
std::random_device rd;
std::mt19937 gen(rd());
std::shuffle(allItems.begin(), allItems.end(), gen);
// Take up to maxCount items
uint32 itemsToTake = std::min(maxCount, static_cast<uint32>(allItems.size()));
randomItems.reserve(itemsToTake);
for (uint32 i = 0; i < itemsToTake; ++i)
randomItems.push_back(allItems[i]);
return randomItems;
}
@@ -53,19 +53,27 @@ public:
void Initialize();
std::unordered_map<uint32, PerkProgramData::PerkVendorItem> GetVendorItems();
std::unordered_map<uint32, PerkProgramData::PerkVendorItem> GetVendorItems() const;
PerkProgramData::PerkVendorItem const* GetVendorItem(uint32 entry);
PerkProgramData::PerkVendorItem const* GetRandomVendorItem();
std::vector<PerkProgramData::ActivityTemplate> GetActivities();
uint32 GetActivityCriteriatree(uint32 activityId);
uint32 GetActivityCurrencyAmount(uint32 activityId);
// DB2 auto-population functions
void RefreshFromDB2(); // Force refresh all items from DB2
uint32 GetTotalItemCount() const { return GetVendorItems().size(); }
// Get limited random items for vendor display
std::vector<PerkProgramData::PerkVendorItem> GetRandomVendorItems(uint32 maxCount = 12);
private:
void LoadPerkVendorItem();
void LoadActivities();
void GenerateRandomActivities(bool clearOld);
void PopulateFromDB2(); // Auto-populate from DB2 store
time_t m_activityTime;
};
+41 -19
View File
@@ -89,22 +89,25 @@ void PerkProgramManager::SendVendorItems(Creature* vendor, std::vector<uint32> p
}
}
for (auto items : sPerkProgramDataStore->GetVendorItems())
// Use limited random items for better performance and UI
auto randomItems = sPerkProgramDataStore->GetRandomVendorItems(12);
for (auto const& items : randomItems)
{
WorldPackets::PerksProgram::PerksVendorItem vendorItem;
vendorItem.VendorItemID = items.second.Id;
vendorItem.MountID = items.second.MountSpellID;
vendorItem.BattlePetSpeciesID = items.second.PetSpellID;
vendorItem.ItemModifiedAppearanceID = items.second.ItemModifiedAppearanceID;
vendorItem.ToyID = items.second.ToyItem;
vendorItem.TransmogSetID = items.second.TransmogSet;
vendorItem.Price = items.second.Cost;
vendorItem.OriginalPrice = items.second.CategoryId;
vendorItem.VendorItemID = items.Id;
vendorItem.MountID = items.MountSpellID;
vendorItem.BattlePetSpeciesID = items.PetSpellID;
vendorItem.ItemModifiedAppearanceID = items.ItemModifiedAppearanceID;
vendorItem.ToyID = items.ToyItem;
vendorItem.TransmogSetID = items.TransmogSet;
vendorItem.Price = items.Cost;
vendorItem.OriginalPrice = items.CategoryId;
vendorItem.Disabled = false;
vendorItem.AvailableUntil = items.second.AvailableUntil;
vendorItem.AvailableUntil = items.AvailableUntil;
// Skip items that are no longer available
if (items.second.AvailableUntil > 0 && GameTime::GetGameTime() > items.second.AvailableUntil)
if (items.AvailableUntil > 0 && GameTime::GetGameTime() > items.AvailableUntil)
continue;
perks.Params.VendorItem.FrozenPerksVendorItems.emplace_back(vendorItem);
@@ -165,36 +168,55 @@ void PerkProgramManager::GrantItem(PerkProgramData::PerkVendorItem const* vendor
{
case PerkPrograms::PERKS_VENDOR_CATEGORY_TOYS:
{
m_player->GetSession()->GetCollectionMgr()->AddToy(vendorItem->ToyItem, false, false);
if (vendorItem->ToyItem != 0)
m_player->GetSession()->GetCollectionMgr()->AddToy(vendorItem->ToyItem, false, false);
else if (vendorItem->ItemID != 0)
m_player->GetSession()->GetCollectionMgr()->AddToy(vendorItem->ItemID, false, false);
break;
}
case PerkPrograms::PERKS_VENDOR_CATEGORY_MOUNTS:
{
m_player->LearnSpell(vendorItem->MountSpellID, false);
if (vendorItem->MountSpellID != 0)
m_player->LearnSpell(vendorItem->MountSpellID, false);
else if (vendorItem->ItemID != 0)
{
// Try to learn mount spell from item
if (ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(vendorItem->ItemID))
{
if (!itemProto->Effects.empty() && itemProto->Effects[0] && itemProto->Effects[0]->SpellID != 0)
m_player->LearnSpell(itemProto->Effects[0]->SpellID, false);
}
}
break;
}
case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG_SETS:
{
m_player->GetSession()->GetCollectionMgr()->AddTransmogSet(vendorItem->TransmogSet);
if (vendorItem->TransmogSet != 0)
m_player->GetSession()->GetCollectionMgr()->AddTransmogSet(vendorItem->TransmogSet);
break;
}
case PerkPrograms::PERKS_VENDOR_CATEGORY_TRANSMOG:
{
m_player->GetSession()->GetCollectionMgr()->AddItemAppearance(vendorItem->ItemModifiedAppearanceID);
if (vendorItem->ItemModifiedAppearanceID != 0)
m_player->GetSession()->GetCollectionMgr()->AddItemAppearance(vendorItem->ItemModifiedAppearanceID);
break;
}
case PerkPrograms::PERKS_VENDOR_CATEGORY_PETS:
{
// m_player->AddBattlePet(vendorItem->PetSpellID);
if (vendorItem->PetSpellID != 0)
{
// Add battle pet (implementation depends on your pet system)
// m_player->AddBattlePet(vendorItem->PetSpellID);
}
break;
}
case PerkPrograms::PERKS_VENDOR_CATEGORY_CONSUMABLES:
default:
{
m_player->AddItem(vendorItem->ItemID, 1);
if (vendorItem->ItemID != 0)
m_player->AddItem(vendorItem->ItemID, 1);
break;
}
default:
break;
}
}