diff --git a/data/sql/updates/pending_db_world/rev_1758916393003655928.sql b/data/sql/updates/pending_db_world/rev_1758916393003655928.sql new file mode 100644 index 000000000..980a335d2 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1758916393003655928.sql @@ -0,0 +1,6 @@ +-- +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=23 AND `SourceEntry`=0 AND `SourceId`=0 AND `SourceGroup` IN (3443, 12919, 15471); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 9087) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 12) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 109) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 9087, 0, 0, 0, 12, 0, 109, 0, 0, 0, 0, 0, '', 'event \'Sun\'s Reach Reclamation Phase Anvil\' must be active'); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index d310563ce..a82d55eb8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -90,6 +90,7 @@ #include "WorldStateDefines.h" #include "WorldStatePackets.h" #include +#include /// @todo: this import is not necessary for compilation and marked as unused by the IDE // however, for some reasons removing it would cause a damn linking issue @@ -14364,6 +14365,67 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const return false; } +/** + * @brief Checks if any vendor option is available in the gossip menu tree for a given creature. + * + * @param menuId The starting gossip menu ID to check. + * @param creature Pointer to the creature whose gossip menus are being checked. + * @return true if a vendor option is available in any accessible menu; false otherwise. + */ +bool Player::AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const +{ + std::set visitedMenus; + std::queue menusToCheck; + menusToCheck.push(menuId); + + while (!menusToCheck.empty()) + { + uint32 const currentMenuId = menusToCheck.front(); + menusToCheck.pop(); + + if (visitedMenus.find(currentMenuId) != visitedMenus.end()) + continue; + + visitedMenus.insert(currentMenuId); + + GossipMenuItemsMapBounds menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(currentMenuId); + + if (menuItemBounds.first == menuItemBounds.second && currentMenuId != 0) + continue; + + for (auto itr = menuItemBounds.first; itr != menuItemBounds.second; ++itr) + { + if (!sConditionMgr->IsObjectMeetToConditions(const_cast(this), const_cast(creature), itr->second.Conditions)) + continue; + + if (itr->second.OptionType == GOSSIP_OPTION_VENDOR) + return true; + else if (itr->second.ActionMenuID) + { + GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(itr->second.ActionMenuID); + bool menuAccessible = false; + + if (menuBounds.first == menuBounds.second) + menuAccessible = true; + else + { + for (auto menuItr = menuBounds.first; menuItr != menuBounds.second; ++menuItr) + if (sConditionMgr->IsObjectMeetToConditions(const_cast(this), const_cast(creature), menuItr->second.Conditions)) + { + menuAccessible = true; + break; + } + } + + if (menuAccessible) + menusToCheck.push(itr->second.ActionMenuID); + } + } + } + + return false; +} + bool Player::CanSeeVendor(Creature const* creature) const { if (!creature->HasNpcFlag(UNIT_NPC_FLAG_VENDOR)) @@ -14371,9 +14433,11 @@ bool Player::CanSeeVendor(Creature const* creature) const ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(creature->GetEntry(), 0); if (!sConditionMgr->IsObjectMeetToConditions(const_cast(this), const_cast(creature), conditions)) - { return false; - } + + uint32 const menuId = creature->GetCreatureTemplate()->GossipMenuId; + if (!AnyVendorOptionAvailable(menuId, creature)) + return false; return true; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 7cf536582..ea2cd277a 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -41,6 +41,7 @@ #include "TradeData.h" #include "Unit.h" #include "WorldSession.h" +#include #include #include @@ -2548,7 +2549,9 @@ public: //bool isActiveObject() const { return true; } bool CanSeeSpellClickOn(Creature const* creature) const; [[nodiscard]] bool CanSeeVendor(Creature const* creature) const; - +private: + [[nodiscard]] bool AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const; +public: [[nodiscard]] uint32 GetChampioningFaction() const { return m_ChampioningFaction; } void SetChampioningFaction(uint32 faction) { m_ChampioningFaction = faction; } Spell* m_spellModTakingSpell; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index eb1a3f06b..1fbbf0d9c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -20791,7 +20791,10 @@ void Unit::PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPoi appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK; if (!target->CanSeeVendor(creature)) + { + appendValue &= ~UNIT_NPC_FLAG_REPAIR; appendValue &= ~UNIT_NPC_FLAG_VENDOR_MASK; + } if (!creature->IsValidTrainerForPlayer(target, &appendValue)) appendValue &= ~UNIT_NPC_FLAG_TRAINER;