add optional resist/expiry sounds
This commit is contained in:
@@ -11,13 +11,39 @@
|
||||
|
||||
Cursive combines ShaguScan unit scanning with curse tracking similar to DotTimer and the ability to automatically curse targets similar to Decursive.
|
||||
|
||||
## Commands
|
||||
`/cursive` for commands, minimap icon to edit options.
|
||||
|
||||
### Curse
|
||||
`/cursive curse <spellName:str>|<guid?:str>|<noFailureWarning?:int[0,1]`: Casts spell if not already on target/guid
|
||||
|
||||
EXAMPLE: `/cursive curse Corruption|target|1` will attempt to cast Corruption on your target if it's not already on them and won't warn you if it does nothing.
|
||||
|
||||
### Multicurse
|
||||
`/cursive multicurse <spellName:str>|<priority?:str>|<noFailureWarning?:int[0,1]`: Picks target based on priority and casts spell if not already on target.
|
||||
Priority options: HIGHEST_HP, RAID_MARK
|
||||
|
||||
EXAMPLE: `/cursive multicurse Corruption|HIGHEST_HP` will attempt to cast Corruption picking the target with the highest HP that doesn't already have it and will warn you if it does nothing.
|
||||
|
||||
## Command Options
|
||||
All commands can take the following options separated by commas:
|
||||
- ["warnings"] : "Display text warnings when a curse fails to cast.",
|
||||
- ["resistsound"] : "Play a sound when a curse is resisted.",
|
||||
- ["expiringsound"] : "Play a sound when a curse is about to expire.",
|
||||
|
||||
EXAMPLE: `/cursive multicurse Corruption|HIGHEST_HP|warnings,resistsound,expiringsound`
|
||||
|
||||
## Important info
|
||||
|
||||
All commands will prioritize targets within 28 yards of you first to have a better chance of being in range.
|
||||
|
||||
All commands will ignore targets with the following CCs on them:
|
||||
- Sleep
|
||||
- Entangling Roots
|
||||
- Shackle Undead
|
||||
- Polymorph
|
||||
- Turn Undead
|
||||
- Blind
|
||||
- Sap
|
||||
- Gouge
|
||||
- Freezing Trap
|
||||
|
||||
+52
-13
@@ -1,28 +1,53 @@
|
||||
local curseCommands = "|cffffcc00Cursive:|cffffaaaa Commands:"
|
||||
local curseOptions = "Options (separate with ,):"
|
||||
|
||||
local commandOptions = {
|
||||
["warnings"] = "Display text warnings when a curse fails to cast.",
|
||||
["resistsound"] = "Play a sound when a curse is resisted.",
|
||||
["expiringsound"] = "Play a sound when a curse is about to expire.",
|
||||
}
|
||||
|
||||
local commands = {
|
||||
["curse"] = "/cursive curse <spellName:str>|<guid?:str>|<noFailureWarning:int[0,1]: Casts spell if not already on target/guid",
|
||||
["multicurse"] = "/cursive multicurse <spellName:str>|<priority?:str>|<noFailureWarning:int[0,1]: Picks target based on priority and casts spell if not already on target. Priority options: HIGHEST_HP, RAID_MARK.",
|
||||
["curse"] = "/cursive curse <spellName:str>|<guid?:str>|<options?:List<str>>: Casts spell if not already on target/guid",
|
||||
["multicurse"] = "/cursive multicurse <spellName:str>|<priority?:str>|<options?:List<str>>: Picks target based on priority and casts spell if not already on target. Priority options: HIGHEST_HP, RAID_MARK.",
|
||||
}
|
||||
|
||||
local curseNoTarget = "|cffffcc00Cursive:|cffffaaaa Couldn't find a target to curse."
|
||||
|
||||
local function parseOptions(optionsStr)
|
||||
local options = { }
|
||||
|
||||
for option, _ in pairs(commandOptions) do
|
||||
if string.find(optionsStr, option) then
|
||||
options[option] = true
|
||||
end
|
||||
end
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
local function handleSlashCommands(msg, editbox)
|
||||
if not msg or msg == "" then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(curseCommands)
|
||||
for command, description in pairs(commands) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(description)
|
||||
end
|
||||
DEFAULT_CHAT_FRAME:AddMessage(curseOptions)
|
||||
for option, description in pairs(commandOptions) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(option .. ": " .. description)
|
||||
end
|
||||
return
|
||||
end
|
||||
-- get first word in string
|
||||
local _, _, command, args = string.find(msg, "(%w+) (.*)")
|
||||
if command == "curse" then
|
||||
local spellName, targetedGuid, noFailureWarning = Cursive.utils.strsplit("|", args)
|
||||
Cursive:Curse(spellName, targetedGuid, noFailureWarning)
|
||||
local spellName, targetedGuid, optionsStr = Cursive.utils.strsplit("|", args)
|
||||
local options = parseOptions(optionsStr)
|
||||
Cursive:Curse(spellName, targetedGuid, options)
|
||||
elseif command == "multicurse" then
|
||||
local spellName, priority, noFailureWarning = Cursive.utils.strsplit("|", args)
|
||||
Cursive:Multicurse(spellName, priority, noFailureWarning)
|
||||
local spellName, priority, optionsStr = Cursive.utils.strsplit("|", args)
|
||||
local options = parseOptions(optionsStr)
|
||||
Cursive:Multicurse(spellName, priority, options)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,6 +93,10 @@ local crowdControlledSpellIds = {
|
||||
[8629] = { name = "Gouge", rank = 3, duration = 4 },
|
||||
[11285] = { name = "Gouge", rank = 4, duration = 4 },
|
||||
[11286] = { name = "Gouge", rank = 5, duration = 4 },
|
||||
|
||||
[3355] = { name = "Freezing Trap", rank = 1, duration = 10 },
|
||||
[14308] = { name = "Freezing Trap", rank = 2, duration = 15 },
|
||||
[14309] = { name = "Freezing Trap", rank = 3, duration = 20 },
|
||||
}
|
||||
|
||||
local function isMobCrowdControlled(guid)
|
||||
@@ -168,7 +197,17 @@ local function pickRaidMarkTarget(selectedPriority, spellNameNoRank, skipCheckRa
|
||||
return targetedGuid
|
||||
end
|
||||
|
||||
function Cursive:Curse(spellName, targetedGuid, noFailureWarning)
|
||||
local function castSpellWithOptions(spellName, targetedGuid, options)
|
||||
if options["resistsound"] then
|
||||
Cursive.curses:EnableResistSound(targetedGuid)
|
||||
end
|
||||
if options["expiringsound"] then
|
||||
Cursive.curses:RequestExpiringSound(spellName, targetedGuid)
|
||||
end
|
||||
CastSpellByName(spellName, targetedGuid)
|
||||
end
|
||||
|
||||
function Cursive:Curse(spellName, targetedGuid, options)
|
||||
if not spellName or not targetedGuid then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(commands["curse"])
|
||||
return
|
||||
@@ -178,7 +217,7 @@ function Cursive:Curse(spellName, targetedGuid, noFailureWarning)
|
||||
_, targetedGuid = UnitExists(targetedGuid)
|
||||
|
||||
if not targetedGuid then
|
||||
if not (noFailureWarning and noFailureWarning == "1") then
|
||||
if options["warnings"] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget)
|
||||
end
|
||||
return
|
||||
@@ -189,13 +228,13 @@ function Cursive:Curse(spellName, targetedGuid, noFailureWarning)
|
||||
local spellNameNoRank = string.gsub(spellName, "%(.+%)", "")
|
||||
|
||||
if targetedGuid and not Cursive.curses:HasCurse(spellNameNoRank, targetedGuid) and not isMobCrowdControlled(targetedGuid) then
|
||||
CastSpellByName(spellName, targetedGuid)
|
||||
elseif not (noFailureWarning and noFailureWarning == "1") then
|
||||
castSpellWithOptions(spellName, targetedGuid, options)
|
||||
elseif options["warnings"] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursive:Multicurse(spellName, priority, noFailureWarning)
|
||||
function Cursive:Multicurse(spellName, priority, options)
|
||||
if not spellName then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(commands["multicurse"])
|
||||
return
|
||||
@@ -219,8 +258,8 @@ function Cursive:Multicurse(spellName, priority, noFailureWarning)
|
||||
end
|
||||
|
||||
if targetedGuid then
|
||||
CastSpellByName(spellName, targetedGuid)
|
||||
elseif not (noFailureWarning and noFailureWarning == "1") then
|
||||
castSpellWithOptions(spellName, targetedGuid, options)
|
||||
elseif options["warnings"] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(curseNoTarget)
|
||||
end
|
||||
end
|
||||
|
||||
+54
@@ -6,6 +6,9 @@ local curses = {
|
||||
trackedCurseIds = {},
|
||||
trackedCurseNamesToTextures = {},
|
||||
guids = {},
|
||||
resistSoundGuids = {},
|
||||
expiringSoundGuids = {},
|
||||
requestedExpiringSoundGuids = {} -- guid added on spellcast, moved to expiringSoundGuids once rendered by ui
|
||||
}
|
||||
|
||||
-- combat events for curses
|
||||
@@ -65,6 +68,10 @@ Cursive:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE",
|
||||
if spell and target then
|
||||
if curses.trackedCurseNamesToTextures[spell] and lastGuid then
|
||||
Cursive:CancelScheduledEvent("addCurse" .. lastGuid .. spell)
|
||||
-- check if sound should be played
|
||||
if curses:ShouldPlayResistSound(lastGuid) then
|
||||
PlaySoundFile("Interface\\AddOns\\Cursive\\Sounds\\resist.mp3")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -74,6 +81,50 @@ function curses:TimeRemaining(curseData)
|
||||
return math.ceil(curseData.duration - (GetTime() - curseData.start))
|
||||
end
|
||||
|
||||
function curses:EnableResistSound(guid)
|
||||
curses.resistSoundGuids[guid] = true
|
||||
end
|
||||
|
||||
function curses:EnableExpiringSound(spellName, guid)
|
||||
if curses.requestedExpiringSoundGuids[guid] and curses.requestedExpiringSoundGuids[guid][spellName] then
|
||||
curses.requestedExpiringSoundGuids[guid][spellName] = nil
|
||||
end
|
||||
|
||||
if not curses.expiringSoundGuids[guid] then
|
||||
curses.expiringSoundGuids[guid] = {}
|
||||
end
|
||||
curses.expiringSoundGuids[guid][spellName] = true
|
||||
end
|
||||
|
||||
function curses:RequestExpiringSound(spellName, guid)
|
||||
if not curses.requestedExpiringSoundGuids[guid] then
|
||||
curses.requestedExpiringSoundGuids[guid] = {}
|
||||
end
|
||||
curses.requestedExpiringSoundGuids[guid][spellName] = true
|
||||
end
|
||||
|
||||
function curses:HasRequestedExpiringSound(spellName, guid)
|
||||
return curses.requestedExpiringSoundGuids[guid] and curses.requestedExpiringSoundGuids[guid][spellName]
|
||||
end
|
||||
|
||||
function curses:ShouldPlayExpiringSound(spellName, guid)
|
||||
if curses.expiringSoundGuids[guid] and curses.expiringSoundGuids[guid][spellName] then
|
||||
curses.expiringSoundGuids[guid][spellName] = nil -- remove entry to avoid playing sound multiple times
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function curses:ShouldPlayResistSound(guid)
|
||||
if curses.resistSoundGuids[guid] then
|
||||
curses.resistSoundGuids[guid] = nil -- remove entry to avoid playing sound multiple times
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function curses:HasCurse(spellName, targetGuid)
|
||||
if curses.guids[targetGuid] and curses.guids[targetGuid][spellName] then
|
||||
local remaining = Cursive.curses:TimeRemaining(curses.guids[targetGuid][spellName])
|
||||
@@ -101,6 +152,9 @@ end
|
||||
|
||||
function curses:RemoveGuid(guid)
|
||||
curses.guids[guid] = nil
|
||||
curses.resistSoundGuids[guid] = nil
|
||||
curses.expiringSoundGuids[guid] = nil
|
||||
curses.requestedExpiringSoundGuids[guid] = nil
|
||||
end
|
||||
|
||||
Cursive.curses = curses
|
||||
|
||||
@@ -321,6 +321,14 @@ local function DisplayGuid(guid, row, col)
|
||||
curse.timer:SetText(remaining)
|
||||
curse.timer:Show()
|
||||
curse:Show()
|
||||
|
||||
if remaining < 1 then
|
||||
if Cursive.curses:ShouldPlayExpiringSound(curseName, guid) then
|
||||
PlaySoundFile("Interface\\AddOns\\Cursive\\sounds\\expiring.mp3")
|
||||
end
|
||||
elseif Cursive.curses:HasRequestedExpiringSound(curseName, guid) then
|
||||
Cursive.curses:EnableExpiringSound(curseName, guid)
|
||||
end
|
||||
else
|
||||
curse.timer:Hide()
|
||||
curse:Hide()
|
||||
@@ -343,7 +351,7 @@ ui:SetScript("OnUpdate", function()
|
||||
if (this.tick or 1) > GetTime() then
|
||||
return
|
||||
else
|
||||
this.tick = GetTime() + .8
|
||||
this.tick = GetTime() + .2
|
||||
end
|
||||
|
||||
if not ui.rootBarFrame then
|
||||
@@ -386,6 +394,13 @@ ui:SetScript("OnUpdate", function()
|
||||
end
|
||||
|
||||
local active = UnitExists(guid) and Cursive.filter.alive(guid)
|
||||
if active then
|
||||
local old = GetTime() - time >= 900 -- >= 15 minutes old
|
||||
if old and not UnitIsVisible(guid) then
|
||||
active = false
|
||||
end
|
||||
end
|
||||
|
||||
if not active then
|
||||
-- remove from core
|
||||
Cursive.core.remove(guid)
|
||||
|
||||
Reference in New Issue
Block a user