diff --git a/sql/updates/world/4.3.4/2023_07_17_00_world.sql b/sql/updates/world/4.3.4/2023_07_17_00_world.sql new file mode 100644 index 0000000000..04b4e19fb5 --- /dev/null +++ b/sql/updates/world/4.3.4/2023_07_17_00_world.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS `playercondition_dbc`; +CREATE TABLE `playercondition_dbc` ( + `ID` INT UNSIGNED NOT NULL, + `FailureDescription` VARCHAR(100) NOT NULL, + PRIMARY KEY (`ID`) +); + +INSERT INTO `playercondition_dbc` (`ID`, `FailureDescription`) VALUES +(12251, 'Serverside/Non-DB2: Requires Stormwind - Revered'), +(12252, 'Serverside/Non-DB2: Requires Stormwind - Exalted'); + +UPDATE `npc_vendor` SET `PlayerConditionID`= 12252 WHERE `item` IN (64901, 64902, 64903); +UPDATE `npc_vendor` SET `PlayerConditionID`= 12251 WHERE `item`= 67531; diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index dc50e42153..da3c0aced6 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1086,6 +1086,26 @@ bool ConditionMgr::IsSpellUsedInSpellClickConditions(uint32 spellId) const return SpellsUsedInSpellClickConditions.find(spellId) != SpellsUsedInSpellClickConditions.end(); } +/*static*/ bool ConditionMgr::IsPlayerMeetingCondition(Player const* player, PlayerConditionEntry const* playerCondition) +{ + // Blizzard has never shipped a proper PlayerCondition.dbc entry with Cataclysm and many of its entries are serverside. So we have to hardcode the checks on our own. + switch (playerCondition->ID) + { + case 9414: return player->GetQuestStatus(46) == QUEST_STATUS_REWARDED; + case 12161: return player->GetQuestStatus(29279) == QUEST_STATUS_REWARDED; + case 12163: return player->GetQuestStatus(29283) == QUEST_STATUS_REWARDED; + case 12212: return player->GetQuestStatus(29281) == QUEST_STATUS_REWARDED; + case 12251: return player->GetReputationRank(72) == REP_REVERED; + case 12252: return player->GetReputationRank(72) == REP_EXALTED; + case 12380: return player->GetQuestStatus(29281) == QUEST_STATUS_REWARDED && player->GetQuestStatus(29283) == QUEST_STATUS_REWARDED && player->GetQuestStatus(29279) == QUEST_STATUS_REWARDED; + case 12515: return player->GetQuestStatus(29201) == QUEST_STATUS_REWARDED; + default: + return true; + } + + return true; +} + ConditionMgr* ConditionMgr::instance() { static ConditionMgr instance; diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index 1246c44eed..a42c1749f5 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -33,6 +33,7 @@ class Unit; class WorldObject; class LootTemplate; struct Condition; +struct PlayerConditionEntry; enum ConditionTypes { // value1 value2 value3 @@ -269,6 +270,8 @@ class TC_GAME_API ConditionMgr bool IsSpellUsedInSpellClickConditions(uint32 spellId) const; + static bool IsPlayerMeetingCondition(Player const* player, PlayerConditionEntry const* playerCondition); + struct ConditionTypeInfo { char const* Name; diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index ff93c0aea5..6542d5dc9c 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -170,6 +170,7 @@ NameValidationRegexContainer NamesReservedValidators; DBCStorage sOverrideSpellDataStore(OverrideSpellDatafmt); +DBCStorage sPlayerConditionStore(PlayerConditionfmt); DBCStorage sPowerDisplayStore(PowerDisplayfmt); DBCStorage sPvPDifficultyStore(PvPDifficultyfmt); @@ -522,10 +523,11 @@ void DBCManager::LoadStores(const std::string& dataPath, uint32 defaultLocale) #define LOAD_DBC_EXT(store, file, dbformat, dbpk) LoadDBC(availableDbcLocales, bad_dbc_files, store, dbcPath, file, defaultLocale, dbformat, dbpk) - LOAD_DBC_EXT(sAchievementStore, "Achievement.dbc", CustomAchievementfmt, CustomAchievementIndex);//15595 - LOAD_DBC_EXT(sSpellStore, "Spell.dbc", CustomSpellEntryfmt, CustomSpellEntryIndex);// - LOAD_DBC_EXT(sSpellEffectStore, "SpellEffect.dbc", CustomSpellEffectEntryfmt, CustomSpellEffectEntryIndex);//15595 - LOAD_DBC_EXT(sSpellDifficultyStore, "SpellDifficulty.dbc", CustomSpellDifficultyfmt, CustomSpellDifficultyIndex);//15595 + LOAD_DBC_EXT(sAchievementStore, "Achievement.dbc", CustomAchievementfmt, CustomAchievementIndex);//15595 + LOAD_DBC_EXT(sSpellStore, "Spell.dbc", CustomSpellEntryfmt, CustomSpellEntryIndex);//15595 + LOAD_DBC_EXT(sSpellEffectStore, "SpellEffect.dbc", CustomSpellEffectEntryfmt, CustomSpellEffectEntryIndex);//15595 + LOAD_DBC_EXT(sSpellDifficultyStore, "SpellDifficulty.dbc", CustomSpellDifficultyfmt, CustomSpellDifficultyIndex);//15595 + LOAD_DBC_EXT(sPlayerConditionStore, "PlayerCondition.dbc", CustomPlayerConditionEntryfmt, CustomPlayerConditionEntryIndex);//15595 #undef LOAD_DBC_EXT diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index f931d26406..a992243e74 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -80,7 +80,6 @@ TC_GAME_API extern DBCStorage sGameObjectDisplayI TC_GAME_API extern DBCStorage sGemPropertiesStore; TC_GAME_API extern DBCStorage sGlyphPropertiesStore; TC_GAME_API extern DBCStorage sGlyphSlotStore; - TC_GAME_API extern DBCStorage sGtBarberShopCostBaseStore; TC_GAME_API extern DBCStorage sGtCombatRatingsStore; TC_GAME_API extern DBCStorage sGtChanceToMeleeCritBaseStore; @@ -139,6 +138,7 @@ TC_GAME_API extern DBCStorage sLightStore; TC_GAME_API extern MapDifficultyMap sMapDifficultyMap; TC_GAME_API extern DBCStorage sMovieStore; TC_GAME_API extern DBCStorage sOverrideSpellDataStore; +TC_GAME_API extern DBCStorage sPlayerConditionStore; TC_GAME_API extern DBCStorage sPowerDisplayStore; TC_GAME_API extern DBCStorage sQuestSortStore; TC_GAME_API extern DBCStorage sQuestXPStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 0bea4627c4..ccc3ebb1d9 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1350,6 +1350,12 @@ struct OverrideSpellDataEntry // char* PlayerActionbar; // 12 }; +struct PlayerConditionEntry +{ + uint32 ID; // 0 + char* FailureDescription; // 1 +}; + struct PowerDisplayEntry { uint32 ID; // 0 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 2848c82bde..85d9b85958 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -124,6 +124,9 @@ const char QuestPOIBlobfmt[] = "niii"; const char QuestPOIPointfmt[] = "niii"; char const QuestSortEntryfmt[] = "nx"; char const QuestXPfmt[] = "niiiiiiiiii"; +char const PlayerConditionfmt[] = "ns"; +const std::string CustomPlayerConditionEntryfmt = "pa"; +const std::string CustomPlayerConditionEntryIndex = "ID"; char const PowerDisplayfmt[] = "nixxxx"; char const PvPDifficultyfmt[] = "diiiii"; char const RandomPropertiesPointsfmt[] = "niiiiiiiiiiiiiii"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b33359e231..319d27db54 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -21690,6 +21690,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/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index e106c057f8..da90f40d4f 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -634,12 +634,9 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid) WorldPackets::NPC::VendorItem& item = packet.Items[count]; - /* - // Todo: implement dbc file and handling if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(vendorItem->PlayerConditionId)) if (!ConditionMgr::IsPlayerMeetingCondition(_player, playerCondition)) item.PlayerConditionFailed = playerCondition->ID; - */ if (vendorItem->Type == ITEM_VENDOR_TYPE_ITEM) {