320 lines
15 KiB
Lua
320 lines
15 KiB
Lua
-- DopingControl data/hit.lua
|
|
-- Hit-chance tables for the HIT tab: what GEAR and AURAS contribute, plus
|
|
-- the caps every column is judged against. The third contributor,
|
|
-- TALENTS, lives in its own file (data/talenthit.lua) because it is a
|
|
-- different kind of source -- looked up by talent name and rank rather
|
|
-- than parsed out of a tooltip.
|
|
--
|
|
-- WHY TOOLTIP PARSING (measured, not assumed): this client family exposes
|
|
-- NO hit API at all -- GetHitModifier, GetSpellHitModifier and
|
|
-- GetCombatRating are all absent (verified on the live client). The only
|
|
-- readable source of a player's +hit is the text of the equipped items'
|
|
-- tooltips, which IS readable for foreign units in range via
|
|
-- GameTooltip:SetInventoryItem(unit, slot). Everything in this file exists
|
|
-- to turn those tooltip lines into a number and to bound that number
|
|
-- against the caps of the current patch.
|
|
--
|
|
-- WHAT CANNOT BE READ (in principle, not a missing feature): a foreign
|
|
-- player's WEAPON SKILL. The server never sends PLAYER_SKILL_INFO for
|
|
-- anyone but the own character and no inspect-skill channel exists, so
|
|
-- weapon skill is MEASURED for the own character (GetSkillLineInfo, see
|
|
-- scan/hit.lua) and ESTIMATED for everyone else -- base skill + racial
|
|
-- specialization + an optional per-player "weapon skill book" flag the
|
|
-- user sets in the options. Every consumer must present the foreign value
|
|
-- as an assumption, never as a measurement.
|
|
--
|
|
-- PATCH BASIS (current server patch): weapon-skill scaling was rewritten
|
|
-- to LINEAR, the old 305-skill breakpoint was removed and the hidden 1%
|
|
-- suppression was dropped. Consequences encoded below: the yellow melee
|
|
-- hit cap against a boss three levels above the player is 8% (it was 9%
|
|
-- before the rewrite) and the dual-wield WHITE cap is 27%. The spell hit
|
|
-- cap is 16%, derived from the server's own clamp (see DC.HIT_CAPS below)
|
|
-- rather than measured -- it is a single constant here so a later
|
|
-- correction changes one line.
|
|
--
|
|
-- English client assumed: both the tooltip patterns and the weapon-skill
|
|
-- names below are English strings. On a localized client nothing matches
|
|
-- and the hit columns simply read 0 rather than showing wrong numbers.
|
|
--
|
|
-- Pure Lua 5.0, no WoW API -- dofile-loadable offline.
|
|
|
|
DopingControl = DopingControl or {}
|
|
local DC = DopingControl
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- Magic schools that can carry a school-specific spell-hit line.
|
|
-- Order is display order for tooltip detail lines.
|
|
--
|
|
-- The CAPITALIZED spelling is load-bearing twice over: it is the word that
|
|
-- appears in the item tooltip ("Improves your chance to hit with Fire
|
|
-- spells by 1%"), so it is both the generated pattern text below and the
|
|
-- key of the parsed `schools` table.
|
|
-- ------------------------------------------------------------------
|
|
DC.HIT_SCHOOLS = { "Arcane", "Fire", "Frost", "Holy", "Nature", "Shadow" }
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.HIT_SCHOOL_KEY -- lowercase school id -> the CAPITALIZED key used by
|
|
-- everything that came out of a tooltip.
|
|
--
|
|
-- Two spellings exist because two sources exist and neither may be bent to
|
|
-- the other: tooltip text dictates "Fire", while the column definitions
|
|
-- (DC.SLOTS_HIT, core/const.lua) and the talent tables
|
|
-- (DC.TALENT_HIT, data/talenthit.lua) are hand-written data that follows
|
|
-- the file-wide lowercase-id convention. This table is GENERATED from
|
|
-- DC.HIT_SCHOOLS so the two can never drift apart, and DC_Hit.SchoolHit
|
|
-- (scan/hit.lua) is the one function callers should use instead of
|
|
-- indexing either table by hand.
|
|
-- ------------------------------------------------------------------
|
|
DC.HIT_SCHOOL_KEY = {}
|
|
for i = 1, table.getn(DC.HIT_SCHOOLS) do
|
|
local school = DC.HIT_SCHOOLS[i]
|
|
DC.HIT_SCHOOL_KEY[string.lower(school)] = school
|
|
end
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.HIT_PATTERNS -- ARRAY (order matters, FIRST MATCH WINS), each entry
|
|
-- { pattern = <Lua 5.0 string.find pattern with ONE numeric capture>,
|
|
-- kind = "melee" | "spell" | "both" | <school name> }
|
|
--
|
|
-- kind semantics (folded by DC_Hit.SumGear):
|
|
-- "melee" generic physical hit. 1.12 has no separate ranged-hit affix:
|
|
-- the same "chance to hit" line applies to melee AND ranged
|
|
-- attacks, so the MELEE bucket feeds both hit columns.
|
|
-- "spell" spell hit against every school.
|
|
-- "both" "spells and attacks": adds to melee AND spell.
|
|
-- <school> school-specific spell hit (e.g. "Fire"): it does NOT raise
|
|
-- the general spell-hit number, it is carried separately and
|
|
-- shown as an extra, because it only helps that one school.
|
|
--
|
|
-- Ordering rule: the more specific phrasings come first. The four
|
|
-- phrasings do not actually overlap as literals ("hit with spells and
|
|
-- attacks by", "hit with Fire spells by", "hit with spells by", "hit by"),
|
|
-- but the order keeps that from becoming load-bearing if a pattern is ever
|
|
-- widened.
|
|
--
|
|
-- Extension point: a phrasing that is not listed here is SILENTLY IGNORED
|
|
-- (never guessed at). Server-custom item text that grants hit therefore
|
|
-- needs its pattern added here -- this table is the single place.
|
|
-- ------------------------------------------------------------------
|
|
DC.HIT_PATTERNS = {
|
|
-- combined affix ("spells and attacks")
|
|
{ pattern = "Improves your chance to hit with spells and attacks by (%d+)%%",
|
|
kind = "both" },
|
|
}
|
|
|
|
-- school-specific spell hit, generated so the school list stays the single
|
|
-- source of truth ("Improves your chance to hit with Fire spells by 1%")
|
|
for i = 1, table.getn(DC.HIT_SCHOOLS) do
|
|
local school = DC.HIT_SCHOOLS[i]
|
|
table.insert(DC.HIT_PATTERNS, {
|
|
pattern = "Improves your chance to hit with " .. school
|
|
.. " spells by (%d+)%%",
|
|
kind = school,
|
|
})
|
|
end
|
|
|
|
-- general spell hit, then the generic (melee + ranged) hit line
|
|
table.insert(DC.HIT_PATTERNS,
|
|
{ pattern = "Improves your chance to hit with spells by (%d+)%%",
|
|
kind = "spell" })
|
|
table.insert(DC.HIT_PATTERNS,
|
|
{ pattern = "Improves your chance to hit by (%d+)%%",
|
|
kind = "melee" })
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.WEAPON_SKILL_OF_SUBTYPE -- GetItemInfo itemSubType -> weapon SKILL
|
|
-- name (the string GetSkillLineInfo reports for that weapon).
|
|
--
|
|
-- Absent key = "not a weapon" and that is load-bearing: off-hand items
|
|
-- whose subtype is missing here (shields, held-in-off-hand items, totems,
|
|
-- idols, librams, fishing poles) are what tells the caller that the player
|
|
-- is NOT dual-wielding.
|
|
--
|
|
-- Verified live on this client: "One-Handed Maces" (plus the non-weapon
|
|
-- subtypes "Shields" and "Totems", deliberately absent). The remaining
|
|
-- strings follow the standard 1.12 weapon subclass naming and are
|
|
-- UNVERIFIED -- each one is only a lookup key, so a wrong string degrades
|
|
-- to "unknown weapon" (no skill assumption, no dual-wield claim), never to
|
|
-- a wrong number.
|
|
--
|
|
-- Two mappings are worth calling out:
|
|
-- "Fist Weapons" -> "Unarmed": fist weapons run on the Unarmed skill
|
|
-- line, they have no skill of their own.
|
|
-- "Wands" -> "Wands": the skill line exists, but whether wand attacks
|
|
-- obey weapon skill or spell hit is DISPUTED on this patch. The
|
|
-- mapping only names the skill line; the ranged hit column
|
|
-- deliberately ignores wands (see DC.RANGED_ATTACK_SUBTYPE).
|
|
-- ------------------------------------------------------------------
|
|
DC.WEAPON_SKILL_OF_SUBTYPE = {
|
|
["One-Handed Axes"] = "Axes",
|
|
["Two-Handed Axes"] = "Two-Handed Axes",
|
|
["One-Handed Maces"] = "Maces", -- verified live
|
|
["Two-Handed Maces"] = "Two-Handed Maces",
|
|
["One-Handed Swords"] = "Swords",
|
|
["Two-Handed Swords"] = "Two-Handed Swords",
|
|
["Daggers"] = "Daggers",
|
|
["Fist Weapons"] = "Unarmed", -- see header
|
|
["Polearms"] = "Polearms",
|
|
["Staves"] = "Staves",
|
|
["Bows"] = "Bows",
|
|
["Crossbows"] = "Crossbows",
|
|
["Guns"] = "Guns",
|
|
["Thrown"] = "Thrown",
|
|
["Wands"] = "Wands", -- see header
|
|
}
|
|
|
|
-- Subtypes that make the RANGED hit question meaningful, i.e. things that
|
|
-- actually swing as a ranged ATTACK. Wands are excluded on purpose (their
|
|
-- hit mechanic is disputed, see above) and so is every non-weapon relic;
|
|
-- a player without one of these gets no ranged hit column value at all.
|
|
DC.RANGED_ATTACK_SUBTYPE = {
|
|
["Bows"] = true,
|
|
["Crossbows"] = true,
|
|
["Guns"] = true,
|
|
["Thrown"] = true,
|
|
}
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.CLASS_WEAPON_SKILLS -- class token -> ordered array of the weapon
|
|
-- skills that class can train (Vanilla 1.12 base list). Every name here is
|
|
-- a VALUE of DC.WEAPON_SKILL_OF_SUBTYPE above, same spelling -- the
|
|
-- options editor's Skill books tab (ui/options.lua, O.SkillBookRows)
|
|
-- writes db.skillBooks[player][skill] keyed by these exact strings, so a
|
|
-- typo here would silently create a book entry the hit calc never reads.
|
|
--
|
|
-- This server can diverge from the Vanilla baseline (extra or missing
|
|
-- trainable weapons for a class). If a player is seen wielding a skill
|
|
-- that is not in their class's list here, the Skill books tab still
|
|
-- offers it -- a WORN skill always rides along regardless of the class
|
|
-- list (see O.SkillBookRows) -- but if that turns out to be the NORMAL
|
|
-- case for a class rather than a one-off exception, extend the list below
|
|
-- rather than leaning on the worn-skill fallback forever.
|
|
-- ------------------------------------------------------------------
|
|
DC.CLASS_WEAPON_SKILLS = {
|
|
WARRIOR = { "Axes", "Two-Handed Axes", "Maces", "Two-Handed Maces",
|
|
"Swords", "Two-Handed Swords", "Daggers", "Unarmed",
|
|
"Polearms", "Staves", "Bows", "Crossbows", "Guns",
|
|
"Thrown" },
|
|
PALADIN = { "Axes", "Two-Handed Axes", "Maces", "Two-Handed Maces",
|
|
"Swords", "Two-Handed Swords", "Polearms" },
|
|
HUNTER = { "Axes", "Two-Handed Axes", "Swords", "Two-Handed Swords",
|
|
"Daggers", "Unarmed", "Polearms", "Staves", "Bows",
|
|
"Crossbows", "Guns", "Thrown" },
|
|
ROGUE = { "Daggers", "Unarmed", "Maces", "Swords", "Bows",
|
|
"Crossbows", "Guns", "Thrown" },
|
|
PRIEST = { "Daggers", "Maces", "Staves", "Wands" },
|
|
SHAMAN = { "Axes", "Two-Handed Axes", "Maces", "Two-Handed Maces",
|
|
"Daggers", "Unarmed", "Staves" },
|
|
MAGE = { "Daggers", "Swords", "Staves", "Wands" },
|
|
WARLOCK = { "Daggers", "Swords", "Staves", "Wands" },
|
|
DRUID = { "Maces", "Two-Handed Maces", "Daggers", "Unarmed",
|
|
"Staves", "Polearms" },
|
|
}
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- Weapon skill baseline and modifiers.
|
|
--
|
|
-- DC.BASE_WEAPON_SKILL: a level-60 character caps every weapon skill at
|
|
-- 300 (5 x level). This tool is a raid tool, so 60 is assumed for the
|
|
-- estimate of FOREIGN players; the own character is measured and never
|
|
-- uses this number.
|
|
--
|
|
-- DC.RACIAL_SKILL_BONUS: the racial weapon specialization bonus. The value
|
|
-- is DISPUTED -- the classic value is +5, and a server-side change to +3
|
|
-- has been reported but is UNVERIFIED. It is ONE constant on purpose:
|
|
-- changing this single line changes every racial bonus and every derived
|
|
-- cap.
|
|
--
|
|
-- DC.SKILL_BOOK_BONUS: a quest weapon-skill book grants +3 skill. It is
|
|
-- invisible on other players (see the header), which is why it is an
|
|
-- opt-in per-player checkbox in the options rather than a scan result.
|
|
-- ------------------------------------------------------------------
|
|
DC.BASE_WEAPON_SKILL = 300
|
|
DC.RACIAL_SKILL_BONUS = 5
|
|
DC.SKILL_BOOK_BONUS = 3
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.RACE_WEAPON_SKILL -- race -> { [weapon skill name] = bonus }.
|
|
--
|
|
-- Keys: BOTH spellings are listed for every race that has a bonus -- the
|
|
-- display name ("Night Elf") and the file token ("NightElf", "Scourge"),
|
|
-- because the race string reaches us from UnitRace, whose two returns are
|
|
-- exactly those two spellings. Aliases point at the SAME table, so a race
|
|
-- is never counted twice.
|
|
--
|
|
-- Races without a weapon specialization (night elves, tauren, undead) are
|
|
-- deliberately absent: an absent race resolves to "no bonus", which is
|
|
-- also what an unknown/newer race token resolves to (nil-safe by design).
|
|
-- ------------------------------------------------------------------
|
|
local B = DC.RACIAL_SKILL_BONUS
|
|
|
|
local HUMAN_SKILLS = {
|
|
["Swords"] = B, ["Two-Handed Swords"] = B,
|
|
["Maces"] = B, ["Two-Handed Maces"] = B,
|
|
}
|
|
local ORC_SKILLS = {
|
|
["Axes"] = B, ["Two-Handed Axes"] = B,
|
|
}
|
|
local DWARF_SKILLS = { ["Guns"] = B }
|
|
local TROLL_SKILLS = { ["Bows"] = B, ["Thrown"] = B }
|
|
local GNOME_SKILLS = { ["Daggers"] = B, ["Swords"] = B }
|
|
-- races without a weapon specialization share ONE empty table
|
|
local NO_SKILLS = {}
|
|
|
|
DC.RACE_WEAPON_SKILL = {
|
|
["Human"] = HUMAN_SKILLS,
|
|
["Orc"] = ORC_SKILLS,
|
|
["Dwarf"] = DWARF_SKILLS,
|
|
["Troll"] = TROLL_SKILLS,
|
|
["Gnome"] = GNOME_SKILLS,
|
|
-- no weapon specialization; listed for readability, empty on purpose
|
|
["Night Elf"] = NO_SKILLS,
|
|
["NightElf"] = NO_SKILLS,
|
|
["Tauren"] = NO_SKILLS,
|
|
["Undead"] = NO_SKILLS,
|
|
["Scourge"] = NO_SKILLS,
|
|
}
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.HIT_CAPS -- hit percentages needed against a boss THREE LEVELS above
|
|
-- the player on the current patch, at base weapon skill (300):
|
|
-- meleeYellow 8 -- special attacks / abilities ("yellow" swings)
|
|
-- meleeWhiteDW 27 -- auto attacks while DUAL WIELDING (the 19% dual-
|
|
-- wield penalty rides on top); a single weapon's white
|
|
-- swings share the yellow cap
|
|
-- ranged 8 -- ranged attacks behave like yellow melee here
|
|
-- spell 16 -- the server clamps spell hit chance to 99%, so 1%
|
|
-- miss can never be removed: base miss vs a +3-level
|
|
-- boss is 17%, which makes 16% the effective cap
|
|
--
|
|
-- DC.CAP_PER_SKILL: every weapon skill point ABOVE 300 lowers the melee/
|
|
-- ranged cap by 0.2 percentage points (linear scaling, see header). A
|
|
-- racial specialization (+5 skill) therefore lowers the yellow cap from
|
|
-- 8.0 to 7.0, and a weapon skill book (+3) is worth about 0.6% hit.
|
|
-- ------------------------------------------------------------------
|
|
DC.HIT_CAPS = {
|
|
meleeYellow = 8,
|
|
meleeWhiteDW = 27,
|
|
ranged = 8,
|
|
spell = 16,
|
|
}
|
|
|
|
DC.CAP_PER_SKILL = 0.2
|
|
|
|
-- ------------------------------------------------------------------
|
|
-- DC.HIT_AURAS -- hit granted by AURAS rather than gear, folded on top of
|
|
-- the gear sum by scan/engine.lua's BuildPlayer (no extra scan: the buff
|
|
-- names are already in the store from the same aura read the class-buff
|
|
-- tab uses). Keys are buff NAMES (matched against player.auras.names);
|
|
-- values are { melee=, ranged=, spell= } percentages -- every field is
|
|
-- OPTIONAL, since a hit-granting aura need not touch all three columns.
|
|
-- ------------------------------------------------------------------
|
|
DC.HIT_AURAS = {
|
|
-- raid-wide druid buff: +1% SPELL hit only (also grants movement
|
|
-- speed and casting mana regen, which do not concern this addon) --
|
|
-- verified against the in-game tooltip text, so this is deliberately
|
|
-- NOT a melee/ranged entry; add further hit-granting auras here (a
|
|
-- food buff with hit would be the likeliest addition)
|
|
["Emerald Blessing"] = { spell = 1 },
|
|
}
|