Paragon: narrow weapon bypass; cascade talent ranks; Savage Defense

* Weapon-narrow: replace blanket EquippedItemSubClassMask bypass with an
  explicit allowlist (IsParagonWeaponSubclassWildcardSpell, currently
  Maelstrom Weapon 51528 only). Applied at all three gates: Player::
  HasItemFitToSpellRequirements, Player::CheckAttackFitToAuraRequirement,
  and Aura::IsProcTriggeredOnEvent. Maelstrom Weapon still procs from any
  weapon for Paragon, but Hack and Slash / Sword / Mace Specialization
  remain correctly weapon-gated.
* Talent ability-rank cascade: TeachLevelGatedAbilityChainNoPanel +
  CascadeRanksForTalentLearnSpellEffects walk the SPELL_EFFECT_LEARN_SPELL
  chain a talent rank grants and learn each rank up to the player's level.
  Wired into HandleCommit (on talent purchase) and OnPlayerLevelChanged
  (on level-up). Fixes Mangle (and any future LEARN_SPELL talent) being
  stuck at rank 1 because Player::learnSkillRewardedSpells is intentionally
  disabled for Paragon's class skill lines.
* Riding-skill gate for flight forms (IsParagonSpellAllowedByRidingSkill):
  Flight Form (33943) requires Expert Riding (34090); Swift Flight Form
  (40120) requires Artisan Riding (34091). Applied in PanelLearnSpellChain
  and TeachLevelGatedAbilityChainNoPanel so the cascade can't push past a
  rank the player isn't trained for. Also backticks `rank` in the level-up
  query (MySQL reserved word).
* Savage Defense (62600) on the panel: the SpellData bake's blanket
  SPELL_ATTR0_PASSIVE filter dropped Savage Defense even though Druid
  trainer 33 sells it at level 40. Bake (in fractured-tooling) now carves
  out a small PASSIVE_TRAINER_ALLOWLIST and the regenerated
  paragon_spell_ae_cost.sql + 2026_05_11_05.sql migration surface it on
  the Druid Feral spell tab.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Docker Build
2026-05-11 19:09:25 -04:00
parent 7c57abd69f
commit 999f7e94bd
7 changed files with 228 additions and 31 deletions
@@ -472,6 +472,7 @@ INSERT INTO `paragon_spell_ae_cost` (`spell_id`, `ae_cost`) VALUES
(61999, 1),
(62078, 1),
(62124, 1),
(62600, 1),
(62757, 1),
(64382, 1),
(64843, 1),
@@ -0,0 +1,19 @@
-- mod-paragon: surface Savage Defense (62600) on the Druid Feral spell tab
-- of the Character Advancement panel.
--
-- The bake (tools/_gen_paragon_advancement_spells_lua.py) used to drop every
-- SPELL_ATTR0_PASSIVE spell up front, even when the trainer explicitly sells
-- it. That filter was correct for class-internal triggers (Feline Grace, etc.)
-- but kicked out Savage Defense -- a passive that DRUID trainer 33 sells at
-- level 40 (trainer_spell.sql line 2457). Bake now carves out a small
-- PASSIVE_TRAINER_ALLOWLIST so legitimate trainer-taught passives survive.
--
-- The base file (data/sql/db-world/base/paragon_spell_ae_cost.sql) was
-- regenerated alongside this migration so fresh deployments already have
-- this row. Existing servers do not re-run base files on content change,
-- so this update inserts the new (spell_id, ae_cost) pair idempotently.
-- INSERT IGNORE keeps any per-row tuning a server operator may have already
-- applied.
INSERT IGNORE INTO `paragon_spell_ae_cost` (`spell_id`, `ae_cost`) VALUES
(62600, 1); -- Savage Defense (Druid, trainer 33, level 40)