diff --git a/Localization.lua b/Localization.lua index 8d152ec..8dab68b 100644 --- a/Localization.lua +++ b/Localization.lua @@ -130,7 +130,7 @@ L:RegisterTranslations("enUS", function() ["holy fire"] = true, -- Paladin - ["Turn Undead"] = true, + ["turn undead"] = true, -- rogue ["blind"] = true, @@ -158,6 +158,7 @@ L:RegisterTranslations("enUS", function() ["curse of tongues"] = true, ["curse of weakness"] = true, ["curse of exhaustion"] = true, + ["dark harvest"] = true, ["immolate"] = true, ["death coil"] = true, ["banish"] = true, @@ -294,7 +295,7 @@ L:RegisterTranslations("zhCN", function() ["holy fire"] = "神圣之火", -- Paladin - ["Turn Undead"] = "超度亡灵", + ["turn undead"] = "超度亡灵", -- rogue ["blind"] = "致盲", @@ -322,6 +323,7 @@ L:RegisterTranslations("zhCN", function() ["curse of tongues"] = "语言诅咒", ["curse of weakness"] = "虚弱诅咒", ["curse of exhaustion"] = "疲劳诅咒", + ["dark harvest"] = "黑暗收割", ["immolate"] = "献祭", ["death coil"] = "死亡缠绕", ["banish"] = "放逐", diff --git a/README.md b/README.md index 96f0d26..da8a23b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ so that it doesn't block your clicks when it's not displaying mobs. `/cursive curse ||`: Casts spell if not already on target/guid -EXAMPLE: `/cursive curse corruption|target` will attempt to cast corruption on your target if it's not already on them +EXAMPLE: `/cursive curse Corruption|target` will attempt to cast Corruption on your target if it's not already on them and they aren't cc'ed. ### Multicurse @@ -33,7 +33,7 @@ and they aren't cc'ed. `/cursive multicurse ||`: Picks target based on priority and casts spell if not already on target and they aren't cc'ed. -EXAMPLE: `/cursive multicurse corruption|HIGHEST_HP` will attempt to cast corruption picking the target with the highest +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. ## Priority Options @@ -82,8 +82,8 @@ All commands can take the following options separated by commas: - `ignorespellid=` : "Ignore targets with the specified spell id already on them. Useful for ignoring targets that already have a shared debuff.", - `ignorespelltexture=` : "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,ignorespelltexture=Spell_Shadow_UnholyStrength,resistsound,expiringsound` +EXAMPLE: `/cursive multicurse Corruption|HIGHEST_HP|warnings,resistsound,expiringsound,minhp=10000,refreshtime=2` +EXAMPLE: `/cursive multicurse Curse of Recklessness|RAID_MARK|name=Touched Warrior,ignorespelltexture=Spell_Shadow_UnholyStrength,resistsound,expiringsound` ## Important info @@ -93,16 +93,16 @@ Otherwise, all commands will prioritize targets within 28 yards of you first to All commands will ignore targets with the following CCs on them: -- sleep -- entangling roots -- shackle undead -- polymorph +- Sleep +- Entangling Roots +- Shackle Undead +- Polymorph - Turn Undead -- blind -- sap -- gouge -- freezing trap -- banish +- Blind +- Sap +- Gouge +- Freezing Trap +- Banish Multicurse will only ever target enemies that are already in combat (except if you target a mob directly first) to prevent pulling things you didn't intend like marked patrols. diff --git a/commands.lua b/commands.lua index adf5395..0274ca7 100644 --- a/commands.lua +++ b/commands.lua @@ -141,9 +141,9 @@ local crowdControlledSpellIds = { [28271] = { name = L["polymorph: turtle"], rank = L["Rank 1"], duration = 50 }, [28272] = { name = L["polymorph: pig"], rank = L["Rank 1"], duration = 50 }, - [2878] = { name = L["Turn Undead"], rank = 1, duration = 10 }, - [5627] = { name = L["Turn Undead"], rank = 2, duration = 15 }, - [10326] = { name = L["Turn Undead"], rank = 3, duration = 20 }, + [2878] = { name = L["turn undead"], rank = 1, duration = 10 }, + [5627] = { name = L["turn undead"], rank = 2, duration = 15 }, + [10326] = { name = L["turn undead"], rank = 3, duration = 20 }, [2094] = { name = L["blind"], rank = 1, duration = 10 }, [21060] = { name = L["blind"], rank = 1, duration = 10 }, @@ -183,15 +183,15 @@ local crowdControlledSpellIds = { [20740] = { name = "Dominate Mind" }, [17405] = { name = "Domination" }, [3442] = { name = "Enslave" }, - [13181] = { name = "Gnomish mind control Cap" }, - [26740] = { name = "Gnomish mind control Cap" }, + [13181] = { name = "Gnomish Mind Control Cap" }, + [26740] = { name = "Gnomish Mind Control Cap" }, [12483] = { name = "Hex of Jammal'an" }, [25772] = { name = "Mental Domination" }, [7967] = { name = "Naralex's Nightmare" }, [19469] = { name = "Poison Mind" }, [17244] = { name = "Possess" }, [22667] = { name = "Shadow Command" }, - [20668] = { name = "sleepwalk" }, + [20668] = { name = "Sleepwalk" }, [785] = { name = "True Fulfillment" }, [26195] = { name = "Whisperings of C'Thun" }, [26197] = { name = "Whisperings of C'Thun" }, diff --git a/curses.lua b/curses.lua index fe1d4e6..2e8318a 100644 --- a/curses.lua +++ b/curses.lua @@ -15,7 +15,14 @@ local curses = { [18931] = true, [18932] = true, }, + darkHarvestSpellIds = { + [52550] = true, + [52551] = true, + [52552] = true, + }, + darkHarvestData = {}, guids = {}, + isChanneling = false, pendingCast = {}, resistSoundGuids = {}, expiringSoundGuids = {}, @@ -103,7 +110,7 @@ function curses:ScanTooltipForDuration(curseSpellID) end function curses:GetCurseDuration(curseSpellID) - if curses.trackedCurseIds[curseSpellID].variable_duration then + if curses.trackedCurseIds[curseSpellID].variableDuration then return curses:ScanTooltipForDuration(curseSpellID) end @@ -140,6 +147,17 @@ Cursive:RegisterEvent("LEARNED_SPELL_IN_TAB", function() curses:LoadCurses() end) +local function StopChanneling() + curses.isChanneling = false +end + +Cursive:RegisterEvent("SPELLCAST_CHANNEL_START", function() + curses.isChanneling = true +end); +Cursive:RegisterEvent("SPELLCAST_CHANNEL_STOP", StopChanneling); +Cursive:RegisterEvent("SPELLCAST_INTERRUPTED", StopChanneling); +Cursive:RegisterEvent("SPELLCAST_FAILED", StopChanneling); + Cursive:RegisterEvent("UNIT_CASTEVENT", function(casterGuid, targetGuid, event, spellID, castDuration) -- immolate will fire both start and cast if event == "CAST" then @@ -184,6 +202,21 @@ Cursive:RegisterEvent("UNIT_CASTEVENT", function(casterGuid, targetGuid, event, -- clear pending cast curses.pendingCast = {} end + elseif event == "CHANNEL" then + -- dark harvest + if curses.darkHarvestSpellIds[spellID] then + local _, guid = UnitExists("player") + if casterGuid ~= guid then + return + end + + curses.darkHarvestData = { + spellID = spellID, + targetGuid = targetGuid, + castDuration = curses:ScanTooltipForDuration(spellID), + start = GetTime() + } + end end end) @@ -233,8 +266,61 @@ Cursive:RegisterEvent("CHAT_MSG_SPELL_AURA_GONE_OTHER", function(message) end ) +function curses:GetLastTickTime(curseData) + local ticks = curses.trackedCurseIds[curseData.spellID].numTicks + if not ticks then + return GetTime() + end + + local tickTime = curseData.duration / ticks + local currentTime = GetTime() + tickTime * .2 -- dh won't apply to previous tick if within 20% of tick time + + return math.floor((currentTime - curseData.start) / tickTime) * tickTime + curseData.start +end + +function curses:TrackDarkHarvest(curseData) + if curses.darkHarvestData["targetGuid"] and curses.darkHarvestData["targetGuid"] == curseData["targetGuid"] then + local dhActive = false + -- check if still channeling + if curses.isChanneling then + local dhTimeRemaining = curses.darkHarvestData.castDuration - (GetTime() - curses.darkHarvestData.start) + -- check if dh still active based on cast duration + if dhTimeRemaining > 0 then + dhActive = true + -- dh is active + if not curseData["dhStartTime"] then + curseData["dhStartTime"] = curses:GetLastTickTime(curseData) -- dh will reduce full tick duration + end + end + end + if curseData["dhStartTime"] and dhActive == false and not curseData["dhEndTime"] then + -- if dh no longer active, store end time if not already stored + curseData["dhEndTime"] = GetTime() + end + end +end + +function curses:GetDarkHarvestReduction(curseData) + if curseData["dhStartTime"] then + local endTime = curseData["dhEndTime"] or GetTime() + local dhActiveTime = endTime - curseData["dhStartTime"] + if dhActiveTime > 0 then + return dhActiveTime * .2 -- 20% reduction + end + end + return 0 +end + function curses:TimeRemaining(curseData) - return math.ceil(curseData.duration - (GetTime() - curseData.start)) + local dhReduction = 0 + + if curses.trackedCurseIds[curseData.spellID].darkHarvest then + curses:TrackDarkHarvest(curseData) + + dhReduction = curses:GetDarkHarvestReduction(curseData) + end + + return math.ceil(curseData.duration - (GetTime() - curseData.start) - dhReduction) end function curses:EnableResistSound(guid) @@ -329,6 +415,7 @@ function curses:ApplyCurse(spellID, targetGuid, startTime) duration = duration, start = startTime, spellID = spellID, + targetGuid = targetGuid } end diff --git a/spells/druid.lua b/spells/druid.lua index 72f4608..bc51118 100644 --- a/spells/druid.lua +++ b/spells/druid.lua @@ -31,34 +31,34 @@ function getDruidSpells() [18657] = { name = L["hibernate"], rank = 2, duration = 30 }, [18658] = { name = L["hibernate"], rank = 3, duration = 40 }, - [5570] = { name = L["insect swarm"], rank = 1, duration = 18, variable_duration = true }, - [24974] = { name = L["insect swarm"], rank = 2, duration = 18, variable_duration = true }, - [24975] = { name = L["insect swarm"], rank = 3, duration = 18, variable_duration = true }, - [24976] = { name = L["insect swarm"], rank = 4, duration = 18, variable_duration = true }, - [24977] = { name = L["insect swarm"], rank = 5, duration = 18, variable_duration = true }, + [5570] = { name = L["insect swarm"], rank = 1, duration = 18, variableDuration = true }, + [24974] = { name = L["insect swarm"], rank = 2, duration = 18, variableDuration = true }, + [24975] = { name = L["insect swarm"], rank = 3, duration = 18, variableDuration = true }, + [24976] = { name = L["insect swarm"], rank = 4, duration = 18, variableDuration = true }, + [24977] = { name = L["insect swarm"], rank = 5, duration = 18, variableDuration = true }, - [8921] = { name = L["moonfire"], rank = 1, duration = 9, variable_duration = true }, - [8924] = { name = L["moonfire"], rank = 2, duration = 18, variable_duration = true }, - [8925] = { name = L["moonfire"], rank = 3, duration = 18, variable_duration = true }, - [8926] = { name = L["moonfire"], rank = 4, duration = 18, variable_duration = true }, - [8927] = { name = L["moonfire"], rank = 5, duration = 18, variable_duration = true }, - [8928] = { name = L["moonfire"], rank = 6, duration = 18, variable_duration = true }, - [8929] = { name = L["moonfire"], rank = 7, duration = 18, variable_duration = true }, - [9833] = { name = L["moonfire"], rank = 8, duration = 18, variable_duration = true }, - [9834] = { name = L["moonfire"], rank = 9, duration = 18, variable_duration = true }, - [9835] = { name = L["moonfire"], rank = 10, duration = 18, variable_duration = true }, + [8921] = { name = L["moonfire"], rank = 1, duration = 9, variableDuration = true }, + [8924] = { name = L["moonfire"], rank = 2, duration = 18, variableDuration = true }, + [8925] = { name = L["moonfire"], rank = 3, duration = 18, variableDuration = true }, + [8926] = { name = L["moonfire"], rank = 4, duration = 18, variableDuration = true }, + [8927] = { name = L["moonfire"], rank = 5, duration = 18, variableDuration = true }, + [8928] = { name = L["moonfire"], rank = 6, duration = 18, variableDuration = true }, + [8929] = { name = L["moonfire"], rank = 7, duration = 18, variableDuration = true }, + [9833] = { name = L["moonfire"], rank = 8, duration = 18, variableDuration = true }, + [9834] = { name = L["moonfire"], rank = 9, duration = 18, variableDuration = true }, + [9835] = { name = L["moonfire"], rank = 10, duration = 18, variableDuration = true }, - [1822] = { name = L["rake"], rank = 1, duration = 9, variable_duration = true }, - [1823] = { name = L["rake"], rank = 2, duration = 9, variable_duration = true }, - [1824] = { name = L["rake"], rank = 3, duration = 9, variable_duration = true }, - [9904] = { name = L["rake"], rank = 4, duration = 9, variable_duration = true }, + [1822] = { name = L["rake"], rank = 1, duration = 9, variableDuration = true }, + [1823] = { name = L["rake"], rank = 2, duration = 9, variableDuration = true }, + [1824] = { name = L["rake"], rank = 3, duration = 9, variableDuration = true }, + [9904] = { name = L["rake"], rank = 4, duration = 9, variableDuration = true }, - [1079] = { name = L["rip"], rank = 1, duration = 12, variable_duration = true }, - [9492] = { name = L["rip"], rank = 2, duration = 12, variable_duration = true }, - [9493] = { name = L["rip"], rank = 3, duration = 12, variable_duration = true }, - [9752] = { name = L["rip"], rank = 4, duration = 12, variable_duration = true }, - [9894] = { name = L["rip"], rank = 5, duration = 12, variable_duration = true }, - [9896] = { name = L["rip"], rank = 6, duration = 12, variable_duration = true }, + [1079] = { name = L["rip"], rank = 1, duration = 12, variableDuration = true }, + [9492] = { name = L["rip"], rank = 2, duration = 12, variableDuration = true }, + [9493] = { name = L["rip"], rank = 3, duration = 12, variableDuration = true }, + [9752] = { name = L["rip"], rank = 4, duration = 12, variableDuration = true }, + [9894] = { name = L["rip"], rank = 5, duration = 12, variableDuration = true }, + [9896] = { name = L["rip"], rank = 6, duration = 12, variableDuration = true }, [2908] = { name = L["soothe animal"], rank = 1, duration = 15 }, [8955] = { name = L["soothe animal"], rank = 2, duration = 15 }, diff --git a/spells/shaman.lua b/spells/shaman.lua index 69dfa53..b56a2c7 100644 --- a/spells/shaman.lua +++ b/spells/shaman.lua @@ -1,11 +1,11 @@ local L = AceLibrary("AceLocale-2.2"):new("Cursive") function getShamanSpells() return { - [8050] = { name = L["flame shock"], rank = 1, duration = 12, variable_duration = true }, - [8052] = { name = L["flame shock"], rank = 2, duration = 12, variable_duration = true }, - [8053] = { name = L["flame shock"], rank = 3, duration = 12, variable_duration = true }, - [10447] = { name = L["flame shock"], rank = 4, duration = 12, variable_duration = true }, - [10448] = { name = L["flame shock"], rank = 5, duration = 12, variable_duration = true }, - [29228] = { name = L["flame shock"], rank = 6, duration = 12, variable_duration = true }, + [8050] = { name = L["flame shock"], rank = 1, duration = 12, variableDuration = true }, + [8052] = { name = L["flame shock"], rank = 2, duration = 12, variableDuration = true }, + [8053] = { name = L["flame shock"], rank = 3, duration = 12, variableDuration = true }, + [10447] = { name = L["flame shock"], rank = 4, duration = 12, variableDuration = true }, + [10448] = { name = L["flame shock"], rank = 5, duration = 12, variableDuration = true }, + [29228] = { name = L["flame shock"], rank = 6, duration = 12, variableDuration = true }, } end diff --git a/spells/warlock.lua b/spells/warlock.lua index a3d5ed1..eeb98e2 100644 --- a/spells/warlock.lua +++ b/spells/warlock.lua @@ -1,20 +1,29 @@ local L = AceLibrary("AceLocale-2.2"):new("Cursive") function getWarlockSpells() return { - [172] = { name = L["corruption"], rank = 1, duration = 12, variable_duration = true }, - [6222] = { name = L["corruption"], rank = 2, duration = 15, variable_duration = true }, - [6223] = { name = L["corruption"], rank = 3, duration = 18, variable_duration = true }, - [7648] = { name = L["corruption"], rank = 4, duration = 18, variable_duration = true }, - [11671] = { name = L["corruption"], rank = 5, duration = 18, variable_duration = true }, - [11672] = { name = L["corruption"], rank = 6, duration = 18, variable_duration = true }, - [25311] = { name = L["corruption"], rank = 7, duration = 18, variable_duration = true }, + [172] = { name = L["corruption"], rank = 1, duration = 12, variableDuration = true, darkHarvest = true, numTicks=4}, + [6222] = { name = L["corruption"], rank = 2, duration = 15, variableDuration = true, darkHarvest = true, numTicks=5 }, + [6223] = { name = L["corruption"], rank = 3, duration = 18, variableDuration = true, darkHarvest = true, numTicks=6 }, + [7648] = { name = L["corruption"], rank = 4, duration = 18, variableDuration = true, darkHarvest = true, numTicks=6 }, + [11671] = { name = L["corruption"], rank = 5, duration = 18, variableDuration = true, darkHarvest = true, numTicks=6 }, + [11672] = { name = L["corruption"], rank = 6, duration = 18, variableDuration = true, darkHarvest = true, numTicks=6 }, + [25311] = { name = L["corruption"], rank = 7, duration = 18, variableDuration = true, darkHarvest = true, numTicks=6 }, - [980] = { name = L["curse of agony"], rank = 1, duration = 24, variable_duration = true }, - [1014] = { name = L["curse of agony"], rank = 2, duration = 24, variable_duration = true }, - [6217] = { name = L["curse of agony"], rank = 3, duration = 24, variable_duration = true }, - [11711] = { name = L["curse of agony"], rank = 4, duration = 24, variable_duration = true }, - [11712] = { name = L["curse of agony"], rank = 5, duration = 24, variable_duration = true }, - [11713] = { name = L["curse of agony"], rank = 6, duration = 24, variable_duration = true }, + [980] = { name = L["curse of agony"], rank = 1, duration = 24, variableDuration = true, darkHarvest = true, numTicks=12 }, + [1014] = { name = L["curse of agony"], rank = 2, duration = 24, variableDuration = true, darkHarvest = true, numTicks=12 }, + [6217] = { name = L["curse of agony"], rank = 3, duration = 24, variableDuration = true, darkHarvest = true, numTicks=12 }, + [11711] = { name = L["curse of agony"], rank = 4, duration = 24, variableDuration = true, darkHarvest = true, numTicks=12 }, + [11712] = { name = L["curse of agony"], rank = 5, duration = 24, variableDuration = true, darkHarvest = true, numTicks=12 }, + [11713] = { name = L["curse of agony"], rank = 6, duration = 24, variableDuration = true, darkHarvest = true, numTicks=12 }, + + [18265] = { name = L["siphon life"], rank = 1, duration = 30, variableDuration = true, darkHarvest = true, numTicks=10 }, + [18879] = { name = L["siphon life"], rank = 2, duration = 30, variableDuration = true, darkHarvest = true, numTicks=10 }, + [18880] = { name = L["siphon life"], rank = 3, duration = 30, variableDuration = true, darkHarvest = true, numTicks=10 }, + [18881] = { name = L["siphon life"], rank = 4, duration = 30, variableDuration = true, darkHarvest = true, numTicks=10 }, + + [52550] = { name = L["dark harvest"], rank = 1, duration = 8, variableDuration = true }, + [52551] = { name = L["dark harvest"], rank = 2, duration = 8, variableDuration = true }, + [52552] = { name = L["dark harvest"], rank = 3, duration = 8, variableDuration = true }, [603] = { name = L["curse of doom"], rank = 1, duration = 60 }, @@ -58,11 +67,6 @@ function getWarlockSpells() [710] = { name = L["banish"], rank = 1, duration = 20 }, [18647] = { name = L["banish"], rank = 2, duration = 30 }, - [18265] = { name = L["siphon life"], rank = 1, duration = 30, variable_duration = true }, - [18879] = { name = L["siphon life"], rank = 2, duration = 30, variable_duration = true }, - [18880] = { name = L["siphon life"], rank = 3, duration = 30, variable_duration = true }, - [18881] = { name = L["siphon life"], rank = 4, duration = 30, variable_duration = true }, - [5782] = { name = L["fear"], rank = 1, duration = 10 }, [6213] = { name = L["fear"], rank = 2, duration = 15 }, [6215] = { name = L["fear"], rank = 3, duration = 20 },