diff options
-rw-r--r-- | sql/updates/world/master/2017_10_09_00_world.sql | 9 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/WorldDatabase.cpp | 2 | ||||
-rw-r--r-- | src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp | 96 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/CreatureData.h | 10 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 11 | ||||
-rw-r--r-- | src/server/game/Events/GameEventMgr.cpp | 57 | ||||
-rw-r--r-- | src/server/game/Events/GameEventMgr.h | 19 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 119 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 6 | ||||
-rw-r--r-- | src/server/game/Handlers/ItemHandler.cpp | 11 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_npc.cpp | 19 |
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); |