diff --git a/modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql b/modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql new file mode 100644 index 0000000..06abc7c --- /dev/null +++ b/modules/mod-paragon/data/sql/db-world/updates/2026_05_10_04.sql @@ -0,0 +1,30 @@ +-- mod-paragon: extend ItemTemplate::AllowableClass to include class 12 +-- (Paragon, bit 1<<11 = 2048) for every class-restricted item. +-- +-- Server-side, Player::CanUseItem (PlayerStorage.cpp) already short- +-- circuits the AllowableClass check for class 12. That's enough for any +-- code path the server controls (vendor list filter, AH "usable" filter, +-- CanRollForItemInLFG, CanBuyItem). It is NOT enough on the 3.3.5 client: +-- the WoW.exe binary independently pre-checks AllowableClass against the +-- player's class on right-click of a bag item and refuses *locally* with +-- the red "You can't use that item." text in UIErrorsFrame, never sending +-- CMSG_USE_ITEM at all. Server logs stay silent; only client knows it +-- refused. +-- +-- Fix: OR class 12's bit into AllowableClass on every class-restricted +-- row so the client engine's pre-check passes for Paragon. Other +-- classes' bits are unchanged, so e.g. a warrior-only item is still +-- warrior-only for everyone except Paragon. Items with AllowableClass +-- == -1 ("all classes") or 0 ("no restriction recorded") already pass +-- the client engine's check and are not touched. +-- +-- After applying this migration the *client* still caches item info in +-- Cache//itemcache.wdb. Players who already inspected the item +-- before the change must delete that file (or the whole Cache folder) +-- and reconnect to repopulate it from the worldserver, otherwise the +-- stale cached AllowableClass keeps the engine pre-check failing. + +UPDATE `item_template` + SET `AllowableClass` = `AllowableClass` | 2048 + WHERE `AllowableClass` > 0 + AND (`AllowableClass` & 2048) = 0; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 2084e1d..75b2198 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -7296,8 +7296,16 @@ SpellCastResult Spell::CheckItems(uint32* param1, uint32* param2) { // Xinef: this is not true in my opinion, in eg bladestorm will not be canceled after disarm //if (!HasTriggeredCastFlag(TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT)) - if (m_caster->IsPlayer() && !m_caster->ToPlayer()->HasItemFitToSpellRequirements(m_spellInfo)) - return SPELL_FAILED_EQUIPPED_ITEM_CLASS; + if (m_caster->IsPlayer()) + { + // Cast-from-glyph: many glyph on-use spells set EquippedItemClass to ITEM_CLASS_GLYPH. + // HasItemFitToSpellRequirements only implements weapon/armor, so it would always fail here + // even though the glyph item in the bag is the valid spell source. + bool const castFromGlyphScroll = m_CastItem && m_CastItem->GetTemplate() && + m_CastItem->GetTemplate()->Class == ITEM_CLASS_GLYPH; + if (!castFromGlyphScroll && !m_caster->ToPlayer()->HasItemFitToSpellRequirements(m_spellInfo)) + return SPELL_FAILED_EQUIPPED_ITEM_CLASS; + } } // do not take reagents for these item casts