Files
Fractured/modules/mod-paragon
Docker Build 4d2a80ddb8 Paragon: server-authoritative CP/rune sync + cascade-spell revoke hardening
mod-paragon Paragon_Essence.cpp:
- Broaden SkillLinesLinkedToSpell: collect every SkillLineAbility row for
  an anchor spell regardless of AcquireMethod, so anchor spells whose
  primary SLA uses AcquireMethod 0 (e.g. Blood Strike) correctly identify
  their skill lines and let the dependent classifier do its job.
- IsSpellSkillLineCascadeDependent / RevokeUnwantedCascadeSpellsForPlayer
  use the broadened helper. HandleCommit calls the post-purchase sweep
  immediately so the spellbook never carries lingering cascade dependents
  (Blood Presence / Forceful Deflection / Death Coil / Death Grip).
- New character_paragon_panel_spell_revoked table tracks which active
  dependents we've revoked per (guid, parent) so OnPlayerLogin can
  re-revoke them after AC's _LoadSkills -> learnSkillRewardedSpells
  silently re-grants them.
- OnPlayerLogin opens the client SILENCE window via SendSilenceOpenForCommit
  with an empty allow list and intentionally omits the matching
  SendSilenceClose: the chat frame buffers CHAT_MSG_SYSTEM during the
  loading screen and only flushes after PLAYER_ENTERING_WORLD, so a paired
  CLOSE would shut the filter before the buffered "you have unlearned X"
  toasts hit it. The addon's 8s fail-open closes the window after the flush.
- New `.paragon hat` chat command for diagnosing Honor Among Thieves
  triggers (talent rank, learned spell, applied aura, proc table entry).

mod-paragon Paragon_SC.cpp:
- OnPlayerUpdate pushes server-authoritative combo points to the client
  via PARAA "R CP <n>" whenever the count changes. The client-side
  ComboFrame Paragon simulator listens for this and updates the target
  frame, fixing HAT-generated CP not displaying (HAT's trigger casts
  with a null target, which the combat-log inference path can't see).
- OnPlayerUpdate also pushes "R RUNES <cd0..cd5>" (ms remaining per
  rune slot) on rune mask changes, so the client RuneFrame simulator
  stays in lock-step with Spell::TakeRunePower instead of drifting
  through combat-log latency.

mod-paragon SQL:
- New updates/2026_05_09_00.sql migration creates
  character_paragon_panel_spell_revoked for AC's auto-DBUpdater so a
  fresh checkout can stand up an existing characters DB without
  manual intervention. Matching CREATE TABLE IF NOT EXISTS in
  base/character_paragon_panel_learned.sql for fresh installs.

mod-paragon conf:
- New Paragon.Diag.PanelLearn flag traces every PanelLearnSpellChain
  commit (chain ids, before/after spell-map sizes, side-spell
  classification) for diagnosing "spell reappears on relog" bugs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-09 11:22:57 -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.