From fd8050aee18592235f91da7eb282a6a10536d6a7 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:42:09 -0500 Subject: [PATCH] Add [mhenchant]/[ohenchant] weapon temp-enchant conditionals Uses ClassicAPI's modern C_Item.GetWeaponEnchantInfo (12-tuple with enchant IDs) plus C_Item.GetEnchantInfo (ID -> localized name) to match WHICH temporary weapon enchant is applied -- by SpellItemEnchantment ID or name -- not just that one exists. Locale-proof and exact, unlike [mhimbue:Name]'s green-text tooltip scan. ClassicAPI.lua gains GetWeaponEnchant(slot) (centralizes the tuple indexing, vanilla-global fallback) and GetEnchantName(id). ValidateWeaponImbue now reads through GetWeaponEnchant. New keywords mhenchant/nomhenchant/ ohenchant/noohenchant support bare (any enchant), single ID/name, and OR-lists ([mhenchant:2823/Deadly_Poison]); registered in BOOLEAN_CONDITIONALS for the bare form and auto-added to VALID_CONDITIONALS via Keywords. --- ClassicAPI.lua | 34 ++++++++++++++++++++++ Conditionals.lua | 76 +++++++++++++++++++++++++++++++++++++++--------- Core.lua | 4 +++ 3 files changed, 101 insertions(+), 13 deletions(-) diff --git a/ClassicAPI.lua b/ClassicAPI.lua index c481b90..be2da09 100644 --- a/ClassicAPI.lua +++ b/ClassicAPI.lua @@ -174,6 +174,40 @@ function API.IsEquipmentSetEquipped(name) return isEquipped and true or false end +-------------------------------------------------------------------------------- +-- Weapon Enchant +-------------------------------------------------------------------------------- + +-- Temporary weapon-enchant state for a slot: "mh" (main), "oh" (off), or +-- "ranged". Returns hasEnchant, expirationMs, charges, enchantID. The enchantID +-- comes from ClassicAPI's modern C_Item.GetWeaponEnchantInfo 12-tuple (the +-- vanilla global omits it), so [mhenchant] can tell WHICH imbue is applied, not +-- just that one exists. Falls back to the vanilla 6-tuple global (enchantID nil, +-- no ranged slot) when the C_Item version is unavailable. +function API.GetWeaponEnchant(slot) + if type(C_Item) == "table" and type(C_Item.GetWeaponEnchantInfo) == "function" then + local hasM, mExp, mChg, mID, hasO, oExp, oChg, oID, hasR, rExp, rChg, rID = + C_Item.GetWeaponEnchantInfo() + if slot == "oh" then return hasO, oExp, oChg, oID + elseif slot == "ranged" then return hasR, rExp, rChg, rID + else return hasM, mExp, mChg, mID end + end + local hasM, mExp, mChg, hasO, oExp, oChg = GetWeaponEnchantInfo() + if slot == "oh" then return hasO, oExp, oChg, nil end + return hasM, mExp, mChg, nil +end + +-- Localized name of an item-enchant ID (poison/oil/sharpening stone/permanent), +-- read straight from SpellItemEnchantment.dbc via ClassicAPI -- or nil for an +-- unknown id / a client without C_Item.GetEnchantInfo. Lets [mhenchant:Name] +-- resolve the applied enchant's name without scraping the weapon tooltip. +function API.GetEnchantName(enchantID) + if not enchantID or enchantID == 0 then return nil end + if type(C_Item) ~= "table" or type(C_Item.GetEnchantInfo) ~= "function" then return nil end + local info = C_Item.GetEnchantInfo(enchantID) + return info and info.name or nil +end + -------------------------------------------------------------------------------- -- Spell -------------------------------------------------------------------------------- diff --git a/Conditionals.lua b/Conditionals.lua index 8a072c0..e614a6a 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -4635,19 +4635,9 @@ function CleveRoids.ValidatePlayerDebuff(args) end function CleveRoids.ValidateWeaponImbue(slot, args) - -- Check if weapon has enchant via API - local hasMainEnchant, mainExpiration, mainCharges, hasOffEnchant, offExpiration, offCharges = GetWeaponEnchantInfo() - - local hasEnchant, expiration, charges - if slot == "mh" then - hasEnchant = hasMainEnchant - expiration = mainExpiration - charges = mainCharges - else - hasEnchant = hasOffEnchant - expiration = offExpiration - charges = offCharges - end + -- Temp-enchant state for this slot, read via ClassicAPI (centralizes the + -- 12-tuple indexing; enchantID unused here but powers [mhenchant]). + local hasEnchant, expiration, charges = CleveRoids.ClassicAPI.GetWeaponEnchant(slot) -- Only consider temporary enchants (with time or charges) -- This filters out permanent enchants like Crusader, Lifestealing, etc. @@ -4802,6 +4792,48 @@ function CleveRoids.CheckWeaponImbueByName(slot, imbueName) return false end +-- Does a single [mhenchant]/[ohenchant] value match the applied enchant? +-- value is a numeric enchant ID (exact) or a name (resolved from the applied +-- enchantID via ClassicAPI's SpellItemEnchantment lookup -- no tooltip scan). +-- Name match is case-insensitive, exact-or-substring (so "Deadly" matches +-- "Deadly Poison"). A parsed-arg table (from an operator form) uses its .name. +local function EnchantValueMatches(value, enchantID) + if type(value) == "table" then value = value.name end + if not value or value == "" then return false end + local wantId = tonumber(value) + if wantId then + return enchantID == wantId + end + local name = CleveRoids.ClassicAPI.GetEnchantName(enchantID) + if not name then return false end + local nlower = GetLowerNormalizedName(name) + local want = GetLowerNormalizedName(value) + return nlower == want or string.find(nlower, want, 1, true) ~= nil +end + +-- Matches the temporary weapon enchant on `slot` ("mh"/"oh") by enchant ID or +-- localized name, both resolved via ClassicAPI (C_Item.GetWeaponEnchantInfo + +-- GetEnchantInfo) -- locale-proof and exact, unlike the tooltip-scanned +-- [mhimbue:Name]. value may be true/nil (any temp enchant), a single ID/name, +-- or an OR-list array; returns true if the applied enchant matches any entry. +function CleveRoids.ValidateWeaponEnchant(slot, value) + local hasEnchant, expiration, charges, enchantID = CleveRoids.ClassicAPI.GetWeaponEnchant(slot) + local hasTemp = hasEnchant and ((expiration and expiration > 0) or (charges and charges > 0)) + if not hasTemp then return false end + + -- Bare [mhenchant]: any temporary enchant present. + if value == nil or value == true then return true end + + -- OR-list (e.g. [mhenchant:2823/Deadly_Poison]) is an array of strings. + if type(value) == "table" and not value.name and table.getn(value) > 0 then + for i = 1, table.getn(value) do + if EnchantValueMatches(value[i], enchantID) then return true end + end + return false + end + return EnchantValueMatches(value, enchantID) +end + -- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed -- PERFORMANCE: Uncached version - called by GetCachedCooldown function CleveRoids._GetCooldownUncached(name, ignoreGCD) @@ -7669,6 +7701,24 @@ CleveRoids.Keywords = { return not CleveRoids.ValidateWeaponImbue("oh", args) end, + -- [mhenchant:ID] / [mhenchant:Name] - main hand has that SPECIFIC temporary + -- weapon enchant, matched by SpellItemEnchantment ID or localized name via + -- ClassicAPI (locale-proof, unlike [mhimbue:Name]'s tooltip scan). Bare + -- [mhenchant] = any temp enchant; [mhenchant:A/B] = either; [nomhenchant:X] + -- = not that enchant. Names use _ for spaces, e.g. [mhenchant:Deadly_Poison]. + mhenchant = function(conditionals) + return CleveRoids.ValidateWeaponEnchant("mh", conditionals.mhenchant) + end, + nomhenchant = function(conditionals) + return not CleveRoids.ValidateWeaponEnchant("mh", conditionals.nomhenchant) + end, + ohenchant = function(conditionals) + return CleveRoids.ValidateWeaponEnchant("oh", conditionals.ohenchant) + end, + noohenchant = function(conditionals) + return not CleveRoids.ValidateWeaponEnchant("oh", conditionals.noohenchant) + end, + immune = function(conditionals) -- Check if target is immune to the spell being cast or damage school -- Usage: [immune] SpellName OR [immune:SpellName] OR [immune:fire] diff --git a/Core.lua b/Core.lua index f96d46c..260ef06 100644 --- a/Core.lua +++ b/Core.lua @@ -147,6 +147,10 @@ local BOOLEAN_CONDITIONALS = { nomhimbue = true, ohimbue = true, noohimbue = true, + mhenchant = true, + nomhenchant = true, + ohenchant = true, + noohenchant = true, group = true, nogroup = true, moving = true,