fix trading post
This commit is contained in:
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user