mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
2193 lines
76 KiB
Lua
2193 lines
76 KiB
Lua
--[[
|
|
Author: Dennis Werner Garske (DWG) / brian / Mewtiny
|
|
License: MIT License
|
|
]]
|
|
local _G = _G or getfenv(0)
|
|
local CleveRoids = _G.CleveRoids or {}
|
|
|
|
--This table maps stat keys to the functions that retrieve their values.
|
|
local stat_checks = {
|
|
-- Base Stats (Corrected to use the 'effective' stat with gear)
|
|
str = function() local _, effective = UnitStat("player", 1); return effective end,
|
|
strength = function() local _, effective = UnitStat("player", 1); return effective end,
|
|
agi = function() local _, effective = UnitStat("player", 2); return effective end,
|
|
agility = function() local _, effective = UnitStat("player", 2); return effective end,
|
|
stam = function() local _, effective = UnitStat("player", 3); return effective end,
|
|
stamina = function() local _, effective = UnitStat("player", 3); return effective end,
|
|
int = function() local _, effective = UnitStat("player", 4); return effective end,
|
|
intellect = function() local _, effective = UnitStat("player", 4); return effective end,
|
|
spi = function() local _, effective = UnitStat("player", 5); return effective end,
|
|
spirit = function() local _, effective = UnitStat("player", 5); return effective end,
|
|
|
|
-- Combat Ratings (Corrected to use UnitAttackPower and UnitRangedAttackPower)
|
|
ap = function() local base, pos, neg = UnitAttackPower("player"); return base + pos + neg end,
|
|
attackpower = function() local base, pos, neg = UnitAttackPower("player"); return base + pos + neg end,
|
|
rap = function() local base, pos, neg = UnitRangedAttackPower("player"); return base + pos + neg end,
|
|
rangedattackpower = function() local base, pos, neg = UnitRangedAttackPower("player"); return base + pos + neg end,
|
|
healing = function() return GetBonusHealing() end,
|
|
healingpower = function() return GetBonusHealing() end,
|
|
|
|
-- Bonus Spell Damage by School
|
|
arcane_power = function() return GetSpellBonusDamage(6) end,
|
|
fire_power = function() return GetSpellBonusDamage(3) end,
|
|
frost_power = function() return GetSpellBonusDamage(4) end,
|
|
nature_power = function() return GetSpellBonusDamage(2) end,
|
|
shadow_power = function() return GetSpellBonusDamage(5) end,
|
|
|
|
-- Defensive Stats
|
|
armor = function() local _, effective = UnitArmor("player"); return effective end,
|
|
defense = function()
|
|
local base, modifier = UnitDefense("player")
|
|
return (base or 0) + (modifier or 0)
|
|
end,
|
|
|
|
-- Resistances
|
|
arcane_res = function() local _, val = UnitResistance("player", 7); return val end,
|
|
fire_res = function() local _, val = UnitResistance("player", 3); return val end,
|
|
frost_res = function() local _, val = UnitResistance("player", 5); return val end,
|
|
nature_res = function() local _, val = UnitResistance("player", 4); return val end,
|
|
shadow_res = function() local _, val = UnitResistance("player", 6); return val end
|
|
}
|
|
|
|
local function And(t,func)
|
|
if type(func) ~= "function" then return false end
|
|
if type(t) ~= "table" then
|
|
t = { [1] = t }
|
|
end
|
|
for k,v in pairs(t) do
|
|
if not func(v) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function Or(t,func)
|
|
if type(func) ~= "function" then return false end
|
|
if type(t) ~= "table" then
|
|
t = { [1] = t }
|
|
end
|
|
for k,v in pairs(t) do
|
|
if func(v) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- pfUI debuff time helper (Vanilla 1.12.1 / Lua 5.0 safe)
|
|
local function PFUI_HasLibDebuff()
|
|
return type(pfUI) == "table"
|
|
and type(pfUI.api) == "table"
|
|
and type(pfUI.api.libdebuff) == "table"
|
|
and type(pfUI.api.libdebuff.UnitDebuff) == "function"
|
|
end
|
|
|
|
-- Helper: Get debuff time-left (seconds) from CleveRoids.libdebuff only
|
|
local function _get_debuff_timeleft(unitToken, auraName)
|
|
-- SuperWoW path: GUID-based lookup
|
|
if CleveRoids.hasSuperwow then
|
|
local _, guid = UnitExists(unitToken)
|
|
if guid and CleveRoids.libdebuff and CleveRoids.libdebuff.objects[guid] then
|
|
for i = 1, 16 do
|
|
local effect, _, _, _, _, duration, timeleft = CleveRoids.libdebuff:UnitDebuff(unitToken, i)
|
|
if not effect then break end
|
|
if effect == auraName and timeleft and timeleft >= 0 then
|
|
return timeleft, duration
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Non-SuperWoW fallback
|
|
if CleveRoids.libdebuff and CleveRoids.libdebuff.UnitDebuff then
|
|
for idx = 1, 16 do
|
|
local effect, _, _, _, _, duration, timeleft = CleveRoids.libdebuff:UnitDebuff(unitToken, idx)
|
|
if not effect then break end
|
|
if effect == auraName and timeleft and timeleft >= 0 then
|
|
return timeleft, duration
|
|
end
|
|
end
|
|
end
|
|
|
|
return nil, nil
|
|
end
|
|
|
|
-- Validates that the given target is either friend (if [help]) or foe (if [harm])
|
|
-- target: The unit id to check
|
|
-- help: Optional. If set to 1 then the target must be friendly. If set to 0 it must be an enemy.
|
|
-- remarks: Will always return true if help is not given
|
|
-- returns: Whether or not the given target can either be attacked or supported, depending on help
|
|
function CleveRoids.CheckHelp(target, help)
|
|
if help == nil then return true end
|
|
if help then
|
|
return UnitCanAssist("player", target)
|
|
else
|
|
return UnitCanAttack("player", target)
|
|
end
|
|
end
|
|
|
|
-- Ensures the validity of the given target
|
|
-- target: The unit id to check
|
|
-- help: Optional. If set to 1 then the target must be friendly. If set to 0 it must be an enemy
|
|
-- returns: Whether or not the target is a viable target
|
|
function CleveRoids.IsValidTarget(target, help)
|
|
-- If the conditional is not for @mouseover, use the existing logic.
|
|
if target ~= "mouseover" then
|
|
if not UnitExists(target) or not CleveRoids.CheckHelp(target, help) then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- --- START OF PATCH ---
|
|
-- New logic to handle [@mouseover] with pfUI compatibility.
|
|
|
|
local effectiveMouseoverUnit = "mouseover" -- Start with the default game token.
|
|
|
|
-- Check if the default mouseover exists. If not, check pfUI's internal data,
|
|
-- which is necessary because pfUI frames don't always update the default token.
|
|
if not UnitExists(effectiveMouseoverUnit) then
|
|
if pfUI and pfUI.uf and pfUI.uf.mouseover and pfUI.uf.mouseover.unit and UnitExists(pfUI.uf.mouseover.unit) then
|
|
-- If pfUI has a valid mouseover unit recorded, use that instead.
|
|
effectiveMouseoverUnit = pfUI.uf.mouseover.unit
|
|
else
|
|
-- If neither the default token nor the pfUI unit exists, there's no valid mouseover.
|
|
return false
|
|
end
|
|
end
|
|
-- --- END OF PATCH ---
|
|
|
|
-- Finally, perform the help/harm check on the determined mouseover unit (either from the game or from pfUI).
|
|
if not UnitExists(effectiveMouseoverUnit) or not CleveRoids.CheckHelp(effectiveMouseoverUnit, help) then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- Returns the current shapeshift / stance index
|
|
-- returns: The index of the current shapeshift form / stance. 0 if in no shapeshift form / stance
|
|
function CleveRoids.GetCurrentShapeshiftIndex()
|
|
if CleveRoids.playerClass == "PRIEST" then
|
|
return CleveRoids.ValidatePlayerBuff(CleveRoids.Localized.Spells["Shadowform"]) and 1 or 0
|
|
elseif CleveRoids.playerClass == "ROGUE" then
|
|
return CleveRoids.ValidatePlayerBuff(CleveRoids.Localized.Spells["Stealth"]) and 1 or 0
|
|
end
|
|
for i=1, GetNumShapeshiftForms() do
|
|
_, _, active = GetShapeshiftFormInfo(i)
|
|
if active then
|
|
return i
|
|
end
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
function CleveRoids.CancelAura(auraName)
|
|
local ix = 0
|
|
auraName = string.lower(string.gsub(auraName, "_"," "))
|
|
while true do
|
|
local aura_ix = GetPlayerBuff(ix,"HELPFUL")
|
|
ix = ix + 1
|
|
if aura_ix == -1 then break end
|
|
|
|
if CleveRoids.hasSuperwow then
|
|
local bid = GetPlayerBuffID(aura_ix)
|
|
bid = (bid < -1) and (bid + 65536) or bid
|
|
if string.lower(SpellInfo(bid)) == auraName then
|
|
CancelPlayerBuff(aura_ix)
|
|
return true
|
|
end
|
|
else
|
|
AuraScanTooltip:SetPlayerBuff(aura_ix)
|
|
local name = string.lower(getglobal("AuraScanTooltipTextLeft1"):GetText())
|
|
if name == auraName then
|
|
CancelPlayerBuff(aura_ix)
|
|
break
|
|
end
|
|
end
|
|
|
|
end
|
|
return false
|
|
end
|
|
|
|
function CleveRoids.HasGearEquipped(gearId)
|
|
if not gearId then return false end
|
|
|
|
-- Handle both numeric IDs and string IDs like "5196"
|
|
local wantId = tonumber(gearId)
|
|
local wantName = (type(gearId) == "string" and not wantId) and string.lower(gearId) or nil
|
|
|
|
for slot = 1, 19 do
|
|
local link = GetInventoryItemLink("player", slot)
|
|
if link then
|
|
local _, _, id = string.find(link, "item:(%d+)")
|
|
local _, _, nameInBrackets = string.find(link, "%[(.+)%]")
|
|
|
|
if wantId and id and tonumber(id) == wantId then
|
|
return true
|
|
end
|
|
if wantName and nameInBrackets and string.lower(nameInBrackets) == wantName then
|
|
return true
|
|
end
|
|
-- Fallback: resolve via GetItemInfo
|
|
if wantName and not nameInBrackets and id then
|
|
local itemName = GetItemInfo(tonumber(id))
|
|
if itemName and string.lower(itemName) == wantName then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
-- Checks whether or not the given weaponType is currently equipped
|
|
-- weaponType: The name of the weapon's type (e.g. Axe, Shield, etc.)
|
|
-- returns: True when equipped, otherwhise false
|
|
function CleveRoids.HasWeaponEquipped(weaponType)
|
|
if not CleveRoids.WeaponTypeNames[weaponType] then
|
|
return false
|
|
end
|
|
|
|
local slotName = CleveRoids.WeaponTypeNames[weaponType].slot
|
|
local localizedName = CleveRoids.WeaponTypeNames[weaponType].name
|
|
local slotId = GetInventorySlotInfo(slotName)
|
|
local slotLink = GetInventoryItemLink("player",slotId)
|
|
|
|
if not slotLink then
|
|
return false
|
|
end
|
|
|
|
local _,_,itemId = string.find(slotLink,"item:(%d+)")
|
|
if not itemId then -- Also good to check if itemId was found
|
|
return false
|
|
end
|
|
local _name,_link,_,_lvl,_type,subtype = GetItemInfo(itemId)
|
|
-- just had to be special huh?
|
|
local fist = string.find(subtype,"^Fist")
|
|
-- drops things like the One-Handed prefix
|
|
local _,_,subtype = string.find(subtype,"%s?(%S+)$")
|
|
|
|
if subtype == localizedName or (fist and (CleveRoids.WeaponTypeNames[weaponType].name == CleveRoids.Localized.FistWeapon)) then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Checks whether or not the given UnitId is in your party or your raid
|
|
-- target: The UnitId of the target to check
|
|
-- groupType: The name of the group type your target has to be in ("party" or "raid")
|
|
-- returns: True when the given target is in the given groupType, otherwhise false
|
|
function CleveRoids.IsTargetInGroupType(target, groupType)
|
|
local groupSize = (groupType == "raid") and 40 or 5
|
|
|
|
for i = 1, groupSize do
|
|
if UnitIsUnit(groupType..i, target) then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function CleveRoids.GetSpammableConditional(name)
|
|
return CleveRoids.spamConditions[name] or "nomybuff"
|
|
end
|
|
|
|
-- Checks whether or not we're currently casting a channeled spell
|
|
function CleveRoids.CheckChanneled(channeledSpell)
|
|
if not channeledSpell then return false end
|
|
|
|
-- Remove the "(Rank X)" part from the spells name in order to allow downranking
|
|
local spellName = string.gsub(CleveRoids.CurrentSpell.spellName, "%(.-%)%s*", "")
|
|
local channeled = string.gsub(channeledSpell, "%(.-%)%s*", "")
|
|
|
|
if CleveRoids.CurrentSpell.type == "channeled" and spellName == channeled then
|
|
return false
|
|
end
|
|
|
|
if channeled == CleveRoids.Localized.Attack then
|
|
return not CleveRoids.CurrentSpell.autoAttack
|
|
end
|
|
|
|
if channeled == CleveRoids.Localized.AutoShot then
|
|
return not CleveRoids.CurrentSpell.autoShot
|
|
end
|
|
|
|
if channeled == CleveRoids.Localized.Shoot then
|
|
return not CleveRoids.CurrentSpell.wand
|
|
end
|
|
|
|
CleveRoids.CurrentSpell.spellName = channeled
|
|
return true
|
|
end
|
|
|
|
function CleveRoids.ValidateComboPoints(operator, amount)
|
|
if not operator or not amount then return false end
|
|
local points = GetComboPoints()
|
|
|
|
if CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](points, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function CleveRoids.ValidateLevel(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local level = UnitLevel(unit)
|
|
|
|
if level and CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](level, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function CleveRoids.ValidateKnown(args)
|
|
if not args then
|
|
return false
|
|
end
|
|
if table.getn(CleveRoids.Talents) == 0 then
|
|
CleveRoids.IndexTalents()
|
|
end
|
|
|
|
local effective_name_to_check
|
|
local original_args_for_rank_check = args
|
|
|
|
if type(args) ~= "table" then
|
|
effective_name_to_check = args
|
|
args = { name = args }
|
|
else
|
|
effective_name_to_check = args.name
|
|
end
|
|
|
|
local spell = CleveRoids.GetSpell(effective_name_to_check)
|
|
local talent_points = nil
|
|
|
|
if not spell then
|
|
talent_points = CleveRoids.GetTalent(effective_name_to_check)
|
|
end
|
|
|
|
if not spell and talent_points == nil then
|
|
return false
|
|
end
|
|
|
|
local arg_amount = nil
|
|
local arg_operator = nil
|
|
if type(original_args_for_rank_check) == "table" then
|
|
arg_amount = original_args_for_rank_check.amount
|
|
arg_operator = original_args_for_rank_check.operator
|
|
end
|
|
|
|
if spell then
|
|
local spell_rank_str = spell.rank or (spell.highest and spell.highest.rank) or ""
|
|
-- FLEXIBLY extract just the number from the rank string
|
|
local _, _, spell_rank_num_str = string.find(spell_rank_str, "(%d+)")
|
|
|
|
if not arg_amount and not arg_operator then
|
|
return true
|
|
elseif arg_amount and arg_operator and CleveRoids.operators[arg_operator] and spell_rank_num_str and spell_rank_num_str ~= "" then
|
|
local numeric_rank = tonumber(spell_rank_num_str)
|
|
if numeric_rank then
|
|
return CleveRoids.comparators[arg_operator](numeric_rank, arg_amount)
|
|
else
|
|
return false
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
elseif talent_points ~= nil then
|
|
if not arg_amount and not arg_operator then
|
|
return talent_points > 0
|
|
elseif arg_amount and arg_operator and CleveRoids.operators[arg_operator] then
|
|
return CleveRoids.comparators[arg_operator](talent_points, arg_amount)
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function CleveRoids.ValidateResting()
|
|
return IsResting()
|
|
end
|
|
|
|
|
|
-- TODO: refactor numeric comparisons...
|
|
|
|
-- Checks whether or not the given unit has power in percent vs the given amount
|
|
-- unit: The unit we're checking
|
|
-- operator: valid comparitive operator symbol
|
|
-- amount: The required amount
|
|
-- returns: True or false
|
|
function CleveRoids.ValidatePower(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local powerPercent = 100 / UnitManaMax(unit) * UnitMana(unit)
|
|
|
|
if powerPercent and CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](powerPercent, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Checks whether or not the given unit has current power vs the given amount
|
|
-- unit: The unit we're checking
|
|
-- operator: valid comparitive operator symbol
|
|
-- amount: The required amount
|
|
-- returns: True or false
|
|
function CleveRoids.ValidateRawPower(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local power = UnitMana(unit)
|
|
|
|
if power and CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](power, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Raw caster-form mana for druids (SuperWoW: 2nd return of UnitMana)
|
|
function CleveRoids.ValidateDruidRawMana(unit, operator, amount)
|
|
unit = unit or "player"
|
|
if not operator or amount == nil then return false end
|
|
if (CleveRoids.playerClass ~= "DRUID") then return false end
|
|
|
|
-- SuperWoW returns: current-form power, caster-form mana
|
|
local _, casterMana = UnitMana(unit)
|
|
|
|
-- Fallback: if for some reason we didn't get a 2nd value and we're in caster form now
|
|
if type(casterMana) ~= "number" then
|
|
if UnitPowerType and UnitPowerType(unit) == 0 then
|
|
casterMana = UnitMana(unit)
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local cmp = CleveRoids.comparators and CleveRoids.comparators[operator]
|
|
return cmp and cmp(casterMana, amount) or false
|
|
end
|
|
|
|
-- Checks whether or not the given unit has a power deficit vs the amount specified
|
|
-- unit: The unit we're checking
|
|
-- operator: valid comparitive operator symbol
|
|
-- amount: The required amount
|
|
-- returns: True or false
|
|
function CleveRoids.ValidatePowerLost(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local powerLost = UnitManaMax(unit) - UnitMana(unit)
|
|
|
|
if CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](powerLost, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Checks whether or not the given unit has hp in percent vs the given amount
|
|
-- unit: The unit we're checking
|
|
-- operator: valid comparitive operator symbol
|
|
-- amount: The required amount
|
|
-- returns: True or false
|
|
function CleveRoids.ValidateHp(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local hpPercent = 100 / UnitHealthMax(unit) * UnitHealth(unit)
|
|
|
|
if CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](hpPercent, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Checks whether or not the given unit has hp vs the given amount
|
|
-- unit: The unit we're checking
|
|
-- operator: valid comparitive operator symbol
|
|
-- amount: The required amount
|
|
-- returns: True or false
|
|
function CleveRoids.ValidateRawHp(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local rawhp = UnitHealth(unit)
|
|
|
|
if CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](rawhp, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Checks whether or not the given unit has an hp deficit vs the amount specified
|
|
-- unit: The unit we're checking
|
|
-- operator: valid comparitive operator symbol
|
|
-- amount: The required amount
|
|
-- returns: True or false
|
|
function CleveRoids.ValidateHpLost(unit, operator, amount)
|
|
if not unit or not operator or not amount then return false end
|
|
local hpLost = UnitHealthMax(unit) - UnitHealth(unit)
|
|
|
|
if CleveRoids.operators[operator] then
|
|
return CleveRoids.comparators[operator](hpLost, amount)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Checks whether the given creatureType is the same as the target's creature type
|
|
-- creatureType: The type to check
|
|
-- target: The target's unitID
|
|
-- returns: True or false
|
|
-- remarks: Allows for both localized and unlocalized type names
|
|
function CleveRoids.ValidateCreatureType(creatureType, target)
|
|
if not target then return false end
|
|
local targetType = UnitCreatureType(target)
|
|
if not targetType then return false end -- ooze or silithid etc
|
|
local ct = string.lower(creatureType)
|
|
local cl = UnitClassification(target)
|
|
if (ct == "boss" and "worldboss" or ct) == cl then
|
|
return true
|
|
end
|
|
if string.lower(creatureType) == "boss" then creatureType = "worldboss" end
|
|
local englishType = CleveRoids.Localized.CreatureTypes[targetType]
|
|
return ct == string.lower(targetType) or creatureType == englishType
|
|
end
|
|
|
|
-- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed
|
|
function CleveRoids.ValidateCooldown(args, ignoreGCD)
|
|
if not args then return false end
|
|
if type(args) ~= "table" then
|
|
-- Normalize the spell name by replacing underscores with spaces
|
|
local normalized = string.gsub(args, "_", " ")
|
|
|
|
-- NEW: If this is a numeric slot (1-19), resolve to the equipped item's name
|
|
local slotNum = tonumber(normalized)
|
|
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
|
local link = GetInventoryItemLink("player", slotNum)
|
|
if link then
|
|
-- Extract item name from link
|
|
local _, _, itemName = string.find(link, "%[(.+)%]")
|
|
if itemName then
|
|
normalized = itemName
|
|
end
|
|
end
|
|
end
|
|
|
|
args = {name = normalized}
|
|
else
|
|
-- Also normalize if it's already a table
|
|
if args.name then
|
|
args.name = string.gsub(args.name, "_", " ")
|
|
|
|
-- NEW: If this is a numeric slot (1-19), resolve to the equipped item's name
|
|
local slotNum = tonumber(args.name)
|
|
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
|
local link = GetInventoryItemLink("player", slotNum)
|
|
if link then
|
|
-- Extract item name from link
|
|
local _, _, itemName = string.find(link, "%[(.+)%]")
|
|
if itemName then
|
|
args.name = itemName
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local expires = CleveRoids.GetCooldown(args.name, ignoreGCD)
|
|
|
|
if not args.operator and not args.amount then
|
|
return expires > GetTime()
|
|
elseif CleveRoids.operators[args.operator] then
|
|
return CleveRoids.comparators[args.operator](expires - GetTime(), args.amount)
|
|
end
|
|
end
|
|
|
|
function CleveRoids.GetPlayerAura(index, isbuff)
|
|
if not index then return false end
|
|
|
|
local buffType = isbuff and "HELPFUL" or "HARMFUL"
|
|
local bid = GetPlayerBuff(index, buffType)
|
|
if bid < 0 then return end
|
|
|
|
local spellID = CleveRoids.hasSuperwow and GetPlayerBuffID(bid)
|
|
|
|
return GetPlayerBuffTexture(bid), GetPlayerBuffApplications(bid), spellID, GetPlayerBuffTimeLeft(bid)
|
|
end
|
|
|
|
function CleveRoids.ValidateAura(unit, args, isbuff)
|
|
if not args or not UnitExists(unit) then return false end
|
|
|
|
if not CleveRoids.hasSuperwow then
|
|
return false
|
|
end
|
|
|
|
if type(args) ~= "table" then
|
|
args = {name = args}
|
|
end
|
|
|
|
local isPlayer = UnitIsUnit(unit, "player")
|
|
local found = false
|
|
local stacks, remaining
|
|
local i = isPlayer and 0 or 1
|
|
|
|
-- Primary search: BUFFS if isbuff==true, DEBUFFS if isbuff==false
|
|
while true do
|
|
local texture
|
|
local current_spellID = nil
|
|
|
|
if isPlayer then
|
|
-- GetPlayerAura(index, isbuff) => texture, stacks, spellID, timeLeft
|
|
texture, stacks, current_spellID, remaining = CleveRoids.GetPlayerAura(i, isbuff)
|
|
else
|
|
if isbuff then
|
|
-- UnitBuff => texture, stacks, spellID
|
|
texture, stacks, current_spellID = UnitBuff(unit, i)
|
|
else
|
|
-- UnitDebuff => texture, stacks, _, spellID
|
|
texture, stacks, _, current_spellID = UnitDebuff(unit, i)
|
|
end
|
|
remaining = nil
|
|
end
|
|
|
|
if not texture then break end
|
|
|
|
if current_spellID then
|
|
local auraName = SpellInfo(current_spellID)
|
|
if auraName and args.name then
|
|
if string.lower(auraName) == string.lower(args.name) then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
i = i + 1
|
|
end
|
|
|
|
-- Overflow handling: when searching DEBUFFS on non-players, also scan BUFFS
|
|
if not isbuff and not isPlayer and not found then
|
|
i = 1
|
|
while true do
|
|
local texture
|
|
local current_spellID = nil
|
|
|
|
-- UnitBuff => texture, stacks, spellID
|
|
texture, stacks, current_spellID = UnitBuff(unit, i)
|
|
if not texture then break end
|
|
|
|
if current_spellID then
|
|
local auraName = SpellInfo(current_spellID)
|
|
if auraName and args.name then
|
|
if string.lower(auraName) == string.lower(args.name) then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
local ops = CleveRoids.operators
|
|
if not args.amount and not args.operator and not args.checkStacks then
|
|
return found
|
|
elseif isPlayer and not args.checkStacks and args.amount and ops[args.operator] then
|
|
return CleveRoids.comparators[args.operator](remaining or -1, args.amount)
|
|
elseif args.amount and args.checkStacks and ops[args.operator] then
|
|
return CleveRoids.comparators[args.operator](stacks or -1, args.amount)
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function CleveRoids.ValidateUnitBuff(unit, args)
|
|
return CleveRoids.ValidateAura(unit, args, true)
|
|
end
|
|
|
|
function CleveRoids.ValidateUnitDebuff(unit, args)
|
|
if not args or not UnitExists(unit) then return false end
|
|
if type(args) ~= "table" then
|
|
args = { name = args }
|
|
end
|
|
if not args.name then return false end
|
|
|
|
local found = false
|
|
local texture, stacks, spellID, remaining
|
|
local i
|
|
|
|
-- Step 1: Search DEBUFFS first
|
|
i = (unit == "player") and 0 or 1
|
|
while true do
|
|
if unit == "player" then
|
|
texture, stacks, spellID, remaining = CleveRoids.GetPlayerAura(i, false)
|
|
else
|
|
texture, stacks, _, spellID = UnitDebuff(unit, i)
|
|
end
|
|
if not texture then break end
|
|
|
|
if (CleveRoids.hasSuperwow and args.name == SpellInfo(spellID))
|
|
or (not CleveRoids.hasSuperwow and texture == CleveRoids.auraTextures[args.name]) then
|
|
found = true
|
|
break
|
|
end
|
|
i = i + 1
|
|
end
|
|
|
|
-- Step 2: If not found, search BUFFS (overflow debuffs shown as buffs on some servers)
|
|
if not found then
|
|
i = (unit == "player") and 0 or 1
|
|
while true do
|
|
if unit == "player" then
|
|
texture, stacks, spellID, remaining = CleveRoids.GetPlayerAura(i, true)
|
|
else
|
|
texture, stacks, spellID = UnitBuff(unit, i)
|
|
end
|
|
if not texture then break end
|
|
|
|
if (CleveRoids.hasSuperwow and args.name == SpellInfo(spellID))
|
|
or (not CleveRoids.hasSuperwow and texture == CleveRoids.auraTextures[args.name]) then
|
|
found = true
|
|
break
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
-- Step 3: Perform conditional validation
|
|
local ops = CleveRoids.operators
|
|
local cmp = CleveRoids.comparators
|
|
local hasNumCheck = (args.amount ~= nil) and (args.operator ~= nil) and ops[args.operator]
|
|
|
|
-- Case A: No numeric/stack condition, just check for existence.
|
|
if not hasNumCheck and not args.checkStacks then
|
|
return found
|
|
end
|
|
|
|
-- Case B: Numeric/stack condition exists.
|
|
if hasNumCheck then
|
|
-- Stacks compare path
|
|
if args.checkStacks then
|
|
if found then
|
|
return cmp[args.operator](stacks or 0, args.amount)
|
|
else
|
|
return cmp[args.operator](0, args.amount)
|
|
end
|
|
end
|
|
|
|
-- Time-left compare path
|
|
if unit == "player" then
|
|
if not found then
|
|
return false -- debuff doesn't exist, fail the check
|
|
end
|
|
local tl = remaining or 0
|
|
return cmp[args.operator](tl, args.amount)
|
|
else
|
|
-- Non-player: try pfUI → internal libdebuff → 0s
|
|
local tl = _get_debuff_timeleft(unit, args.name)
|
|
if tl ~= nil then
|
|
return cmp[args.operator](tl or 0, args.amount)
|
|
end
|
|
|
|
if CleveRoids.libdebuff and CleveRoids.libdebuff.UnitDebuff then
|
|
local atl = nil
|
|
for idx = 1, 16 do
|
|
local effect, _, _, _, _, duration, timeleft = CleveRoids.libdebuff:UnitDebuff(unit, idx)
|
|
if effect == args.name then
|
|
atl = (timeleft and timeleft >= 0) and timeleft or 0
|
|
break
|
|
end
|
|
end
|
|
if atl ~= nil then
|
|
return cmp[args.operator](atl, args.amount)
|
|
end
|
|
end
|
|
|
|
-- No timers at all: treat missing/unknown as 0s and compare
|
|
if not found then
|
|
-- If debuff doesn't exist, treat as 0 seconds and compare
|
|
return cmp[args.operator](0, args.amount)
|
|
end
|
|
-- If we reach here with no timer data, treat as 0
|
|
return cmp[args.operator](0, args.amount)
|
|
end
|
|
end
|
|
|
|
-- If we get here, nothing matched
|
|
return false
|
|
end
|
|
|
|
function CleveRoids.ValidatePlayerBuff(args)
|
|
return CleveRoids.ValidateAura("player", args, true)
|
|
end
|
|
|
|
function CleveRoids.ValidatePlayerDebuff(args)
|
|
return CleveRoids.ValidateAura("player", args, false)
|
|
end
|
|
|
|
function CleveRoids.ValidateWeaponImbue(slot, imbueName)
|
|
-- 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
|
|
|
|
-- Only consider temporary enchants (with time or charges)
|
|
-- This filters out permanent enchants like Crusader, Lifestealing, etc.
|
|
local hasTemporaryEnchant = hasEnchant and (expiration and expiration > 0 or charges and charges > 0)
|
|
|
|
-- If no specific imbue requested, return temporary enchant status
|
|
if not imbueName or imbueName == "" then
|
|
return hasTemporaryEnchant
|
|
end
|
|
|
|
-- If no temporary enchant, don't bother scanning
|
|
if not hasTemporaryEnchant then
|
|
return false
|
|
end
|
|
|
|
-- For specific imbue names, scan tooltip to match the name
|
|
-- BUT only check lines that have time markers (temporary enchants)
|
|
-- This prevents matching weapon stats like "Equip: ... critical strike ..."
|
|
|
|
-- Create tooltip scanner if needed
|
|
if not CleveRoidsTooltip then
|
|
CreateFrame("GameTooltip", "CleveRoidsTooltip", nil, "GameTooltipTemplate")
|
|
end
|
|
|
|
-- Scan weapon tooltip
|
|
CleveRoidsTooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
CleveRoidsTooltip:ClearLines()
|
|
CleveRoidsTooltip:SetInventoryItem("player", slot == "mh" and 16 or 17)
|
|
|
|
-- Normalize search term once
|
|
local searchTerm = string.lower(string.gsub(imbueName, "_", " "))
|
|
|
|
-- Look for green text with time markers - check ALL green lines with time
|
|
for i = 1, CleveRoidsTooltip:NumLines() do
|
|
local text = _G["CleveRoidsTooltipTextLeft"..i]
|
|
if text then
|
|
local line = text:GetText()
|
|
if line then
|
|
local r, g, b = text:GetTextColor()
|
|
-- Green text indicates enchant
|
|
if g > 0.8 and r < 0.2 and b < 0.2 then
|
|
local lowerLine = string.lower(line)
|
|
-- Only check lines with time markers (temporary enchants)
|
|
-- This skips permanent weapon stats like "Equip: ... critical strike ..."
|
|
if string.find(lowerLine, "%(") and (string.find(lowerLine, " min%)") or string.find(lowerLine, " sec%)") or string.find(lowerLine, " charge")) then
|
|
-- This is a temporary enchant line, check if it matches
|
|
if string.find(lowerLine, searchTerm, 1, true) then
|
|
return true -- Found it!
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Checked all lines, didn't find it
|
|
return false
|
|
end
|
|
|
|
-- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed
|
|
function CleveRoids.GetCooldown(name, ignoreGCD)
|
|
if not name then return 0 end
|
|
|
|
-- Check if it's a spell first
|
|
local spell = CleveRoids.GetSpell(name)
|
|
if spell then
|
|
local expires = CleveRoids.GetSpellCooldown(name, ignoreGCD)
|
|
return expires -- GetSpellCooldown already returns absolute time
|
|
end
|
|
|
|
-- Not a spell, check if it's an item
|
|
-- GetItemCooldown returns (remainingSeconds, totalDuration, enabled)
|
|
local remaining, duration, enabled = CleveRoids.GetItemCooldown(name, ignoreGCD)
|
|
|
|
-- Convert remaining seconds to absolute expiry time
|
|
if remaining and remaining > 0 then
|
|
return GetTime() + remaining
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
-- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed
|
|
-- Returns the cooldown of the given spellName or nil if no such spell was found
|
|
function CleveRoids.GetSpellCooldown(spellName, ignoreGCD)
|
|
if not spellName then return 0 end
|
|
|
|
local spell = CleveRoids.GetSpell(spellName)
|
|
if not spell then return 0 end
|
|
|
|
local start, cd = GetSpellCooldown(spell.spellSlot, spell.bookType)
|
|
if ignoreGCD and cd and cd > 0 and cd <= 1.5 then
|
|
return 0
|
|
else
|
|
return (start + cd)
|
|
end
|
|
end
|
|
|
|
-- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed
|
|
-- Hardened item cooldown resolver (Vanilla 1.12.1 / Lua 5.0)
|
|
-- Returns: remainingSeconds, totalDuration, enabled
|
|
function CleveRoids.GetItemCooldown(item)
|
|
local start, duration, enable = nil, nil, nil
|
|
|
|
local function _norm(s, d, e)
|
|
s = tonumber(s) or 0
|
|
d = tonumber(d) or 0
|
|
e = tonumber(e) or 0
|
|
if d <= 0 or s <= 0 then
|
|
return 0, 0, e
|
|
end
|
|
local rem = (s + d) - GetTime()
|
|
if rem < 0 then rem = 0 end
|
|
return rem, d, e
|
|
end
|
|
|
|
-- Helper: Check if a numeric value is a valid inventory slot (1-19)
|
|
local function _isInventorySlot(num)
|
|
return num and num >= 1 and num <= 19
|
|
end
|
|
|
|
-- Case A: numeric value passed
|
|
local numericItem = tonumber(item)
|
|
if numericItem then
|
|
-- Is it an inventory slot?
|
|
if _isInventorySlot(numericItem) then
|
|
start, duration, enable = GetInventoryItemCooldown("player", numericItem)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
|
|
-- Otherwise, treat it as an item ID - search equipped items first
|
|
for slot = 0, 19 do
|
|
local link = GetInventoryItemLink("player", slot)
|
|
if link then
|
|
local _, _, id = string.find(link, "item:(%d+)")
|
|
if id and tonumber(id) == numericItem then
|
|
start, duration, enable = GetInventoryItemCooldown("player", slot)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Then search bags for the item ID
|
|
for bag = 0, 4 do
|
|
local size = GetContainerNumSlots(bag)
|
|
if size and size > 0 then
|
|
for slotIndex = 1, size do
|
|
local link = GetContainerItemLink(bag, slotIndex)
|
|
if link then
|
|
local _, _, id = string.find(link, "item:(%d+)")
|
|
if id and tonumber(id) == numericItem then
|
|
start, duration, enable = GetContainerItemCooldown(bag, slotIndex)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Item ID not found in inventory or bags
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Case B: string item name -> try equipped slots first
|
|
if type(item) == "string" and item ~= "" then
|
|
local itemLower = string.lower(item)
|
|
|
|
-- scan a few common equipment slots; expand if your engine needs more
|
|
local slots = { 13, 14, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19 } -- trinkets first
|
|
for i = 1, table.getn(slots) do
|
|
local s = slots[i]
|
|
local link = GetInventoryItemLink("player", s)
|
|
if link then
|
|
-- Extract item name from link using pattern [ItemName]
|
|
local _, _, linkName = string.find(link, "%[(.+)%]")
|
|
if linkName and string.lower(linkName) == itemLower then
|
|
start, duration, enable = GetInventoryItemCooldown("player", s)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
-- Fallback: simple substring match
|
|
if string.find(string.lower(link), itemLower, 1, true) then
|
|
start, duration, enable = GetInventoryItemCooldown("player", s)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Case C: search bags for the named item
|
|
for bag = 0, 4 do
|
|
local size = GetContainerNumSlots(bag)
|
|
if size and size > 0 then
|
|
for slotIndex = 1, size do
|
|
local link = GetContainerItemLink(bag, slotIndex)
|
|
if link then
|
|
-- Extract item name from link using pattern [ItemName]
|
|
local _, _, linkName = string.find(link, "%[(.+)%]")
|
|
if linkName and string.lower(linkName) == itemLower then
|
|
start, duration, enable = GetContainerItemCooldown(bag, slotIndex)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
-- Fallback: simple substring match
|
|
if string.find(string.lower(link), itemLower, 1, true) then
|
|
start, duration, enable = GetContainerItemCooldown(bag, slotIndex)
|
|
return _norm(start, duration, enable)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Fallback: unknown item → no cooldown
|
|
return 0, 0, 0
|
|
end
|
|
|
|
function CleveRoids.ValidatePlayerAuraCount(bigger, amount)
|
|
local aura_ix = -1
|
|
local num = 0
|
|
while true do
|
|
aura_ix = GetPlayerBuff(num,"HELPFUL|PASSIVE")
|
|
if aura_ix == -1 then break end
|
|
num = num + 1
|
|
end
|
|
if bigger == 0 then
|
|
return num < tonumber(amount)
|
|
else
|
|
return num > tonumber(amount)
|
|
end
|
|
end
|
|
|
|
function CleveRoids.IsReactive(name)
|
|
return CleveRoids.reactiveSpells[spellName] ~= nil
|
|
end
|
|
|
|
function CleveRoids.GetActionButtonInfo(slot)
|
|
local macroName, actionType, id = GetActionText(slot)
|
|
if actionType == "MACRO" then
|
|
return actionType, id, macroName
|
|
elseif actionType == "SPELL" and id then
|
|
local spellName, rank = SpellInfo(id)
|
|
return actionType, id, spellName, rank
|
|
elseif actionType == "ITEM" and id then
|
|
local item = CleveRoids.GetItem(id)
|
|
return actionType, id, (item and item.name), (item and item.id)
|
|
end
|
|
end
|
|
|
|
function CleveRoids.IsReactiveUsable(spellName)
|
|
-- Use Nampower's IsSpellUsable if available (more accurate)
|
|
if IsSpellUsable then
|
|
local usable, oom = IsSpellUsable(spellName)
|
|
if usable == 1 and oom ~= 1 then
|
|
return 1
|
|
else
|
|
return nil, oom
|
|
end
|
|
end
|
|
|
|
-- Fallback to original method
|
|
if not CleveRoids.reactiveSlots[spellName] then return false end
|
|
local actionSlot = CleveRoids.reactiveSlots[spellName]
|
|
local isUsable, oom = CleveRoids.Hooks.OriginalIsUsableAction(actionSlot)
|
|
local start, duration = GetActionCooldown(actionSlot)
|
|
if isUsable and (start == 0 or duration == 1.5) then -- 1.5 just means gcd is active
|
|
return 1
|
|
else
|
|
return nil, oom
|
|
end
|
|
end
|
|
|
|
-- Check if any spell is usable (not just reactive)
|
|
function CleveRoids.CheckSpellUsable(spellName)
|
|
if not spellName then return false end
|
|
|
|
-- Use Nampower's IsSpellUsable if available
|
|
if IsSpellUsable then
|
|
local usable, oom = IsSpellUsable(spellName)
|
|
return (usable == 1 and oom ~= 1)
|
|
end
|
|
|
|
-- Fallback: check if spell exists and player has mana/rage/energy
|
|
local spell = CleveRoids.GetSpell(spellName)
|
|
if not spell then return false end
|
|
|
|
-- Check mana cost
|
|
local currentPower = UnitMana("player")
|
|
if spell.cost and currentPower < spell.cost then
|
|
return false
|
|
end
|
|
|
|
-- Check cooldown (ignore GCD)
|
|
local start, duration = GetSpellCooldown(spell.spellSlot, spell.bookType)
|
|
if start > 0 and duration > 1.5 then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function CleveRoids.CheckSpellCast(unit, spell)
|
|
if not CleveRoids.hasSuperwow then return false end
|
|
|
|
local spell = spell or ""
|
|
local _,guid = UnitExists(unit)
|
|
if not guid or (guid and not CleveRoids.spell_tracking[guid]) then
|
|
return false
|
|
else
|
|
-- are we casting a specific spell, or any spell
|
|
if spell == SpellInfo(CleveRoids.spell_tracking[guid].spell_id) or (spell == "") then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- A list of Conditionals and their functions to validate them
|
|
CleveRoids.Keywords = {
|
|
exists = function(conditionals)
|
|
return UnitExists(conditionals.target)
|
|
end,
|
|
|
|
noexists = function(conditionals)
|
|
return not UnitExists(conditionals.target)
|
|
end,
|
|
|
|
help = function(conditionals)
|
|
return conditionals.help and conditionals.target and UnitExists(conditionals.target) and UnitCanAssist("player", conditionals.target)
|
|
end,
|
|
|
|
harm = function(conditionals)
|
|
return conditionals.harm and conditionals.target and UnitExists(conditionals.target) and UnitCanAttack("player", conditionals.target)
|
|
end,
|
|
|
|
stance = function(conditionals)
|
|
local i = CleveRoids.GetCurrentShapeshiftIndex()
|
|
return Or(conditionals.stance, function (v)
|
|
return (i == tonumber(v))
|
|
end)
|
|
end,
|
|
|
|
nostance = function(conditionals)
|
|
local i = CleveRoids.GetCurrentShapeshiftIndex()
|
|
local forbiddenStances = conditionals.nostance
|
|
if type(forbiddenStances) ~= "table" then
|
|
return i == 0
|
|
end
|
|
return And(forbiddenStances, function (v)
|
|
return (i ~= tonumber(v))
|
|
end)
|
|
end,
|
|
|
|
noform = function(conditionals)
|
|
local i = CleveRoids.GetCurrentShapeshiftIndex()
|
|
local forbiddenForms = conditionals.noform
|
|
if type(forbiddenForms) ~= "table" then
|
|
return i == 0
|
|
end
|
|
return And(forbiddenForms, function (v)
|
|
return (i ~= tonumber(v))
|
|
end)
|
|
end,
|
|
|
|
form = function(conditionals)
|
|
local i = CleveRoids.GetCurrentShapeshiftIndex()
|
|
return Or(conditionals.form, function (v)
|
|
return (i == tonumber(v))
|
|
end)
|
|
end,
|
|
|
|
mod = function(conditionals)
|
|
if type(conditionals.mod) ~= "table" then
|
|
return CleveRoids.kmods.mod()
|
|
end
|
|
return Or(conditionals.mod, function(mod)
|
|
return CleveRoids.kmods[mod]()
|
|
end)
|
|
end,
|
|
|
|
nomod = function(conditionals)
|
|
if type(conditionals.nomod) ~= "table" then
|
|
return CleveRoids.kmods.nomod()
|
|
end
|
|
return And(conditionals.nomod, function(mod)
|
|
return not CleveRoids.kmods[mod]()
|
|
end)
|
|
end,
|
|
|
|
target = function(conditionals)
|
|
return CleveRoids.IsValidTarget(conditionals.target, conditionals.help)
|
|
end,
|
|
|
|
combat = function(conditionals)
|
|
-- Check if an argument like :target or :focus was provided. The parser turns this into a table.
|
|
if type(conditionals.combat) == "table" then
|
|
-- If so, run the check on the provided unit(s).
|
|
return Or(conditionals.combat, function(unit)
|
|
return UnitExists(unit) and UnitAffectingCombat(unit)
|
|
end)
|
|
else
|
|
-- Otherwise, this is a bare [combat]. The value might be 'true' or a spell name.
|
|
-- In either case, it should safely default to checking the player.
|
|
return UnitAffectingCombat("player")
|
|
end
|
|
end,
|
|
|
|
nocombat = function(conditionals)
|
|
-- Check if an argument like :target or :focus was provided.
|
|
if type(conditionals.nocombat) == "table" then
|
|
-- If so, run the check on the provided unit(s).
|
|
return And(conditionals.nocombat, function(unit)
|
|
if not UnitExists(unit) then
|
|
return true
|
|
end
|
|
return not UnitAffectingCombat(unit)
|
|
end)
|
|
else
|
|
-- Otherwise, this is a bare [nocombat]. Default to checking the player.
|
|
return not UnitAffectingCombat("player")
|
|
end
|
|
end,
|
|
|
|
stealth = function(conditionals)
|
|
return (
|
|
(CleveRoids.playerClass == "ROGUE" and CleveRoids.ValidatePlayerBuff(CleveRoids.Localized.Spells["Stealth"]))
|
|
or (CleveRoids.playerClass == "DRUID" and CleveRoids.ValidatePlayerBuff(CleveRoids.Localized.Spells["Prowl"]))
|
|
)
|
|
end,
|
|
|
|
nostealth = function(conditionals)
|
|
return (
|
|
(CleveRoids.playerClass == "ROGUE" and not CleveRoids.ValidatePlayerBuff(CleveRoids.Localized.Spells["Stealth"]))
|
|
or (CleveRoids.playerClass == "DRUID" and not CleveRoids.ValidatePlayerBuff(CleveRoids.Localized.Spells["Prowl"]))
|
|
)
|
|
end,
|
|
|
|
casting = function(conditionals)
|
|
if type(conditionals.casting) ~= "table" then return CleveRoids.CheckSpellCast(conditionals.target, "") end
|
|
return Or(conditionals.casting, function (spell)
|
|
return CleveRoids.CheckSpellCast(conditionals.target, spell)
|
|
end)
|
|
end,
|
|
|
|
nocasting = function(conditionals)
|
|
if type(conditionals.nocasting) ~= "table" then return CleveRoids.CheckSpellCast(conditionals.target, "") end
|
|
return And(conditionals.nocasting, function (spell)
|
|
return not CleveRoids.CheckSpellCast(conditionals.target, spell)
|
|
end)
|
|
end,
|
|
|
|
zone = function(conditionals)
|
|
local zone = GetRealZoneText()
|
|
local sub_zone = GetSubZoneText()
|
|
return Or(conditionals.zone, function (v)
|
|
return (sub_zone ~= "" and (v == sub_zone) or (v == zone))
|
|
end)
|
|
end,
|
|
|
|
nozone = function(conditionals)
|
|
local zone = GetRealZoneText()
|
|
local sub_zone = GetSubZoneText()
|
|
return And(conditionals.nozone, function (v)
|
|
return not ((sub_zone ~= "" and v == sub_zone)) or (v == zone)
|
|
end)
|
|
end,
|
|
|
|
equipped = function(conditionals)
|
|
local itemsToCheck = {}
|
|
|
|
-- Case 1: conditionals.equipped is a string (e.g., [equipped]ItemName)
|
|
if type(conditionals.equipped) == "string" then
|
|
table.insert(itemsToCheck, conditionals.equipped)
|
|
-- Case 2: conditionals.equipped is a table (e.g., [equipped:Shields])
|
|
elseif type(conditionals.equipped) == "table" and table.getn(conditionals.equipped) > 0 then
|
|
itemsToCheck = conditionals.equipped
|
|
-- Case 3: No value provided, check the action
|
|
elseif conditionals.action then
|
|
table.insert(itemsToCheck, conditionals.action)
|
|
else
|
|
return false
|
|
end
|
|
|
|
-- Check all items
|
|
return Or(itemsToCheck, function(v)
|
|
return (CleveRoids.HasWeaponEquipped(v) or CleveRoids.HasGearEquipped(v))
|
|
end)
|
|
end,
|
|
|
|
noequipped = function(conditionals)
|
|
local itemsToCheck = {}
|
|
|
|
-- Case 1: conditionals.noequipped is a string (e.g., [noequipped]ItemName)
|
|
if type(conditionals.noequipped) == "string" then
|
|
table.insert(itemsToCheck, conditionals.noequipped)
|
|
-- Case 2: conditionals.noequipped is a table (e.g., [noequipped:Shields])
|
|
elseif type(conditionals.noequipped) == "table" and table.getn(conditionals.noequipped) > 0 then
|
|
itemsToCheck = conditionals.noequipped
|
|
-- Case 3: No value provided, check the action
|
|
elseif conditionals.action then
|
|
table.insert(itemsToCheck, conditionals.action)
|
|
else
|
|
return false
|
|
end
|
|
|
|
-- Check all items - ALL must be NOT equipped for this to pass
|
|
return And(itemsToCheck, function(v)
|
|
return not (CleveRoids.HasWeaponEquipped(v) or CleveRoids.HasGearEquipped(v))
|
|
end)
|
|
end,
|
|
|
|
dead = function(conditionals)
|
|
if not conditionals.target then return false end
|
|
return UnitIsDeadOrGhost(conditionals.target)
|
|
end,
|
|
|
|
alive = function(conditionals)
|
|
if not conditionals.target then return false end
|
|
return not UnitIsDeadOrGhost(conditionals.target)
|
|
end,
|
|
|
|
noalive = function(conditionals)
|
|
if not conditionals.target then return false end
|
|
return UnitIsDeadOrGhost(conditionals.target)
|
|
end,
|
|
|
|
nodead = function(conditionals)
|
|
if not conditionals.target then return false end
|
|
return not UnitIsDeadOrGhost(conditionals.target)
|
|
end,
|
|
|
|
reactive = function(conditionals)
|
|
return Or(conditionals.reactive, function (v)
|
|
return CleveRoids.IsReactiveUsable(v)
|
|
end)
|
|
end,
|
|
|
|
noreactive = function(conditionals)
|
|
return And(conditionals.noreactive, function (v)
|
|
return not CleveRoids.IsReactiveUsable(v)
|
|
end)
|
|
end,
|
|
|
|
usable = function(conditionals)
|
|
return Or(conditionals.usable, function(name)
|
|
-- If checking a reactive spell, use reactive logic
|
|
if CleveRoids.reactiveSpells[name] then
|
|
return CleveRoids.IsReactiveUsable(name)
|
|
end
|
|
|
|
-- Check if it's a spell first
|
|
local spell = CleveRoids.GetSpell(name)
|
|
if spell then
|
|
return CleveRoids.CheckSpellUsable(name)
|
|
end
|
|
|
|
-- Not a spell - check if it's an item or slot number
|
|
local itemName = name
|
|
local slotNum = tonumber(name)
|
|
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
|
-- Resolve slot number to item name
|
|
local link = GetInventoryItemLink("player", slotNum)
|
|
if link then
|
|
local _, _, extractedName = string.find(link, "%[(.+)%]")
|
|
if extractedName then
|
|
itemName = extractedName
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check item cooldown (0 remaining = usable)
|
|
local remaining = CleveRoids.GetItemCooldown(itemName)
|
|
return remaining == 0
|
|
end)
|
|
end,
|
|
|
|
nousable = function(conditionals)
|
|
return And(conditionals.nousable, function(name)
|
|
-- If checking a reactive spell, use reactive logic
|
|
if CleveRoids.reactiveSpells[name] then
|
|
return not CleveRoids.IsReactiveUsable(name)
|
|
end
|
|
|
|
-- Check if it's a spell first
|
|
local spell = CleveRoids.GetSpell(name)
|
|
if spell then
|
|
return not CleveRoids.CheckSpellUsable(name)
|
|
end
|
|
|
|
-- Not a spell - check if it's an item or slot number
|
|
local itemName = name
|
|
local slotNum = tonumber(name)
|
|
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
|
-- Resolve slot number to item name
|
|
local link = GetInventoryItemLink("player", slotNum)
|
|
if link then
|
|
local _, _, extractedName = string.find(link, "%[(.+)%]")
|
|
if extractedName then
|
|
itemName = extractedName
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check item cooldown (>0 remaining = not usable)
|
|
local remaining = CleveRoids.GetItemCooldown(itemName)
|
|
return remaining > 0
|
|
end)
|
|
end,
|
|
|
|
member = function(conditionals)
|
|
return Or(conditionals.member, function(v)
|
|
return
|
|
CleveRoids.IsTargetInGroupType(conditionals.target, "party")
|
|
or CleveRoids.IsTargetInGroupType(conditionals.target, "raid")
|
|
end)
|
|
end,
|
|
|
|
party = function(conditionals)
|
|
return CleveRoids.IsTargetInGroupType(conditionals.target, "party")
|
|
end,
|
|
|
|
noparty = function(conditionals)
|
|
return not CleveRoids.IsTargetInGroupType(conditionals.target, "party")
|
|
end,
|
|
|
|
raid = function(conditionals)
|
|
return CleveRoids.IsTargetInGroupType(conditionals.target, "raid")
|
|
end,
|
|
|
|
noraid = function(conditionals)
|
|
return not CleveRoids.IsTargetInGroupType(conditionals.target, "raid")
|
|
end,
|
|
|
|
group = function(conditionals)
|
|
if type(conditionals.group) ~= "table" then
|
|
conditionals.group = { "party", "raid" }
|
|
end
|
|
return Or(conditionals.group, function(groups)
|
|
if groups == "party" then
|
|
return GetNumPartyMembers() > 0
|
|
elseif groups == "raid" then
|
|
return GetNumRaidMembers() > 0
|
|
end
|
|
end)
|
|
end,
|
|
|
|
checkchanneled = function(conditionals)
|
|
return And(conditionals.checkchanneled, function(channeledSpells)
|
|
return CleveRoids.CheckChanneled(channeledSpells)
|
|
end)
|
|
end,
|
|
|
|
buff = function(conditionals)
|
|
return And(conditionals.buff, function(v)
|
|
return CleveRoids.ValidateUnitBuff(conditionals.target, v)
|
|
end)
|
|
end,
|
|
|
|
nobuff = function(conditionals)
|
|
return And(conditionals.nobuff, function(v)
|
|
return not CleveRoids.ValidateUnitBuff(conditionals.target, v)
|
|
end)
|
|
end,
|
|
|
|
debuff = function(conditionals)
|
|
return And(conditionals.debuff, function(v)
|
|
return CleveRoids.ValidateUnitDebuff(conditionals.target, v)
|
|
end)
|
|
end,
|
|
|
|
nodebuff = function(conditionals)
|
|
return And(conditionals.nodebuff, function(v)
|
|
return not CleveRoids.ValidateUnitDebuff(conditionals.target, v)
|
|
end)
|
|
end,
|
|
|
|
mybuff = function(conditionals)
|
|
return And(conditionals.mybuff, function(v)
|
|
return CleveRoids.ValidatePlayerBuff(v)
|
|
end)
|
|
end,
|
|
|
|
nomybuff = function(conditionals)
|
|
return And(conditionals.nomybuff, function(v)
|
|
return not CleveRoids.ValidatePlayerBuff(v)
|
|
end)
|
|
end,
|
|
|
|
mydebuff = function(conditionals)
|
|
return And(conditionals.mydebuff, function(v)
|
|
return CleveRoids.ValidatePlayerDebuff(v)
|
|
end)
|
|
end,
|
|
|
|
nomydebuff = function(conditionals)
|
|
return And(conditionals.nomydebuff, function(v)
|
|
return not CleveRoids.ValidatePlayerDebuff(v)
|
|
end)
|
|
end,
|
|
|
|
power = function(conditionals)
|
|
return And(conditionals.power, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidatePower(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
mypower = function(conditionals)
|
|
return And(conditionals.mypower, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidatePower("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
rawpower = function(conditionals)
|
|
return And(conditionals.rawpower, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateRawPower(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
myrawpower = function(conditionals)
|
|
return And(conditionals.myrawpower, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateRawPower("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
druidmana = function(conditionals)
|
|
return And(conditionals.druidmana, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateDruidRawMana("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
powerlost = function(conditionals)
|
|
return And(conditionals.powerlost, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidatePowerLost(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
mypowerlost = function(conditionals)
|
|
return And(conditionals.mypowerlost, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidatePowerLost("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
hp = function(conditionals)
|
|
return And(conditionals.hp, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateHp(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
level = function(conditionals)
|
|
return And(conditionals.level, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateLevel(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
mylevel = function(conditionals)
|
|
return And(conditionals.mylevel, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateLevel("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
myhp = function(conditionals)
|
|
return And(conditionals.myhp, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateHp("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
rawhp = function(conditionals)
|
|
return And(conditionals.rawhp, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateRawHp(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
myrawhp = function(conditionals)
|
|
return And(conditionals.myrawhp, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateRawHp("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
hplost = function(conditionals)
|
|
return And(conditionals.hplost, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateHpLost(conditionals.target, args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
myhplost = function(conditionals)
|
|
return And(conditionals.myhplost, function(args)
|
|
if type(args) ~= "table" then return false end
|
|
return CleveRoids.ValidateHpLost("player", args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
type = function(conditionals)
|
|
return Or(conditionals.type, function(unittype)
|
|
return CleveRoids.ValidateCreatureType(unittype, conditionals.target)
|
|
end)
|
|
end,
|
|
|
|
notype = function(conditionals)
|
|
return And(conditionals.notype, function(unittype)
|
|
return not CleveRoids.ValidateCreatureType(unittype, conditionals.target)
|
|
end)
|
|
end,
|
|
|
|
cooldown = function(conditionals)
|
|
return And(conditionals.cooldown,function (v)
|
|
return CleveRoids.ValidateCooldown(v, true)
|
|
end)
|
|
end,
|
|
|
|
nocooldown = function(conditionals)
|
|
return And(conditionals.nocooldown,function (v)
|
|
return not CleveRoids.ValidateCooldown(v, true)
|
|
end)
|
|
end,
|
|
|
|
cdgcd = function(conditionals)
|
|
return And(conditionals.cdgcd,function (v)
|
|
return CleveRoids.ValidateCooldown(v, false)
|
|
end)
|
|
end,
|
|
|
|
nocdgcd = function(conditionals)
|
|
return And(conditionals.nocdgcd,function (v)
|
|
return not CleveRoids.ValidateCooldown(v, false)
|
|
end)
|
|
end,
|
|
|
|
channeled = function(conditionals)
|
|
if GetCurrentCastingInfo then
|
|
local _, _, _, _, channeling = GetCurrentCastingInfo()
|
|
return channeling == 1
|
|
end
|
|
return CleveRoids.CurrentSpell.type == "channeled"
|
|
end,
|
|
|
|
nochanneled = function(conditionals)
|
|
if GetCurrentCastingInfo then
|
|
local _, _, _, _, channeling = GetCurrentCastingInfo()
|
|
return channeling ~= 1
|
|
end
|
|
return CleveRoids.CurrentSpell.type ~= "channeled"
|
|
end,
|
|
|
|
targeting = function(conditionals)
|
|
return Or(conditionals.targeting, function (unit)
|
|
return (UnitIsUnit("targettarget", unit) == 1)
|
|
end)
|
|
end,
|
|
|
|
notargeting = function(conditionals)
|
|
return And(conditionals.notargeting, function (unit)
|
|
return UnitIsUnit("targettarget", unit) ~= 1
|
|
end)
|
|
end,
|
|
|
|
isplayer = function(conditionals)
|
|
return UnitIsPlayer(conditionals.target)
|
|
end,
|
|
|
|
isnpc = function(conditionals)
|
|
return not UnitIsPlayer(conditionals.target)
|
|
end,
|
|
|
|
inrange = function(conditionals)
|
|
if not IsSpellInRange then return end
|
|
return And(conditionals.inrange, function(spellName)
|
|
local target = conditionals.target or "target"
|
|
local checkValue = spellName or conditionals.action
|
|
|
|
-- Try to convert spell name to ID for better accuracy (Nampower)
|
|
if type(checkValue) == "string" and GetSpellIdForName then
|
|
local spellId = GetSpellIdForName(checkValue)
|
|
if spellId and spellId > 0 then
|
|
checkValue = spellId
|
|
end
|
|
end
|
|
|
|
return IsSpellInRange(checkValue, target) == 1
|
|
end)
|
|
end,
|
|
|
|
noinrange = function(conditionals)
|
|
if not IsSpellInRange then return end
|
|
return And(conditionals.noinrange, function(spellName)
|
|
local target = conditionals.target or "target"
|
|
local checkValue = spellName or conditionals.action
|
|
|
|
if type(checkValue) == "string" and GetSpellIdForName then
|
|
local spellId = GetSpellIdForName(checkValue)
|
|
if spellId and spellId > 0 then
|
|
checkValue = spellId
|
|
end
|
|
end
|
|
|
|
return IsSpellInRange(checkValue, target) == 0
|
|
end)
|
|
end,
|
|
|
|
outrange = function(conditionals)
|
|
if not IsSpellInRange then return end
|
|
return And(conditionals.outrange, function(spellName)
|
|
local target = conditionals.target or "target"
|
|
local checkValue = spellName or conditionals.action
|
|
|
|
if type(checkValue) == "string" and GetSpellIdForName then
|
|
local spellId = GetSpellIdForName(checkValue)
|
|
if spellId and spellId > 0 then
|
|
checkValue = spellId
|
|
end
|
|
end
|
|
|
|
return IsSpellInRange(checkValue, target) == 0
|
|
end)
|
|
end,
|
|
|
|
combo = function(conditionals)
|
|
return And(conditionals.combo, function(args)
|
|
return CleveRoids.ValidateComboPoints(args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
nocombo = function(conditionals)
|
|
return And(conditionals.nocombo, function(args)
|
|
return not CleveRoids.ValidateComboPoints(args.operator, args.amount)
|
|
end)
|
|
end,
|
|
|
|
known = function(conditionals)
|
|
return And(conditionals.known, function(args)
|
|
return CleveRoids.ValidateKnown(args)
|
|
end)
|
|
end,
|
|
|
|
noknown = function(conditionals)
|
|
return And(conditionals.noknown, function(args)
|
|
return not CleveRoids.ValidateKnown(args)
|
|
end)
|
|
end,
|
|
|
|
resting = function()
|
|
return IsResting() == 1
|
|
end,
|
|
|
|
noresting = function()
|
|
return IsResting() == nil
|
|
end,
|
|
|
|
stat = function(conditionals)
|
|
return And(conditionals.stat, function(args)
|
|
if type(args) ~= "table" or not args.name then
|
|
return false -- Malformed arguments from the parser.
|
|
end
|
|
|
|
local stat_key = string.lower(args.name)
|
|
local get_stat_func = stat_checks[stat_key]
|
|
|
|
if not get_stat_func then
|
|
return false -- The requested stat key is invalid.
|
|
end
|
|
|
|
local current_value = get_stat_func()
|
|
if not current_value then return false end
|
|
|
|
-- Check if this is a multi-comparison stat conditional
|
|
-- args.comparisons will be a table of {operator=, amount=} if multiple
|
|
if args.comparisons and type(args.comparisons) == "table" then
|
|
-- ALL comparisons must pass (AND logic)
|
|
for _, comp in ipairs(args.comparisons) do
|
|
if not CleveRoids.comparators[comp.operator] then
|
|
return false -- Invalid operator
|
|
end
|
|
if not CleveRoids.comparators[comp.operator](current_value, comp.amount) then
|
|
return false -- One comparison failed, so the whole conditional fails
|
|
end
|
|
end
|
|
return true -- All comparisons passed
|
|
else
|
|
-- Single comparison (backward compatibility)
|
|
if not args.operator or not args.amount then
|
|
return false
|
|
end
|
|
return CleveRoids.comparators[args.operator](current_value, args.amount)
|
|
end
|
|
end)
|
|
end,
|
|
|
|
class = function(conditionals)
|
|
-- Determine which unit to check. Defaults to 'target' if no @unitid was specified.
|
|
local unitToCheck = conditionals.target or "target"
|
|
|
|
-- The conditional must fail if the unit doesn't exist OR is not a player.
|
|
if not UnitExists(unitToCheck) or not UnitIsPlayer(unitToCheck) then
|
|
return false
|
|
end
|
|
|
|
-- Get the player's class.
|
|
local localizedClass, englishClass = UnitClass(unitToCheck)
|
|
if not localizedClass then return false end -- Failsafe for unusual cases
|
|
|
|
-- The "Or" helper handles multiple values like [class:Warrior/Druid].
|
|
return Or(conditionals.class, function(requiredClass)
|
|
return string.lower(requiredClass) == string.lower(localizedClass) or string.lower(requiredClass) == string.lower(englishClass)
|
|
end)
|
|
end,
|
|
|
|
noclass = function(conditionals)
|
|
-- Determine which unit to check. Defaults to 'target' if no @unitid was specified.
|
|
local unitToCheck = conditionals.target or "target"
|
|
|
|
-- A unit that doesn't exist cannot have a specific player class.
|
|
if not UnitExists(unitToCheck) then
|
|
return true
|
|
end
|
|
|
|
-- An NPC cannot have a specific player class.
|
|
if not UnitIsPlayer(unitToCheck) then
|
|
return true
|
|
end
|
|
|
|
-- If we get here, the unit is a player. Now check their class.
|
|
local localizedClass, englishClass = UnitClass(unitToCheck)
|
|
-- A player should always have a class, but if not, this condition is still met.
|
|
if not localizedClass then return true end
|
|
|
|
-- The "And" helper ensures the player's class is not any of the forbidden classes.
|
|
return And(conditionals.noclass, function(forbiddenClass)
|
|
return string.lower(forbiddenClass) ~= string.lower(localizedClass) and string.lower(forbiddenClass) ~= string.lower(englishClass)
|
|
end)
|
|
end,
|
|
|
|
pet = function(conditionals)
|
|
if not UnitExists("pet") then
|
|
return false
|
|
end
|
|
|
|
return Or(conditionals.pet, function(petType)
|
|
local currentPet = UnitCreatureFamily("pet")
|
|
if not currentPet then
|
|
return false
|
|
end
|
|
return string.lower(currentPet) == string.lower(petType)
|
|
end)
|
|
end,
|
|
|
|
nopet = function(conditionals)
|
|
if not UnitExists("pet") then
|
|
return true
|
|
end
|
|
|
|
return And(conditionals.nopet, function(petType)
|
|
local currentPet = UnitCreatureFamily("pet")
|
|
if not currentPet then
|
|
return true
|
|
end
|
|
return string.lower(currentPet) ~= string.lower(petType)
|
|
end)
|
|
end,
|
|
|
|
swimming = function(conditionals)
|
|
-- Check if "Aquatic Form" is in the reactive list and usable
|
|
return CleveRoids.IsReactiveUsable("Aquatic Form")
|
|
end,
|
|
|
|
noswimming = function(conditionals)
|
|
-- Check if "Aquatic Form" is NOT usable
|
|
return not CleveRoids.IsReactiveUsable("Aquatic Form")
|
|
end,
|
|
|
|
distance = function(conditionals)
|
|
if not CleveRoids.hasUnitXP then return false end
|
|
|
|
return And(conditionals.distance, function(args)
|
|
if type(args) ~= "table" or not args.operator or not args.amount then
|
|
return false
|
|
end
|
|
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
local distance = UnitXP("distanceBetween", "player", unit)
|
|
if not distance then return false end
|
|
|
|
return CleveRoids.comparators[args.operator](distance, args.amount)
|
|
end)
|
|
end,
|
|
|
|
nodistance = function(conditionals)
|
|
if not CleveRoids.hasUnitXP then return false end
|
|
|
|
return And(conditionals.nodistance, function(args)
|
|
if type(args) ~= "table" or not args.operator or not args.amount then
|
|
return false
|
|
end
|
|
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
local distance = UnitXP("distanceBetween", "player", unit)
|
|
if not distance then return false end
|
|
|
|
return not CleveRoids.comparators[args.operator](distance, args.amount)
|
|
end)
|
|
end,
|
|
|
|
behind = function(conditionals)
|
|
if not CleveRoids.hasUnitXP then return false end
|
|
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
return UnitXP("behind", "player", unit) == true
|
|
end,
|
|
|
|
nobehind = function(conditionals)
|
|
if not CleveRoids.hasUnitXP then return false end
|
|
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
return UnitXP("behind", "player", unit) ~= true
|
|
end,
|
|
|
|
insight = function(conditionals)
|
|
if not CleveRoids.hasUnitXP then return false end
|
|
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
return UnitXP("inSight", "player", unit) == true
|
|
end,
|
|
|
|
noinsight = function(conditionals)
|
|
if not CleveRoids.hasUnitXP then return false end
|
|
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
return UnitXP("inSight", "player", unit) ~= true
|
|
end,
|
|
|
|
meleerange = function(conditionals)
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return false end
|
|
|
|
if CleveRoids.hasUnitXP then
|
|
local distance = UnitXP("distanceBetween", "player", unit, "meleeAutoAttack")
|
|
return distance and distance <= 5
|
|
else
|
|
-- Fallback: use CheckInteractDistance (3 = melee range)
|
|
return CheckInteractDistance(unit, 3)
|
|
end
|
|
end,
|
|
|
|
nomeleerange = function(conditionals)
|
|
local unit = conditionals.target or "target"
|
|
if not UnitExists(unit) then return true end
|
|
|
|
if CleveRoids.hasUnitXP then
|
|
local distance = UnitXP("distanceBetween", "player", unit, "meleeAutoAttack")
|
|
return not distance or distance > 5
|
|
else
|
|
return not CheckInteractDistance(unit, 3)
|
|
end
|
|
end,
|
|
|
|
queuedspell = function(conditionals)
|
|
if not CleveRoids.hasNampower then return false end
|
|
if not CleveRoids.queuedSpell then return false end
|
|
|
|
-- If no specific spell name provided, check if ANY spell is queued
|
|
if not conditionals.queuedspell or table.getn(conditionals.queuedspell) == 0 then
|
|
return true
|
|
end
|
|
|
|
-- Check if specific spell is queued
|
|
return Or(conditionals.queuedspell, function(spellName)
|
|
if not CleveRoids.queuedSpell.spellName then return false end
|
|
local queuedName = string.gsub(CleveRoids.queuedSpell.spellName, "%s*%(.-%)%s*$", "")
|
|
local checkName = string.gsub(spellName, "%s*%(.-%)%s*$", "")
|
|
return string.lower(queuedName) == string.lower(checkName)
|
|
end)
|
|
end,
|
|
|
|
noqueuedspell = function(conditionals)
|
|
if not CleveRoids.hasNampower then return false end
|
|
|
|
-- If no specific spell name, check if NO spell is queued
|
|
if not conditionals.noqueuedspell or table.getn(conditionals.noqueuedspell) == 0 then
|
|
return CleveRoids.queuedSpell == nil
|
|
end
|
|
|
|
-- Check if specific spell is NOT queued
|
|
if not CleveRoids.queuedSpell or not CleveRoids.queuedSpell.spellName then
|
|
return true
|
|
end
|
|
|
|
return And(conditionals.noqueuedspell, function(spellName)
|
|
local queuedName = string.gsub(CleveRoids.queuedSpell.spellName, "%s*%(.-%)%s*$", "")
|
|
local checkName = string.gsub(spellName, "%s*%(.-%)%s*$", "")
|
|
return string.lower(queuedName) ~= string.lower(checkName)
|
|
end)
|
|
end,
|
|
|
|
onswingpending = function(conditionals)
|
|
if not GetCurrentCastingInfo then return false end
|
|
|
|
local _, _, _, _, _, onswing = GetCurrentCastingInfo()
|
|
return onswing == 1
|
|
end,
|
|
|
|
noonswingpending = function(conditionals)
|
|
if not GetCurrentCastingInfo then return true end
|
|
|
|
local _, _, _, _, _, onswing = GetCurrentCastingInfo()
|
|
return onswing ~= 1
|
|
end,
|
|
|
|
mybuffcount = function(conditionals)
|
|
return And(conditionals.mybuffcount,function (v) return CleveRoids.ValidatePlayerAuraCount(v.bigger, v.amount) end)
|
|
end,
|
|
|
|
mhimbue = function(conditionals)
|
|
local imbueName = nil
|
|
|
|
-- Case 1: conditionals.mhimbue is a string (e.g., [mhimbue]Frostbrand)
|
|
if type(conditionals.mhimbue) == "string" then
|
|
imbueName = conditionals.mhimbue
|
|
-- Case 2: conditionals.mhimbue is a table (e.g., [mhimbue:Frostbrand])
|
|
elseif type(conditionals.mhimbue) == "table" and table.getn(conditionals.mhimbue) > 0 then
|
|
imbueName = conditionals.mhimbue[1] -- Use first value
|
|
-- Case 3: Boolean true means check for any imbue
|
|
elseif conditionals.mhimbue == true then
|
|
imbueName = nil -- Check for existence only
|
|
end
|
|
|
|
return CleveRoids.ValidateWeaponImbue("mh", imbueName)
|
|
end,
|
|
|
|
nomhimbue = function(conditionals)
|
|
local imbueName = nil
|
|
|
|
-- Case 1: conditionals.nomhimbue is a string
|
|
if type(conditionals.nomhimbue) == "string" then
|
|
imbueName = conditionals.nomhimbue
|
|
-- Case 2: conditionals.nomhimbue is a table
|
|
elseif type(conditionals.nomhimbue) == "table" and table.getn(conditionals.nomhimbue) > 0 then
|
|
imbueName = conditionals.nomhimbue[1]
|
|
-- Case 3: Boolean true
|
|
elseif conditionals.nomhimbue == true then
|
|
imbueName = nil
|
|
end
|
|
|
|
return not CleveRoids.ValidateWeaponImbue("mh", imbueName)
|
|
end,
|
|
|
|
ohimbue = function(conditionals)
|
|
local imbueName = nil
|
|
|
|
-- Case 1: conditionals.ohimbue is a string
|
|
if type(conditionals.ohimbue) == "string" then
|
|
imbueName = conditionals.ohimbue
|
|
-- Case 2: conditionals.ohimbue is a table
|
|
elseif type(conditionals.ohimbue) == "table" and table.getn(conditionals.ohimbue) > 0 then
|
|
imbueName = conditionals.ohimbue[1]
|
|
-- Case 3: Boolean true
|
|
elseif conditionals.ohimbue == true then
|
|
imbueName = nil
|
|
end
|
|
|
|
return CleveRoids.ValidateWeaponImbue("oh", imbueName)
|
|
end,
|
|
|
|
noohimbue = function(conditionals)
|
|
local imbueName = nil
|
|
|
|
-- Case 1: conditionals.noohimbue is a string
|
|
if type(conditionals.noohimbue) == "string" then
|
|
imbueName = conditionals.noohimbue
|
|
-- Case 2: conditionals.noohimbue is a table
|
|
elseif type(conditionals.noohimbue) == "table" and table.getn(conditionals.noohimbue) > 0 then
|
|
imbueName = conditionals.noohimbue[1]
|
|
-- Case 3: Boolean true
|
|
elseif conditionals.noohimbue == true then
|
|
imbueName = nil
|
|
end
|
|
|
|
return not CleveRoids.ValidateWeaponImbue("oh", imbueName)
|
|
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]
|
|
local checkValue = nil
|
|
|
|
-- Case 1: [immune:SpellName] or [immune:fire]
|
|
if type(conditionals.immune) == "table" and table.getn(conditionals.immune) > 0 then
|
|
checkValue = conditionals.immune[1]
|
|
elseif type(conditionals.immune) == "string" then
|
|
checkValue = conditionals.immune
|
|
-- Case 2: [immune] SpellName (check the action being cast)
|
|
elseif conditionals.action then
|
|
checkValue = conditionals.action
|
|
end
|
|
|
|
if not checkValue then
|
|
return false
|
|
end
|
|
|
|
return CleveRoids.CheckImmunity(conditionals.target or "target", checkValue)
|
|
end,
|
|
|
|
noimmune = function(conditionals)
|
|
-- Check if target is NOT immune to the spell being cast or damage school
|
|
-- Usage: [noimmune] SpellName OR [noimmune:SpellName] OR [noimmune:fire]
|
|
local checkValue = nil
|
|
|
|
-- Case 1: [noimmune:SpellName] or [noimmune:fire]
|
|
if type(conditionals.noimmune) == "table" and table.getn(conditionals.noimmune) > 0 then
|
|
checkValue = conditionals.noimmune[1]
|
|
elseif type(conditionals.noimmune) == "string" then
|
|
checkValue = conditionals.noimmune
|
|
-- Case 2: [noimmune] SpellName (check the action being cast)
|
|
elseif conditionals.action then
|
|
checkValue = conditionals.action
|
|
end
|
|
|
|
if not checkValue then
|
|
return true -- If we can't determine spell/school, assume not immune
|
|
end
|
|
|
|
return not CleveRoids.CheckImmunity(conditionals.target or "target", checkValue)
|
|
end
|
|
}
|