调整dps插件对仇恨的估算方式

优化dps插件
This commit is contained in:
rucky
2026-03-25 00:57:35 +08:00
parent 5c3f2243c4
commit 12c8c55159
16 changed files with 2454 additions and 165 deletions

View File

@@ -16,163 +16,22 @@ Parser.validUnits = validUnits
Parser.validPets = validPets
-----------------------------------------------------------------------
-- Spell threat coefficients (vanilla / classic values, max rank)
-- Reference: classic-warrior wiki, warcrafttavern.com
-- bonus = fixed threat added ON TOP of damage
-- mult = multiplier applied to the DAMAGE portion (default 1.0)
-- Threat calculation now delegated to ThreatEngine + ThreatCoefficients.
-- The engine handles stance/buff scanning, talent multipliers, and the
-- complete spell coefficient database. These wrappers remain for
-- backward compatibility with ProcessDamage/ProcessHealing.
-----------------------------------------------------------------------
local spellThreatData = {
["Heroic Strike"] = { bonus = 173 },
["Shield Slam"] = { bonus = 254 },
["Revenge"] = { bonus = 270, mult = 2.25 },
["Shield Bash"] = { bonus = 156, mult = 1.5 },
["Cleave"] = { bonus = 100 },
["Execute"] = { mult = 1.25 },
["Hamstring"] = { bonus = 135, mult = 1.25 },
["Thunder Clap"] = { mult = 2.5 },
["Overpower"] = { mult = 0.75 },
["Disarm"] = { bonus = 99 },
["Sunder Armor"] = { bonus = 261 },
["Maul"] = { mult = 1.75 },
["Swipe"] = { mult = 1.75 },
["Faerie Fire (Feral)"] = { bonus = 108 },
["Mind Blast"] = { mult = 2.0 },
["Holy Nova"] = { mult = 0 },
["Earth Shock"] = { mult = 2.0 },
["Searing Pain"] = { mult = 2.0 },
["Distracting Shot"] = { bonus = 600 },
["Scorpid Poison"] = { bonus = 5 },
["Intimidation"] = { bonus = 580 },
["Life Tap"] = { mult = 0 },
["Counterspell"] = { bonus = 300 },
["Mocking Blow"] = { bonus = 250 },
}
do
local cnAliases = {
["\232\139\177\229\139\135\230\137\147\229\135\187"] = "Heroic Strike",
["\231\155\190\231\137\140\231\140\155\229\135\187"] = "Shield Slam",
["\229\164\141\228\187\135"] = "Revenge",
["\231\155\190\229\135\187"] = "Shield Bash",
["\233\161\186\229\138\136\230\150\169"] = "Cleave",
["\230\150\169\230\157\128"] = "Execute",
["\230\150\173\231\173\139"] = "Hamstring",
["\233\155\183\233\156\134\228\184\128\229\135\187"] = "Thunder Clap",
["\229\142\139\229\136\182"] = "Overpower",
["\231\188\180\230\162\176"] = "Disarm",
["\231\160\180\231\148\178\230\148\187\229\135\187"] = "Sunder Armor",
["\230\167\152\229\135\187"] = "Maul",
["\230\140\165\229\135\187"] = "Swipe",
["\231\178\190\231\129\181\228\185\139\231\129\171(\233\135\142\233\135\145)"] = "Faerie Fire (Feral)",
["\229\191\131\231\129\181\233\156\135\231\136\134"] = "Mind Blast",
["\231\165\158\229\156\163\230\150\176\230\152\159"] = "Holy Nova",
["\229\156\176\233\156\135\230\156\175"] = "Earth Shock",
["\231\129\188\231\131\173\228\185\139\231\151\155"] = "Searing Pain",
["\230\137\176\228\185\177\229\176\132\229\135\187"] = "Distracting Shot",
["\232\157\157\230\175\146"] = "Scorpid Poison",
["\232\131\129\232\191\171"] = "Intimidation",
["\231\148\159\229\145\189\229\136\134\230\181\129"] = "Life Tap",
["\229\143\141\229\136\182\233\173\148\230\179\149"] = "Counterspell",
["\229\152\178\229\188\132\230\137\147\229\135\187"] = "Mocking Blow",
}
for cn, en in pairs(cnAliases) do
if spellThreatData[en] then
spellThreatData[cn] = spellThreatData[en]
end
end
end
local classThreatMod = {
ROGUE = 0.8,
}
-----------------------------------------------------------------------
-- Stance / form / buff threat modifier detection
-- Scans UnitBuff textures to detect known threat-altering states.
-- Cached per-unit for 2 seconds to avoid excessive buff scanning.
-----------------------------------------------------------------------
local stanceScanPatterns = {
{ pat = "DefensiveStance", mod = 1.3 },
{ pat = "OffensiveStance", mod = 0.8 },
{ pat = "Racial_Avatar", mod = 0.8 },
{ pat = "BerserkStance", mod = 0.8 },
{ pat = "BearForm", mod = 1.3 },
{ pat = "CatForm", mod = 0.8 },
}
local buffScanPatterns = {
{ pat = "SealOfSalvation", mod = 0.7 },
}
local function ScanUnitThreatMod(unit)
if not unit or not UnitExists(unit) then return 1.0 end
local stanceMod = 1.0
local buffMod = 1.0
for i = 1, 32 do
local texture = UnitBuff(unit, i)
if not texture then break end
for _, s in ipairs(stanceScanPatterns) do
if string.find(texture, s.pat) then
stanceMod = s.mod
end
end
for _, b in ipairs(buffScanPatterns) do
if string.find(texture, b.pat) then
buffMod = buffMod * b.mod
end
end
end
return stanceMod * buffMod
end
local stanceModCache = {}
local STANCE_CACHE_TTL = 2
function Parser:GetUnitThreatMod(name)
local now = GetTime()
local cached = stanceModCache[name]
if cached and (now - cached.time) < STANCE_CACHE_TTL then
return cached.mod
end
local unit = self:UnitByName(name)
local mod = ScanUnitThreatMod(unit)
stanceModCache[name] = { mod = mod, time = now }
return mod
end
function Parser:CalculateSpellThreat(source, spell, damage)
if not damage or damage <= 0 then return 0 end
local threat = damage
local coeff = spell and spellThreatData[spell]
if coeff then
threat = damage * (coeff.mult or 1.0) + (coeff.bonus or 0)
end
local class = DataStore:GetClass(source)
if class and classThreatMod[class] then
threat = threat * classThreatMod[class]
end
threat = threat * self:GetUnitThreatMod(source)
return math.max(threat, 0)
return NanamiDPS.ThreatEngine:CalculateLocalThreat(source, spell, damage, false, class)
end
function Parser:CalculateHealThreat(source, amount)
if not amount or amount <= 0 then return 0 end
local threat = amount * 0.5
local class = DataStore:GetClass(source)
if class and classThreatMod[class] then
threat = threat * classThreatMod[class]
end
threat = threat * self:GetUnitThreatMod(source)
return threat
return NanamiDPS.ThreatEngine:CalculateLocalThreat(source, nil, amount, true, class)
end
local unit_cache = {}
@@ -257,6 +116,7 @@ function Parser:ResolveSource(source)
end
function Parser:ProcessDamage(source, spell, target, amount, school)
if NanamiDPS.config and NanamiDPS.config.paused then return end
if type(source) ~= "string" or not tonumber(amount) then return end
source = NanamiDPS.trim(source)
amount = tonumber(amount)
@@ -279,7 +139,22 @@ function Parser:ProcessDamage(source, spell, target, amount, school)
DataStore:AddDamageTaken(target, finalSpell, resolvedSource, amount, school)
end
DataStore:AddThreat(source, self:CalculateSpellThreat(source, spell, amount))
local threatAmount = self:CalculateSpellThreat(source, spell, amount)
DataStore:AddThreat(source, threatAmount)
local TE = NanamiDPS.ThreatEngine
local TC = NanamiDPS.ThreatCoefficients
if TE then
local targetKey = TE:GetCurrentTargetKey()
if targetKey and not TE:IsAPIActiveForTarget(targetKey) then
TE:AddLocalThreat(targetKey, source, threatAmount)
if spell and TC and TC.tauntSpells and TC.tauntSpells[spell] then
TE:HandleTaunt(source, targetKey)
end
end
end
DataStore:UpdateActivity(resolvedSource, GetTime())
if targetType == "PLAYER" then
@@ -297,6 +172,7 @@ function Parser:ProcessDamage(source, spell, target, amount, school)
end
function Parser:ProcessHealing(source, spell, target, amount, school)
if NanamiDPS.config and NanamiDPS.config.paused then return end
if type(source) ~= "string" or not tonumber(amount) then return end
source = NanamiDPS.trim(source)
amount = tonumber(amount)
@@ -318,7 +194,17 @@ function Parser:ProcessHealing(source, spell, target, amount, school)
DataStore:AddHealing(resolvedSource, finalSpell, target, amount, effective)
DataStore:AddThreat(source, self:CalculateHealThreat(source, amount))
local healThreat = self:CalculateHealThreat(source, effective)
DataStore:AddThreat(source, healThreat)
local TE = NanamiDPS.ThreatEngine
if TE then
local targetKey = TE:GetCurrentTargetKey()
if targetKey and not TE:IsAPIActiveForTarget(targetKey) then
TE:AddLocalThreat(targetKey, source, healThreat)
end
end
DataStore:UpdateActivity(resolvedSource, GetTime())
local targetType = self:ScanName(target)