From f2952c905aa2adf0e5228ec4a94c467a3f8bc5c7 Mon Sep 17 00:00:00 2001 From: Docker Build Date: Sat, 9 May 2026 22:38:32 -0400 Subject: [PATCH] Fractured: strip class-spell reagents at load; Paragon relic ranged slot - SpellInfoCorrections: zero Reagent/ReagentCount on spells with non-zero SpellFamilyName so class abilities no longer require shards, candles, etc., while profession crafts (SpellFamilyName 0) keep mats. Matches the client Spell.dbc bake in patch-enUS-4.MPQ. - Paragon_SC: OnPlayerIsClass returns true for CLASS_CONTEXT_EQUIP_RELIC for paladin/druid/shaman/warlock/dk so Paragon can equip all relic types in the ranged slot. - CLIENT-PATCHES: document Spell.dbc reagent pass, rune script order, and stock ammo slot behavior in patch-enUS-5. Co-authored-by: Cursor --- .../fractured-dev-extras/CLIENT-PATCHES.md | 11 ++++-- modules/mod-paragon/src/Paragon_SC.cpp | 31 +++++++++++++++ .../game/Spells/SpellInfoCorrections.cpp | 38 +++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/contrib/fractured-dev-extras/CLIENT-PATCHES.md b/contrib/fractured-dev-extras/CLIENT-PATCHES.md index a2ef527..a6fc8b2 100644 --- a/contrib/fractured-dev-extras/CLIENT-PATCHES.md +++ b/contrib/fractured-dev-extras/CLIENT-PATCHES.md @@ -13,8 +13,8 @@ This file is the table of contents and install guide. | Artifact | Size | Purpose | |---|---|---| -| `patch-enUS-4.MPQ` | ~5 MB | DBC + GlueXML bake. Adds `CLASS_PARAGON` (id 12), the character-create slot, glue strings, talent-tab DBC entries, and the Paragon resource bar definitions. Required for character creation as Paragon to even show up. | -| `patch-enUS-5.MPQ` | ~50 KB | FrameXML overrides. Replaces stock `PlayerFrame.lua` / `RuneFrame.lua` / `ComboFrame.lua` / `UnitFrame.lua` / `SpellBookFrame.lua` + `SpellBookFrame.xml` with Paragon-aware versions: rune simulator, combo-point simulator, server-authoritative resource sync over the `PARAA` addon channel, action-button usability + click guards, an expanded spellbook (higher `MAX_SPELLS`, 24 skill-line tabs instead of stock 8) so all-class spells render, and a tooltip post-processor that appends ", Paragon" to the "Classes:" line on class-restricted gear / glyphs (the server bypasses `AllowableClass` for class 12, but the engine paints the line red and omits Paragon — the Lua hook recolors it green and adds the name so the player can tell it's wearable). | +| `patch-enUS-4.MPQ` | ~5 MB | DBC + GlueXML bake. Adds `CLASS_PARAGON` (id 12), the character-create slot, glue strings, game-table DBCs, and a patched `Spell.dbc`: **(1)** `RuneCostID` zeroed on every rune-cost spell so non–Death Knight clients still send DK casts (rune costs are shown via `RuneFrame.lua`); **(2)** `Reagent[]` / `ReagentCount[]` zeroed on every spell whose `SpellFamilyName` is non-zero (all class abilities), while profession crafts (`SpellFamilyName == 0`) keep their materials. Both edits mirror server load-time corrections so client preflight and server validation stay aligned. Required for character creation as Paragon to even show up. | +| `patch-enUS-5.MPQ` | ~50 KB | FrameXML overrides. Replaces stock `PlayerFrame.lua` / `RuneFrame.lua` / `ComboFrame.lua` / `UnitFrame.lua` / `SpellBookFrame.lua` + `SpellBookFrame.xml` with Paragon-aware versions: rune simulator, combo-point simulator, server-authoritative resource sync over the `PARAA` addon channel, action-button usability + click guards, an expanded spellbook (higher `MAX_SPELLS`, 24 skill-line tabs instead of stock 8) so all-class spells render, Paragon stat tooltips on the character sheet, and a tooltip post-processor that appends ", Paragon" to the "Classes:" line on class-restricted gear / glyphs (the server bypasses `AllowableClass` for class 12, but the engine paints the line red and omits Paragon — the Lua hook recolors it green and adds the name so the player can tell it's wearable). The paper-doll **ammo slot** follows stock visibility rules (shown for hunters / ranged weapons; hidden when `UnitHasRelicSlot` applies). | | `patch-enUS-6.MPQ` | ~160 KB | The `ParagonAdvancement` addon. Replaces the talent pane (`N` key) for Paragon characters with the Character Advancement panel: per-class spell tabs, talent grid, Overview/Search tabs, AE/TE currency, commit / reset / preview, login-time toast suppression. | | `Wow.exe` | ~7.5 MB | 3.3.5a (build 12340) client byte-patched to skip the MPQ signature check so custom `patch-enUS-N.MPQ` files load. Diff against stock is a few bytes; everything else is unchanged. | @@ -233,7 +233,12 @@ tools\build_paragon_advancement_patch.ps1 -Deploy # -> patch-enUS-6.MPQ `patch-enUS-4.MPQ` is the DBC + GlueXML bake; the bake scripts live with the rest of the dev tooling and are not part of this repo by design -(see the repo-tidy policy in `README.txt` next to this file). +(see the repo-tidy policy in `README.txt` next to this file). Typical +order on a maintainer machine: + +1. `fractured-tooling/from-workspace-root/_patch_spell_dbc_runes.py` — stage `Spell.dbc` with `RuneCostID` cleared. +2. `fractured-tooling/from-workspace-root/_patch_spell_dbc_reagents.py` — same staged `Spell.dbc`, clear class-spell reagents for client preflight. +3. `fractured-tooling/from-workspace-root/_make_paragon_dbc_patch.py` — rebuild `ChrClasses` / `CharBaseInfo` / game tables, then pack `patch-enUS-4.MPQ`. The patched `Wow.exe` is a one-time hex-edit of the stock 3.3.5a client. The diff is publicly documented in the WoW emulation community diff --git a/modules/mod-paragon/src/Paragon_SC.cpp b/modules/mod-paragon/src/Paragon_SC.cpp index 476e36b..1493c27 100644 --- a/modules/mod-paragon/src/Paragon_SC.cpp +++ b/modules/mod-paragon/src/Paragon_SC.cpp @@ -65,6 +65,37 @@ public: return true; } + // Unified relic / ranged slot for class 12. + // ---------------------------------------------------------------- + // CLASS_CONTEXT_EQUIP_RELIC is read in exactly two places in core + // (PlayerStorage.cpp): FindEquipSlot's INVTYPE_RELIC switch, which + // routes Librams/Idols/Totems/Misc/Sigils into EQUIPMENT_SLOT_RANGED + // for the matching class only, and CanEquipUniqueItem's per-subclass + // proficiency gate. By claiming this context for paladin/druid/ + // shaman/warlock/dk we let Paragon drop any of those relics into the + // ranged slot exactly the same way each native class does, with no + // core patch and no other side effects (the constant is not read + // anywhere else in the codebase). + // + // Bows/guns/crossbows already equip via the regular + // INVTYPE_RANGED/RANGEDRIGHT routing -- weapon proficiencies for + // class 12 are seeded by the Paragon proficiency SQL migrations, so + // they pass the GetSkillValue check in CanEquipUniqueItem. + if (context == CLASS_CONTEXT_EQUIP_RELIC) + { + switch (unitClass) + { + case CLASS_PALADIN: + case CLASS_DRUID: + case CLASS_SHAMAN: + case CLASS_WARLOCK: + case CLASS_DEATH_KNIGHT: + return true; + default: + break; + } + } + return std::nullopt; } diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 03b9107..39905a4 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5368,6 +5368,44 @@ void SpellMgr::LoadSpellInfoCorrections() LockEntry* key = const_cast(sLockStore.LookupEntry(36)); // 3366 Opening, allows to open without proper key key->Type[2] = LOCK_KEY_NONE; + // Fractured: strip reagent requirements from every player-class spell at + // load time. Filtered by SpellFamilyName != 0 so that profession spells + // (cooking, alchemy, enchanting, blacksmithing, jewelcrafting, leatherworking, + // tailoring, engineering, inscription, mining, herbalism, skinning, fishing, + // first aid — all SpellFamilyName == SPELLFAMILY_GENERIC == 0) keep their + // mats and only the class abilities that asked for ankhs / candles / soul + // shards / verdant spheres / etc. cast freely. Done here in core spell + // data rather than as a runtime bypass in Spell::CheckItems / TakeReagents + // so the change is data-driven (the in-memory SpellInfo simply has no + // reagents to require). The client-side preflight is mirrored by the + // matching Spell.dbc patch shipped via patch-enUS-4.MPQ + // (fractured-tooling/_patch_spell_dbc_reagents.py). + { + uint32 fixedClassSpells = 0; + for (uint32 spellId = 1; spellId < sSpellMgr->GetSpellInfoStoreSize(); ++spellId) + { + SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId); + if (!info || info->SpellFamilyName == 0) + continue; + + bool hadAny = false; + for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i) + if (info->Reagent[i] != 0 || info->ReagentCount[i] != 0) + { hadAny = true; break; } + if (!hadAny) + continue; + + SpellInfo* mut = const_cast(info); + for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i) + { + mut->Reagent[i] = 0; + mut->ReagentCount[i] = 0; + } + ++fixedClassSpells; + } + LOG_INFO("server.loading", ">> Fractured: cleared reagents on {} class spells", fixedClassSpells); + } + LOG_INFO("server.loading", ">> Loading spell dbc data corrections in {} ms", GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); }