aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/world/master/2017_10_09_00_world.sql9
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp2
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp96
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h10
-rw-r--r--src/server/game/Entities/Player/Player.cpp11
-rw-r--r--src/server/game/Events/GameEventMgr.cpp57
-rw-r--r--src/server/game/Events/GameEventMgr.h19
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp119
-rw-r--r--src/server/game/Globals/ObjectMgr.h6
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp11
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp19
11 files changed, 202 insertions, 157 deletions
diff --git a/sql/updates/world/master/2017_10_09_00_world.sql b/sql/updates/world/master/2017_10_09_00_world.sql
new file mode 100644
index 00000000000..8646ecb9eeb
--- /dev/null
+++ b/sql/updates/world/master/2017_10_09_00_world.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `npc_vendor`
+ ADD `BonusListIDs` TEXT NULL AFTER `type`;
+
+ALTER TABLE `game_event_npc_vendor`
+ ADD `BonusListIDs` TEXT NULL,
+ ADD PlayerConditionID INT(10) unsigned NOT NULL DEFAULT '0',
+ ADD IgnoreFiltering TINYINT(3) unsigned NOT NULL DEFAULT '0';
+
+UPDATE `command` SET `help`='Syntax: .npc add item #itemId <#maxcount><#incrtime><#extendedcost><#bonusListIDs>\r\nAdd item #itemid to item list of selected vendor. Also optionally set max count item in vendor item list and time to item count restoring and items ExtendedCost.\r\n#bonusListIDs is a semicolon separated list of bonuses to add to item (such as Mythic/Heroic/Warforged/socket)' WHERE `name`='npc add item';
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index 8f0bf1e384d..f532093d022 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -37,7 +37,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_DEL_GAME_TELE, "DELETE FROM game_tele WHERE name = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_INS_NPC_VENDOR, "INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost, type) VALUES(?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_NPC_VENDOR, "DELETE FROM npc_vendor WHERE entry = ? AND item = ? AND type = ?", CONNECTION_ASYNC);
- PrepareStatement(WORLD_SEL_NPC_VENDOR_REF, "SELECT item, maxcount, incrtime, ExtendedCost, type FROM npc_vendor WHERE entry = ? AND type = ? ORDER BY slot ASC", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_NPC_VENDOR_REF, "SELECT item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionID, IgnoreFiltering FROM npc_vendor WHERE entry = ? ORDER BY slot ASC", CONNECTION_SYNCH);
PrepareStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE, "UPDATE creature SET MovementType = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_UPD_CREATURE_FACTION, "UPDATE creature_template SET faction = ? WHERE entry = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_UPD_CREATURE_NPCFLAG, "UPDATE creature_template SET npcflag = ? WHERE entry = ?", CONNECTION_ASYNC);
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
index 3a05b3b1898..e71a7fa7a62 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
@@ -38,10 +38,10 @@ AuctionBotSeller::~AuctionBotSeller()
bool AuctionBotSeller::Initialize()
{
- std::vector<uint32> npcItems;
- std::vector<uint32> lootItems;
- std::vector<uint32> includeItems;
- std::vector<uint32> excludeItems;
+ std::unordered_set<uint32> npcItems;
+ std::unordered_set<uint32> lootItems;
+ std::unordered_set<uint32> includeItems;
+ std::unordered_set<uint32> excludeItems;
TC_LOG_DEBUG("ahbot", "AHBot seller filters:");
@@ -49,31 +49,27 @@ bool AuctionBotSeller::Initialize()
std::stringstream includeStream(sAuctionBotConfig->GetAHBotIncludes());
std::string temp;
while (std::getline(includeStream, temp, ','))
- includeItems.push_back(atoi(temp.c_str()));
+ includeItems.insert(atoi(temp.c_str()));
}
{
std::stringstream excludeStream(sAuctionBotConfig->GetAHBotExcludes());
std::string temp;
while (std::getline(excludeStream, temp, ','))
- excludeItems.push_back(atoi(temp.c_str()));
+ excludeItems.insert(atol(temp.c_str()));
}
- TC_LOG_DEBUG("ahbot", "Forced Inclusion %u items", (uint32)includeItems.size());
- TC_LOG_DEBUG("ahbot", "Forced Exclusion %u items", (uint32)excludeItems.size());
+ TC_LOG_DEBUG("ahbot", "Forced Inclusion " SZFMTD " items", includeItems.size());
+ TC_LOG_DEBUG("ahbot", "Forced Exclusion " SZFMTD " items", excludeItems.size());
TC_LOG_DEBUG("ahbot", "Loading npc vendor items for filter..");
- const CreatureTemplateContainer* creatures = sObjectMgr->GetCreatureTemplates();
- std::set<uint32> tempItems;
- for (CreatureTemplateContainer::const_iterator it = creatures->begin(); it != creatures->end(); ++it)
- if (const VendorItemData* data = sObjectMgr->GetNpcVendorItemList(it->first))
- for (VendorItem const& it2 : data->m_items)
- tempItems.insert(it2.item);
+ CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
+ for (auto it = creatures->begin(); it != creatures->end(); ++it)
+ if (VendorItemData const* data = sObjectMgr->GetNpcVendorItemList(it->first))
+ for (VendorItem const& vendorItem : data->m_items)
+ npcItems.insert(vendorItem.item);
- for (uint32 itemId : tempItems)
- npcItems.push_back(itemId);
-
- TC_LOG_DEBUG("ahbot", "Npc vendor filter has %u items", (uint32)npcItems.size());
+ TC_LOG_DEBUG("ahbot", "Npc vendor filter has " SZFMTD " items", npcItems.size());
TC_LOG_DEBUG("ahbot", "Loading loot items for filter..");
QueryResult result = WorldDatabase.PQuery(
@@ -99,11 +95,11 @@ bool AuctionBotSeller::Initialize()
if (!entry)
continue;
- lootItems.push_back(entry);
+ lootItems.insert(entry);
} while (result->NextRow());
}
- TC_LOG_DEBUG("ahbot", "Loot filter has %u items", (uint32)lootItems.size());
+ TC_LOG_DEBUG("ahbot", "Loot filter has " SZFMTD " items", lootItems.size());
TC_LOG_DEBUG("ahbot", "Sorting and cleaning items for AHBot seller...");
uint32 itemsAdded = 0;
@@ -120,21 +116,11 @@ bool AuctionBotSeller::Initialize()
continue;
// forced exclude filter
- bool isExcludeItem = false;
- for (size_t i = 0; i < excludeItems.size() && !isExcludeItem; ++i)
- if (itemId == excludeItems[i])
- isExcludeItem = true;
-
- if (isExcludeItem)
+ if (excludeItems.find(itemId) != excludeItems.end())
continue;
// forced include filter
- bool isForcedIncludeItem = false;
- for (size_t i = 0; i < includeItems.size() && !isForcedIncludeItem; ++i)
- if (itemId == includeItems[i])
- isForcedIncludeItem = true;
-
- if (isForcedIncludeItem)
+ if (includeItems.find(itemId) != includeItems.end())
{
_itemPool[prototype->GetQuality()][prototype->GetClass()].push_back(itemId);
++itemsAdded;
@@ -220,42 +206,19 @@ bool AuctionBotSeller::Initialize()
// vendor filter
if (!sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEMS_VENDOR))
- {
- bool isVendorItem = false;
- for (size_t i = 0; i < npcItems.size() && !isVendorItem; ++i)
- if (itemId == npcItems[i])
- isVendorItem = true;
-
- if (isVendorItem)
+ if (npcItems.find(itemId) != npcItems.end())
continue;
- }
// loot filter
if (!sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEMS_LOOT))
- {
- bool isLootItem = false;
- for (size_t i = 0; i < lootItems.size() && !isLootItem; ++i)
- if (itemId == lootItems[i])
- isLootItem = true;
-
- if (isLootItem)
+ if (lootItems.find(itemId) != lootItems.end())
continue;
- }
// not vendor/loot filter
if (!sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEMS_MISC))
{
- bool isVendorItem = false;
- bool isLootItem = false;
-
- for (size_t i = 0; i < npcItems.size() && !isVendorItem; ++i)
- if (itemId == npcItems[i])
- isVendorItem = true;
-
- for (size_t i = 0; i < lootItems.size() && !isLootItem; ++i)
- if (itemId == lootItems[i])
- isLootItem = true;
-
+ bool isVendorItem = npcItems.find(itemId) != npcItems.end();
+ bool isLootItem = lootItems.find(itemId) != lootItems.end();
if (!isLootItem && !isVendorItem)
continue;
}
@@ -388,12 +351,15 @@ bool AuctionBotSeller::Initialize()
LoadConfig();
- TC_LOG_DEBUG("ahbot", "Items loaded \tGray\tWhite\tGreen\tBlue\tPurple\tOrange\tYellow");
- for (uint32 i = 0; i < MAX_ITEM_CLASS; ++i)
- TC_LOG_DEBUG("ahbot", "\t\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
- (uint32)_itemPool[0][i].size(), (uint32)_itemPool[1][i].size(), (uint32)_itemPool[2][i].size(),
- (uint32)_itemPool[3][i].size(), (uint32)_itemPool[4][i].size(), (uint32)_itemPool[5][i].size(),
- (uint32)_itemPool[6][i].size());
+ if (sLog->ShouldLog("ahbot", LOG_LEVEL_DEBUG))
+ {
+ sLog->outMessage("ahbot", LOG_LEVEL_DEBUG, "Items loaded \tGray\tWhite\tGreen\tBlue\tPurple\tOrange\tYellow");
+ for (uint32 i = 0; i < MAX_ITEM_CLASS; ++i)
+ sLog->outMessage("ahbot", LOG_LEVEL_DEBUG, "\t\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
+ (uint32)_itemPool[0][i].size(), (uint32)_itemPool[1][i].size(), (uint32)_itemPool[2][i].size(),
+ (uint32)_itemPool[3][i].size(), (uint32)_itemPool[4][i].size(), (uint32)_itemPool[5][i].size(),
+ (uint32)_itemPool[6][i].size());
+ }
TC_LOG_DEBUG("ahbot", "AHBot seller configuration data loaded and initialized");
return true;
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index 876148a1c1f..29f261f5e01 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -564,14 +564,16 @@ struct CreatureAddon
// Vendors
struct VendorItem
{
- VendorItem(uint32 _item, int32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost, uint8 _Type)
- : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost), Type(_Type) { }
+ VendorItem() : item(0), maxcount(0), incrtime(0), ExtendedCost(0), Type(0), PlayerConditionId(0), IgnoreFiltering(false) { }
uint32 item;
uint32 maxcount; // 0 for infinity item amount
uint32 incrtime; // time for restore items amount if maxcount != 0
uint32 ExtendedCost;
uint8 Type;
+ std::vector<int32> BonusListIDs;
+ uint32 PlayerConditionId;
+ bool IgnoreFiltering;
//helpers
bool IsGoldRequired(ItemTemplate const* pProto) const;
@@ -590,9 +592,9 @@ struct VendorItemData
}
bool Empty() const { return m_items.empty(); }
uint32 GetItemCount() const { return uint32(m_items.size()); }
- void AddItem(uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost, uint8 type)
+ void AddItem(VendorItem vItem)
{
- m_items.emplace_back(item, maxcount, ptime, ExtendedCost, type);
+ m_items.emplace_back(std::move(vItem));
}
bool RemoveItem(uint32 item_id, uint8 type);
VendorItem const* FindItemCostPair(uint32 item_id, uint32 extendedCost, uint8 type) const;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 11d96a95d9a..993dfa9caa3 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -22226,7 +22226,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c
}
Item* it = bStore ?
- StoreNewItem(vDest, item, true, GenerateItemRandomPropertyId(item), {}, 0, {}, false) :
+ StoreNewItem(vDest, item, true, GenerateItemRandomPropertyId(item), {}, 0, crItem->BonusListIDs, false) :
EquipNewItem(uiDest, item, true);
if (it)
{
@@ -22480,6 +22480,15 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
return false;
}
+ if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(crItem->PlayerConditionId))
+ {
+ if (!ConditionMgr::IsPlayerMeetingCondition(this, playerCondition))
+ {
+ SendEquipError(EQUIP_ERR_ITEM_LOCKED, nullptr, nullptr);
+ return false;
+ }
+ }
+
// check current item amount if it limited
if (crItem->maxcount != 0)
{
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index 95300e622bd..7b33a102ff0 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -792,8 +792,8 @@ void GameEventMgr::LoadFromDB()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2 3 4 5 6
- QueryResult result = WorldDatabase.Query("SELECT eventEntry, guid, item, maxcount, incrtime, ExtendedCost, type FROM game_event_npc_vendor ORDER BY guid, slot ASC");
+ // 0 1 2 3 4 5 6 7 8 9
+ QueryResult result = WorldDatabase.Query("SELECT eventEntry, guid, item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionId, IgnoreFiltering FROM game_event_npc_vendor ORDER BY guid, slot ASC");
if (!result)
TC_LOG_INFO("server.loading", ">> Loaded 0 vendor additions in game events. DB table `game_event_npc_vendor` is empty.");
@@ -805,6 +805,7 @@ void GameEventMgr::LoadFromDB()
Field* fields = result->Fetch();
uint8 event_id = fields[0].GetUInt8();
+ ObjectGuid::LowType guid = fields[1].GetUInt64();
if (event_id >= mGameEventVendors.size())
{
@@ -812,14 +813,6 @@ void GameEventMgr::LoadFromDB()
continue;
}
- NPCVendorList& vendors = mGameEventVendors[event_id];
- NPCVendorEntry newEntry;
- ObjectGuid::LowType guid = fields[1].GetUInt64();
- newEntry.item = fields[2].GetUInt32();
- newEntry.maxcount = fields[3].GetUInt32();
- newEntry.incrtime = fields[4].GetUInt32();
- newEntry.ExtendedCost = fields[5].GetUInt32();
- newEntry.Type = fields[6].GetUInt8();
// get the event npc flag for checking if the npc will be vendor during the event or not
uint32 event_npc_flag = 0;
NPCFlagList& flist = mGameEventNPCFlags[event_id];
@@ -831,17 +824,30 @@ void GameEventMgr::LoadFromDB()
break;
}
}
- // get creature entry
- newEntry.entry = 0;
+ uint32 entry = 0;
if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
- newEntry.entry = data->id;
+ entry = data->id;
+
+ VendorItem vItem;
+ vItem.item = fields[2].GetUInt32();
+ vItem.maxcount = fields[3].GetUInt32();
+ vItem.incrtime = fields[4].GetUInt32();
+ vItem.ExtendedCost = fields[5].GetUInt32();
+ vItem.Type = fields[6].GetUInt8();
+ vItem.PlayerConditionId = fields[8].GetUInt32();
+ vItem.IgnoreFiltering = fields[9].GetBool();
+
+ Tokenizer bonusListIDsTok(fields[7].GetString(), ' ');
+ for (char const* token : bonusListIDsTok)
+ vItem.BonusListIDs.push_back(int32(atol(token)));
// check validity with event's npcflag
- if (!sObjectMgr->IsVendorItemValid(newEntry.entry, newEntry.item, newEntry.maxcount, newEntry.incrtime, newEntry.ExtendedCost, newEntry.Type, NULL, NULL, event_npc_flag))
+ if (!sObjectMgr->IsVendorItemValid(entry, vItem, nullptr, nullptr, event_npc_flag))
continue;
- vendors.push_back(newEntry);
+ NPCVendorMap& vendors = mGameEventVendors[event_id];
+ vendors[entry].emplace_back(std::move(vItem));
++count;
}
@@ -1177,12 +1183,15 @@ void GameEventMgr::UpdateBattlegroundSettings()
void GameEventMgr::UpdateEventNPCVendor(uint16 event_id, bool activate)
{
- for (NPCVendorList::iterator itr = mGameEventVendors[event_id].begin(); itr != mGameEventVendors[event_id].end(); ++itr)
+ for (NPCVendorMap::iterator itr = mGameEventVendors[event_id].begin(); itr != mGameEventVendors[event_id].end(); ++itr)
{
- if (activate)
- sObjectMgr->AddVendorItem(itr->entry, itr->item, itr->maxcount, itr->incrtime, itr->ExtendedCost, itr->Type, false);
- else
- sObjectMgr->RemoveVendorItem(itr->entry, itr->item, itr->Type, false);
+ for (VendorItem const& vItem : itr->second)
+ {
+ if (activate)
+ sObjectMgr->AddVendorItem(itr->first, vItem, false);
+ else
+ sObjectMgr->RemoveVendorItem(itr->first, vItem.item, vItem.Type, false);
+ }
}
}
@@ -1531,7 +1540,13 @@ void GameEventMgr::UpdateWorldStates(uint16 event_id, bool Activate)
}
}
-GameEventMgr::GameEventMgr() : isSystemInit(false) { }
+GameEventMgr::GameEventMgr() : isSystemInit(false)
+{
+}
+
+GameEventMgr::~GameEventMgr()
+{
+}
void GameEventMgr::HandleQuestComplete(uint32 quest_id)
{
diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h
index 81fa6f02e81..74f2fe1703a 100644
--- a/src/server/game/Events/GameEventMgr.h
+++ b/src/server/game/Events/GameEventMgr.h
@@ -85,25 +85,16 @@ struct ModelEquip
uint8 equipement_id_prev;
};
-struct NPCVendorEntry
-{
- uint32 entry; // creature entry
- uint32 item; // item id
- int32 maxcount; // 0 for infinite
- uint32 incrtime; // time for restore items amount if maxcount != 0
- uint32 ExtendedCost;
- uint8 Type; // 1 item, 2 currency
-};
-
-class Player;
class Creature;
+class Player;
class Quest;
+struct VendorItem;
class TC_GAME_API GameEventMgr
{
private:
GameEventMgr();
- ~GameEventMgr() { }
+ ~GameEventMgr();
public:
static GameEventMgr* instance();
@@ -160,8 +151,8 @@ class TC_GAME_API GameEventMgr
typedef std::pair<uint32, uint32> QuestRelation;
typedef std::list<QuestRelation> QuestRelList;
typedef std::vector<QuestRelList> GameEventQuestMap;
- typedef std::list<NPCVendorEntry> NPCVendorList;
- typedef std::vector<NPCVendorList> GameEventNPCVendorMap;
+ typedef std::unordered_map<uint32, std::vector<VendorItem>> NPCVendorMap;
+ typedef std::vector<NPCVendorMap> GameEventNPCVendorMap;
typedef std::map<uint32 /*quest id*/, GameEventQuestToEventConditionNum> QuestIdToEventConditionMap;
typedef std::pair<ObjectGuid::LowType /*guid*/, uint64 /*npcflag*/> GuidNPCFlagPair;
typedef std::list<GuidNPCFlagPair> NPCFlagList;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 365b329f421..24a54057d0d 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -8666,12 +8666,11 @@ void ObjectMgr::LoadCreatureDefaultTrainers()
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " default trainers in %u ms", _creatureDefaultTrainers.size(), GetMSTimeDiffToNow(oldMSTime));
}
-int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, uint8 referenceType, std::set<uint32> *skip_vendors)
+int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32> *skip_vendors)
{
// find all items from the reference vendor
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF);
stmt->setUInt32(0, uint32(item));
- stmt->setUInt8(1, referenceType);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (!result)
@@ -8686,20 +8685,27 @@ int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, uint8 referenceType
// if item is a negative, its a reference
if (item_id < 0)
- count += LoadReferenceVendor(vendor, -item_id, referenceType, skip_vendors);
+ count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
else
{
- int32 maxcount = fields[1].GetUInt32();
- uint32 incrtime = fields[2].GetUInt32();
- uint32 ExtendedCost = fields[3].GetUInt32();
- uint8 type = fields[4].GetUInt8();
+ VendorItem vItem;
+ vItem.item = item_id;
+ vItem.maxcount = fields[1].GetUInt32();
+ vItem.incrtime = fields[2].GetUInt32();
+ vItem.ExtendedCost = fields[3].GetUInt32();
+ vItem.Type = fields[4].GetUInt8();
+ vItem.PlayerConditionId = fields[6].GetUInt32();
+ vItem.IgnoreFiltering = fields[7].GetBool();
- if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, type, nullptr, skip_vendors))
+ Tokenizer bonusListIDsTok(fields[5].GetString(), ' ');
+ for (char const* token : bonusListIDsTok)
+ vItem.BonusListIDs.push_back(int32(atol(token)));
+
+ if (!IsVendorItemValid(vendor, vItem, nullptr, skip_vendors))
continue;
VendorItemData& vList = _cacheVendorItemStore[vendor];
-
- vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type);
+ vList.AddItem(std::move(vItem));
++count;
}
} while (result->NextRow());
@@ -8718,10 +8724,9 @@ void ObjectMgr::LoadVendors()
std::set<uint32> skip_vendors;
- QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type FROM npc_vendor ORDER BY entry, slot ASC");
+ QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionID, IgnoreFiltering FROM npc_vendor ORDER BY entry, slot ASC");
if (!result)
{
-
TC_LOG_ERROR("server.loading", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
return;
}
@@ -8737,20 +8742,27 @@ void ObjectMgr::LoadVendors()
// if item is a negative, its a reference
if (item_id < 0)
- count += LoadReferenceVendor(entry, -item_id, 0, &skip_vendors);
+ count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
else
{
- uint32 maxcount = fields[2].GetUInt32();
- uint32 incrtime = fields[3].GetUInt32();
- uint32 ExtendedCost = fields[4].GetUInt32();
- uint8 type = fields[5].GetUInt8();
+ VendorItem vItem;
+ vItem.item = item_id;
+ vItem.maxcount = fields[2].GetUInt32();
+ vItem.incrtime = fields[3].GetUInt32();
+ vItem.ExtendedCost = fields[4].GetUInt32();
+ vItem.Type = fields[5].GetUInt8();
+ vItem.PlayerConditionId = fields[7].GetUInt32();
+ vItem.IgnoreFiltering = fields[8].GetBool();
+
+ Tokenizer bonusListIDsTok(fields[6].GetString(), ' ');
+ for (char const* token : bonusListIDsTok)
+ vItem.BonusListIDs.push_back(int32(atol(token)));
- if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, type, nullptr, &skip_vendors))
+ if (!IsVendorItemValid(entry, vItem, nullptr, &skip_vendors))
continue;
VendorItemData& vList = _cacheVendorItemStore[entry];
-
- vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type);
+ vList.AddItem(std::move(vItem));
++count;
}
}
@@ -8910,21 +8922,21 @@ uint32 ObjectMgr::GetCreatureDefaultTrainer(uint32 creatureId) const
return 0;
}
-void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, uint8 type, bool persist /*= true*/)
+void ObjectMgr::AddVendorItem(uint32 entry, VendorItem const& vItem, bool persist /*= true*/)
{
VendorItemData& vList = _cacheVendorItemStore[entry];
- vList.AddItem(item, maxcount, incrtime, extendedCost, type);
+ vList.AddItem(vItem);
if (persist)
{
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR);
stmt->setUInt32(0, entry);
- stmt->setUInt32(1, item);
- stmt->setUInt8(2, maxcount);
- stmt->setUInt32(3, incrtime);
- stmt->setUInt32(4, extendedCost);
- stmt->setUInt8(5, type);
+ stmt->setUInt32(1, vItem.item);
+ stmt->setUInt8(2, vItem.maxcount);
+ stmt->setUInt32(3, vItem.incrtime);
+ stmt->setUInt32(4, vItem.ExtendedCost);
+ stmt->setUInt8(5, vItem.Type);
WorldDatabase.Execute(stmt);
}
@@ -8953,7 +8965,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool per
return true;
}
-bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, uint8 type, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
+bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
{
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
if (!cInfo)
@@ -8980,61 +8992,76 @@ bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount
return false;
}
- if ((type == ITEM_VENDOR_TYPE_ITEM && !sObjectMgr->GetItemTemplate(id)) ||
- (type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(id)))
+ if ((vItem.Type == ITEM_VENDOR_TYPE_ITEM && !sObjectMgr->GetItemTemplate(vItem.item)) ||
+ (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(vItem.item)))
{
if (player)
- ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_NOT_FOUND, id, type);
+ ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_NOT_FOUND, vItem.item, vItem.Type);
else
- TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u, type %u), ignore", vendor_entry, id, type);
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u, type %u), ignore", vendor_entry, vItem.item, vItem.Type);
+ return false;
+ }
+
+ if (vItem.PlayerConditionId && !sPlayerConditionStore.LookupEntry(vItem.PlayerConditionId))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: %u) with invalid PlayerConditionId (%u) for vendor (%u), ignore", vItem.item, vItem.PlayerConditionId, vendor_entry);
return false;
}
- if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
+ if (vItem.ExtendedCost && !sItemExtendedCostStore.LookupEntry(vItem.ExtendedCost))
{
if (player)
- ChatHandler(player->GetSession()).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost);
+ ChatHandler(player->GetSession()).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, vItem.ExtendedCost);
else
- TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", id, ExtendedCost, vendor_entry);
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", vItem.item, vItem.ExtendedCost, vendor_entry);
return false;
}
- if (type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
+ if (vItem.Type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
{
- if (maxcount > 0 && incrtime == 0)
+ if (vItem.maxcount > 0 && vItem.incrtime == 0)
{
if (player)
- ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
+ ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", vItem.maxcount);
else
- TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, id, vendor_entry);
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", vItem.maxcount, vItem.item, vendor_entry);
return false;
}
- else if (maxcount == 0 && incrtime > 0)
+ else if (vItem.maxcount == 0 && vItem.incrtime > 0)
{
if (player)
ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
else
- TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", id, vendor_entry);
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", vItem.item, vendor_entry);
return false;
}
+
+ for (int32 bonusList : vItem.BonusListIDs)
+ {
+ if (!sDB2Manager.GetItemBonusList(bonusList))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: %u) with invalid bonus %u for vendor (%u), ignore", vItem.item, bonusList, vendor_entry);
+ return false;
+ }
+ }
}
VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
if (!vItems)
return true; // later checks for non-empty lists
- if (vItems->FindItemCostPair(id, ExtendedCost, type))
+ if (vItems->FindItemCostPair(vItem.item, vItem.ExtendedCost, vItem.Type))
{
if (player)
- ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, id, ExtendedCost, type);
+ ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, vItem.item, vItem.ExtendedCost, vItem.Type);
else
- TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items %u (with extended cost %u, type %u) for vendor (Entry: %u), ignoring", id, ExtendedCost, type, vendor_entry);
+ TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items %u (with extended cost %u, type %u) for vendor (Entry: %u), ignoring", vItem.item, vItem.ExtendedCost, vItem.Type, vendor_entry);
return false;
}
- if (type == ITEM_VENDOR_TYPE_CURRENCY && maxcount == 0)
+ if (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && vItem.maxcount == 0)
{
- TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: %u, type: %u) with missing maxcount for vendor (%u), ignore", id, type, vendor_entry);
+ TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: %u, type: %u) with missing maxcount for vendor (%u), ignore", vItem.item, vItem.Type, vendor_entry);
return false;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index ff394c9ed3d..18f24f28df0 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -867,7 +867,7 @@ class TC_GAME_API ObjectMgr
GameObjectTemplate const* GetGameObjectTemplate(uint32 entry) const;
GameObjectTemplateContainer const* GetGameObjectTemplates() const { return &_gameObjectTemplateStore; }
- int LoadReferenceVendor(int32 vendor, int32 item, uint8 type, std::set<uint32> *skip_vendors);
+ int LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32> *skip_vendors);
void LoadGameObjectTemplate();
void LoadGameObjectTemplateAddons();
@@ -1391,9 +1391,9 @@ class TC_GAME_API ObjectMgr
return &iter->second;
}
- void AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, uint8 type, bool persist = true); // for event
+ void AddVendorItem(uint32 entry, VendorItem const& vItem, bool persist = true); // for event
bool RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist = true); // for event
- bool IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 ptime, uint32 ExtendedCost, uint8 type, Player* player = nullptr, std::set<uint32>* skip_vendors = nullptr, uint32 ORnpcflag = 0) const;
+ bool IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, Player* player = nullptr, std::set<uint32>* skip_vendors = nullptr, uint32 ORnpcflag = 0) const;
void LoadScriptNames();
ScriptNameContainer const& GetAllScriptNames() const;
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index e24e2bfc7a2..cee3a98a452 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -617,6 +617,10 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid)
WorldPackets::NPC::VendorItem& item = packet.Items[count];
+ if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(vendorItem->PlayerConditionId))
+ if (!ConditionMgr::IsPlayerMeetingCondition(_player, playerCondition))
+ item.PlayerConditionFailed = playerCondition->ID;
+
if (vendorItem->Type == ITEM_VENDOR_TYPE_ITEM)
{
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(vendorItem->item);
@@ -658,8 +662,14 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid)
item.Quantity = leftInStock;
item.StackCount = itemTemplate->GetBuyCount();
item.Price = price;
+ item.DoNotFilterOnVendor = vendorItem->IgnoreFiltering;
item.Item.ItemID = vendorItem->item;
+ if (!vendorItem->BonusListIDs.empty())
+ {
+ item.Item.ItemBonus = boost::in_place();
+ item.Item.ItemBonus->BonusListIDs = vendorItem->BonusListIDs;
+ }
}
else if (vendorItem->Type == ITEM_VENDOR_TYPE_CURRENCY)
{
@@ -675,6 +685,7 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid)
item.Item.ItemID = vendorItem->item;
item.Type = vendorItem->Type;
item.StackCount = vendorItem->maxcount;
+ item.DoNotFilterOnVendor = vendorItem->IgnoreFiltering;
}
else
continue;
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 09b04004108..ad49f616877 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -368,6 +368,7 @@ public:
char* fextendedcost = strtok(nullptr, " "); //add ExtendedCost, default: 0
uint32 extendedcost = fextendedcost ? atoul(fextendedcost) : 0;
+ char const* fbonuslist = strtok(nullptr, " ");
Creature* vendor = handler->getSelectedCreature();
if (!vendor)
{
@@ -378,13 +379,27 @@ public:
uint32 vendor_entry = vendor->GetEntry();
- if (!sObjectMgr->IsVendorItemValid(vendor_entry, itemId, maxcount, incrtime, extendedcost, type, handler->GetSession()->GetPlayer()))
+ VendorItem vItem;
+ vItem.item = itemId;
+ vItem.maxcount = maxcount;
+ vItem.incrtime = incrtime;
+ vItem.ExtendedCost = extendedcost;
+ vItem.Type = type;
+
+ if (fbonuslist)
+ {
+ Tokenizer bonusListIDsTok(fbonuslist, ';');
+ for (char const* token : bonusListIDsTok)
+ vItem.BonusListIDs.push_back(int32(atol(token)));
+ }
+
+ if (!sObjectMgr->IsVendorItemValid(vendor_entry, vItem, handler->GetSession()->GetPlayer()))
{
handler->SetSentErrorMessage(true);
return false;
}
- sObjectMgr->AddVendorItem(vendor_entry, itemId, maxcount, incrtime, extendedcost, type);
+ sObjectMgr->AddVendorItem(vendor_entry, vItem);
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);