Files
Fractured/modules/mod-paragon
Docker Build 9fb80102c8 Paragon: spell unlearn queue + AE/TE reconciliation
Two related additions to mod-paragon:

  * HandleCommit gains a third payload section, " u:<id>,...", carrying
    spell IDs the player wants to refund + unlearn in the same commit
    that learns / talents through. The protocol stays backward-compat
    (older clients omit the section). PanelUnlearnSpellPurchase mirrors
    the per-spell branch of HandleParagonResetAbilities: tracked passive
    children are removed first, then the chain head, then panel_spells /
    panel_spell_children / panel_spell_revoked rows for that purchase
    are dropped, then LookupSpellAECost(head) is refunded into the
    cache. Unlearns are applied before learns inside the commit so the
    refund covers the same-commit spends. Allow-list for the silence
    window now includes chain ranks + panel_spell_children for the
    intentional unlearns so "You have unlearned X" toasts stay visible
    for the targeted spell while cascade dependents stay silenced.

  * ReconcileEssenceForPlayer reads panel_spells + panel_talents and
    sets the cache to ComputeStartingAE/TE(level) - sum-of-spends.
    Self-heals drift in either direction: clamps the cache down when
    the player has more essence than their level + spends allow
    (cheese clamp), and tops up when they have less (admin-tweak /
    crash recovery). Wired into OnPlayerLogin (after LoadCurrencyFromDb,
    before PushCurrency so the first balance the client sees is the
    reconciled one) and OnPlayerLevelChanged (replaces the old
    GrantLevelUpEssence delta -- Reconcile sets the absolute correct
    balance from level + spend, so it subsumes the per-level grant and
    the cheese clamp in one call). Costs come from the same
    paragon_spell_ae_cost / config keys HandleCommit uses so the math
    stays in lockstep across any future cost rebalance.

Both features ship in patch-enUS-6.MPQ v0.9.16: right-click a learned
spell row to queue an unlearn (header shows +N AE refund preview) and
hit Learn All to apply. The icon picker also got two fixes -- the
leading INV_Misc_QuestionMark is no longer duplicated, and the
selection ring is now a tooltip-border Frame anchored to the cell
bounds (the prior UI-ActionButton-Border texture rendered nearly
invisible at non-native sizes).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 19:57:47 -04:00
..

mod-paragon

Server-side support module for the custom CLASS_PARAGON (id 12).

What it does today

Hooks Player::IsClass / Player::HasActivePowerType so Paragon inherits Death Knight ability mechanics where it matters:

  • Rune system: InitRunes, rune cooldown tick, runic power regen, Spell::CheckRuneCost / Spell::TakeRunePower, SMSG_RESYNC_RUNES cast flags, combat exit grace reset.
  • DK ability scripts: spell_dk_*, MUST_BE_DEATH_KNIGHT cast result, DK aura effects.

It is intentionally narrow: the hook only fires for (realClass == PARAGON, queriedClass == DEATH_KNIGHT, context == CLASS_CONTEXT_ABILITY). Other DK-flavored contexts (Ebon Hold teleport gating, heroic start level, DK-only taxi mount, talent point math on Ebon Hold, etc.) keep their normal behavior for Paragon.

HasActivePowerType additionally claims POWER_RUNIC_POWER and POWER_RUNE for Paragon, which keeps the rune pool sized correctly in Player::InitStatsForLevel even if Paragon's primary power type is later switched away from runic power.

Building

Auto-detected by modules/CMakeLists.txt (GetModuleSourceList globs every subdirectory). No additional CMake plumbing is needed; rebuild the worldserver image and the loader symbol Addmod_paragonScripts gets linked into the static modules target.

SQL layout

SQL files live under data/sql/db-world/base/ and data/sql/db-characters/base/ — the standard AzerothCore module path that the built-in DBUpdater scans (see src/server/database/Updater/UpdateFetcher.cpp). Files placed there are applied automatically by worldserver / dbimport on startup and recorded by hash in the updates table of the target database, so re-runs are idempotent. Any new SQL added under those directories will be picked up on the next container/server start without manual import.