From adaf3eb19d1f909a79aeb8aa1ad241c46e823542 Mon Sep 17 00:00:00 2001 From: errolgr Date: Sun, 4 Oct 2026 20:38:42 -0400 Subject: [PATCH 1/3] Stats panel: apply weapon masteries only with matching weapons The client's stat list carries the passive stats of Two Hand Mastery (crushing blow, crushing blow efficiency), Javelin and Spear Mastery (critical strike multiplier) and Throwing Mastery (pierce) whatever weapon is equipped. GetMasteryStat only took the pierce bonus back out when the weapon does not match, and the pierce call itself was commented out, so the panel showed e.g. 2HM crushing blow with a one-handed axe or unarmed, the JSM multiplier with a bow, and Throwing Mastery pierce with any weapon. Remove all four when the weapon does not match the mastery's passive item type, and use the pierce correction again. Claw and Dagger Mastery's critical strike was only counted when the right skill's item type is hand-to-hand (Dragon Claw, Weapon Block). Attack ("weap") and PD2's claw skills ("asm": Tiger Strike, Fists of Fire, ...) never matched, so it was missing for nearly every skill. Count it for melee weapon skills, like One Hand Mastery, and for "asm" skills. --- BH/Constants.h | 1 + BH/D2Helpers.cpp | 9 ++++++--- BH/Drawing/Stats/StatsDisplay.cpp | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/BH/Constants.h b/BH/Constants.h index ef5d5ca2..41ab839a 100644 --- a/BH/Constants.h +++ b/BH/Constants.h @@ -1490,6 +1490,7 @@ enum TransactionTypes #define ITEM_TYPE_MISSILE_WEAPON 47 #define ITEM_TYPE_THROWN_WEAPON 48 #define ITEM_TYPE_HAND_TO_HAND 67 +#define ITEM_TYPE_ASSASSIN_MASTERY 91 #define ITEM_TYPE_TWOHANDED_MELEE_WEAPON 114 #define ITEM_TYPE_GENERAL_WEAPON 115 #define ITEM_TYPE_ONEHANDED_MELEE_WEAPON 116 diff --git a/BH/D2Helpers.cpp b/BH/D2Helpers.cpp index 3ff7d617..96770696 100644 --- a/BH/D2Helpers.cpp +++ b/BH/D2Helpers.cpp @@ -463,8 +463,10 @@ int __stdcall GetMasteryStat(UnitAny* pUnit, int nStatId, int nSkill) break; case STAT_CRITICALSTRIKE: { - if (((nSkill == SKILL_ONEHANDMASTERY && D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_WEAPON) && !D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_MISSILE_WEAPON) && !D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_THROWN_WEAPON)) || nRightSkillItemType <= 0) || - (nSkill == SKILL_CLAWANDDAGGERMASTERY && D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_HAND_TO_HAND)) || + // Claw and Dagger Mastery: PD2's claw skills (Tiger Strike, Fists of Fire, ...) use the "asm" item type, plain melee skills (Attack) use "weap" + bool bMeleeWeaponSkill = D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_WEAPON) && !D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_MISSILE_WEAPON) && !D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_THROWN_WEAPON); + if (((nSkill == SKILL_ONEHANDMASTERY && bMeleeWeaponSkill) || nRightSkillItemType <= 0) || + (nSkill == SKILL_CLAWANDDAGGERMASTERY && (bMeleeWeaponSkill || D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_ASSASSIN_MASTERY))) || (nSkill == SKILL_THROWINGMASTERY && D2COMMON_11034_ITEMS_CheckItemTypes(nRightSkillItemType, ITEM_TYPE_THROWN_WEAPON))) { nStatValue = D2COMMON_10786_SKILLS_EvaluateSkillFormula(pUnit, pSkillsTxt->dwPassiveCalc3, nSkill, nSkillLevel); @@ -535,7 +537,8 @@ int __stdcall GetMasteryStat(UnitAny* pUnit, int nStatId, int nSkill) } else { - if (nStatId == STAT_PIERCE) + // The client's stat list carries these passive stats whatever the weapon; the critical strike chance is only added above on a match + if (nStatId == STAT_CRUSHINGBLOW || nStatId == STAT_CRUSHINGBLOW_EFFICIENCY || nStatId == STAT_CRITICALSTRIKE_MULTIPLIER || nStatId == STAT_PIERCE) { return -nStatValue; } diff --git a/BH/Drawing/Stats/StatsDisplay.cpp b/BH/Drawing/Stats/StatsDisplay.cpp index 444f25d7..8549fddc 100644 --- a/BH/Drawing/Stats/StatsDisplay.cpp +++ b/BH/Drawing/Stats/StatsDisplay.cpp @@ -729,7 +729,7 @@ void StatsDisplay::OnDraw() int nCrushingBlowEfficiency = 100 + D2COMMON_GetUnitStat(unit, STAT_CRUSHINGBLOW_EFFICIENCY, 0) + GetMasteryStat(unit, STAT_CRUSHINGBLOW_EFFICIENCY, SKILL_TWOHANDMASTERY); int nCriticalStrike = D2COMMON_GetUnitStat(unit, STAT_CRITICALSTRIKE, 0) + D2COMMON_GetUnitStat(unit, STAT_ITEM_CRITICALSTRIKE_CHANCE, 0) + GetMasteryStat(unit, STAT_CRITICALSTRIKE, SKILL_ONEHANDMASTERY) + GetMasteryStat(unit, STAT_CRITICALSTRIKE, SKILL_THROWINGMASTERY) + GetMasteryStat(unit, STAT_CRITICALSTRIKE, SKILL_CLAWANDDAGGERMASTERY); int nCriticalStrikeMultiplier = 200 + D2COMMON_GetUnitStat(unit, STAT_CRITICALSTRIKE_MULTIPLIER, 0) + GetMasteryStat(unit, STAT_CRITICALSTRIKE_MULTIPLIER, SKILL_JAVELINANDSPEARMASTERY); - int nPierce = D2COMMON_GetUnitStat(unit, STAT_PIERCINGATTACK, 0) + D2COMMON_GetUnitStat(unit, STAT_PIERCE, 0); //+ GetMasteryStat(unit, STAT_PIERCE, SKILL_THROWINGMASTERY); + int nPierce = D2COMMON_GetUnitStat(unit, STAT_PIERCINGATTACK, 0) + D2COMMON_GetUnitStat(unit, STAT_PIERCE, 0) + GetMasteryStat(unit, STAT_PIERCE, SKILL_THROWINGMASTERY); Texthook::Draw(column1, (y += 16), From 56a6fa765ec6a69e9326ea358fd218612578ea13 Mon Sep 17 00:00:00 2001 From: errolgr Date: Sun, 4 Oct 2026 20:38:46 -0400 Subject: [PATCH 2/3] Stats panel: clamp poison length to the game's 25%-200% range The game clamps a player's poison length reduction, after the difficulty penalty, to -100..75 (Hell: 175% reduction reaches the 75% cap), so poison lasts between 25% and 200% of its base length. The panel printed 100 - penalty - reduction unclamped, e.g. 0% for 100% reduction in Normal or -25% for 225% in Hell. Apply the same clamp. --- BH/Drawing/Stats/StatsDisplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BH/Drawing/Stats/StatsDisplay.cpp b/BH/Drawing/Stats/StatsDisplay.cpp index 8549fddc..60f2a8f9 100644 --- a/BH/Drawing/Stats/StatsDisplay.cpp +++ b/BH/Drawing/Stats/StatsDisplay.cpp @@ -428,7 +428,7 @@ void StatsDisplay::OnDraw() L"Poison Resist:ÿc2 %d ÿc0/ %d ÿc4Length:ÿc: %d%%", static_cast(D2COMMON_GetUnitStat(unit, STAT_POISONRESIST, 0)) + penalty, pMax, - (100 - penalty - pLengthReduce) + 100 - max(min(pLengthReduce + penalty, 75), -100) ); Texthook::Draw(column1, (y += 16), From 5c11f0889a5981c68fb4d66f8a1cb3cdd54863e6 Mon Sep 17 00:00:00 2001 From: errolgr Date: Sun, 4 Oct 2026 20:38:46 -0400 Subject: [PATCH 3/3] Stats panel: draw the curse resist '/' in white like the other resist rows --- BH/Drawing/Stats/StatsDisplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BH/Drawing/Stats/StatsDisplay.cpp b/BH/Drawing/Stats/StatsDisplay.cpp index 60f2a8f9..acc36c0e 100644 --- a/BH/Drawing/Stats/StatsDisplay.cpp +++ b/BH/Drawing/Stats/StatsDisplay.cpp @@ -435,7 +435,7 @@ void StatsDisplay::OnDraw() None, 6, Gold, - L"Curse Resist:ÿc8 %d /ÿc0 75 ÿc4Length:ÿc0 %d%%", + L"Curse Resist:ÿc8 %d ÿc0/ 75 ÿc4Length:ÿc0 %d%%", static_cast(min(D2COMMON_GetUnitStat(unit, STAT_CURSE_EFFECTIVENESS, 0), 75)), static_cast(max(100 - D2COMMON_GetUnitStat(unit, STAT_CURSERESISTANCE, 0), 25))); y += 8;