Fractured: Paragon core hooks, mod-paragon, mod-ale, Docker build cap

- Track mod-paragon and mod-ale (un-ignore modules in .gitignore).
- Ship docker-compose.override.yml with CMAKE_EXTRA_OPTIONS for LuaJIT (mod-ale).
- Dockerfile: CBUILD_PARALLEL default to limit OOM under Docker/WSL2.
- Core: CLASS_PARAGON sticky combo points (DetachComboTarget), selection rebind,
  Spell::CheckPower rune path for multi-resource Paragon.
- spell_dk_death_rune: IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY) for
  Blood of the North / Reaping / DRM on Paragon.
- Remove temporary Paragon CheckPower logging.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Docker Build
2026-05-08 00:03:09 -04:00
parent f9f2bc5e0c
commit 8e4c8f57e4
163 changed files with 54817 additions and 10 deletions
+107 -1
View File
@@ -25,6 +25,7 @@
#include "CellImpl.h"
#include "CharacterCache.h"
#include "CharmInfo.h"
#include "Config.h"
#include "Chat.h"
#include "ChatPackets.h"
#include "ChatTextBuilder.h"
@@ -13224,6 +13225,37 @@ void Unit::RestoreDisplayId()
SetDisplayId(GetNativeDisplayId());
}
// mod-paragon: returns true if this unit is a player whose combo-point pool
// should persist across target swaps. Originally Paragon-only; widened to
// cover the natively combo-point-using classes (Rogue, Druid) so the same
// "stored CP pool" UX applies to them. Gate retains the existing config key
// `Paragon.StickyComboPoints` for backward compatibility.
static bool IsStickyComboPointsClass(Unit const* unit)
{
if (!unit || !unit->IsPlayer())
return false;
if (!sConfigMgr->GetOption<bool>("Paragon.StickyComboPoints", true))
return false;
uint8 cls = unit->ToPlayer()->getClass();
return cls == CLASS_PARAGON || cls == CLASS_ROGUE || cls == CLASS_DRUID;
}
uint8 Unit::GetComboPoints(Unit const* who) const
{
if (IsStickyComboPointsClass(this))
return m_comboPoints;
return (who && m_comboTarget != who) ? 0 : m_comboPoints;
}
uint8 Unit::GetComboPoints(ObjectGuid const& guid) const
{
if (IsStickyComboPointsClass(this))
return m_comboPoints;
return (m_comboTarget && m_comboTarget->GetGUID() == guid) ? m_comboPoints : 0;
}
void Unit::AddComboPoints(Unit* target, int8 count)
{
if (!count)
@@ -13231,6 +13263,32 @@ void Unit::AddComboPoints(Unit* target, int8 count)
return;
}
if (IsStickyComboPointsClass(this))
{
if (target)
{
if (target != m_comboTarget)
{
if (m_comboTarget)
m_comboTarget->RemoveComboPointHolder(this);
m_comboTarget = target;
target->AddComboPointHolder(this);
}
}
m_comboPoints = std::max<int8>(std::min<int8>(m_comboPoints + count, 5), 0);
if (!m_comboPoints && m_comboTarget)
{
m_comboTarget->RemoveComboPointHolder(this);
m_comboTarget = nullptr;
}
SendComboPoints();
return;
}
if (target && target != m_comboTarget)
{
if (m_comboTarget)
@@ -13250,6 +13308,20 @@ void Unit::AddComboPoints(Unit* target, int8 count)
SendComboPoints();
}
void Unit::RebindComboTarget(Unit* newTarget)
{
if (!newTarget || newTarget == m_comboTarget || m_comboPoints <= 0)
return;
if (m_comboTarget)
m_comboTarget->RemoveComboPointHolder(this);
m_comboTarget = newTarget;
newTarget->AddComboPointHolder(this);
SendComboPoints();
}
void Unit::ClearComboPoints()
{
if (!m_comboTarget)
@@ -13267,6 +13339,26 @@ void Unit::ClearComboPoints()
m_comboTarget = nullptr;
}
void Unit::DetachComboTarget()
{
// mod-paragon: sticky-CP holder cleanup. Used when the target unit is
// dying / despawning but we want to keep the holder's m_comboPoints
// intact so they can finish on a fresh target. Pool re-binds via
// Unit::RebindComboTarget on the next SetSelection (or via the next
// AddComboPoints if the player generates more before tabbing).
//
// This intentionally does NOT touch SPELL_AURA_RETAIN_COMBO_POINTS and
// does NOT call SendComboPoints — sticky-CP classes drive their target-
// frame paint client-side (ComboFrame.lua's simulator for Paragon, the
// sticky cache for Rogue/Druid), and a fake "binding to nullguid" packet
// would just confuse the engine.
if (m_comboTarget)
{
m_comboTarget->RemoveComboPointHolder(this);
m_comboTarget = nullptr;
}
}
void Unit::SendComboPoints()
{
if (m_cleanupDone)
@@ -13309,7 +13401,21 @@ void Unit::ClearComboPointHolders()
{
while (!m_ComboPointHolders.empty())
{
(*m_ComboPointHolders.begin())->ClearComboPoints(); // this also removes it from m_comboPointHolders
Unit* holder = *m_ComboPointHolders.begin();
// mod-paragon: this is the *target* dying / despawning, iterating
// every player who has CPs anchored to it. Stock behavior wipes the
// holder's pool — that's the literal opposite of what
// Paragon.StickyComboPoints promises. For sticky-CP classes
// (Paragon / Rogue / Druid) detach the binding only and keep the
// count, so the next finisher on a fresh target still has fuel.
if (IsStickyComboPointsClass(holder))
{
holder->DetachComboTarget(); // also removes holder from m_ComboPointHolders
}
else
{
holder->ClearComboPoints(); // this also removes it from m_comboPointHolders
}
}
}