From 9e562d1545fae02a0b8bd0a1ad99ad140001fef5 Mon Sep 17 00:00:00 2001 From: pepopo978 Date: Fri, 13 Dec 2024 17:58:09 -0800 Subject: [PATCH] add more options --- Localization.lua | 4 ++ README.md | 4 +- commands.lua | 110 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/Localization.lua b/Localization.lua index 2b51859..114534e 100644 --- a/Localization.lua +++ b/Localization.lua @@ -11,6 +11,8 @@ L:RegisterTranslations("enUS", function() ["Allow out of combat targets to be multicursed. Would only consider using this solo to avoid potentially griefing raids/dungeons by pulling unintended mobs."] = true, ["Minimum HP for a target to be considered. Example usage minhp=10000. "] = true, ["Filter targets by name. Can be a partial match. If no match is found, the command will do nothing."] = true, + ["Ignore targets with the specified spell id already on them. Useful for ignoring targets that already have a shared debuff."] = true, + ["Ignore targets with the specified spell texture already on them. Useful for ignoring targets that already have a shared debuff."] = true, ["Time threshold at which to allow refreshing a curse. Default is 0 seconds."] = true, ["Ignore the current target when choosing target for multicurse. Does not affect 'curse' command."] = true, ["/cursive curse ||>: Casts spell if not already on target/guid"] = true, @@ -160,6 +162,8 @@ L:RegisterTranslations("zhCN", function() ["Allow out of combat targets to be multicursed. Would only consider using this solo to avoid potentially griefing raids/dungeons by pulling unintended mobs."] = "允许战斗外目标被多重诅咒。只有在单人游戏中才会考虑使用这个选项,以避免在副本中意外拉到怪物而造成困扰.", ["Minimum HP for a target to be considered. Example usage minhp=10000. "] = "目标考虑的最小生命值。例如:minhp=10000.", ["Filter targets by name. Can be a partial match. If no match is found, the command will do nothing."] = "通过名称过滤目标。可以是部分匹配。如果没有找到匹配项,命令将不执行.", + ["Ignore targets with the specified spell id already on them. Useful for ignoring targets that already have a shared debuff."] = "忽略已经在目标身上的指定法术ID。对于忽略已经有共享debuff的目标很有用.", + ["Ignore targets with the specified spell texture already on them. Useful for ignoring targets that already have a shared debuff."] = "忽略已经在目标身上的指定法术纹理。对于忽略已经有共享debuff的目标很有用.", ["Time threshold at which to allow refreshing a curse. Default is 0 seconds."] = "允许刷新诅咒的时间阈值。默认是0秒.例如:refreshtime=2", ["Ignore the current target when choosing target for multicurse. Does not affect 'curse' command."] = "选择多重诅咒目标时忽略当前目标。不影响‘诅咒’命令。", ["/cursive curse ||>: Casts spell if not already on target/guid"] = "/cursive curse <法术名称>|<目标ID>|<选项列表,用半角逗号分隔>: 如果目标/ID身上没有此法术,目标ID可以是以下内容(mouseover), (player), (pet), (party)(%d), (partypet)(%d), (raid)(%d+), (raidpet)(%d+), (target), (targettarget)", diff --git a/README.md b/README.md index 2c0b7ca..f6230a5 100644 --- a/README.md +++ b/README.md @@ -79,9 +79,11 @@ All commands can take the following options separated by commas: - `minhp=` : "Minimum HP for a target to be considered.", - `refreshtime=` : "Time threshold at which to allow refreshing a curse. Default is 0 seconds.", - `name=` : "Filter targets by name. Can be a partial match. If no match is found, the command will do nothing.", +- `ignorespellid=` : "Ignore targets with the specified spell id already on them. Useful for ignoring targets that already have a shared debuff.", +- `ignoretexture=` : "Ignore targets with the specified spell texture already on them. Useful for ignoring targets that already have a shared debuff.", EXAMPLE: `/cursive multicurse Corruption|HIGHEST_HP|warnings,resistsound,expiringsound,minhp=10000,refreshtime=2` -EXAMPLE: `/cursive multicurse Curse of Recklessness|RAID_MARK|name=Touched Warrior,resistsound,expiringsound` +EXAMPLE: `/cursive multicurse Curse of Recklessness|RAID_MARK|name=Touched Warrior,ignoretexture=Spell_Shadow_UnholyStrength,resistsound,expiringsound` ## Important info diff --git a/commands.lua b/commands.lua index fe1d473..75d687d 100644 --- a/commands.lua +++ b/commands.lua @@ -12,6 +12,8 @@ local commandOptions = { refreshtime = L["Time threshold at which to allow refreshing a curse. Default is 0 seconds."], ignoretarget = L["Ignore the current target when choosing target for multicurse. Does not affect 'curse' command."], name = L["Filter targets by name. Can be a partial match. If no match is found, the command will do nothing."], + ignorespellid = L["Ignore targets with the specified spell id already on them. Useful for ignoring targets that already have a shared debuff."], + ignorespelltexture = L["Ignore targets with the specified spell texture already on them. Useful for ignoring targets that already have a shared debuff."], } local commands = { @@ -60,6 +62,16 @@ local function parseOptions(optionsStr) if name then options["name"] = name end + elseif option == "ignorespellid" then + local _, _, spellId = string.find(optionsStr, "ignorespellid=(%d+)") + if spellId then + options["ignorespellid"] = tonumber(spellId) + end + elseif option == "ignorespelltexture" then + local _, _, texture = string.find(optionsStr, "ignorespelltexture=([%w_]+)") + if texture then + options["ignorespelltexture"] = texture + end elseif string.find(optionsStr, option) then options[option] = true end @@ -231,6 +243,87 @@ local function GetSquarePrioRaidTargetIndex(guid) return index or -2 end + +local function hasSpellId(guid, ignoreSpellId) + for i = 1, 16 do + local texture, stacks, spellSchool, spellId = UnitDebuff(guid, i); + if not spellId then + break + end + if spellId == ignoreSpellId then + if options["warnings"] then + DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget) + end + return true + end + end + + for i = 1, 32 do + local texture, stacks, spellId = UnitBuff(guid, i); + if not spellId then + break + end + if spellId == ignoreSpellId then + if options["warnings"] then + DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget) + end + return true + end + end + + return false +end + +local function hasSpellTexture(guid, ignoreTexture) + for i = 1, 16 do + local texture = UnitDebuff(guid, i); + if not texture then + break + end + if texture == ignoreTexture then + if options["warnings"] then + DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget) + end + return true + end + end + + for i = 1, 32 do + local texture = UnitBuff(guid, i); + if not texture then + break + end + if texture == ignoreTexture then + if options["warnings"] then + DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget) + end + return true + end + end + + return false +end + +local function passedOptionFilters(guid, options) + if options["name"] then + local name = UnitName(guid) + if not string.find(name, options["name"]) then + return false + end + end + if options["ignorespellid"] then + if hasSpellId(guid, options["ignorespellid"]) then + return false + end + end + if options["ignorespelltexture"] then + if hasSpellTexture(guid, options["ignorespelltexture"]) then + return false + end + end + return true +end + local function pickTarget(selectedPriority, spellNameNoRank, checkRange, options) -- Curse the target that best matches the selected priority local highestPrimaryValue = -10 @@ -253,20 +346,15 @@ local function pickTarget(selectedPriority, spellNameNoRank, checkRange, options if not options["ignoretarget"] or guid ~= currentTargetGuid then -- check if in combat already or player is actively targeting the mob if ignoreInFight or Cursive.filter.infight(guid) or guid == currentTargetGuid then - local passedNameCheck = true - if options["name"] then - local name = UnitName(guid) - passedNameCheck = string.find(name, options["name"]) - end - - if passedNameCheck then + if passedOptionFilters(targetedGuid, options) then local passedRangeCheck = false if IsSpellInRange then -- use IsSpellInRange from nampower if available local result = IsSpellInRange(spellNameNoRank, guid) if result == -1 then passedRangeCheck = checkRange == false or CheckInteractDistance(guid, 4) -- fallback to old range check - else -- 0 or 1 + else + -- 0 or 1 passedRangeCheck = result == 1 end else @@ -363,9 +451,9 @@ function Cursive:Curse(spellName, targetedGuid, options) end end - if targetedGuid and options["name"] then - local name = UnitName(targetedGuid) - if not string.find(name, options["name"]) then + if targetedGuid then + -- check for options + if not passedOptionFilters(targetedGuid, options) then if options["warnings"] then DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget) end