You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3392 lines
133 KiB
3392 lines
133 KiB
|
4 years ago
|
local major = "LibHealComm-4.0"
|
||
|
3 years ago
|
local minor = 107
|
||
|
4 years ago
|
assert(LibStub, format("%s requires LibStub.", major))
|
||
|
|
|
||
|
|
local HealComm = LibStub:NewLibrary(major, minor)
|
||
|
|
if( not HealComm ) then return end
|
||
|
|
|
||
|
|
local COMM_PREFIX = "LHC40"
|
||
|
|
C_ChatInfo.RegisterAddonMessagePrefix(COMM_PREFIX)
|
||
|
|
|
||
|
|
local bit = bit
|
||
|
|
local ceil = ceil
|
||
|
|
local error = error
|
||
|
|
local floor = floor
|
||
|
|
local format = format
|
||
|
|
local gsub = gsub
|
||
|
|
local max = max
|
||
|
|
local min = min
|
||
|
|
local pairs = pairs
|
||
|
|
local ipairs = ipairs
|
||
|
|
local rawset = rawset
|
||
|
|
local select = select
|
||
|
|
local setmetatable = setmetatable
|
||
|
|
local strlen = strlen
|
||
|
|
local strmatch = strmatch
|
||
|
|
local strsplit = strsplit
|
||
|
|
local strsub = strsub
|
||
|
|
local tinsert = tinsert
|
||
|
|
local tonumber = tonumber
|
||
|
|
local tremove = tremove
|
||
|
|
local type = type
|
||
|
|
local unpack = unpack
|
||
|
|
local wipe = wipe
|
||
|
|
|
||
|
|
local Ambiguate = Ambiguate
|
||
|
|
local CastingInfo = CastingInfo
|
||
|
|
local ChannelInfo = ChannelInfo
|
||
|
|
local CreateFrame = CreateFrame
|
||
|
|
local GetInventoryItemLink = GetInventoryItemLink
|
||
|
|
local GetInventorySlotInfo = GetInventorySlotInfo
|
||
|
|
local GetNumGroupMembers = GetNumGroupMembers
|
||
|
|
local GetNumTalents = GetNumTalents
|
||
|
|
local GetNumTalentTabs = GetNumTalentTabs
|
||
|
|
local GetRaidRosterInfo = GetRaidRosterInfo
|
||
|
|
local GetSpellBonusHealing = GetSpellBonusHealing
|
||
|
|
local GetSpellCritChance = GetSpellCritChance
|
||
|
|
local GetSpellInfo = GetSpellInfo
|
||
|
|
local GetTalentInfo = GetTalentInfo
|
||
|
|
local GetTime = GetTime
|
||
|
|
local GetZonePVPInfo = GetZonePVPInfo
|
||
|
|
local hooksecurefunc = hooksecurefunc
|
||
|
|
local InCombatLockdown = InCombatLockdown
|
||
|
|
local IsEquippedItem = IsEquippedItem
|
||
|
|
local IsInGroup = IsInGroup
|
||
|
|
local IsInInstance = IsInInstance
|
||
|
|
local IsInRaid = IsInRaid
|
||
|
|
local IsLoggedIn = IsLoggedIn
|
||
|
|
local IsSpellInRange = IsSpellInRange
|
||
|
|
local SpellIsTargeting = SpellIsTargeting
|
||
|
|
local UnitAura = UnitAura
|
||
|
|
local UnitCanAssist = UnitCanAssist
|
||
|
|
local UnitExists = UnitExists
|
||
|
|
local UnitBuff = UnitBuff
|
||
|
|
local UnitGUID = UnitGUID
|
||
|
|
local UnitIsCharmed = UnitIsCharmed
|
||
|
|
local UnitIsVisible = UnitIsVisible
|
||
|
|
local UnitInRaid = UnitInRaid
|
||
|
|
local UnitLevel = UnitLevel
|
||
|
|
local UnitName = UnitName
|
||
|
|
local UnitPlayerControlled = UnitPlayerControlled
|
||
|
|
local CheckInteractDistance = CheckInteractDistance
|
||
|
|
local CombatLogGetCurrentEventInfo = CombatLogGetCurrentEventInfo
|
||
|
3 years ago
|
local UnitHasVehicleUI = UnitHasVehicleUI or function() end
|
||
|
|
local GetGlyphSocketInfo = GetGlyphSocketInfo or function() end
|
||
|
|
local GetNumGlyphSockets = GetNumGlyphSockets or function() return 0 end
|
||
|
4 years ago
|
|
||
|
|
local MAX_RAID_MEMBERS = MAX_RAID_MEMBERS
|
||
|
|
local MAX_PARTY_MEMBERS = MAX_PARTY_MEMBERS
|
||
|
|
local COMBATLOG_OBJECT_AFFILIATION_MINE = COMBATLOG_OBJECT_AFFILIATION_MINE
|
||
|
|
|
||
|
3 years ago
|
local build = floor(select(4,GetBuildInfo())/10000)
|
||
|
|
local isTBC = build == 2
|
||
|
|
local isWrath = build == 3
|
||
|
4 years ago
|
|
||
|
|
local spellRankTableData = {
|
||
|
3 years ago
|
[1] = { 774, 8936, 5185, 740, 635, 19750, 139, 2060, 596, 2061, 2054, 2050, 1064, 331, 8004, 136, 755, 689, 746, 33763, 32546, 37563, 48438, 61295, 51945, 50464, 47757 },
|
||
|
|
[2] = { 1058, 8938, 5186, 8918, 639, 19939, 6074, 10963, 996, 9472, 2055, 2052, 10622, 332, 8008, 3111, 3698, 699, 1159, 53248, 61299, 51990, 48450, 52986, 48119 },
|
||
|
|
[3] = { 1430, 8939, 5187, 9862, 647, 19940, 6075, 10964, 10960, 9473, 6063, 2053, 10623, 547, 8010, 3661, 3699, 709, 3267, 53249, 61300, 51997, 48451, 52987, 48120 },
|
||
|
|
[4] = { 2090, 8940, 5188, 9863, 1026, 19941, 6076, 10965, 10961, 9474, 6064, 913, 10466, 3662, 3700, 7651, 3268, 25422, 53251, 61301, 51998, 52988 },
|
||
|
|
[5] = { 2091, 8941, 5189, 1042, 19942, 6077, 22009, 25314, 25316, 10915, 939, 10467, 13542, 11693, 11699, 7926, 25423, 26983, 51999 },
|
||
|
|
[6] = { 3627, 9750, 6778, 3472, 19943, 6078, 10916, 959, 10468, 13543, 11694, 11700, 7927, 23569, 24412, 25210, 25308, 52000, 55458, 48446 },
|
||
|
|
[7] = { 8910, 9856, 8903, 10328, 10927, 10917, 8005, 13544, 11695, 10838, 27137, 25213, 25420, 27219, 55459, 48447, 48072 },
|
||
|
|
[8] = { 9839, 9857, 9758, 10329, 10928, 10395, 10839, 23568, 24413, 25233, 27259, 27220, 27046, 48784, 49275, 48062 },
|
||
|
|
[9] = { 9840, 9858, 9888, 25292, 10929, 10396, 18608, 25235, 48785, 49276, 48063, 48989, 47856, 47857 },
|
||
|
|
[10] = { 9841, 9889, 25315, 25357, 18610, 23567, 24414, 26980, 27135, 48070, 48990 },
|
||
|
|
[11] = { 25299, 25297, 30020, 27136, 25221, 25391, 27030, 48442, 48071 },
|
||
|
|
[12] = { 26981, 26978, 25222, 25396, 27031, 48781, 48443 },
|
||
|
|
[13] = { 26982, 26979, 48782, 49272, 48067, 45543 },
|
||
|
3 years ago
|
[14] = { 49273, 48377, 48440, 48068, 51827 },
|
||
|
|
[15] = { 48378, 48441, 45544 },
|
||
|
|
[16] = { 51803 },
|
||
|
4 years ago
|
}
|
||
|
|
|
||
|
|
local SpellIDToRank = {}
|
||
|
|
for rankIndex, spellIDTable in pairs(spellRankTableData) do
|
||
|
|
for _, spellID in pairs(spellIDTable) do
|
||
|
|
SpellIDToRank[spellID] = rankIndex
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- API CONSTANTS
|
||
|
|
local ALL_DATA = 0x0f
|
||
|
|
local DIRECT_HEALS = 0x01
|
||
|
|
local CHANNEL_HEALS = 0x02
|
||
|
|
local HOT_HEALS = 0x04
|
||
|
|
local ABSORB_SHIELDS = 0x08
|
||
|
|
local BOMB_HEALS = 0x10
|
||
|
|
local ALL_HEALS = bit.bor(DIRECT_HEALS, CHANNEL_HEALS, HOT_HEALS, BOMB_HEALS)
|
||
|
|
local CASTED_HEALS = bit.bor(DIRECT_HEALS, CHANNEL_HEALS)
|
||
|
|
local OVERTIME_HEALS = bit.bor(HOT_HEALS, CHANNEL_HEALS)
|
||
|
|
local OVERTIME_AND_BOMB_HEALS = bit.bor(HOT_HEALS, CHANNEL_HEALS, BOMB_HEALS)
|
||
|
|
|
||
|
|
HealComm.ALL_HEALS, HealComm.OVERTIME_HEALS, HealComm.OVERTIME_AND_BOMB_HEALS, HealComm.CHANNEL_HEALS, HealComm.DIRECT_HEALS, HealComm.HOT_HEALS, HealComm.CASTED_HEALS, HealComm.ABSORB_SHIELDS, HealComm.ALL_DATA, HealComm.BOMB_HEALS = ALL_HEALS, OVERTIME_HEALS, OVERTIME_AND_BOMB_HEALS, CHANNEL_HEALS, DIRECT_HEALS, HOT_HEALS, CASTED_HEALS, ABSORB_SHIELDS, ALL_DATA, BOMB_HEALS
|
||
|
|
|
||
|
|
local playerGUID, playerName, playerLevel
|
||
|
|
local playerHealModifier = 1
|
||
|
|
|
||
|
|
HealComm.callbacks = HealComm.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(HealComm)
|
||
|
|
HealComm.activeHots = HealComm.activeHots or {}
|
||
|
|
HealComm.activePets = HealComm.activePets or {}
|
||
|
|
HealComm.equippedSetCache = HealComm.equippedSetCache or {}
|
||
|
|
HealComm.guidToGroup = HealComm.guidToGroup or {}
|
||
|
|
HealComm.guidToUnit = HealComm.guidToUnit or {}
|
||
|
|
HealComm.hotData = HealComm.hotData or {}
|
||
|
|
HealComm.itemSetsData = HealComm.itemSetsData or {}
|
||
|
|
HealComm.pendingHeals = HealComm.pendingHeals or {}
|
||
|
|
HealComm.pendingHots = HealComm.pendingHots or {}
|
||
|
|
HealComm.spellData = HealComm.spellData or {}
|
||
|
|
HealComm.talentData = HealComm.talentData or {}
|
||
|
|
HealComm.tempPlayerList = HealComm.tempPlayerList or {}
|
||
|
3 years ago
|
HealComm.glyphCache = HealComm.glyphCache or {}
|
||
|
4 years ago
|
|
||
|
|
if( not HealComm.unitToPet ) then
|
||
|
|
HealComm.unitToPet = {["player"] = "pet"}
|
||
|
|
for i = 1, MAX_PARTY_MEMBERS do HealComm.unitToPet["party" .. i] = "partypet" .. i end
|
||
|
|
for i = 1, MAX_RAID_MEMBERS do HealComm.unitToPet["raid" .. i] = "raidpet" .. i end
|
||
|
|
end
|
||
|
|
|
||
|
|
local spellData, hotData, tempPlayerList, pendingHeals, pendingHots = HealComm.spellData, HealComm.hotData, HealComm.tempPlayerList, HealComm.pendingHeals, HealComm.pendingHots
|
||
|
|
local equippedSetCache, itemSetsData, talentData = HealComm.equippedSetCache, HealComm.itemSetsData, HealComm.talentData
|
||
|
|
local activeHots, activePets = HealComm.activeHots, HealComm.activePets
|
||
|
|
|
||
|
|
-- Figure out what they are now since a few things change based off of this
|
||
|
|
local playerClass = select(2, UnitClass("player"))
|
||
|
|
|
||
|
|
if( not HealComm.compressGUID ) then
|
||
|
|
HealComm.compressGUID = setmetatable({}, {
|
||
|
|
__index = function(tbl, guid)
|
||
|
|
local str
|
||
|
|
if strsub(guid,1,6) ~= "Player" then
|
||
|
|
for unit,pguid in pairs(activePets) do
|
||
|
|
if pguid == guid and UnitExists(unit) then
|
||
|
|
str = "p-" .. strmatch(UnitGUID(unit), "^%w*-([-%w]*)$")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if not str then
|
||
|
|
--assert(str, "Could not encode: "..guid)
|
||
|
|
return nil
|
||
|
|
end
|
||
|
|
else
|
||
|
|
str = strmatch(guid, "^%w*-([-%w]*)$")
|
||
|
|
end
|
||
|
|
rawset(tbl, guid, str)
|
||
|
|
return str
|
||
|
|
end})
|
||
|
|
|
||
|
|
HealComm.decompressGUID = setmetatable({}, {
|
||
|
|
__index = function(tbl, str)
|
||
|
|
if( not str ) then return nil end
|
||
|
|
local guid
|
||
|
|
if strsub(str,1,2) == "p-" then
|
||
|
|
local unit = HealComm.guidToUnit["Player-"..strsub(str,3)]
|
||
|
|
if not unit then
|
||
|
|
return nil
|
||
|
|
end
|
||
|
|
guid = activePets[unit]
|
||
|
|
else
|
||
|
|
guid = "Player-"..str
|
||
|
|
end
|
||
|
|
|
||
|
|
rawset(tbl, str, guid)
|
||
|
|
return guid
|
||
|
|
end})
|
||
|
|
end
|
||
|
|
|
||
|
|
local compressGUID, decompressGUID = HealComm.compressGUID, HealComm.decompressGUID
|
||
|
|
|
||
|
|
-- Handles caching of tables for variable tick spells, like Wild Growth
|
||
|
|
if( not HealComm.tableCache ) then
|
||
|
|
HealComm.tableCache = setmetatable({}, {__mode = "k"})
|
||
|
|
function HealComm:RetrieveTable()
|
||
|
|
return tremove(HealComm.tableCache, 1) or {}
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:DeleteTable(tbl)
|
||
|
|
wipe(tbl)
|
||
|
|
tinsert(HealComm.tableCache, tbl)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Validation for passed arguments
|
||
|
|
if( not HealComm.tooltip ) then
|
||
|
|
local tooltip = CreateFrame("GameTooltip")
|
||
|
|
tooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
||
|
|
tooltip.TextLeft1 = tooltip:CreateFontString()
|
||
|
|
tooltip.TextRight1 = tooltip:CreateFontString()
|
||
|
|
tooltip:AddFontStrings(tooltip.TextLeft1, tooltip.TextRight1)
|
||
|
|
|
||
|
|
HealComm.tooltip = tooltip
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Record management, because this is getting more complicted to deal with
|
||
|
|
local function updateRecord(pending, guid, amount, stack, endTime, ticksLeft)
|
||
|
|
if( pending[guid] ) then
|
||
|
|
local id = pending[guid]
|
||
|
|
|
||
|
|
pending[id] = guid
|
||
|
|
pending[id + 1] = amount
|
||
|
|
pending[id + 2] = stack
|
||
|
|
pending[id + 3] = endTime or 0
|
||
|
|
pending[id + 4] = ticksLeft or 0
|
||
|
|
else
|
||
|
|
pending[guid] = #(pending) + 1
|
||
|
|
tinsert(pending, guid)
|
||
|
|
tinsert(pending, amount)
|
||
|
|
tinsert(pending, stack)
|
||
|
|
tinsert(pending, endTime or 0)
|
||
|
|
tinsert(pending, ticksLeft or 0)
|
||
|
|
|
||
|
|
if( pending.bitType == HOT_HEALS ) then
|
||
|
|
activeHots[guid] = (activeHots[guid] or 0) + 1
|
||
|
|
HealComm.hotMonitor:Show()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function getRecord(pending, guid)
|
||
|
|
local id = pending[guid]
|
||
|
|
if( not id ) then return nil end
|
||
|
|
|
||
|
|
-- amount, stack, endTime, ticksLeft
|
||
|
|
return pending[id + 1], pending[id + 2], pending[id + 3], pending[id + 4]
|
||
|
|
end
|
||
|
|
|
||
|
|
local function removeRecord(pending, guid)
|
||
|
|
local id = pending[guid]
|
||
|
|
if( not id ) then return nil end
|
||
|
|
|
||
|
|
-- ticksLeft, endTime, stack, amount, guid
|
||
|
|
tremove(pending, id + 4)
|
||
|
|
tremove(pending, id + 3)
|
||
|
|
tremove(pending, id + 2)
|
||
|
|
local amount = tremove(pending, id + 1)
|
||
|
|
tremove(pending, id)
|
||
|
|
pending[guid] = nil
|
||
|
|
|
||
|
|
-- Release the table
|
||
|
|
if( type(amount) == "table" ) then HealComm:DeleteTable(amount) end
|
||
|
|
|
||
|
|
if( pending.bitType == HOT_HEALS and activeHots[guid] ) then
|
||
|
|
activeHots[guid] = activeHots[guid] - 1
|
||
|
|
activeHots[guid] = activeHots[guid] > 0 and activeHots[guid] or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Shift any records after this ones index down 5 to account for the removal
|
||
|
|
for i=1, #(pending), 5 do
|
||
|
|
local guid = pending[i]
|
||
|
|
if( pending[guid] > id ) then
|
||
|
|
pending[guid] = pending[guid] - 5
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function removeRecordList(pending, inc, comp, ...)
|
||
|
|
for i=1, select("#", ...), inc do
|
||
|
|
local guid = select(i, ...)
|
||
|
|
guid = comp and decompressGUID[guid] or guid
|
||
|
|
|
||
|
|
if guid then
|
||
|
|
local id = pending[guid]
|
||
|
|
-- ticksLeft, endTime, stack, amount, guid
|
||
|
|
tremove(pending, id + 4)
|
||
|
|
tremove(pending, id + 3)
|
||
|
|
tremove(pending, id + 2)
|
||
|
|
local amount = tremove(pending, id + 1)
|
||
|
|
tremove(pending, id)
|
||
|
|
pending[guid] = nil
|
||
|
|
|
||
|
|
-- Release the table
|
||
|
|
if( type(amount) == "table" ) then HealComm:DeleteTable(amount) end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Redo all the id maps
|
||
|
|
for i=1, #(pending), 5 do
|
||
|
|
pending[pending[i]] = i
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Removes every mention to the given GUID
|
||
|
|
local function removeAllRecords(guid)
|
||
|
|
local changed
|
||
|
|
|
||
|
|
for _, tbl in pairs({pendingHeals, pendingHots}) do
|
||
|
|
for _, spells in pairs(tbl) do
|
||
|
|
for _, pending in pairs(spells) do
|
||
|
|
if( pending.bitType and pending[guid] ) then
|
||
|
|
local id = pending[guid]
|
||
|
|
|
||
|
|
-- ticksLeft, endTime, stack, amount, guid
|
||
|
|
tremove(pending, id + 4)
|
||
|
|
tremove(pending, id + 3)
|
||
|
|
tremove(pending, id + 2)
|
||
|
|
local amount = tremove(pending, id + 1)
|
||
|
|
tremove(pending, id)
|
||
|
|
pending[guid] = nil
|
||
|
|
|
||
|
|
-- Release the table
|
||
|
|
if( type(amount) == "table" ) then HealComm:DeleteTable(amount) end
|
||
|
|
|
||
|
|
-- Shift everything back
|
||
|
|
if( #(pending) > 0 ) then
|
||
|
|
for i=1, #(pending), 5 do
|
||
|
|
local guid = pending[i]
|
||
|
|
if( pending[guid] > id ) then
|
||
|
|
pending[guid] = pending[guid] - 5
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
wipe(pending)
|
||
|
|
end
|
||
|
|
|
||
|
|
changed = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
activeHots[guid] = nil
|
||
|
|
|
||
|
|
if( changed ) then
|
||
|
|
HealComm.callbacks:Fire("HealComm_GUIDDisappeared", guid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- These are not public APIs and are purely for the wrapper to use
|
||
|
|
HealComm.removeRecordList = removeRecordList
|
||
|
|
HealComm.removeRecord = removeRecord
|
||
|
|
HealComm.getRecord = getRecord
|
||
|
|
HealComm.updateRecord = updateRecord
|
||
|
|
|
||
|
|
-- Removes all pending heals, if it's a group that is causing the clear then we won't remove the players heals on themselves
|
||
|
|
local function clearPendingHeals()
|
||
|
|
for _, tbl in pairs({pendingHeals, pendingHots}) do
|
||
|
|
for casterGUID, spells in pairs(tbl) do
|
||
|
|
for _, pending in pairs(spells) do
|
||
|
|
if( pending.bitType ) then
|
||
|
|
wipe(tempPlayerList)
|
||
|
|
for i=#(pending), 1, -5 do tinsert(tempPlayerList, pending[i - 4]) end
|
||
|
|
|
||
|
|
if( #(tempPlayerList) > 0 ) then
|
||
|
|
local spellID, bitType = pending.spellID, pending.bitType
|
||
|
|
wipe(pending)
|
||
|
|
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealStopped", casterGUID, spellID, bitType, true, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- APIs
|
||
|
|
-- Returns the players current heaing modifier
|
||
|
|
function HealComm:GetPlayerHealingMod()
|
||
|
|
return playerHealModifier or 1
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Returns the current healing modifier for the GUID
|
||
|
|
function HealComm:GetHealModifier(guid)
|
||
|
|
return HealComm.currentModifiers[guid] or 1
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Returns whether or not the GUID has casted a heal
|
||
|
|
function HealComm:GUIDHasHealed(guid)
|
||
|
|
return (pendingHeals[guid] or pendingHots[guid]) and true or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Returns the guid to unit table
|
||
|
|
function HealComm:GetGUIDUnitMapTable()
|
||
|
|
if( not HealComm.protectedMap ) then
|
||
|
|
HealComm.protectedMap = setmetatable({}, {
|
||
|
|
__index = function(tbl, key) return HealComm.guidToUnit[key] end,
|
||
|
|
__newindex = function() error("This is a read only table and cannot be modified.", 2) end,
|
||
|
|
__metatable = false
|
||
|
|
})
|
||
|
|
end
|
||
|
|
|
||
|
|
return HealComm.protectedMap
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Gets the next heal landing on someone using the passed filters
|
||
|
|
function HealComm:GetNextHealAmount(guid, bitFlag, time, ignoreGUID, srcGUID)
|
||
|
|
local healTime, healAmount, healFrom
|
||
|
|
local currentTime = GetTime()
|
||
|
|
|
||
|
|
for _, tbl in pairs({pendingHeals, pendingHots}) do
|
||
|
|
for casterGUID, spells in pairs(tbl) do
|
||
|
|
if( not ignoreGUID or ignoreGUID ~= casterGUID ) and (not srcGUID or srcGUID == casterGUID) then
|
||
|
|
for _, pending in pairs(spells) do
|
||
|
|
if( pending.bitType and bit.band(pending.bitType, bitFlag) > 0 ) then
|
||
|
|
for i=1, #(pending), 5 do
|
||
|
|
local targetGUID = pending[i]
|
||
|
|
if(not guid or targetGUID == guid) then
|
||
|
|
local amount = pending[i + 1]
|
||
|
|
local stack = pending[i + 2]
|
||
|
|
local endTime = pending[i + 3]
|
||
|
|
endTime = endTime > 0 and endTime or pending.endTime
|
||
|
|
|
||
|
|
-- Direct heals are easy, if they match the filter then return them
|
||
|
|
if( ( pending.bitType == DIRECT_HEALS or pending.bitType == BOMB_HEALS ) and ( not time or endTime <= time ) ) then
|
||
|
|
if( not healTime or endTime < healTime ) then
|
||
|
|
healTime = endTime
|
||
|
|
healAmount = amount * stack
|
||
|
|
healFrom = casterGUID
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Channeled heals and hots, have to figure out how many times it'll tick within the given time band
|
||
|
|
elseif( ( pending.bitType == CHANNEL_HEALS or pending.bitType == HOT_HEALS ) ) then
|
||
|
|
local secondsLeft = time and time - currentTime or endTime - currentTime
|
||
|
|
local nextTick = currentTime + (secondsLeft % pending.tickInterval)
|
||
|
|
if( not healTime or nextTick < healTime ) then
|
||
|
|
healTime = nextTick
|
||
|
3 years ago
|
if pending.hasVariableTicks then
|
||
|
|
healAmount = amount[pending.totalTicks - pending[i + 4] + 1]
|
||
|
|
else
|
||
|
|
healAmount = amount * stack
|
||
|
|
end
|
||
|
4 years ago
|
healFrom = casterGUID
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return healTime, healFrom, healAmount
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Get the healing amount that matches the passed filters
|
||
|
|
local function filterData(spells, filterGUID, bitFlag, time, ignoreGUID)
|
||
|
|
local healAmount = 0
|
||
|
|
local currentTime = GetTime()
|
||
|
|
|
||
|
|
if spells then
|
||
|
|
for _, pending in pairs(spells) do
|
||
|
|
if( pending.bitType and bit.band(pending.bitType, bitFlag) > 0 ) then
|
||
|
|
for i = 1, #(pending), 5 do
|
||
|
|
local guid = pending[i]
|
||
|
|
if( guid == filterGUID or ignoreGUID ) then
|
||
|
|
local amount = pending[i + 1]
|
||
|
|
local stack = pending[i + 2]
|
||
|
|
local endTime = pending[i + 3]
|
||
|
|
endTime = endTime > 0 and endTime or pending.endTime
|
||
|
|
|
||
|
|
if( ( pending.bitType == DIRECT_HEALS or pending.bitType == BOMB_HEALS ) and ( not time or endTime <= time ) ) then
|
||
|
|
healAmount = healAmount + amount * stack
|
||
|
|
elseif( ( pending.bitType == CHANNEL_HEALS or pending.bitType == HOT_HEALS ) and endTime > currentTime ) then
|
||
|
|
local ticksLeft = pending[i + 4]
|
||
|
|
if( not time or time >= endTime ) then
|
||
|
3 years ago
|
if( not pending.hasVariableTicks ) then
|
||
|
|
healAmount = healAmount + (amount * stack) * ticksLeft
|
||
|
|
else
|
||
|
|
local ticksPassed = pending.totalTicks - ticksLeft
|
||
|
|
for numTick, heal in pairs(amount) do
|
||
|
|
if numTick > ticksPassed then
|
||
|
|
healAmount = healAmount + (heal * stack)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
4 years ago
|
else
|
||
|
|
local secondsLeft = endTime - currentTime
|
||
|
|
local bandSeconds = time - currentTime
|
||
|
|
local ticks = floor(min(bandSeconds, secondsLeft) / pending.tickInterval)
|
||
|
|
local nextTickIn = secondsLeft % pending.tickInterval
|
||
|
|
local fractionalBand = bandSeconds % pending.tickInterval
|
||
|
|
if( nextTickIn > 0 and nextTickIn < fractionalBand ) then
|
||
|
|
ticks = ticks + 1
|
||
|
|
end
|
||
|
3 years ago
|
if( not pending.hasVariableTicks ) then
|
||
|
|
healAmount = healAmount + (amount * stack) * min(ticks, ticksLeft)
|
||
|
|
else
|
||
|
|
local ticksPassed = pending.totalTicks - ticksLeft
|
||
|
|
for numTick, heal in ipairs(amount) do
|
||
|
|
if numTick > ticksPassed then
|
||
|
|
healAmount = healAmount + (heal * stack)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return healAmount
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Gets healing amount using the passed filters
|
||
|
|
function HealComm:GetHealAmount(guid, bitFlag, time, casterGUID)
|
||
|
|
local amount = 0
|
||
|
|
if( casterGUID and (pendingHeals[casterGUID] or pendingHots[casterGUID]) ) then
|
||
|
|
amount = filterData(pendingHeals[casterGUID], guid, bitFlag, time) + filterData(pendingHots[casterGUID], guid, bitFlag, time)
|
||
|
|
elseif( not casterGUID ) then
|
||
|
|
for _, tbl in pairs({pendingHeals, pendingHots}) do
|
||
|
|
for _, spells in pairs(tbl) do
|
||
|
|
amount = amount + filterData(spells, guid, bitFlag, time)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return amount > 0 and amount or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Gets healing amounts for everyone except the player using the passed filters
|
||
|
|
function HealComm:GetOthersHealAmount(guid, bitFlag, time)
|
||
|
|
local amount = 0
|
||
|
|
for _, tbl in pairs({pendingHeals, pendingHots}) do
|
||
|
|
for casterGUID, spells in pairs(tbl) do
|
||
|
|
if( casterGUID ~= playerGUID ) then
|
||
|
|
amount = amount + filterData(spells, guid, bitFlag, time)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return amount > 0 and amount or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:GetCasterHealAmount(guid, bitFlag, time)
|
||
|
|
local amount = pendingHeals[guid] and filterData(pendingHeals[guid], nil, bitFlag, time, true) or 0
|
||
|
|
amount = amount + (pendingHots[guid] and filterData(pendingHots[guid], nil, bitFlag, time, true) or 0)
|
||
|
|
return amount > 0 and amount or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:GetHealAmountEx(dstGUID, dstBitFlag, dstTime, srcGUID, srcBitFlag, srcTime)
|
||
|
|
local dstAmount1 = 0
|
||
|
|
local dstAmount2 = 0
|
||
|
|
local srcAmount1 = 0
|
||
|
|
local srcAmount2 = 0
|
||
|
|
|
||
|
|
local currTime = GetTime()
|
||
|
|
|
||
|
|
dstBitFlag = dstBitFlag or ALL_HEALS
|
||
|
|
srcBitFlag = srcBitFlag or ALL_HEALS
|
||
|
|
|
||
|
|
for _, tbl in ipairs({pendingHeals, pendingHots}) do
|
||
|
|
for casterGUID, spells in pairs(tbl) do
|
||
|
|
local time
|
||
|
|
|
||
|
|
if casterGUID ~= srcGUID then
|
||
|
|
time = dstTime
|
||
|
|
else
|
||
|
|
time = srcTime
|
||
|
|
end
|
||
|
|
|
||
|
|
if spells then
|
||
|
|
for _, pending in pairs(spells) do
|
||
|
|
local bitType = pending.bitType or 0
|
||
|
|
|
||
|
|
if casterGUID ~= srcGUID then
|
||
|
|
bitType = bit.band(bitType, dstBitFlag)
|
||
|
|
else
|
||
|
|
bitType = bit.band(bitType, srcBitFlag)
|
||
|
|
end
|
||
|
|
|
||
|
|
if bitType > 0 then
|
||
|
|
for i = 1, #pending, 5 do
|
||
|
|
local targetGUID = pending[i]
|
||
|
|
|
||
|
|
if targetGUID == dstGUID then
|
||
|
3 years ago
|
local amount = 0
|
||
|
|
if not pending.hasVariableTicks then
|
||
|
|
amount = pending[i + 1]
|
||
|
|
end
|
||
|
4 years ago
|
local stack = pending[i + 2]
|
||
|
|
local endTime = pending[i + 3]
|
||
|
|
|
||
|
|
endTime = endTime > 0 and endTime or pending.endTime
|
||
|
|
|
||
|
|
if endTime > currTime then
|
||
|
|
amount = amount * stack
|
||
|
|
|
||
|
|
local amount1 = 0
|
||
|
|
local amount2 = 0
|
||
|
|
|
||
|
|
if bitType == DIRECT_HEALS or bitType == BOMB_HEALS then
|
||
|
|
if not time or endTime <= time then
|
||
|
|
amount1 = amount
|
||
|
|
end
|
||
|
|
|
||
|
|
amount2 = amount
|
||
|
|
elseif bitType == HOT_HEALS or bitType == CHANNEL_HEALS then
|
||
|
|
local ticksLeft = pending[i + 4]
|
||
|
|
local ticks
|
||
|
|
|
||
|
|
if not time then
|
||
|
|
ticks = 1
|
||
|
|
elseif time >= endTime then
|
||
|
|
ticks = ticksLeft
|
||
|
|
else
|
||
|
|
local tickInterval = pending.tickInterval
|
||
|
|
local secondsLeft = endTime - currTime
|
||
|
|
local bandSeconds = max(time - currTime, 0)
|
||
|
|
|
||
|
|
ticks = floor(min(bandSeconds, secondsLeft) / tickInterval)
|
||
|
|
|
||
|
|
local nextTickIn = secondsLeft % tickInterval
|
||
|
|
local fractionalBand = bandSeconds % tickInterval
|
||
|
|
|
||
|
|
if nextTickIn > 0 and nextTickIn < fractionalBand then
|
||
|
|
ticks = ticks + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if ticks > ticksLeft then
|
||
|
|
ticks = ticksLeft
|
||
|
|
end
|
||
|
3 years ago
|
if not pending.hasVariableTicks then
|
||
|
|
amount1 = amount * ticks
|
||
|
|
amount2 = amount * ticksLeft
|
||
|
|
else
|
||
|
|
amount = pending[i + 1]
|
||
|
|
amount1 = amount[pending.totalTicks - pending[i + 4] + 1] or 0
|
||
|
|
local ticksPassed = pending.totalTicks - ticksLeft
|
||
|
|
for numTick, heal in ipairs(amount) do
|
||
|
|
if numTick > ticksPassed then
|
||
|
|
amount2 = amount2 + (heal * stack)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
if casterGUID ~= srcGUID then
|
||
|
|
dstAmount1 = dstAmount1 + amount1
|
||
|
|
dstAmount2 = dstAmount2 + amount2
|
||
|
|
else
|
||
|
|
srcAmount1 = srcAmount1 + amount1
|
||
|
|
srcAmount2 = srcAmount2 + amount2
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
dstAmount2 = dstAmount2 - dstAmount1
|
||
|
|
srcAmount2 = srcAmount2 - srcAmount1
|
||
|
|
|
||
|
|
dstAmount1 = dstAmount1 > 0 and dstAmount1 or nil
|
||
|
|
dstAmount2 = dstAmount2 > 0 and dstAmount2 or nil
|
||
|
|
srcAmount1 = srcAmount1 > 0 and srcAmount1 or nil
|
||
|
|
srcAmount2 = srcAmount2 > 0 and srcAmount2 or nil
|
||
|
|
|
||
|
|
return dstAmount1, dstAmount2, srcAmount1, srcAmount2
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Get the number of direct heals on a target
|
||
|
|
function HealComm:GetNumHeals(filterGUID, time)
|
||
|
|
local numHeals = 0
|
||
|
|
|
||
|
|
for _, spells in pairs(pendingHeals) do
|
||
|
|
if spells then
|
||
|
|
for _, pending in pairs(spells) do
|
||
|
|
for i = 1, #(pending), 5 do
|
||
|
|
local guid = pending[i]
|
||
|
|
if( guid == filterGUID ) then
|
||
|
|
local endTime = pending[i + 3]
|
||
|
|
endTime = endTime > 0 and endTime or pending.endTime
|
||
|
|
|
||
|
|
if( pending.bitType == DIRECT_HEALS and ( not time or endTime <= time ) ) then
|
||
|
|
numHeals = numHeals + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return numHeals
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Healing class data
|
||
|
|
-- Thanks to Gagorian (DrDamage) for letting me steal his formulas and such
|
||
|
|
local playerCurrentRelic
|
||
|
3 years ago
|
local guidToUnit, guidToGroup, glyphCache = HealComm.guidToUnit, HealComm.guidToGroup, HealComm.glyphCache
|
||
|
4 years ago
|
|
||
|
|
local unitHasAura
|
||
|
|
|
||
|
|
do
|
||
|
|
local findAura = AuraUtil.FindAura
|
||
|
|
local findAuraByName = AuraUtil.FindAuraByName
|
||
|
|
|
||
|
|
local function spellIdPredicate(spellIdToFind, _, _, _, _, _, _, _, _, _, _, _, spellId)
|
||
|
|
return spellIdToFind == spellId
|
||
|
|
end
|
||
|
|
|
||
|
|
local function findAuraBySpellId(spellId, unit, filter)
|
||
|
|
return findAura(spellIdPredicate, unit, filter, spellId)
|
||
|
|
end
|
||
|
|
|
||
|
|
function unitHasAura(unit, name)
|
||
|
|
if type(name) == "number" then
|
||
|
|
return findAuraBySpellId(name, unit)
|
||
|
|
else
|
||
|
|
return findAuraByName(name, unit)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Note because I always forget on the order:
|
||
|
|
-- Talents that effective the coeffiency of spell power to healing are first and are tacked directly onto the coeffiency (Empowered Rejuvenation)
|
||
|
|
-- Penalty modifiers (downranking/spell level too low) are applied directly to the spell power
|
||
|
|
-- Spell power modifiers are then applied to the spell power
|
||
|
|
-- Heal modifiers are applied after all of that
|
||
|
|
-- Crit modifiers are applied after
|
||
|
|
-- Any other modifiers such as Mortal Strike or Avenging Wrath are applied after everything else
|
||
|
|
|
||
|
|
local function calculateGeneralAmount(level, amount, spellPower, spModifier, healModifier)
|
||
|
|
local penalty = level > 20 and 1 or (1 - ((20 - level) * 0.0375))
|
||
|
3 years ago
|
if isWrath then
|
||
|
|
--https://wowwiki-archive.fandom.com/wiki/Downranking
|
||
|
|
penalty = min(1,max(0,(22+(level+5)-playerLevel)/20))
|
||
|
|
elseif isTBC then
|
||
|
4 years ago
|
-- TBC added another downrank penalty
|
||
|
|
penalty = penalty * min(1, (level + 11) / playerLevel)
|
||
|
|
end
|
||
|
|
spellPower = spellPower * penalty
|
||
|
|
|
||
|
|
return healModifier * (amount + (spellPower * spModifier))
|
||
|
|
end
|
||
|
|
|
||
|
|
local function DirectCoefficient(castTime)
|
||
|
|
return castTime / 3.5
|
||
|
|
end
|
||
|
|
|
||
|
|
local function HotCoefficient(duration)
|
||
|
|
return duration / 15
|
||
|
|
end
|
||
|
|
|
||
|
|
local function avg(a, b)
|
||
|
|
return (a + b) / 2
|
||
|
|
end
|
||
|
|
|
||
|
|
--[[
|
||
|
|
What the different callbacks do:
|
||
|
|
|
||
|
|
AuraHandler: Specific aura tracking needed for this class, who has Beacon up on them and such
|
||
|
|
|
||
|
|
ResetChargeData: Due to spell "queuing" you can't always rely on aura data for buffs that last one or two casts, for example Divine Favor (+100% crit, one spell)
|
||
|
|
if you cast Holy Light and queue Flash of Light the library would still see they have Divine Favor and give them crits on both spells. The reset means that the flag that indicates
|
||
|
|
they have the aura can be killed and if they interrupt the cast then it will call this and let you reset the flags.
|
||
|
|
|
||
|
|
What happens in terms of what the client thinks and what actually is, is something like this:
|
||
|
|
|
||
|
|
UNIT_SPELLCAST_START, Holy Light -> Divine Favor up
|
||
|
|
UNIT_SPELLCAST_SUCCEEDED, Holy Light -> Divine Favor up (But it was really used)
|
||
|
|
UNIT_SPELLCAST_START, Flash of Light -> Divine Favor up (It's not actually up but auras didn't update)
|
||
|
|
UNIT_AURA -> Divine Favor up (Split second where it still thinks it's up)
|
||
|
|
UNIT_AURA -> Divine Favor faded (Client catches up and realizes it's down)
|
||
|
|
|
||
|
|
CalculateHealing: Calculates the healing value, does all the formula calculations talent modifiers and such
|
||
|
|
|
||
|
|
CalculateHotHealing: Used specifically for calculating the heals of hots
|
||
|
|
|
||
|
|
GetHealTargets: Who the heal is going to hit, used for setting extra targets for Beacon of Light + Paladin heal or Prayer of Healing.
|
||
|
|
The returns should either be:
|
||
|
|
|
||
|
|
"compressedGUID1,compressedGUID2,compressedGUID3,compressedGUID4", healthAmount
|
||
|
|
Or if you need to set specific healing values for one GUID it should be
|
||
|
|
"compressedGUID1,healthAmount1,compressedGUID2,healAmount2,compressedGUID3,healAmount3", -1
|
||
|
|
|
||
|
|
The latter is for cases like Glyph of Healing Wave where you need a heal for 1,000 on A and a heal for 200 on the player for B without sending 2 events.
|
||
|
|
The -1 tells the library to look in the GUId list for the heal amounts
|
||
|
|
|
||
|
|
**NOTE** Any GUID returned from GetHealTargets must be compressed through a call to compressGUID[guid]
|
||
|
|
]]
|
||
|
|
|
||
|
|
local CalculateHealing, GetHealTargets, AuraHandler, CalculateHotHealing, ResetChargeData, LoadClassData
|
||
|
|
|
||
|
|
local function getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
|
if spellID == 37563 then
|
||
|
|
spellData = spellData["37563"]
|
||
|
|
else
|
||
|
|
spellData = spellData[spellName]
|
||
|
|
end
|
||
|
|
local average = spellData.averages[spellRank]
|
||
|
|
if type(average) == "number" then
|
||
|
|
return average
|
||
|
|
end
|
||
|
|
local requiresLevel = spellData.levels[spellRank]
|
||
|
|
return average[min(playerLevel - requiresLevel + 1, #average)]
|
||
|
|
end
|
||
|
|
|
||
|
|
if( playerClass == "DRUID" ) then
|
||
|
|
LoadClassData = function()
|
||
|
|
local GiftofNature = GetSpellInfo(17104)
|
||
|
|
local HealingTouch = GetSpellInfo(5185)
|
||
|
|
local ImprovedRejuv = GetSpellInfo(17111)
|
||
|
|
local MarkoftheWild = GetSpellInfo(1126)
|
||
|
|
local Regrowth = GetSpellInfo(8936)
|
||
|
|
local Rejuvenation = GetSpellInfo(774)
|
||
|
|
local Tranquility = GetSpellInfo(740)
|
||
|
|
local Lifebloom = GetSpellInfo(33763) or "Lifebloom"
|
||
|
3 years ago
|
local EmpoweredRejuv = GetSpellInfo(33886) or "Empowered Rejuv"
|
||
|
|
local EmpoweredTouch = GetSpellInfo(33879) or "Empowered Touch"
|
||
|
|
local WildGrowth = GetSpellInfo(48438) or "Wild Growth"
|
||
|
|
local Nourish = GetSpellInfo(50464) or "Nourish"
|
||
|
|
local MasterShapeshifter = GetSpellInfo(48411) or "Master Shapeshifter"
|
||
|
|
local Genesis = GetSpellInfo(57810) or "Genesis"
|
||
|
|
local NaturesSplendor = GetSpellInfo(57865) or "Natures Splendor"
|
||
|
|
local TreeofLife = GetSpellInfo(33891) or "Tree of Life"
|
||
|
|
|
||
|
|
hotData[Regrowth] = { interval = 3, ticks = 7, coeff = (isTBC or isWrath) and 0.7 or 0.5, levels = { 12, 18, 24, 30, 36, 42, 48, 54, 60, 65, 71, 77 }, averages = { 98, 175, 259, 343, 427, 546, 686, 861, 1064, 1274, 1792, 2345 }}
|
||
|
|
if isWrath then
|
||
|
|
hotData[Rejuvenation] = { interval = 3, levels = { 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 60, 63, 69, 75, 80 }, averages = { 40, 70, 145, 225, 305, 380, 485, 610, 760, 945, 1110, 1165, 1325, 1490, 1690 }}
|
||
|
|
hotData[Lifebloom] = {interval = 1, ticks = 7, coeff = 0.66626, dhCoeff = 0.517928287, levels = {64, 72, 80}, averages = {224, 287, 371}, bomb = {480, 616, 776}}
|
||
|
|
hotData[WildGrowth] = {interval = 1, ticks = 7, coeff = 0.8056, levels = {60, 70, 75, 80}, averages = {686, 861, 1239, 1442}}
|
||
|
|
else
|
||
|
|
hotData[Rejuvenation] = { interval = 3, levels = { 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 60, 63, 69 }, averages = { 32, 56, 116, 180, 244, 304, 388, 488, 608, 756, 888, 932, 1060 }}
|
||
|
|
hotData[Lifebloom] = {interval = 1, ticks = 7, coeff = 0.52, dhCoeff = 0.34335, levels = {64}, averages = {273}, bomb = {600}}
|
||
|
|
end
|
||
|
|
if isWrath then
|
||
|
|
spellData[HealingTouch] = { levels = {1, 8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 62, 69, 74, 79}, averages = {
|
||
|
|
{avg(37, 51), avg(37, 52), avg(38, 53), avg(39, 54), avg(40, 55)},
|
||
|
|
{avg(88, 112), avg(89, 114), avg(90, 115), avg(91, 116), avg(93, 118), avg(94, 119)},
|
||
|
|
{avg(195, 243), avg(196, 245), avg(198, 247), avg(200, 249), avg(202, 251), avg(204, 253)},
|
||
|
|
{avg(363, 445), avg(365, 448), avg(368, 451), avg(371, 454), avg(373, 456), avg(376, 459)},
|
||
|
|
{avg(490, 594), avg(493, 597), avg(496, 600), avg(499, 603), avg(502, 606), avg(505, 609)},
|
||
|
|
{avg(636, 766), avg(639, 770), avg(642, 773), avg(646, 777), avg(649, 780), avg(653, 783)},
|
||
|
|
{avg(802, 960), avg(805, 964), avg(809, 968), avg(813, 972), avg(817, 976), avg(821, 980)},
|
||
|
|
{avg(1199, 1427), avg(1203, 1432), avg(1208, 1436), avg(1212, 1441), avg(1217, 1445), avg(1221, 1450)},
|
||
|
|
{avg(1299, 1539), avg(1304, 1545), avg(1309, 1550), avg(1314, 1555), avg(1319, 1560), avg(1324, 1565)},
|
||
|
|
{avg(1620, 1912), avg(1625, 1918), avg(1631, 1924), avg(1637, 1930), avg(1642, 1935), avg(1648, 1941)},
|
||
|
|
{avg(1944, 2294), avg(1950, 2301), avg(1956, 2307), avg(1962, 2313), avg(1968, 2319), avg(1975, 2325)},
|
||
|
|
{avg(2026, 2392), avg(2032, 2399), avg(2038, 2405), avg(2044, 2411), avg(2051, 2418), avg(2057, 2424)},
|
||
|
|
{avg(2321, 2739), avg(2328, 2746), avg(2335, 2753), avg(2342, 2760), avg(2349, 2767)},
|
||
|
|
{avg(3223, 3805), avg(3232, 3815), avg(3242, 3825), avg(3252, 3835), avg(3262, 3845)},
|
||
|
|
{avg(3750, 4428), avg(3761, 4440)} }}
|
||
|
|
else
|
||
|
|
spellData[HealingTouch] = { levels = {1, 8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 62, 69}, averages = {
|
||
|
|
{avg(37, 51), avg(37, 52), avg(38, 53), avg(39, 54), avg(40, 55)},
|
||
|
|
{avg(88, 112), avg(89, 114), avg(90, 115), avg(91, 116), avg(93, 118), avg(94, 119)},
|
||
|
|
{avg(195, 243), avg(196, 245), avg(198, 247), avg(200, 249), avg(202, 251), avg(204, 253)},
|
||
|
|
{avg(363, 445), avg(365, 448), avg(368, 451), avg(371, 454), avg(373, 456), avg(376, 459)},
|
||
|
|
{avg(572, 694), avg(575, 698), avg(579, 701), avg(582, 705), avg(586, 708), avg(589, 712)},
|
||
|
|
{avg(742, 894), avg(746, 898), avg(750, 902), avg(754, 906), avg(758, 910), avg(762, 914)},
|
||
|
|
{avg(936, 1120), avg(940, 1125), avg(945, 1129), avg(949, 1134), avg(954, 1138), avg(958, 1143)},
|
||
|
|
{avg(1199, 1427), avg(1204, 1433), avg(1209, 1438), avg(1214, 1443), avg(1219, 1448), avg(1225, 1453)},
|
||
|
|
{avg(1516, 1796), avg(1521, 1802), avg(1527, 1808), avg(1533, 1814), avg(1539, 1820), avg(1545, 1826)},
|
||
|
|
{avg(1890, 2230), avg(1896, 2237), avg(1903, 2244), avg(1909, 2250), avg(1916, 2257), avg(1923, 2263)},
|
||
|
|
{avg(2267, 2677), avg(2274, 2685), avg(2281, 2692), avg(2288, 2699), avg(2296, 2707), avg(2303, 2714)},
|
||
|
|
{avg(2364, 2790), avg(2371, 2798), avg(2378, 2805), avg(2386, 2813), avg(2393, 2820), avg(2401, 2827)},
|
||
|
|
{avg(2707, 3197), avg(2715, 3206)} }}
|
||
|
|
end
|
||
|
|
spellData[Regrowth] = {coeff = (isWrath and 0.6 or 0.5) * (2 / 3.5) , levels = hotData[Regrowth].levels, averages = {
|
||
|
4 years ago
|
{avg(84, 98), avg(85, 100), avg(87, 102), avg(89, 104), avg(91, 106), avg(93, 107)},
|
||
|
|
{avg(164, 188), avg(166, 191), avg(169, 193), avg(171, 196), avg(174, 198), avg(176, 201)},
|
||
|
|
{avg(240, 274), avg(243, 278), avg(246, 281), avg(249, 284), avg(252, 287), avg(255, 290)},
|
||
|
|
{avg(318, 360), avg(321, 364), avg(325, 368), avg(328, 371), avg(332, 375), avg(336, 378)},
|
||
|
|
{avg(405, 457), avg(409, 462), avg(413, 466), avg(417, 470), avg(421, 474), avg(425, 478)},
|
||
|
|
{avg(511, 575), avg(515, 580), avg(520, 585), avg(525, 590), avg(529, 594), avg(534, 599)},
|
||
|
|
{avg(646, 724), avg(651, 730), avg(656, 735), avg(661, 740), avg(667, 746), avg(672, 751)},
|
||
|
|
{avg(809, 905), avg(815, 911), avg(821, 917), avg(827, 923), avg(833, 929), avg(839, 935)},
|
||
|
|
{avg(1003, 1119), avg(1009, 1126), avg(1016, 1133), avg(1023, 1140), avg(1030, 1147), avg(1037, 1153)},
|
||
|
3 years ago
|
{avg(1215, 1355), avg(1222, 1363), avg(1230, 1371), avg(1238, 1379), avg(1245, 1386), avg(1253, 1394)},
|
||
|
|
{avg(1710, 1908), avg(1720, 1919), avg(1731, 1930), avg(1742, 1941), avg(1753, 1952), avg(1764, 1962)},
|
||
|
|
{avg(2234, 2494), avg(2241, 2502), avg(2249, 2510), avg(2257, 2518)} }}
|
||
|
|
if isTBC or isWrath then
|
||
|
|
spellData[Tranquility] = {_isChanneled = true, coeff = 1.145, ticks = 4, interval = 2, levels = {30, 40, 50, 60, 70, 75, 80}, averages = {
|
||
|
4 years ago
|
{351 * 4, 354 * 4, 356 * 4, 358 * 4, 360 * 4, 362 * 4, 365 * 4},
|
||
|
|
{515 * 4, 518 * 4, 521 * 4, 523 * 4, 526 * 4, 528 * 4, 531 * 4},
|
||
|
|
{765 * 4, 769 * 4, 772 * 4, 776 * 4, 779 * 4, 782 * 4, 786 * 4},
|
||
|
|
{1097 * 4, 1101 * 4, 1105 * 4, 1109 * 4, 1112 * 4, 1116 * 4, 1120 * 4},
|
||
|
3 years ago
|
{1518 * 4, 1523 * 4, 1527 * 4, 1532 * 4, 1536 * 4},
|
||
|
|
{2598 * 4, 2606 * 4, 2614 * 4, 2622 * 4, 2629 * 4},
|
||
|
|
{3035 * 4} }}
|
||
|
4 years ago
|
else
|
||
|
3 years ago
|
spellData[Tranquility] = {_isChanneled = true, coeff = 1/3, ticks = 5, interval = 2, levels = {30, 40, 50, 60}, averages = {
|
||
|
4 years ago
|
{94 * 5, 95 * 5, 96 * 5, 96 * 5, 97 * 5, 97 * 5, 98 * 5},
|
||
|
|
{138 * 5, 139 * 5, 140 * 5, 141 * 5, 141 * 5, 142 * 5, 143 * 5},
|
||
|
|
{205 * 5, 206 * 5, 207 * 5, 208 * 5, 209 * 5, 210 * 5, 211 * 5},
|
||
|
|
{294 * 5} }}
|
||
|
|
end
|
||
|
3 years ago
|
spellData[Nourish] = {coeff = 0.358005, levels = {80}, averages = {avg(1883, 2187)}}
|
||
|
4 years ago
|
|
||
|
|
talentData[GiftofNature] = {mod = 0.02, current = 0}
|
||
|
|
talentData[ImprovedRejuv] = {mod = 0.05, current = 0}
|
||
|
|
talentData[EmpoweredRejuv] = {mod = 0.04, current = 0}
|
||
|
|
talentData[EmpoweredTouch] = {mod = 0.1, current = 0}
|
||
|
3 years ago
|
talentData[Genesis] = {mod = 0.01, current = 0}
|
||
|
|
talentData[NaturesSplendor] = {mod = 1, current = 0}
|
||
|
|
talentData[MasterShapeshifter] = {mod = 0.02, current = 0}
|
||
|
4 years ago
|
|
||
|
|
itemSetsData["Stormrage"] = {16903, 16898, 16904, 16897, 16900, 16899, 16901, 16902}
|
||
|
|
itemSetsData["Nordrassil"] = {30216, 30217, 30219, 30220, 30221}
|
||
|
|
itemSetsData["Thunderheart"] = {31041, 31032, 31037, 31045, 31047, 34571, 34445, 34554}
|
||
|
3 years ago
|
itemSetsData["Dreamwalker"] = {40460, 40461, 40462, 40463, 40465, 39531, 39538, 39539, 39542, 39543}
|
||
|
|
itemSetsData["Lasherweave"] = {50106, 50107, 50108, 50109, 50113, 51139, 51138, 51137, 51136, 51135, 51300, 51301, 51302, 51303, 51304}
|
||
|
|
|
||
|
|
local bloomBombIdols = {[28355] = 87, [33076] = 105, [33841] = 116, [35021] = 131, [42576] = 188, [42577] = 217, [42578] = 246, [42579] = 294, [42580] = 376, [51423] = 448}
|
||
|
|
local rejuIdols = {[186054] = 15, [22398] = 50, [25643] = 86, [38366] = 33}
|
||
|
|
local bloomIdols = {[40711] = 125, [27886] = 47}
|
||
|
|
|
||
|
|
local hotTotals, hasRegrowth = {}, {}
|
||
|
|
AuraHandler = function(unit, guid)
|
||
|
|
hotTotals[guid] = 0
|
||
|
|
if( unitHasAura(unit, Rejuvenation) ) then hotTotals[guid] = hotTotals[guid] + 1 end
|
||
|
|
if( unitHasAura(unit, Lifebloom) ) then hotTotals[guid] = hotTotals[guid] + 1 end
|
||
|
|
if( unitHasAura(unit, WildGrowth) ) then hotTotals[guid] = hotTotals[guid] + 1 end
|
||
|
|
if( unitHasAura(unit, Regrowth) ) then
|
||
|
|
hasRegrowth[guid] = true
|
||
|
|
hotTotals[guid] = hotTotals[guid] + 1
|
||
|
|
else
|
||
|
|
hasRegrowth[guid] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
3 years ago
|
GetHealTargets = function(bitType, guid, spellID)
|
||
|
4 years ago
|
-- Tranquility pulses on everyone within 30 yards, if they are in range of Mark of the Wild they'll get Tranquility
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
if( spellName == Tranquility ) then
|
||
|
|
local targets = compressGUID[playerGUID]
|
||
|
|
local playerGroup = guidToGroup[playerGUID]
|
||
|
|
|
||
|
|
for groupGUID, id in pairs(guidToGroup) do
|
||
|
3 years ago
|
if( id == playerGroup and playerGUID ~= groupGUID and not UnitHasVehicleUI(guidToUnit[groupID]) and IsSpellInRange(MarkoftheWild, guidToUnit[groupGUID]) == 1 ) then
|
||
|
4 years ago
|
targets = targets .. "," .. compressGUID[groupGUID]
|
||
|
|
end
|
||
|
|
end
|
||
|
3 years ago
|
return targets
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
return compressGUID[guid]
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
-- Calculate hot heals
|
||
|
3 years ago
|
local wgTicks = {}
|
||
|
4 years ago
|
CalculateHotHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(hotData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
local bombAmount, totalTicks
|
||
|
|
|
||
|
|
-- Gift of Nature
|
||
|
3 years ago
|
if isTBC or isWrath then
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[GiftofNature].current)
|
||
|
|
else
|
||
|
|
-- Gift of Nature does only apply to base values in classic
|
||
|
3 years ago
|
healAmount = healAmount + talentData[GiftofNature].current
|
||
|
|
end
|
||
|
|
|
||
|
|
if( unitHasAura("player", TreeofLife) ) then
|
||
|
|
-- 32387 - Idol of the Raven Godess, +44 SP while in TOL
|
||
|
|
if( playerCurrentRelic == 32387 ) then
|
||
|
|
spellPower = spellPower + 44
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
-- Rejuvenation
|
||
|
|
if( spellName == Rejuvenation ) then
|
||
|
3 years ago
|
if isTBC or isWrath then
|
||
|
|
healModifier = healModifier + talentData[ImprovedRejuv].current
|
||
|
4 years ago
|
else
|
||
|
|
-- Improved Rejuvenation only applies to base values in classic
|
||
|
|
healAmount = healAmount * (1 + talentData[ImprovedRejuv].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( playerCurrentRelic and rejuIdols[playerCurrentRelic] ) then
|
||
|
|
spellPower = spellPower + rejuIdols[playerCurrentRelic]
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
local duration = isWrath and 15 or 12
|
||
|
4 years ago
|
local ticks = duration / hotData[spellName].interval
|
||
|
|
|
||
|
|
if( equippedSetCache["Stormrage"] >= 8 ) then
|
||
|
|
healAmount = healAmount + (healAmount / ticks) -- Add Tick Amount Gained by Set.
|
||
|
3 years ago
|
duration = duration + 3
|
||
|
4 years ago
|
ticks = ticks + 1
|
||
|
|
end
|
||
|
|
|
||
|
|
totalTicks = ticks
|
||
|
3 years ago
|
|
||
|
3 years ago
|
spellPower = spellPower * (((duration / 15) * (isWrath and 1.88 or 1)) * (1 + talentData[EmpoweredRejuv].current))
|
||
|
4 years ago
|
spellPower = spellPower / ticks
|
||
|
|
healAmount = healAmount / ticks
|
||
|
|
elseif( spellName == Regrowth ) then
|
||
|
3 years ago
|
spellPower = spellPower * hotData[spellName].coeff * (isWrath and 1.88 or 1 ) * (1 + talentData[EmpoweredRejuv].current)
|
||
|
4 years ago
|
spellPower = spellPower / hotData[spellName].ticks
|
||
|
|
healAmount = healAmount / hotData[spellName].ticks
|
||
|
|
|
||
|
|
totalTicks = 7
|
||
|
|
|
||
|
3 years ago
|
if( talentData[NaturesSplendor].current >= 1 ) then totalTicks = totalTicks + 2 end
|
||
|
4 years ago
|
if( equippedSetCache["Nordrassil"] >= 2 ) then totalTicks = totalTicks + 2 end
|
||
|
|
|
||
|
|
elseif( spellName == Lifebloom ) then
|
||
|
|
-- Figure out the bomb heal, apparently Gift of Nature double dips and will heal 10% for the HOT + 10% again for the direct heal
|
||
|
|
local bombSpellPower = spellPower
|
||
|
|
if( playerCurrentRelic and bloomBombIdols[playerCurrentRelic] ) then
|
||
|
|
bombSpellPower = bombSpellPower + bloomBombIdols[playerCurrentRelic]
|
||
|
|
end
|
||
|
|
|
||
|
|
local bombSpell = bombSpellPower * hotData[spellName].dhCoeff * (1 + talentData[EmpoweredRejuv].current)
|
||
|
|
bombAmount = ceil(calculateGeneralAmount(hotData[spellName].levels[spellRank], hotData[spellName].bomb[spellRank], bombSpell, spModifier, healModifier))
|
||
|
|
|
||
|
|
-- Figure out the hot tick healing
|
||
|
|
spellPower = spellPower * (hotData[spellName].coeff * (1 + talentData[EmpoweredRejuv].current))
|
||
|
3 years ago
|
spellPower = spellPower / hotData[spellName].ticks
|
||
|
|
healAmount = healAmount / hotData[spellName].ticks
|
||
|
|
-- Figure out total ticks
|
||
|
|
totalTicks = 7
|
||
|
4 years ago
|
|
||
|
3 years ago
|
if( playerCurrentRelic and bloomIdols[playerCurrentRelic] ) then
|
||
|
|
spellPower = spellPower + bloomIdols[playerCurrentRelic]
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
-- Glyph of Lifebloom, +1 second
|
||
|
|
if( glyphCache[54826] ) then totalTicks = totalTicks + 1 end
|
||
|
|
-- Nature's Splendor, +2 seconds
|
||
|
|
if( talentData[NaturesSplendor].current >= 1 ) then totalTicks = totalTicks + 2 end
|
||
|
|
|
||
|
|
-- Wild Growth
|
||
|
|
elseif( spellName == WildGrowth ) then
|
||
|
|
spellPower = spellPower * (hotData[spellName].coeff * (1 + talentData[EmpoweredRejuv].current))
|
||
|
4 years ago
|
spellPower = spellPower / hotData[spellName].ticks
|
||
|
|
healAmount = healAmount / hotData[spellName].ticks
|
||
|
3 years ago
|
|
||
|
3 years ago
|
healModifier = healModifier + talentData[Genesis].current
|
||
|
|
|
||
|
|
table.wipe(wgTicks)
|
||
|
|
local tickModifier = equippedSetCache["Lasherweave"] >= 2 and 0.70 or 1
|
||
|
|
local tickAmount = healAmount / hotData[spellName].ticks
|
||
|
|
for i=1, hotData[spellName].ticks do
|
||
|
|
table.insert(wgTicks, math.ceil(healModifier * ((healAmount + tickAmount * (3 - (i - 1) * tickModifier)) + (spellPower * spModifier))))
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
3 years ago
|
if( isWrath and unitHasAura("player", MasterShapeshifter) ) then
|
||
|
|
healModifier = healModifier * (1 + talentData[MasterShapeshifter].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
return HOT_HEALS, wgTicks, hotData[spellName].ticks, hotData[spellName].interval, nil, true
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
healModifier = healModifier + talentData[Genesis].current
|
||
|
4 years ago
|
|
||
|
3 years ago
|
if( isWrath and unitHasAura("player", MasterShapeshifter) ) then
|
||
|
|
healModifier = healModifier * (1 + talentData[MasterShapeshifter].current)
|
||
|
|
if bombAmount then
|
||
|
|
bombAmount = bombAmount * (1 + talentData[MasterShapeshifter].current)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
healAmount = calculateGeneralAmount(hotData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
return HOT_HEALS, ceil(max(0, healAmount)), totalTicks, hotData[spellName].interval, bombAmount
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
-- Calcualte direct and channeled heals
|
||
|
|
CalculateHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
|
||
|
|
-- Gift of Nature
|
||
|
3 years ago
|
if isTBC or isWrath then
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[GiftofNature].current)
|
||
|
|
else
|
||
|
|
-- Gift of Nature does only apply to base values in classic
|
||
|
|
healAmount = healAmount * (1 + talentData[GiftofNature].current)
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
if( isWrath and unitHasAura("player", MasterShapeshifter) ) then
|
||
|
|
healModifier = healModifier * ( 1 + talentData[MasterShapeshifter].current)
|
||
|
|
-- 32387 - Idol of the Raven Godess, +44 SP while in TOL
|
||
|
|
if( playerCurrentRelic == 32387 ) then
|
||
|
|
spellPower = spellPower + 44
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
-- Regrowth
|
||
|
|
if( spellName == Regrowth ) then
|
||
|
3 years ago
|
-- Glyph of Regrowth - +20% if target has Regrowth
|
||
|
|
if( glyphCache[54743] and hasRegrowth[guid] ) then
|
||
|
|
healModifier = healModifier * 1.20
|
||
|
|
end
|
||
|
|
spellPower = spellPower * spellData[spellName].coeff * (isWrath and 1.88 or 1)
|
||
|
|
-- Nourish
|
||
|
|
elseif( spellName == Nourish ) then
|
||
|
|
-- 46138 - Idol of Flourishing Life, +187 Nourish SP
|
||
|
|
if( playerCurrentRelic == 46138 ) then
|
||
|
|
spellPower = spellPower + 187
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Apply any hot specific bonuses
|
||
|
|
local hots = hotTotals[guid]
|
||
|
|
if( hots and hots > 0 ) then
|
||
|
|
local bonus = 1.20
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- T7 Resto, +5% healing per each of the players hot on their target
|
||
|
|
if( equippedSetCache["Dreamwalker"] >= 2 ) then
|
||
|
|
bonus = bonus + 0.05 * hots
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Glyph of Nourish - 6% per HoT
|
||
|
|
if( glyphCache[62971] ) then
|
||
|
|
bonus = bonus + 0.06 * hots
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
healModifier = healModifier * bonus
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
spellPower = spellPower * ((spellData[spellName].coeff * 1.88) + talentData[EmpoweredTouch].current)
|
||
|
|
-- Healing Touch
|
||
|
4 years ago
|
elseif( spellName == HealingTouch ) then
|
||
|
|
|
||
|
3 years ago
|
local castTime
|
||
|
|
if isWrath then
|
||
|
|
castTime = spellRank > 3 and 3 or spellRank == 3 and 2.5 or spellRank == 2 and 2 or 1.5
|
||
|
|
else
|
||
|
|
castTime = spellRank >= 5 and 3.5 or (spellRank == 4 and 3 or (spellRank == 3 and 2.5 or (spellRank == 2 and 2 or 1.5)))
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
3 years ago
|
-- Glyph of Healing Touch
|
||
|
|
if( glyphCache[54825] ) then
|
||
|
|
healAmount = healAmount / 2
|
||
|
|
castTime = max(castTime - 1.5,1.5)
|
||
|
|
end
|
||
|
|
|
||
|
|
spellPower = spellPower * (((castTime / 3.5) * (isWrath and 1.88 or 1)) + (talentData[EmpoweredTouch].current * (isWrath and 2 or 1)))
|
||
|
4 years ago
|
|
||
|
|
if( playerCurrentRelic == 22399 ) then
|
||
|
|
healAmount = healAmount + 100
|
||
|
|
elseif( playerCurrentRelic == 28568 ) then
|
||
|
|
healAmount = healAmount + 136
|
||
|
|
end
|
||
|
|
|
||
|
|
if equippedSetCache["Thunderheart"] >= 4 then
|
||
|
3 years ago
|
healModifier = healModifier * 1.05
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
-- Tranquility
|
||
|
|
elseif( spellName == Tranquility ) then
|
||
|
3 years ago
|
healModifier = healModifier + talentData[Genesis].current
|
||
|
|
|
||
|
|
spellPower = spellPower * spellData[spellName].coeff * (isWrath and 1.88 or 1) * (1 + talentData[EmpoweredRejuv].current)
|
||
|
4 years ago
|
spellPower = spellPower / spellData[spellName].ticks
|
||
|
|
healAmount = healAmount / spellData[spellName].ticks
|
||
|
|
end
|
||
|
|
|
||
|
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
|
||
|
|
-- 100% chance to crit with Nature, this mostly just covers fights like Loatheb where you will basically have 100% crit
|
||
|
|
if( GetSpellCritChance(4) >= 100 ) then
|
||
|
|
healAmount = healAmount * 1.50
|
||
|
|
end
|
||
|
|
|
||
|
|
if( spellData[spellName].ticks ) then
|
||
|
|
return CHANNEL_HEALS, ceil(healAmount), spellData[spellName].ticks, spellData[spellName].interval
|
||
|
|
end
|
||
|
|
|
||
|
|
return DIRECT_HEALS, ceil(healAmount)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
local hasDivineFavor, activeBeaconGUID
|
||
|
4 years ago
|
|
||
|
|
if( playerClass == "PALADIN" ) then
|
||
|
|
LoadClassData = function()
|
||
|
|
local DivineFavor = GetSpellInfo(20216)
|
||
|
|
local FlashofLight = GetSpellInfo(19750)
|
||
|
|
local HealingLight = GetSpellInfo(20237)
|
||
|
|
local HolyLight = GetSpellInfo(635)
|
||
|
3 years ago
|
local Divinity = GetSpellInfo(63646) or "Divinity"
|
||
|
|
local TouchedbytheLight = GetSpellInfo(53592) or "TouchedbytheLight"
|
||
|
|
local BeaconofLight = GetSpellInfo(53563) or "BeaconofLight"
|
||
|
|
local SealofLight = GetSpellInfo(20165) or "SealofLight"
|
||
|
|
local DivineIllumination = GetSpellInfo(31842) or "DivineIllumination"
|
||
|
|
|
||
|
|
if isWrath then
|
||
|
|
spellData[HolyLight] = { coeff = 2.5 / 3.5, levels = {1, 6, 14, 22, 30, 38, 46, 54, 60, 62, 70, 75, 80}, averages = {
|
||
|
|
{avg(50, 60), avg(50, 61), avg(51, 62), avg(52, 63), avg(53, 64)},
|
||
|
|
{avg(96, 116), avg(97, 118), avg(98, 119), avg(99, 120), avg(100, 121), avg(101, 122)},
|
||
|
|
{avg(203, 239), avg(204, 241), avg(206, 243), avg(208, 245), avg(209, 246), avg(211, 248)},
|
||
|
|
{avg(397, 455), avg(399, 458), avg(401, 460), avg(404, 463), avg(406, 465), avg(409, 467)},
|
||
|
|
{avg(628, 708), avg(631, 712), avg(634, 715), avg(637, 718), avg(640, 721), avg(643, 724)},
|
||
|
|
{avg(894, 998), avg(897, 1002), avg(901, 1006), avg(905, 1010), avg(909, 1014), avg(913, 1017)},
|
||
|
|
{avg(1209, 1349), avg(1213, 1354), avg(1218, 1359), avg(1222, 1363), avg(1227, 1368), avg(1232, 1372)},
|
||
|
|
{avg(1595, 1777), avg(1600, 1783), avg(1605, 1788), avg(1610, 1793), avg(1615, 1798), avg(1621, 1803)},
|
||
|
|
{avg(2034, 2266), avg(2039, 2272), avg(2045, 2278), avg(2051, 2284), avg(2057, 2290), avg(2063, 2295)},
|
||
|
|
{avg(2232, 2486), avg(2238, 2493), avg(2244, 2499), avg(2251, 2506), avg(2257, 2512), avg(2264, 2518)},
|
||
|
|
{avg(2818, 3138), avg(2825, 3145), avg(2832, 3152), avg(2839, 3159), avg(2846, 3166)},
|
||
|
|
{avg(4199, 4677), avg(4209, 4688), avg(4219, 4698), avg(4230, 4709), avg(4240, 4719)},
|
||
|
|
{avg(4888, 5444)} }}
|
||
|
|
spellData[FlashofLight] = { coeff = 1.5 / 3.5, levels = {20, 26, 34, 42, 50, 58, 66, 74, 79}, averages = {
|
||
|
|
{avg(81, 93), avg(82, 94), avg(83, 95), avg(84, 96), avg(85, 97), avg(86, 98)},
|
||
|
|
{avg(124, 144), avg(125, 146), avg(126, 147), avg(127, 148), avg(129, 150), avg(130, 151)},
|
||
|
|
{avg(189, 211), avg(190, 213), avg(192, 215), avg(193, 216), avg(195, 218), avg(197, 219)},
|
||
|
|
{avg(256, 288), avg(257, 290), avg(259, 292), avg(261, 294), avg(263, 296), avg(265, 298)},
|
||
|
|
{avg(346, 390), avg(348, 393), avg(350, 395), avg(352, 397), avg(354, 399), avg(357, 401)},
|
||
|
|
{avg(445, 499), avg(447, 502), avg(450, 505), avg(452, 507), avg(455, 510), avg(458, 512)},
|
||
|
|
{avg(588, 658), avg(591, 661), avg(594, 664), avg(597, 667), avg(600, 670)},
|
||
|
|
{avg(682, 764), avg(685, 768), avg(688, 771), avg(692, 775), avg(695, 778)},
|
||
|
|
{avg(785,879), avg(788,883)} }}
|
||
|
|
else
|
||
|
|
spellData[HolyLight] = { coeff = 2.5 / 3.5, levels = {1, 6, 14, 22, 30, 38, 46, 54, 60, 62, 70}, averages = {
|
||
|
|
{avg(39, 47), avg(39, 48), avg(40, 49), avg(41, 50), avg(42, 51)},
|
||
|
|
{avg(76, 90), avg(77, 92), avg(78, 93), avg(79, 94), avg(80, 95), avg(81, 96)},
|
||
|
|
{avg(159, 187), avg(160, 189), avg(162, 191), avg(164, 193), avg(165, 194), avg(167, 196)},
|
||
|
|
{avg(310, 356), avg(312, 359), avg(314, 361), avg(317, 364), avg(319, 366), avg(322, 368)},
|
||
|
|
{avg(491, 553), avg(494, 557), avg(497, 560), avg(500, 563), avg(503, 566), avg(506, 569)},
|
||
|
|
{avg(698, 780), avg(701, 784), avg(705, 788), avg(709, 792), avg(713, 796), avg(717, 799)},
|
||
|
|
{avg(945, 1053), avg(949, 1058), avg(954, 1063), avg(958, 1067), avg(963, 1072), avg(968, 1076)},
|
||
|
|
{avg(1246, 1388), avg(1251, 1394), avg(1256, 1399), avg(1261, 1404), avg(1266, 1409), avg(1272, 1414)},
|
||
|
|
{avg(1590, 1770), avg(1595, 1775), avg(1601, 1781), avg(1607, 1787), avg(1613, 1793), avg(1619, 1799)},
|
||
|
|
{avg(1741, 1939), avg(1747, 1946), avg(1753, 1952), avg(1760, 1959), avg(1766, 1965), avg(1773, 1971)},
|
||
|
|
{avg(2196, 2446)} }}
|
||
|
|
spellData[FlashofLight] = { coeff = 1.5 / 3.5, levels = {20, 26, 34, 42, 50, 58, 66}, averages = {
|
||
|
|
{avg(62, 72), avg(63, 73), avg(64, 74), avg(65, 75), avg(66, 76), avg(67, 77)},
|
||
|
|
{avg(96, 110), avg(97, 112), avg(98, 113), avg(99, 114), avg(101, 116), avg(102, 117)},
|
||
|
|
{avg(145, 163), avg(146, 165), avg(148, 167), avg(149, 168), avg(151, 170), avg(153, 171)},
|
||
|
|
{avg(197, 221), avg(198, 223), avg(200, 225), avg(202, 227), avg(204, 229), avg(206, 231)},
|
||
|
|
{avg(267, 299), avg(269, 302), avg(271, 304), avg(273, 306), avg(275, 308), avg(278, 310)},
|
||
|
|
{avg(343, 383), avg(345, 386), avg(348, 389), avg(350, 391), avg(353, 394), avg(356, 396)},
|
||
|
|
{avg(448, 502), avg(450, 505), avg(453, 508), avg(455, 510), avg(458, 513)} }}
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
|
talentData[HealingLight] = { mod = 0.04, current = 0 }
|
||
|
3 years ago
|
talentData[Divinity] = { mod = 0.01, current = 0 }
|
||
|
|
talentData[TouchedbytheLight] = {mod = 0.10, current = 0}
|
||
|
4 years ago
|
|
||
|
|
itemSetsData["Lightbringer"] = {30992, 30983, 30988, 30994, 30996, 34432, 34487, 34559}
|
||
|
3 years ago
|
itemSetsData["Lightsworn"] = {50865, 50866, 50867, 50868, 50869, 51270, 51271, 51272, 51273, 51274, 51165, 51166, 51167, 51168, 51169}
|
||
|
4 years ago
|
|
||
|
3 years ago
|
local flashLibrams
|
||
|
|
if isWrath then
|
||
|
|
flashLibrams = {[51472] = 510, [42616] = 436, [42615] = 375, [42614] = 331, [42613] = 293, [42612] = 204, [28592] = 89, [25644] = 79, [23006] = 43, [23201] = 28}
|
||
|
|
else
|
||
|
|
flashLibrams = {[23006] = 83, [23201] = 53, [186065] = 10, [25644] = 79}
|
||
|
|
end
|
||
|
|
local holyLibrams = {[45436] = 160, [40268] = 141, [28296] = 47}
|
||
|
4 years ago
|
|
||
|
|
local blessings = {
|
||
|
|
[19977] = {
|
||
|
|
[HolyLight] = 210,
|
||
|
|
[FlashofLight] = 60,
|
||
|
|
},
|
||
|
|
[19978] = {
|
||
|
|
[HolyLight] = 300,
|
||
|
|
[FlashofLight] = 85,
|
||
|
|
},
|
||
|
|
[19979] = {
|
||
|
|
[HolyLight] = 400,
|
||
|
|
[FlashofLight] = 115,
|
||
|
|
},
|
||
|
|
[25890] = {
|
||
|
|
[HolyLight] = 400,
|
||
|
|
[FlashofLight] = 115,
|
||
|
|
},
|
||
|
|
[27144] = {
|
||
|
|
[HolyLight] = 580,
|
||
|
|
[FlashofLight] = 185,
|
||
|
|
},
|
||
|
|
[27145] = {
|
||
|
|
[HolyLight] = 580,
|
||
|
|
[FlashofLight] = 185,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
AuraHandler = function(unit, guid)
|
||
|
3 years ago
|
if( unitHasAura(unit, BeaconofLight) ) then
|
||
|
|
activeBeaconGUID = guid
|
||
|
|
elseif( activeBeaconGUID == guid ) then
|
||
|
|
activeBeaconGUID = nil
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
if( unit == "player" ) then
|
||
|
|
hasDivineFavor = unitHasAura("player", DivineFavor)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
GetHealTargets = function(bitType, guid, spellID)
|
||
|
|
if( activeBeaconGUID and activeBeaconGUID ~= guid and guidToUnit[activeBeaconGUID] and UnitIsVisible(guidToUnit[activeBeaconGUID]) ) then
|
||
|
|
return string.format("%s,%s", compressGUID[guid], compressGUID[activeBeaconGUID])
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
return compressGUID[guid]
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
CalculateHealing = function(guid, spellID, unit)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
|
||
|
3 years ago
|
if( glyphCache[54943] and unitHasAura("player", SealofLight) ) then
|
||
|
|
healModifier = healModifier * 1.05
|
||
|
|
end
|
||
|
|
|
||
|
|
if isTBC or isWrath then
|
||
|
|
healModifier = healModifier * (1 + talentData[HealingLight].current) * (1 + (talentData[Divinity].current * (UnitIsUnit(unit,"player") and 2 or 1)))
|
||
|
4 years ago
|
else
|
||
|
|
healAmount = healAmount * (1 + talentData[HealingLight].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
if playerCurrentRelic then
|
||
|
3 years ago
|
if( spellName == HolyLight and holyLibrams[playerCurrentRelic] ) then
|
||
|
|
spellPower = spellPower + holyLibrams[playerCurrentRelic]
|
||
|
|
elseif( spellName == FlashofLight and flashLibrams[playerCurrentRelic] ) then
|
||
|
4 years ago
|
spellPower = spellPower + flashLibrams[playerCurrentRelic]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
if( equippedSetCache["Lightsworn"] >= 2 and unitHasAura("player", DivineIllumination) ) then
|
||
|
|
healModifier = healModifier * 1.35
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
if( equippedSetCache["Lightbringer"] >= 4 and spellName == FlashofLight ) then healModifier = healModifier + 0.05 end
|
||
|
|
|
||
|
3 years ago
|
spellPower = spellPower * spellData[spellName].coeff * (isWrath and 2.35 or 1)
|
||
|
4 years ago
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
|
||
|
|
for auraID, values in pairs(blessings) do
|
||
|
|
if unitHasAura(unit, auraID) then
|
||
|
|
if playerCurrentRelic == 28592 then
|
||
|
|
if spellName == FlashofLight then
|
||
|
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, values[spellName] + 60, healModifier, 1)
|
||
|
|
else
|
||
|
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, values[spellName] + 120, healModifier, 1)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, values[spellName], healModifier, 1)
|
||
|
|
end
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( hasDivineFavor or GetSpellCritChance(2) >= 100 ) then
|
||
|
3 years ago
|
healAmount = healAmount * (1.50 * (1 + talentData[TouchedbytheLight].current))
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
return DIRECT_HEALS, ceil(healAmount)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( playerClass == "PRIEST" ) then
|
||
|
|
LoadClassData = function()
|
||
|
|
local Renew = GetSpellInfo(139)
|
||
|
|
local GreaterHeal = GetSpellInfo(2060)
|
||
|
|
local PrayerofHealing = GetSpellInfo(596)
|
||
|
|
local FlashHeal = GetSpellInfo(2061)
|
||
|
|
local Heal = GetSpellInfo(2054)
|
||
|
|
local LesserHeal = GetSpellInfo(2050)
|
||
|
|
local SpiritualHealing = GetSpellInfo(14898)
|
||
|
|
local ImprovedRenew = GetSpellInfo(14908)
|
||
|
|
local GreaterHealHot = GetSpellInfo(22009)
|
||
|
3 years ago
|
local DispelMagic = GetSpellInfo(527)
|
||
|
4 years ago
|
local BindingHeal = GetSpellInfo(32546) or "Binding Heal"
|
||
|
|
local EmpoweredHealing = GetSpellInfo(33158) or "Empowered Healing"
|
||
|
|
local Renewal = GetSpellInfo(37563) and "37563" -- T4 bonus
|
||
|
3 years ago
|
local Penance = GetSpellInfo(53007) or "Penance"
|
||
|
|
local Grace = GetSpellInfo(47517) or "Grace"
|
||
|
|
local BlessedResilience = GetSpellInfo(33142) or "Blessed Resilience"
|
||
|
|
local FocusedPower = GetSpellInfo(33190) or "Focused Power"
|
||
|
|
local DivineProvidence = GetSpellInfo(47567) or "Divine Providence"
|
||
|
|
local EmpoweredRenew = GetSpellInfo(63534) or "Empowered Renew"
|
||
|
|
local TwinDisciplines = GetSpellInfo(47586) or "Twin Disciplines"
|
||
|
|
|
||
|
|
hotData[Renew] = {coeff = 1, interval = 3, ticks = 5, levels = {8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 65, 70, 75, 80}, averages = {
|
||
|
|
45, 100, 175, 245, 315, 400, 510, 650, 810, 970, 1010, 1110, 1235, 1400 }}
|
||
|
4 years ago
|
hotData[GreaterHealHot] = hotData[Renew]
|
||
|
|
|
||
|
|
if Renewal then
|
||
|
|
hotData[Renewal] = {coeff = 0, interval = 3, ticks = 3, levels = {70}, averages = {150}}
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
spellData[FlashHeal] = {coeff = 1.5 / 3.5, levels = {20, 26, 32, 38, 44, 50, 56, 61, 67, 73, 79}, averages = {
|
||
|
4 years ago
|
{avg(193, 237), avg(194, 239), avg(196, 241), avg(198, 243), avg(200, 245), avg(202, 247)},
|
||
|
|
{avg(258, 314), avg(260, 317), avg(262, 319), avg(264, 321), avg(266, 323), avg(269, 325)},
|
||
|
|
{avg(327, 393), avg(329, 396), avg(332, 398), avg(334, 401), avg(337, 403), avg(339, 406)},
|
||
|
|
{avg(400, 478), avg(402, 481), avg(405, 484), avg(408, 487), avg(411, 490), avg(414, 492)},
|
||
|
|
{avg(518, 616), avg(521, 620), avg(524, 623), avg(527, 626), avg(531, 630), avg(534, 633)},
|
||
|
|
{avg(644, 764), avg(647, 768), avg(651, 772), avg(655, 776), avg(658, 779), avg(662, 783)},
|
||
|
|
{avg(812, 958), avg(816, 963), avg(820, 967), avg(824, 971), avg(828, 975), avg(833, 979)},
|
||
|
|
{avg(913, 1059), avg(917, 1064), avg(922, 1069), avg(927, 1074), avg(931, 1078)},
|
||
|
3 years ago
|
{avg(1101, 1279), avg(1106, 1285), avg(1111, 1290), avg(1116, 1295), avg(1121, 1300)},
|
||
|
|
{avg(1578, 1832), avg(1586, 1840), avg(1594, 1848), avg(1602, 1856), avg(1610, 1864), avg(1618, 1872)},
|
||
|
|
{avg(1887, 2193), avg(1896, 2203)} }}
|
||
|
|
spellData[GreaterHeal] = {coeff = 3 / 3.5, levels = {40, 46, 52, 58, 60, 63, 68, 73, 78}, averages = {
|
||
|
4 years ago
|
{avg(899, 1013), avg(904, 1019), avg(909, 1024), avg(914, 1029), avg(919, 1034), avg(924, 1039)},
|
||
|
|
{avg(1149, 1289), avg(1154, 1295), avg(1160, 1301), avg(1166, 1307), avg(1172, 1313), avg(1178, 1318)},
|
||
|
|
{avg(1437, 1609), avg(1443, 1616), avg(1450, 1623), avg(1456, 1629), avg(1463, 1636), avg(1470, 1642)},
|
||
|
|
{avg(1798, 2006), avg(1805, 2014), avg(1813, 2021), avg(1820, 2029), avg(1828, 2036), avg(1835, 2044)},
|
||
|
|
{avg(1966, 2194), avg(1974, 2203), avg(1982, 2211), avg(1990, 2219), avg(1998, 2227), avg(2006, 2235)},
|
||
|
|
{avg(2074, 2410), avg(2082, 2419), avg(2090, 2427), avg(2099, 2436), avg(2107, 2444)},
|
||
|
3 years ago
|
{avg(2396, 2784), avg(2405, 2794), avg(2414, 2803), avg(2423, 2812), avg(2433, 2822)},
|
||
|
|
{avg(3395, 3945), avg(3408, 3959), avg(3421, 3972), avg(3434, 3985), avg(3447, 3998)},
|
||
|
|
{avg(3950, 4590), avg(3965, 4606), avg(3980, 4621)} }}
|
||
|
4 years ago
|
spellData[Heal] = {coeff = 3 / 3.5, levels = {16, 22, 28, 34}, averages = {
|
||
|
|
{avg(295, 341), avg(297, 344), avg(299, 346), avg(302, 349), avg(304, 351), avg(307, 353)},
|
||
|
|
{avg(429, 491), avg(432, 495), avg(435, 498), avg(438, 501), avg(441, 504), avg(445, 507)},
|
||
|
|
{avg(566, 642), avg(570, 646), avg(574, 650), avg(578, 654), avg(582, 658), avg(586, 662)},
|
||
|
|
{avg(712, 804), avg(716, 809), avg(721, 813), avg(725, 818), avg(730, 822), avg(734, 827)} }}
|
||
|
|
spellData[LesserHeal] = {levels = {1, 4, 10}, averages = {
|
||
|
|
{avg(46, 56), avg(46, 57), avg(47, 58)},
|
||
|
|
{avg(71, 85), avg(72, 87), avg(73, 88), avg(74, 89), avg(75, 90), avg(76, 91)},
|
||
|
|
{avg(135, 157), avg(136, 159), avg(138, 161), avg(139, 162), avg(141, 164), avg(143, 165)} }}
|
||
|
3 years ago
|
spellData[PrayerofHealing] = {coeff = (isWrath and 0.2798 or isTBC and 0.431596 or (3/3.5/3)), levels = {30, 40, 50, 60, 60, 68, 76}, averages = {
|
||
|
4 years ago
|
{avg(301, 321), avg(302, 323), avg(303, 324), avg(304, 325), avg(306, 327), avg(307, 328), avg(308, 329), avg(310, 331), avg(311, 332), avg(312, 333)},
|
||
|
|
{avg(444, 472), avg(445, 474), avg(447, 476), avg(448, 477), avg(450, 479), avg(452, 480), avg(453, 482), avg(455, 484), avg(456, 485), avg(458, 487)},
|
||
|
|
{avg(657, 695), avg(659, 697), avg(661, 699), avg(663, 701), avg(665, 703), avg(667, 705), avg(669, 707), avg(671, 709), avg(673, 711), avg(675, 713)},
|
||
|
|
{avg(939, 991), avg(941, 994), avg(943, 996), avg(946, 999), avg(948, 1001), avg(951, 1003), avg(953, 1006), avg(955, 1008), avg(958, 1011), avg(960, 1013)},
|
||
|
|
{avg(997, 1053), avg(999, 1056), avg(1002, 1058), avg(1004, 1061), avg(1007, 1063), avg(1009, 1066), avg(1012, 1068), avg(1014, 1071), avg(1017, 1073), avg(1019, 1076)},
|
||
|
3 years ago
|
{avg(1246, 1316), avg(1248, 1319), avg(1251, 1322), avg(1254, 1325), avg(1257, 1328), avg(1260, 1330), avg(1262, 1333), avg(1265, 1336)},
|
||
|
|
{avg(2091, 2209), avg(2095, 2214), avg(2100, 2219), avg(2105, 2224), avg(2109, 2228)} }}
|
||
|
|
spellData[BindingHeal] = {coeff = 1.5 / 3.5, levels = {64, 72, 78}, averages = {
|
||
|
|
{avg(1042, 1338), avg(1043, 1340), avg(1045, 1342), avg(1047, 1344), avg(1049, 1346), avg(1051, 1348), avg(1053, 1350), avg(1055, 1352)},
|
||
|
|
{avg(1619, 2081), avg(1622, 2084), avg(1625, 2087), avg(1628, 2090), avg(1631, 2093)},
|
||
|
|
{avg(1952, 2508), avg(1955, 2512), avg(1959, 2516)} }}
|
||
|
|
spellData[Penance] = {_isChanneled = true, coeff = 0.857, ticks = 3, levels = {60, 70, 75, 80}, averages = {avg(670, 756), avg(805, 909), avg(1278, 1442), avg(1484, 1676)}}
|
||
|
4 years ago
|
|
||
|
|
talentData[ImprovedRenew] = {mod = 0.05, current = 0}
|
||
|
|
talentData[SpiritualHealing] = {mod = 0.02, current = 0}
|
||
|
|
talentData[EmpoweredHealing] = {mod = 0.02, current = 0}
|
||
|
3 years ago
|
if isWrath then
|
||
|
|
talentData[EmpoweredHealing].mod = 0.04
|
||
|
|
end
|
||
|
|
talentData[BlessedResilience] = {mod = 0.01, current = 0}
|
||
|
|
talentData[FocusedPower] = {mod = 0.02, current = 0}
|
||
|
|
talentData[DivineProvidence] = {mod = 0.02, current = 0}
|
||
|
|
talentData[EmpoweredRenew] = {mod = 0.05, current = 0}
|
||
|
|
talentData[TwinDisciplines] = {mod = 0.01, current = 0}
|
||
|
4 years ago
|
|
||
|
|
itemSetsData["Oracle"] = {21351, 21349, 21350, 21348, 21352}
|
||
|
|
itemSetsData["Absolution"] = {31068, 31063, 31060, 31069, 31066, 34562, 34527, 34435}
|
||
|
|
itemSetsData["Avatar"] = {30153, 30152, 30151, 30154, 30150}
|
||
|
|
|
||
|
3 years ago
|
local activeGraceGUID, activeGraceModifier = nil, 0
|
||
|
|
AuraHandler = function(unit, guid)
|
||
|
|
local stack, _, _, _, caster = select(3, unitHasAura(unit, Grace))
|
||
|
|
if( caster == "player" ) then
|
||
|
|
activeGraceModifier = stack * 0.03
|
||
|
|
activeGraceGUID = guid
|
||
|
|
elseif( activeGraceGUID == guid ) then
|
||
|
|
activeGraceGUID = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
GetHealTargets = function(bitType, guid, spellID)
|
||
|
4 years ago
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
if( spellName == BindingHeal ) then
|
||
|
|
if guid == playerGUID then
|
||
|
3 years ago
|
return format("%s", compressGUID[playerGUID])
|
||
|
4 years ago
|
else
|
||
|
3 years ago
|
return format("%s,%s", compressGUID[guid], compressGUID[playerGUID])
|
||
|
4 years ago
|
end
|
||
|
|
elseif( spellName == PrayerofHealing ) then
|
||
|
3 years ago
|
-- In Wrath PoH can be castet on other groups than your own
|
||
|
|
if not isWrath then
|
||
|
|
guid = UnitGUID("player")
|
||
|
|
end
|
||
|
4 years ago
|
local targets = compressGUID[guid]
|
||
|
|
local group = guidToGroup[guid]
|
||
|
|
|
||
|
|
for groupGUID, id in pairs(guidToGroup) do
|
||
|
3 years ago
|
--We skip the rangecheck in Wrath since range would have to be measured from our target to its party and we can't do that
|
||
|
|
local unit = isWrath and "player" or guidToUnit[groupGUID]
|
||
|
|
local testUnit = guidToUnit[groupGUID]
|
||
|
|
-- Dispel Magic is chosen because it's learned before PoH and maintains 30y range through the expansions
|
||
|
|
if( id == group and guid ~= groupGUID and (IsSpellInRange(DispelMagic, unit) == 1 or CheckInteractDistance(unit, 4)) and UnitIsVisible(testUnit) and not UnitHasVehicleUI(testUnit) ) then
|
||
|
4 years ago
|
targets = targets .. "," .. compressGUID[groupGUID]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
return targets
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
return compressGUID[guid]
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
CalculateHotHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
3 years ago
|
if not spellRank then return end
|
||
|
|
|
||
|
4 years ago
|
local healAmount = getBaseHealAmount(hotData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
local totalTicks
|
||
|
|
|
||
|
3 years ago
|
if isWrath then
|
||
|
|
healModifier = healModifier + talentData[SpiritualHealing].current
|
||
|
|
elseif isTBC then
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[SpiritualHealing].current)
|
||
|
|
else
|
||
|
3 years ago
|
-- Spiritual Healing only applies to base value in classic
|
||
|
4 years ago
|
healAmount = healAmount * (1 + talentData[SpiritualHealing].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( spellName == Renew or spellName == GreaterHealHot ) then
|
||
|
3 years ago
|
if isWrath then
|
||
|
|
healModifier = healModifier + talentData[ImprovedRenew].current
|
||
|
|
elseif isTBC then
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[ImprovedRenew].current)
|
||
|
|
else
|
||
|
|
-- Improved Renew only applies to the base value in classic
|
||
|
|
healAmount = healAmount * (1 + talentData[ImprovedRenew].current)
|
||
|
|
end
|
||
|
3 years ago
|
healModifier = healModifier + talentData[TwinDisciplines].current
|
||
|
4 years ago
|
|
||
|
3 years ago
|
totalTicks = hotData[spellName].ticks
|
||
|
|
|
||
|
|
if( glyphCache[55674] ) then
|
||
|
|
healModifier = healModifier + 0.25
|
||
|
|
-- completly lose a tick
|
||
|
|
healAmount = healAmount - (healAmount / totalTicks)
|
||
|
|
spellPower = spellPower - (spellPower / totalTicks)
|
||
|
|
totalTicks = totalTicks - 1
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
|
if( equippedSetCache["Oracle"] >= 5 or equippedSetCache["Avatar"] >= 4 ) then
|
||
|
3 years ago
|
healAmount = healAmount + (healAmount / totalTicks) -- Add Tick Amount gained by Set.
|
||
|
|
totalTicks = totalTicks + 1
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
spellPower = spellPower * ((isWrath and 1.88 or 1) * (1 + (talentData[EmpoweredRenew].current)))
|
||
|
|
spellPower = spellPower / totalTicks
|
||
|
|
healAmount = healAmount / totalTicks
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
3 years ago
|
if( activeGraceGUID == guid ) then
|
||
|
|
healModifier = healModifier * (1 + activeGraceModifier)
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
healModifier = healModifier * (1 + talentData[FocusedPower].current)
|
||
|
|
healModifier = healModifier * (1 + talentData[BlessedResilience].current)
|
||
|
|
|
||
|
4 years ago
|
healAmount = calculateGeneralAmount(hotData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
return HOT_HEALS, ceil(healAmount), totalTicks, hotData[spellName].interval
|
||
|
|
end
|
||
|
|
|
||
|
|
CalculateHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
3 years ago
|
if not spellRank then return end
|
||
|
|
|
||
|
4 years ago
|
local healAmount = getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
|
||
|
3 years ago
|
if isWrath then
|
||
|
|
healModifier = healModifier + talentData[SpiritualHealing].current
|
||
|
|
elseif isTBC then
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[SpiritualHealing].current)
|
||
|
|
else
|
||
|
|
healAmount = healAmount * (1 + talentData[SpiritualHealing].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Greater Heal
|
||
|
|
if( spellName == GreaterHeal ) then
|
||
|
|
if( equippedSetCache["Absolution"] >= 4 ) then healModifier = healModifier * 1.05 end
|
||
|
3 years ago
|
spellPower = spellPower * ((spellData[spellName].coeff * (isWrath and 1.88 or 1)) * (1 + (talentData[EmpoweredHealing].current * 2)))
|
||
|
4 years ago
|
-- Flash Heal
|
||
|
|
elseif( spellName == FlashHeal ) then
|
||
|
3 years ago
|
spellPower = spellPower * ((spellData[spellName].coeff * (isWrath and 1.88 or 1)) * (1 + talentData[EmpoweredHealing].current))
|
||
|
4 years ago
|
-- Binding Heal
|
||
|
|
elseif( spellName == BindingHeal ) then
|
||
|
3 years ago
|
healModifier = healModifier + talentData[DivineProvidence].current
|
||
|
|
spellPower = spellPower * ((spellData[spellName].coeff * (isWrath and 1.88 or 1)) * (1 + talentData[EmpoweredHealing].current))
|
||
|
|
-- Penance
|
||
|
|
elseif( spellName == Penance ) then
|
||
|
|
spellPower = spellPower * (spellData[spellName].coeff * 1.88)
|
||
|
|
spellPower = spellPower / spellData[spellName].ticks
|
||
|
|
healModifier = healModifier + talentData[TwinDisciplines].current
|
||
|
4 years ago
|
-- Prayer of Healing
|
||
|
|
elseif( spellName == PrayerofHealing ) then
|
||
|
3 years ago
|
healModifier = healModifier + talentData[DivineProvidence].current
|
||
|
|
spellPower = spellPower * spellData[spellName].coeff * (isWrath and 1.88 or 1)
|
||
|
4 years ago
|
-- Heal
|
||
|
|
elseif( spellName == Heal ) then
|
||
|
3 years ago
|
spellPower = spellPower * spellData[spellName].coeff * (isWrath and 1.88 or 1)
|
||
|
4 years ago
|
-- Lesser Heal
|
||
|
|
elseif( spellName == LesserHeal ) then
|
||
|
|
local castTime = spellRank >= 3 and 2.5 or spellRank == 2 and 2 or 1.5
|
||
|
3 years ago
|
spellPower = spellPower * (castTime / 3.5) * (isWrath and 1.88 or 1)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( activeGraceGUID == guid ) then
|
||
|
|
healModifier = healModifier * (1 + activeGraceModifier)
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
3 years ago
|
healModifier = healModifier * (1 + talentData[FocusedPower].current)
|
||
|
|
healModifier = healModifier * (1 + talentData[BlessedResilience].current)
|
||
|
|
|
||
|
4 years ago
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
|
||
|
|
-- Player has over a 100% chance to crit with Holy spells
|
||
|
|
if( GetSpellCritChance(2) >= 100 ) then
|
||
|
|
healAmount = healAmount * 1.50
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
-- Penance ticks 3 times, the player will see all 3 ticks, everyone else should only see the last 2
|
||
|
|
if( spellName == Penance ) then
|
||
|
|
return CHANNEL_HEALS, math.ceil(healAmount), spellData[spellName].ticks, 1
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
return DIRECT_HEALS, ceil(healAmount)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( playerClass == "SHAMAN" ) then
|
||
|
|
LoadClassData = function()
|
||
|
|
local ChainHeal = GetSpellInfo(1064)
|
||
|
|
local HealingWave = GetSpellInfo(331)
|
||
|
|
local LesserHealingWave = GetSpellInfo(8004)
|
||
|
|
local ImpChainHeal = GetSpellInfo(30872) or "Improved Chain Heal"
|
||
|
3 years ago
|
local HealingWay = GetSpellInfo(29206) or "Healing Way"
|
||
|
|
local Purification = GetSpellInfo(16178) or "Purification"
|
||
|
|
local EarthShield = GetSpellInfo(49284) or "Earth Shield"
|
||
|
|
local TidalWaves = GetSpellInfo(51566) or "Tidal Waves"
|
||
|
|
local Riptide = GetSpellInfo(61295) or "Riptide"
|
||
|
|
local Earthliving = GetSpellInfo(52000) or "Earthliving"
|
||
|
|
|
||
|
|
hotData[Riptide] = {interval = 3, ticks = 5, coeff = 0.50, levels = {60, 70, 75, 80}, averages = {665, 885, 1435, 1670}}
|
||
|
|
hotData[Earthliving] = {interval = 3, ticks = 4, coeff = 0.80, levels = {30, 40, 50, 60, 70, 80}, averages = {116, 160, 220, 348, 456, 652}}
|
||
|
4 years ago
|
|
||
|
3 years ago
|
spellData[ChainHeal] = {coeff = 2.5 / 3.5, levels = {40, 46, 54, 61, 68, 74, 80}, averages = {
|
||
|
4 years ago
|
{avg(320, 368), avg(322, 371), avg(325, 373), avg(327, 376), avg(330, 378), avg(332, 381)},
|
||
|
|
{avg(405, 465), avg(407, 468), avg(410, 471), avg(413, 474), avg(416, 477), avg(419, 479)},
|
||
|
|
{avg(551, 629), avg(554, 633), avg(557, 636), avg(560, 639), avg(564, 643), avg(567, 646)},
|
||
|
|
{avg(605, 691), avg(608, 695), avg(612, 699), avg(616, 703), avg(620, 707), avg(624, 710)},
|
||
|
3 years ago
|
{avg(826, 942), avg(829, 946), avg(833, 950), avg(837, 954), avg(841, 958), avg(845, 961)},
|
||
|
|
{avg(906, 1034), avg(909, 1038), avg(913, 1042), avg(917, 1046), avg(921, 1050), avg(925, 1053)},
|
||
|
|
{avg(1055, 1205)} }}
|
||
|
|
spellData[HealingWave] = {levels = {1, 6, 12, 18, 24, 32, 40, 48, 56, 60, 63, 70, 75, 80}, averages = {
|
||
|
4 years ago
|
{avg(34, 44), avg(34, 45), avg(35, 46), avg(36, 47)},
|
||
|
|
{avg(64, 78), avg(65, 79), avg(66, 80), avg(67, 81), avg(68, 82), avg(69, 83)},
|
||
|
|
{avg(129, 155), avg(130, 157), avg(132, 158), avg(133, 160), avg(135, 161), avg(136, 163)},
|
||
|
|
{avg(268, 316), avg(270, 319), avg(272, 321), avg(274, 323), avg(277, 326), avg(279, 328)},
|
||
|
|
{avg(376, 440), avg(378, 443), avg(381, 446), avg(384, 449), avg(386, 451), avg(389, 454)},
|
||
|
|
{avg(536, 622), avg(539, 626), avg(542, 629), avg(545, 632), avg(549, 636), avg(552, 639)},
|
||
|
|
{avg(740, 854), avg(743, 858), avg(747, 862), avg(751, 866), avg(755, 870), avg(759, 874)},
|
||
|
|
{avg(1017, 1167), avg(1021, 1172), avg(1026, 1177), avg(1031, 1182), avg(1035, 1186), avg(1040, 1191)},
|
||
|
|
{avg(1367, 1561), avg(1372, 1567), avg(1378, 1572), avg(1383, 1578), avg(1389, 1583), avg(1394, 1589)},
|
||
|
|
{avg(1620, 1850), avg(1625, 1856), avg(1631, 1861), avg(1636, 1867), avg(1642, 1872), avg(1647, 1878)},
|
||
|
|
{avg(1725, 1969), avg(1731, 1976), avg(1737, 1982), avg(1743, 1988), avg(1750, 1995), avg(1756, 2001)},
|
||
|
3 years ago
|
{avg(2134, 2436), avg(2141, 2444), avg(2148, 2451), avg(2155, 2458), avg(2162, 2465)},
|
||
|
|
{avg(2624, 2996), avg(2632, 3004), avg(2640, 3012), avg(2648, 3020), avg(2656, 3028)},
|
||
|
|
{avg(3034, 3466)} }}
|
||
|
|
spellData[LesserHealingWave] = {coeff = 1.5 / 3.5, levels = {20, 28, 36, 44, 52, 60, 66, 72, 77}, averages = {
|
||
|
4 years ago
|
{avg(162, 186), avg(163, 188), avg(165, 190), avg(167, 192), avg(168, 193), avg(170, 195)},
|
||
|
|
{avg(247, 281), avg(249, 284), avg(251, 286), avg(253, 288), avg(255, 290), avg(257, 292)},
|
||
|
|
{avg(337, 381), avg(339, 384), avg(342, 386), avg(344, 389), avg(347, 391), avg(349, 394)},
|
||
|
|
{avg(458, 514), avg(461, 517), avg(464, 520), avg(467, 523), avg(470, 526), avg(473, 529)},
|
||
|
|
{avg(631, 705), avg(634, 709), avg(638, 713), avg(641, 716), avg(645, 720), avg(649, 723)},
|
||
|
|
{avg(832, 928), avg(836, 933), avg(840, 937), avg(844, 941), avg(848, 945), avg(853, 949)},
|
||
|
3 years ago
|
{avg(1039, 1185), avg(1043, 1190), avg(1047, 1194), avg(1051, 1198), avg(1055, 1202)},
|
||
|
|
{avg(1382, 1578), avg(1387, 1583), avg(1392, 1588), avg(1397, 1593), avg(1402, 1598)},
|
||
|
|
{avg(1606, 1834), avg(1612, 1840), avg(1618, 1846), avg(1624, 1852)} }}
|
||
|
4 years ago
|
|
||
|
3 years ago
|
talentData[HealingWay] = {mod = (0.25/3), current = 0}
|
||
|
4 years ago
|
talentData[ImpChainHeal] = {mod = 0.10, current = 0}
|
||
|
|
talentData[Purification] = {mod = 0.02, current = 0}
|
||
|
3 years ago
|
talentData[TidalWaves] = {mod = 0.04, current = 0}
|
||
|
4 years ago
|
|
||
|
|
itemSetsData["Skyshatter"] = {31016, 31007, 31012, 31019, 31022, 34543, 34438, 34565}
|
||
|
3 years ago
|
itemSetsData["T7 Resto"] = {40508, 40509, 40510, 40512, 40513, 39583, 39588, 39589, 39590, 39591}
|
||
|
|
itemSetsData["T9 Resto"] = {48280, 48281, 48282, 48283, 48284, 48295, 48296, 48297, 48298, 48299, 48301, 48302, 48303, 48304, 48300, 48306, 48307, 48308, 48309, 48305, 48286, 48287, 48288, 48289, 48285, 48293, 48292, 48291, 48290, 48294}
|
||
|
|
|
||
|
3 years ago
|
local lhwTotems = {[42598] = 320, [42597] = 267, [42596] = 236, [42595] = 204, [25645] = 79, [22396] = 80, [23200] = 53, [186072] = 10}
|
||
|
3 years ago
|
local chTotems = {[45114] = 243, [38368] = 102, [28523] = 87}
|
||
|
4 years ago
|
|
||
|
3 years ago
|
-- Keep track of who has riptide on them
|
||
|
|
local riptideData, earthshieldList = {}, {}
|
||
|
|
AuraHandler = function(unit, guid)
|
||
|
|
riptideData[guid] = unitHasAura(unit, Riptide) and true or nil
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Currently, Glyph of Lesser Healing Wave + Any Earth Shield increase the healing not just the players own
|
||
|
|
if( unitHasAura(unit, EarthShield) ) then
|
||
|
|
earthshieldList[guid] = true
|
||
|
|
elseif( earthshieldList[guid] ) then
|
||
|
|
earthshieldList[guid] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
ResetChargeData = function(guid)
|
||
|
|
riptideData[guid] = guidToUnit[guid] and unitHasAura(guidToUnit[guid], Riptide) and true or nil
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
|
-- Lets a specific override on how many people this will hit
|
||
|
3 years ago
|
GetHealTargets = function(bitType, guid, spellID, amount)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
-- Glyph of Healing Wave, heals you for 20% of your heal when you heal someone else
|
||
|
|
if( glyphCache[55440] and guid ~= playerGUID and spellName == HealingWave ) then
|
||
|
|
if guidToUnit[guid] then
|
||
|
|
return string.format("%s,%d,%s,%d", compressGUID[guid], amount, compressGUID[playerGUID], amount * 0.20), -1
|
||
|
|
else
|
||
|
|
return compressGUID[UnitGUID("player")], amount * 0.20
|
||
|
|
end
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
return compressGUID[guid]
|
||
|
|
end
|
||
|
|
|
||
|
|
CalculateHotHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(hotData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
local totalTicks
|
||
|
3 years ago
|
|
||
|
3 years ago
|
healModifier = healModifier * (1 + talentData[Purification].current)
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Riptide
|
||
|
|
if( spellName == Riptide ) then
|
||
|
|
if( equippedSetCache["T9 Resto"] >= 2 ) then
|
||
|
|
spModifier = spModifier * 1.20
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
spellPower = spellPower * (hotData[spellName].coeff * 1.88)
|
||
|
|
spellPower = spellPower / hotData[spellName].ticks
|
||
|
|
healAmount = healAmount / hotData[spellName].ticks
|
||
|
3 years ago
|
|
||
|
3 years ago
|
totalTicks = hotData[spellName].ticks
|
||
|
|
-- Glyph of Riptide, +6 seconds
|
||
|
|
if( glyphCache[63273] ) then totalTicks = totalTicks + 2 end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Earthliving Weapon
|
||
|
|
elseif( spellName == Earthliving ) then
|
||
|
|
spellPower = (spellPower * (hotData[spellName].coeff * 1.88) * 0.45)
|
||
|
|
spellPower = spellPower / hotData[spellName].ticks
|
||
|
|
healAmount = healAmount / hotData[spellName].ticks
|
||
|
3 years ago
|
|
||
|
3 years ago
|
totalTicks = hotData[spellName].ticks
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
healAmount = calculateGeneralAmount(hotData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
return HOT_HEALS, healAmount, totalTicks, hotData[spellName].interval
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
CalculateHealing = function(guid, spellID, unit)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
|
||
|
3 years ago
|
if isTBC or isWrath then
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[Purification].current)
|
||
|
|
else
|
||
|
|
-- Purification only applies to base values in classic
|
||
|
|
healAmount = healAmount * (1 + talentData[Purification].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Chain Heal
|
||
|
|
if( spellName == ChainHeal ) then
|
||
|
3 years ago
|
spellPower = spellPower * spellData[spellName].coeff * (isWrath and 1.88 or 1)
|
||
|
4 years ago
|
|
||
|
|
if( equippedSetCache["Skyshatter"] >= 4 ) then
|
||
|
|
healModifier = healModifier * 1.05
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
if( equippedSetCache["T7 Resto"] >= 4 ) then
|
||
|
|
healModifier = healModifier * 1.05
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
healModifier = healModifier * (1 + talentData[ImpChainHeal].current)
|
||
|
|
|
||
|
3 years ago
|
-- Add +25% from Riptide being up and reset the flag
|
||
|
|
if( riptideData[guid] ) then
|
||
|
|
healModifier = healModifier * 1.25
|
||
|
|
riptideData[guid] = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if playerCurrentRelic and chTotems[playerCurrentRelic] then
|
||
|
|
healAmount = healAmount + chTotems[playerCurrentRelic]
|
||
|
|
end
|
||
|
|
-- Healing Wave
|
||
|
4 years ago
|
elseif( spellName == HealingWave ) then
|
||
|
|
local hwStacks = select(3, unitHasAura(unit, 29203))
|
||
|
|
if( hwStacks ) then
|
||
|
|
healAmount = healAmount * ((hwStacks * 0.06) + 1)
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
|
if isWrath then
|
||
|
|
healModifier = healModifier * (1 + talentData[HealingWay].current)
|
||
|
|
end
|
||
|
|
|
||
|
|
if( equippedSetCache["T7 Resto"] >= 4 ) then
|
||
|
|
healModifier = healModifier * 1.05
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
|
local castTime = spellRank > 3 and 3 or spellRank == 3 and 2.5 or spellRank == 2 and 2 or 1.5
|
||
|
|
|
||
|
|
if playerCurrentRelic == 27544 then spellPower = spellPower + 88 end
|
||
|
|
|
||
|
3 years ago
|
spellPower = spellPower * ((castTime / 3.5) * (isWrath and 1.88 or 1) + talentData[TidalWaves].current)
|
||
|
4 years ago
|
|
||
|
|
-- Lesser Healing Wave
|
||
|
|
elseif( spellName == LesserHealingWave ) then
|
||
|
3 years ago
|
-- Glyph of Lesser Healing Wave, +20% healing on LHW if target has ES up
|
||
|
|
if( glyphCache[55438] and earthshieldList[guid] ) then
|
||
|
|
healModifier = healModifier * 1.20
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
spellPower = spellPower + (playerCurrentRelic and lhwTotems[playerCurrentRelic] or 0)
|
||
|
3 years ago
|
spellPower = spellPower * (spellData[spellName].coeff * (isWrath and 1.88 or 1) + (talentData[TidalWaves].current / 2))
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
|
|
||
|
|
-- Player has over a 100% chance to crit with Nature spells
|
||
|
|
if( GetSpellCritChance(4) >= 100 ) then
|
||
|
|
healAmount = healAmount * 1.50
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Apply the final modifier of any MS or self heal increasing effects
|
||
|
|
return DIRECT_HEALS, ceil(healAmount)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( playerClass == "HUNTER" ) then
|
||
|
|
LoadClassData = function()
|
||
|
|
local MendPet = GetSpellInfo(136)
|
||
|
|
|
||
|
3 years ago
|
if isTBC or isWrath then
|
||
|
|
hotData[MendPet] = { interval = 3, levels = { 12, 20, 28, 36, 44, 52, 60, 68, 74, 80 }, ticks = 5, averages = {125, 250, 450, 700, 1000, 1400, 1825, 2375, 4250, 5250 } }
|
||
|
4 years ago
|
else
|
||
|
|
spellData[MendPet] = { interval = 1, levels = { 12, 20, 28, 36, 44, 52, 60 }, ticks = 5, averages = {100, 190, 340, 515, 710, 945, 1225 } }
|
||
|
|
end
|
||
|
|
|
||
|
|
itemSetsData["Giantstalker"] = {16851, 16849, 16850, 16845, 16848, 16852, 16846, 16847}
|
||
|
|
|
||
|
3 years ago
|
GetHealTargets = function(bitType, guid, spellID)
|
||
|
4 years ago
|
local petGUID = UnitGUID("pet")
|
||
|
3 years ago
|
return petGUID and compressGUID[petGUID]
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
CalculateHotHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local amount = getBaseHealAmount(hotData, spellName, spellID, spellRank)
|
||
|
|
|
||
|
|
if( equippedSetCache["Giantstalker"] >= 3 ) then amount = amount * 1.1 end
|
||
|
|
|
||
|
|
return HOT_HEALS, ceil(amount / hotData[spellName].ticks), hotData[spellName].ticks, hotData[spellName].interval
|
||
|
|
end
|
||
|
|
|
||
|
|
CalculateHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
|
|
||
|
|
if( equippedSetCache["Giantstalker"] >= 3 ) then healAmount = healAmount * 1.1 end
|
||
|
|
|
||
|
|
return CHANNEL_HEALS, ceil(healAmount / spellData[spellName].ticks), spellData[spellName].ticks, spellData[spellName].interval
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
--[[
|
||
|
|
-- While the groundwork is done, "CalculateHealing" needs to be theory crafted for classic / TBC / WotLK. Deactivating until someone steps up.
|
||
|
4 years ago
|
if( playerClass == "WARLOCK" ) then
|
||
|
|
LoadClassData = function()
|
||
|
|
local HealthFunnel = GetSpellInfo(755)
|
||
|
|
local DrainLife = GetSpellInfo(689)
|
||
|
|
local ImpHealthFunnel = GetSpellInfo(18703)
|
||
|
3 years ago
|
local ShadowMastery = GetSpellInfo(18271)
|
||
|
4 years ago
|
|
||
|
3 years ago
|
spellData[HealthFunnel] = {_isChanneled = true, coeff = 0.548, interval = 1, levels = { 12, 20, 28, 36, 44, 52, 60, 68, 76 }, ticks = 10, averages = { 120, 240, 430, 640, 890, 1190, 1530, 1880, 5200 } }
|
||
|
|
spellData[DrainLife] = {_isChanneled = true, coeff = 0.143, interval = 1, levels = { 14, 22, 30, 38, 46, 54, 62, 69, 78 }, ticks = 5, averages = { 10 * 5, 17 * 5, 29 * 5, 41 * 5, 55 * 5, 71 * 5, 87 * 5, 108 * 5, 133 * 5 } }
|
||
|
4 years ago
|
|
||
|
|
talentData[ImpHealthFunnel] = { mod = 0.1, current = 0 }
|
||
|
3 years ago
|
talentData[ShadowMastery] = { mod = 0.1, current = 0 }
|
||
|
4 years ago
|
|
||
|
3 years ago
|
GetHealTargets = function(bitType, guid, spellID)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
4 years ago
|
local petGUID = UnitGUID("pet")
|
||
|
3 years ago
|
if spellName == DrainLife then
|
||
|
|
return compressGUID[playerGUID]
|
||
|
|
else
|
||
|
|
return petGUID and compressGUID[petGUID]
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
CalculateHealing = function(guid, spellID)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
local healAmount = getBaseHealAmount(spellData, spellName, spellID, spellRank)
|
||
|
3 years ago
|
local spellPower = GetSpellBonusHealing()
|
||
|
|
local healModifier, spModifier = playerHealModifier, 1
|
||
|
|
|
||
|
|
if spellName == DrainLife then
|
||
|
|
healModifier = healModifier * (1 + talentData[ShadowMastery].current)
|
||
|
|
elseif spellName == HealthFunnel then
|
||
|
|
healModifier = healModifier * (1 + talentData[ImpHealthFunnel].current)
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
3 years ago
|
spellPower = spellPower * spellData[spellName].coeff
|
||
|
|
|
||
|
|
healAmount = calculateGeneralAmount(spellData[spellName].levels[spellRank], healAmount, spellPower, spModifier, healModifier)
|
||
|
4 years ago
|
|
||
|
|
return CHANNEL_HEALS, ceil(healAmount / spellData[spellName].ticks), spellData[spellName].ticks, spellData[spellName].interval
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
3 years ago
|
]]
|
||
|
4 years ago
|
|
||
|
|
-- Healing modifiers
|
||
|
|
if( not HealComm.aurasUpdated ) then
|
||
|
|
HealComm.aurasUpdated = true
|
||
|
|
HealComm.healingModifiers = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.currentModifiers = HealComm.currentModifiers or {}
|
||
|
|
|
||
|
|
-- The only spell in the game with a name conflict is Ray of Pain from the Nagrand Void Walkers
|
||
|
|
HealComm.healingModifiers = HealComm.healingModifiers or {
|
||
|
|
[28776] = 0.10, -- Necrotic Poison
|
||
|
|
[36693] = 0.55, -- Necrotic Poison
|
||
|
|
[46296] = 0.25, -- Necrotic Poison
|
||
|
|
[19716] = 0.25, -- Gehennas' Curse
|
||
|
|
[13737] = 0.50, -- Mortal Strike
|
||
|
|
[15708] = 0.50, -- Mortal Strike
|
||
|
|
[16856] = 0.50, -- Mortal Strike
|
||
|
|
[17547] = 0.50, -- Mortal Strike
|
||
|
|
[19643] = 0.50, -- Mortal Strike
|
||
|
|
[24573] = 0.50, -- Mortal Strike
|
||
|
|
[27580] = 0.50, -- Mortal Strike
|
||
|
|
[29572] = 0.50, -- Mortal Strike
|
||
|
|
[31911] = 0.50, -- Mortal Strike
|
||
|
|
[32736] = 0.50, -- Mortal Strike
|
||
|
|
[35054] = 0.50, -- Mortal Strike
|
||
|
|
[37335] = 0.50, -- Mortal Strike
|
||
|
|
[39171] = 0.50, -- Mortal Strike
|
||
|
|
[40220] = 0.50, -- Mortal Strike
|
||
|
|
[44268] = 0.50, -- Mortal Strike
|
||
|
|
[12294] = 0.50, -- Mortal Strike (Rank 1)
|
||
|
|
[21551] = 0.50, -- Mortal Strike (Rank 2)
|
||
|
|
[21552] = 0.50, -- Mortal Strike (Rank 3)
|
||
|
|
[21553] = 0.50, -- Mortal Strike (Rank 4)
|
||
|
|
[25248] = 0.50, -- Mortal Strike (Rank 5)
|
||
|
|
[30330] = 0.50, -- Mortal Strike (Rank 6)
|
||
|
|
[43441] = 0.50, -- Mortal Strike
|
||
|
|
[30843] = 0.00, -- Enfeeble
|
||
|
|
[19434] = 0.50, -- Aimed Shot (Rank 1)
|
||
|
|
[20900] = 0.50, -- Aimed Shot (Rank 2)
|
||
|
|
[20901] = 0.50, -- Aimed Shot (Rank 3)
|
||
|
|
[20902] = 0.50, -- Aimed Shot (Rank 4)
|
||
|
|
[20903] = 0.50, -- Aimed Shot (Rank 5)
|
||
|
|
[20904] = 0.50, -- Aimed Shot (Rank 6)
|
||
|
|
[27065] = 0.50, -- Aimed Shot (Rank 7)
|
||
|
|
[34625] = 0.25, -- Demolish
|
||
|
|
[35189] = 0.50, -- Solar Strike
|
||
|
|
[32315] = 0.50, -- Soul Strike
|
||
|
|
[32378] = 0.50, -- Filet
|
||
|
|
[36917] = 0.50, -- Magma-Thrower's Curse
|
||
|
|
[44534] = 0.50, -- Wretched Strike
|
||
|
|
[34366] = 0.75, -- Ebon Poison
|
||
|
|
[36023] = 0.50, -- Deathblow
|
||
|
|
[36054] = 0.50, -- Deathblow
|
||
|
|
[45885] = 0.50, -- Shadow Spike
|
||
|
|
[41292] = 0.00, -- Aura of Suffering
|
||
|
|
[40599] = 0.50, -- Arcing Smash
|
||
|
|
[9035] = 0.80, -- Hex of Weakness (Rank 1)
|
||
|
|
[19281] = 0.80, -- Hex of Weakness (Rank 2)
|
||
|
|
[19282] = 0.80, -- Hex of Weakness (Rank 3)
|
||
|
|
[19283] = 0.80, -- Hex of Weakness (Rank 4)
|
||
|
|
[19284] = 0.80, -- Hex of Weakness (Rank 5)
|
||
|
|
[25470] = 0.80, -- Hex of Weakness (Rank 6)
|
||
|
|
[34073] = 0.85, -- Curse of the Bleeding Hollow
|
||
|
|
[31306] = 0.25, -- Carrion Swarm
|
||
|
|
[44475] = 0.25, -- Magic Dampening Field
|
||
|
|
[23169] = 0.50, -- Brood Affliction: Green
|
||
|
|
[22859] = 0.50, -- Mortal Cleave
|
||
|
|
[38572] = 0.50, -- Mortal Cleave
|
||
|
|
[39595] = 0.50, -- Mortal Cleave
|
||
|
|
[45996] = 0.00, -- Darkness
|
||
|
|
[41350] = 2.00, -- Aura of Desire
|
||
|
|
[28176] = 1.20, -- Fel Armor
|
||
|
|
[7068] = 0.25, -- Veil of Shadow
|
||
|
|
[17820] = 0.25, -- Veil of Shadow
|
||
|
|
[22687] = 0.25, -- Veil of Shadow
|
||
|
|
[23224] = 0.25, -- Veil of Shadow
|
||
|
|
[24674] = 0.25, -- Veil of Shadow
|
||
|
|
[28440] = 0.25, -- Veil of Shadow
|
||
|
|
[13583] = 0.50, -- Curse of the Deadwood
|
||
|
|
[23230] = 0.50, -- Blood Fury
|
||
|
|
[31977] = 1.50, -- Curse of Infinity
|
||
|
|
}
|
||
|
3 years ago
|
if isWrath then
|
||
|
|
HealComm.healingModifiers[34123] = 1.06 -- Tree of Life
|
||
|
|
HealComm.healingModifiers[45237] = 1.03 -- Focused Will Rank 1
|
||
|
|
HealComm.healingModifiers[45241] = 1.04 -- Focused Will Rank 2
|
||
|
|
HealComm.healingModifiers[45242] = 1.05 -- Focused Will Rank 3
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
4 years ago
|
|
||
|
|
HealComm.healingStackMods = HealComm.healingStackMods or {
|
||
|
|
-- Mortal Wound
|
||
|
|
[25646] = function(stacks) return 1 - stacks * 0.10 end,
|
||
|
|
[28467] = function(stacks) return 1 - stacks * 0.10 end,
|
||
|
|
[30641] = function(stacks) return 1 - stacks * 0.05 end,
|
||
|
|
[31464] = function(stacks) return 1 - stacks * 0.10 end,
|
||
|
|
[36814] = function(stacks) return 1 - stacks * 0.10 end,
|
||
|
|
[38770] = function(stacks) return 1 - stacks * 0.05 end,
|
||
|
|
-- Dark Touched
|
||
|
|
[45347] = function(stacks) return 1 - stacks * 0.05 end,
|
||
|
|
-- Nether Portal - Dominance
|
||
|
|
[30423] = function(stacks) return 1 - stacks * 0.01 end,
|
||
|
|
-- Focused Will
|
||
|
|
[45242] = function(stacks) return 1 + stacks * 0.10 end,
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
if isTBC or isWrath then
|
||
|
4 years ago
|
HealComm.healingStackMods[13218] = function(stacks) return 1 - stacks * 0.10 end -- Wound Poison (Rank 1)
|
||
|
|
HealComm.healingStackMods[13222] = function(stacks) return 1 - stacks * 0.10 end -- Wound Poison (Rank 2)
|
||
|
|
HealComm.healingStackMods[13223] = function(stacks) return 1 - stacks * 0.10 end -- Wound Poison (Rank 3)
|
||
|
|
HealComm.healingStackMods[13224] = function(stacks) return 1 - stacks * 0.10 end -- Wound Poison (Rank 4)
|
||
|
|
HealComm.healingStackMods[27189] = function(stacks) return 1 - stacks * 0.10 end -- Wound Poison (Rank 5)
|
||
|
|
end
|
||
|
|
|
||
|
|
local healingStackMods = HealComm.healingStackMods
|
||
|
|
local healingModifiers, currentModifiers = HealComm.healingModifiers, HealComm.currentModifiers
|
||
|
|
|
||
|
|
local distribution
|
||
|
|
local CTL = _G.ChatThrottleLib
|
||
|
|
local function sendMessage(msg)
|
||
|
|
if( distribution and strlen(msg) <= 240 ) then
|
||
|
|
if CTL then
|
||
|
|
CTL:SendAddonMessage("BULK", COMM_PREFIX, msg, distribution or 'GUILD')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Keep track of where all the data should be going
|
||
|
|
local instanceType
|
||
|
|
local function updateDistributionChannel()
|
||
|
|
if( instanceType == "pvp" or instanceType == "arena" ) then
|
||
|
|
distribution = "INSTANCE_CHAT"
|
||
|
|
elseif( IsInRaid() ) then
|
||
|
|
distribution = "RAID"
|
||
|
|
elseif( IsInGroup() ) then
|
||
|
|
distribution = "PARTY"
|
||
|
|
else
|
||
|
|
distribution = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Figure out where we should be sending messages and wipe some caches
|
||
|
|
function HealComm:PLAYER_ENTERING_WORLD()
|
||
|
|
HealComm.eventFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
|
||
|
|
HealComm:ZONE_CHANGED_NEW_AREA()
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:ZONE_CHANGED_NEW_AREA()
|
||
|
|
local pvpType = GetZonePVPInfo()
|
||
|
|
local instance = select(2, IsInInstance())
|
||
|
|
|
||
|
|
HealComm.zoneHealModifier = 1
|
||
|
|
if( pvpType == "combat" or instance == "arena" or instance == "pvp" ) then
|
||
|
|
HealComm.zoneHealModifier = 0.90
|
||
|
|
end
|
||
|
|
|
||
|
|
if( instance ~= instanceType ) then
|
||
|
|
instanceType = instance
|
||
|
|
|
||
|
|
updateDistributionChannel()
|
||
|
|
clearPendingHeals()
|
||
|
|
wipe(activeHots)
|
||
|
|
end
|
||
|
|
|
||
|
|
instanceType = instance
|
||
|
|
end
|
||
|
|
|
||
|
|
local alreadyAdded = {}
|
||
|
|
function HealComm:UNIT_AURA(unit)
|
||
|
|
local guid = UnitGUID(unit)
|
||
|
|
if( not guidToUnit[guid] ) then return end
|
||
|
|
local increase, decrease, playerIncrease, playerDecrease = 1, 1, 1, 1
|
||
|
|
|
||
|
|
-- Scan buffs
|
||
|
|
local id = 1
|
||
|
|
while( true ) do
|
||
|
|
local name, _, stack, _, _, _, _, _, _, spellID = UnitAura(unit, id, "HELPFUL")
|
||
|
|
if( not name ) then break end
|
||
|
|
-- Prevent buffs like Tree of Life that have the same name for the shapeshift/healing increase from being calculated twice
|
||
|
|
if( not alreadyAdded[name] ) then
|
||
|
|
alreadyAdded[name] = true
|
||
|
|
|
||
|
|
if( healingModifiers[spellID] ) then
|
||
|
|
increase = increase * healingModifiers[spellID]
|
||
|
3 years ago
|
|
||
|
4 years ago
|
elseif( healingStackMods[spellID] ) then
|
||
|
|
increase = increase * healingStackMods[spellID](stack)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
id = id + 1
|
||
|
|
end
|
||
|
|
-- Scan debuffs
|
||
|
|
id = 1
|
||
|
|
while( true ) do
|
||
|
|
local name, _, stack, _, _, _, _, _, _, spellID = UnitAura(unit, id, "HARMFUL")
|
||
|
|
if( not name ) then break end
|
||
|
|
|
||
|
|
if( healingModifiers[spellID] ) then
|
||
|
|
decrease = min(decrease, healingModifiers[spellID])
|
||
|
|
elseif( healingStackMods[spellID] ) then
|
||
|
|
decrease = min(decrease, healingStackMods[spellID](stack))
|
||
|
|
end
|
||
|
|
|
||
|
|
id = id + 1
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Check if modifier changed
|
||
|
|
local modifier = increase * decrease
|
||
|
|
if( modifier ~= currentModifiers[guid] ) then
|
||
|
|
if( currentModifiers[guid] or modifier ~= 1 ) then
|
||
|
|
currentModifiers[guid] = modifier
|
||
|
|
self.callbacks:Fire("HealComm_ModifierChanged", guid, modifier)
|
||
|
|
else
|
||
|
|
currentModifiers[guid] = modifier
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
wipe(alreadyAdded)
|
||
|
|
|
||
|
|
if( unit == "player" ) then
|
||
|
3 years ago
|
if unitHasAura("player", 10060) and not (isWrath or isTBC) then -- Power Infusion
|
||
|
4 years ago
|
playerIncrease = playerIncrease * 1.20
|
||
|
|
end
|
||
|
|
|
||
|
|
local npsStacks = select(3, unitHasAura("player", 30422)) -- Nether Portal - Serenity
|
||
|
|
if npsStacks then
|
||
|
|
playerIncrease = playerIncrease * (1 + npsStacks * 0.05)
|
||
|
|
end
|
||
|
|
|
||
|
|
if unitHasAura("player", 41406) then -- Dementia: +5%
|
||
|
|
playerIncrease = playerIncrease * 1.05
|
||
|
|
end
|
||
|
|
|
||
|
|
if unitHasAura("player", 41409) then -- Dementia: -5%
|
||
|
|
playerDecrease = playerDecrease * 0.95
|
||
|
|
end
|
||
|
|
|
||
|
|
if unitHasAura("player", 32346) then -- Stolen Soul
|
||
|
|
playerDecrease = playerDecrease * 0.50
|
||
|
|
end
|
||
|
|
|
||
|
|
if unitHasAura("player", 40099) then -- Vile Slime
|
||
|
|
playerDecrease = playerDecrease * 0.50
|
||
|
|
end
|
||
|
|
|
||
|
|
if unitHasAura("player", 38246) then -- Vile Sludge
|
||
|
|
playerDecrease = playerDecrease * 0.50
|
||
|
|
end
|
||
|
|
|
||
|
|
if unitHasAura("player", 45573) then -- Vile Sludge
|
||
|
|
playerDecrease = playerDecrease * 0.50
|
||
|
|
end
|
||
|
|
|
||
|
|
playerHealModifier = playerIncrease * playerDecrease
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Class has a specific monitor it needs for auras
|
||
|
|
if( AuraHandler ) then
|
||
|
|
AuraHandler(unit, guid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
-- Monitor glyph changes
|
||
|
|
function HealComm:GlyphsUpdated(id)
|
||
|
|
local spellID = glyphCache[id]
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Invalidate the old cache value
|
||
|
|
if( spellID ) then
|
||
|
|
glyphCache[spellID] = nil
|
||
|
|
glyphCache[id] = nil
|
||
|
|
end
|
||
|
3 years ago
|
|
||
|
3 years ago
|
-- Cache the new one if any
|
||
|
|
local enabled, _, glyphID = GetGlyphSocketInfo(id)
|
||
|
|
if( enabled and glyphID ) then
|
||
|
|
glyphCache[glyphID] = true
|
||
|
|
glyphCache[id] = glyphID
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.GLYPH_ADDED = HealComm.GlyphsUpdated
|
||
|
|
HealComm.GLYPH_REMOVED = HealComm.GlyphsUpdated
|
||
|
|
HealComm.GLYPH_UPDATED = HealComm.GlyphsUpdated
|
||
|
|
|
||
|
4 years ago
|
function HealComm:PLAYER_LEVEL_UP(level)
|
||
|
|
playerLevel = tonumber(level) or UnitLevel("player")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Cache player talent data for spells we need
|
||
|
|
function HealComm:CHARACTER_POINTS_CHANGED()
|
||
|
|
for tabIndex=1, GetNumTalentTabs() do
|
||
|
|
for i=1, GetNumTalents(tabIndex) do
|
||
|
|
local name, _, _, _, spent = GetTalentInfo(tabIndex, i)
|
||
|
|
if( name and talentData[name] ) then
|
||
|
|
talentData[name].current = talentData[name].mod * spent
|
||
|
|
talentData[name].spent = spent
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Save the currently equipped range weapon
|
||
|
|
local RANGED_SLOT = GetInventorySlotInfo("RangedSlot")
|
||
|
|
function HealComm:PLAYER_EQUIPMENT_CHANGED()
|
||
|
|
-- Caches set bonus info, as you can't reequip set bonus gear in combat no sense in checking it
|
||
|
|
if( not InCombatLockdown() ) then
|
||
|
|
for name, items in pairs(itemSetsData) do
|
||
|
|
equippedSetCache[name] = 0
|
||
|
|
for _, itemID in pairs(items) do
|
||
|
|
if( IsEquippedItem(itemID) ) then
|
||
|
|
equippedSetCache[name] = equippedSetCache[name] + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Check relic
|
||
|
|
local relic = GetInventoryItemLink("player", RANGED_SLOT)
|
||
|
|
playerCurrentRelic = relic and tonumber(strmatch(relic, "item:(%d+):")) or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- COMM CODE
|
||
|
|
local function loadHealAmount(...)
|
||
|
|
local tbl = HealComm:RetrieveTable()
|
||
|
|
for i=1, select("#", ...) do
|
||
|
|
tbl[i] = tonumber((select(i, ...)))
|
||
|
|
end
|
||
|
|
|
||
|
|
return tbl
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Direct heal started
|
||
|
|
local function loadHealList(pending, amount, stack, endTime, ticksLeft, ...)
|
||
|
|
wipe(tempPlayerList)
|
||
|
|
|
||
|
|
-- For the sake of consistency, even a heal doesn't have multiple end times like a hot, it'll be treated as such in the DB
|
||
|
|
if( amount ~= -1 and amount ~= "-1" ) then
|
||
|
|
amount = not pending.hasVariableTicks and amount or loadHealAmount(strsplit("@", amount))
|
||
|
|
|
||
|
|
for i=1, select("#", ...) do
|
||
|
|
local guid = select(i, ...)
|
||
|
|
local decompGUID = guid and decompressGUID[guid]
|
||
|
|
if( decompGUID ) then
|
||
|
|
updateRecord(pending, decompGUID, amount, stack, endTime, ticksLeft)
|
||
|
|
tinsert(tempPlayerList, decompGUID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for i = 1, select("#", ...), 2 do
|
||
|
|
local guid = select(i, ...)
|
||
|
|
local decompGUID = guid and decompressGUID[guid]
|
||
|
3 years ago
|
amount = not pending.hasVariableTicks and tonumber((select(i + 1, ...))) or loadHealAmount(string.split("@", amount))
|
||
|
4 years ago
|
if( decompGUID and amount ) then
|
||
|
|
updateRecord(pending, decompGUID, amount, stack, endTime, ticksLeft)
|
||
|
|
tinsert(tempPlayerList, decompGUID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function parseDirectHeal(casterGUID, spellID, amount, castTime, ...)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
local unit = guidToUnit[casterGUID]
|
||
|
|
|
||
|
|
if( not unit or not spellName or not amount or select("#", ...) == 0 ) then return end
|
||
|
|
|
||
|
|
local endTime
|
||
|
|
if unit == "player" then
|
||
|
|
endTime = select(5, CastingInfo())
|
||
|
|
if not endTime then return end
|
||
|
|
endTime = endTime / 1000
|
||
|
|
else
|
||
|
|
endTime = GetTime() + (castTime or 1.5)
|
||
|
|
end
|
||
|
|
|
||
|
|
pendingHeals[casterGUID] = pendingHeals[casterGUID] or {}
|
||
|
|
pendingHeals[casterGUID][spellName] = pendingHeals[casterGUID][spellName] or {}
|
||
|
|
|
||
|
|
local pending = pendingHeals[casterGUID][spellName]
|
||
|
|
wipe(pending)
|
||
|
|
pending.endTime = endTime
|
||
|
|
pending.spellID = spellID
|
||
|
|
pending.bitType = DIRECT_HEALS
|
||
|
|
|
||
|
|
loadHealList(pending, amount, 1, pending.endTime, nil, ...)
|
||
|
|
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealStarted", casterGUID, spellID, pending.bitType, pending.endTime, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.parseDirectHeal = parseDirectHeal
|
||
|
|
|
||
|
|
-- Channeled heal started
|
||
|
|
local function parseChannelHeal(casterGUID, spellID, amount, totalTicks, ...)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
local unit = guidToUnit[casterGUID]
|
||
|
|
if( not unit or not spellName or not totalTicks or not amount or select("#", ...) == 0 ) then return end
|
||
|
|
|
||
|
|
local tickInterval = spellName == GetSpellInfo(740) and 2 or 1
|
||
|
|
|
||
|
|
local startTime, endTime
|
||
|
|
if unit == "player" then
|
||
|
|
startTime, endTime = select(4, ChannelInfo())
|
||
|
|
if not startTime then return end
|
||
|
|
startTime = startTime / 1000
|
||
|
|
endTime = endTime / 1000
|
||
|
|
else
|
||
|
|
startTime = GetTime()
|
||
|
|
endTime = startTime + totalTicks * tickInterval
|
||
|
|
end
|
||
|
|
|
||
|
|
pendingHots[casterGUID] = pendingHots[casterGUID] or {}
|
||
|
|
pendingHots[casterGUID][spellName] = pendingHots[casterGUID][spellName] or {}
|
||
|
|
|
||
|
|
local inc = amount == -1 and 2 or 1
|
||
|
|
local pending = pendingHots[casterGUID][spellName]
|
||
|
|
wipe(pending)
|
||
|
|
pending.startTime = startTime
|
||
|
|
pending.endTime = endTime
|
||
|
|
pending.duration = endTime - startTime
|
||
|
|
pending.totalTicks = totalTicks
|
||
|
|
pending.tickInterval = pending.duration / totalTicks
|
||
|
|
pending.spellID = spellID
|
||
|
|
pending.isMultiTarget = (select("#", ...) / inc) > 1
|
||
|
|
pending.bitType = CHANNEL_HEALS
|
||
|
|
|
||
|
|
local ticksLeft = ceil((endTime - GetTime()) / pending.tickInterval)
|
||
|
|
|
||
|
3 years ago
|
loadHealList(pending, amount, 1, endTime, ticksLeft, ...)
|
||
|
4 years ago
|
HealComm.callbacks:Fire("HealComm_HealStarted", casterGUID, spellID, pending.bitType, pending.endTime, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Hot heal started
|
||
|
|
-- When the person is within visible range of us, the aura is available by the time the message reaches the target
|
||
|
|
-- as such, we can rely that at least one person is going to have the aura data on them (and that it won't be different, at least for this cast)
|
||
|
|
local function findAura(casterGUID, spellID, ...)
|
||
|
|
for i = 1, select("#", ...) do
|
||
|
|
local guid = decompressGUID[select(i, ...)]
|
||
|
|
local unit = guid and guidToUnit[guid]
|
||
|
|
if( unit and UnitIsVisible(unit) ) then
|
||
|
|
local id = 1
|
||
|
|
while true do
|
||
|
|
local name, _, stack, _, duration, endTime, caster, _, _, spell = UnitAura(unit, id, 'HELPFUL')
|
||
|
|
if( not spell ) then break end
|
||
|
|
|
||
|
|
if( spell == spellID and caster and UnitGUID(caster) == casterGUID ) then
|
||
|
|
return (stack and stack > 0 and stack or 1), duration or 0, endTime or 0
|
||
|
|
end
|
||
|
|
|
||
|
|
id = id + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function parseHotHeal(casterGUID, wasUpdated, spellID, tickAmount, totalTicks, tickInterval, ...)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
-- If the user is on 3.3, then anything without a total ticks attached to it is rejected
|
||
|
|
if( not tickAmount or not spellName or select("#", ...) == 0 ) then return end
|
||
|
|
|
||
|
3 years ago
|
if type(tickAmount) == "table" then
|
||
|
|
tickAmount = table.concat(tickAmount, "@")
|
||
|
|
end
|
||
|
4 years ago
|
-- Retrieve the hot information
|
||
|
|
local inc = 2
|
||
|
|
local stack, duration, endTime = findAura(casterGUID, spellID, ...)
|
||
|
|
|
||
|
|
if not ( tickAmount == -1 or tickAmount == "-1" ) then
|
||
|
|
inc = 1
|
||
|
3 years ago
|
duration = totalTicks * (tickInterval or 1)
|
||
|
4 years ago
|
endTime = GetTime() + duration
|
||
|
|
end
|
||
|
|
|
||
|
|
if( not stack or stack == 0 or duration == 0 or endTime == 0 ) then return end
|
||
|
|
|
||
|
|
pendingHots[casterGUID] = pendingHots[casterGUID] or {}
|
||
|
|
pendingHots[casterGUID][spellName] = pendingHots[casterGUID][spellName] or {}
|
||
|
|
|
||
|
|
local pending = pendingHots[casterGUID][spellName]
|
||
|
|
pending.duration = duration
|
||
|
|
pending.endTime = endTime
|
||
|
|
pending.stack = stack
|
||
|
|
pending.totalTicks = totalTicks or duration / tickInterval
|
||
|
|
pending.tickInterval = totalTicks and duration / totalTicks or tickInterval
|
||
|
|
pending.spellID = spellID
|
||
|
|
pending.hasVariableTicks = type(tickAmount) == "string"
|
||
|
|
pending.isMutliTarget = (select("#", ...) / inc) > 1
|
||
|
|
pending.bitType = HOT_HEALS
|
||
|
|
|
||
|
|
-- As you can't rely on a hot being the absolutely only one up, have to apply the total amount now :<
|
||
|
|
local ticksLeft = ceil((endTime - GetTime()) / pending.tickInterval)
|
||
|
|
loadHealList(pending, tickAmount, stack, endTime, ticksLeft, ...)
|
||
|
|
|
||
|
|
if( not wasUpdated ) then
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealStarted", casterGUID, spellID, pending.bitType, endTime, unpack(tempPlayerList))
|
||
|
|
else
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealUpdated", casterGUID, spellID, pending.bitType, endTime, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function parseHotBomb(casterGUID, wasUpdated, spellID, amount, ...)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID)
|
||
|
|
if( not amount or not spellName or select("#", ...) == 0 ) then return end
|
||
|
|
|
||
|
|
-- If we don't have a pending hot then there is no bomb as far as were concerned
|
||
|
|
local hotPending = pendingHots[casterGUID] and pendingHots[casterGUID][spellName]
|
||
|
|
if( not hotPending or not hotPending.bitType ) then return end
|
||
|
|
hotPending.hasBomb = true
|
||
|
|
|
||
|
|
pendingHeals[casterGUID] = pendingHeals[casterGUID] or {}
|
||
|
|
pendingHeals[casterGUID][spellName] = pendingHeals[casterGUID][spellName] or {}
|
||
|
|
|
||
|
|
local pending = pendingHeals[casterGUID][spellName]
|
||
|
|
pending.endTime = hotPending.endTime
|
||
|
|
pending.spellID = spellID
|
||
|
|
pending.bitType = BOMB_HEALS
|
||
|
3 years ago
|
pending.stack = isWrath and hotPending.stack or 1 -- TBC Lifebloom bomb heal does not stack
|
||
|
4 years ago
|
|
||
|
|
loadHealList(pending, amount, pending.stack, pending.endTime, nil, ...)
|
||
|
|
|
||
|
|
if( not wasUpdated ) then
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealStarted", casterGUID, spellID, pending.bitType, pending.endTime, unpack(tempPlayerList))
|
||
|
|
else
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealUpdated", casterGUID, spellID, pending.bitType, pending.endTime, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Heal finished
|
||
|
|
local function parseHealEnd(casterGUID, pending, checkField, spellID, interrupted, ...)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
if( not spellName or not casterGUID ) then return end
|
||
|
|
|
||
|
|
if( not pending ) then
|
||
|
|
if pendingHeals[casterGUID] then
|
||
|
|
pending = pendingHeals[casterGUID][spellName]
|
||
|
|
end
|
||
|
|
if (not pending) and pendingHots[casterGUID] then
|
||
|
|
pending = pendingHots[casterGUID][spellName]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if( not pending or not pending.bitType ) then return end
|
||
|
|
|
||
|
|
wipe(tempPlayerList)
|
||
|
|
|
||
|
|
if( select("#", ...) == 0 ) then
|
||
|
|
for i=#(pending), 1, -5 do
|
||
|
|
tinsert(tempPlayerList, pending[i - 4])
|
||
|
|
removeRecord(pending, pending[i - 4])
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for i=1, select("#", ...) do
|
||
|
|
local guid = decompressGUID[select(i, ...)]
|
||
|
|
|
||
|
|
if guid then
|
||
|
|
tinsert(tempPlayerList, guid)
|
||
|
|
removeRecord(pending, guid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Double check and make sure we actually removed at least one person
|
||
|
|
if( #(tempPlayerList) == 0 ) then return end
|
||
|
|
|
||
|
|
-- Heals that also have a bomb associated to them have to end at this point, they will fire there own callback too
|
||
|
|
local bombPending = pending.hasBomb and pendingHeals[casterGUID][spellName]
|
||
|
|
if( bombPending and bombPending.bitType ) then
|
||
|
|
parseHealEnd(casterGUID, bombPending, "name", spellID, interrupted, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
local bitType = pending.bitType
|
||
|
|
-- Clear data if we're done
|
||
|
|
if( #(pending) == 0 ) then wipe(pending) end
|
||
|
|
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealStopped", casterGUID, spellID, bitType, interrupted, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.parseHealEnd = parseHealEnd
|
||
|
|
|
||
|
|
-- Heal delayed
|
||
|
|
local function parseHealDelayed(casterGUID, startTimeRelative, endTimeRelative, spellID)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
local startTime = startTimeRelative + GetTime()
|
||
|
|
local endTime = endTimeRelative + GetTime()
|
||
|
|
if not casterGUID then return end
|
||
|
|
local pending = (pendingHeals[casterGUID][spellName] or pendingHots[casterGUID][spellName])
|
||
|
|
-- It's possible to get duplicate interrupted due to raid1 = party1, player = raid# etc etc, just block it here
|
||
|
|
if( pending.endTime == endTime and pending.startTime == startTime ) then return end
|
||
|
|
|
||
|
|
-- Casted heal
|
||
|
|
if( pending.bitType == DIRECT_HEALS ) then
|
||
|
|
pending.startTime = startTime
|
||
|
|
pending.endTime = endTime
|
||
|
|
-- Channel heal
|
||
|
|
elseif( pending.bitType == CHANNEL_HEALS ) then
|
||
|
|
pending.startTime = startTime
|
||
|
|
pending.endTime = endTime
|
||
|
|
pending.duration = endTime - startTime
|
||
|
|
pending.tickInterval = pending.duration / pending.totalTicks
|
||
|
|
else
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
wipe(tempPlayerList)
|
||
|
|
for i=1, #(pending), 5 do
|
||
|
|
pending[i + 3] = endTime
|
||
|
|
tinsert(tempPlayerList, pending[i])
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealDelayed", casterGUID, pending.spellID, pending.bitType, pending.endTime, unpack(tempPlayerList))
|
||
|
|
end
|
||
|
|
|
||
|
|
-- After checking around 150-200 messages in battlegrounds, server seems to always be passed (if they are from another server)
|
||
|
|
-- Channels use tick total because the tick interval varies by haste
|
||
|
|
-- Hots use tick interval because the total duration varies but the tick interval stays the same
|
||
|
|
function HealComm:CHAT_MSG_ADDON(prefix, message, channel, sender)
|
||
|
|
if( prefix ~= COMM_PREFIX or channel ~= distribution ) then return end
|
||
|
|
|
||
|
|
local commType, extraArg, spellID, arg1, arg2, arg3, arg4, arg5, arg6 = strsplit(":", message)
|
||
|
|
local casterGUID = UnitGUID(Ambiguate(sender, "none"))
|
||
|
|
spellID = tonumber(spellID)
|
||
|
|
|
||
|
|
if( not commType or not spellID or not casterGUID or casterGUID == playerGUID) then return end
|
||
|
|
|
||
|
|
-- New direct heal - D:<extra>:<spellID>:<amount>:target1,target2...
|
||
|
|
if( commType == "D" and arg1 and arg2 ) then
|
||
|
|
parseDirectHeal(casterGUID, spellID, tonumber(arg1), tonumber(extraArg), strsplit(",", arg2))
|
||
|
|
-- New channel heal - C:<extra>:<spellID>:<amount>:<totalTicks>:target1,target2...
|
||
|
|
elseif( commType == "C" and arg1 and arg3 ) then
|
||
|
3 years ago
|
-- Penance heals its first tick instantly and by now has already happened
|
||
|
4 years ago
|
parseChannelHeal(casterGUID, spellID, tonumber(arg1), tonumber(arg2), strsplit(",", arg3))
|
||
|
|
-- New hot with a "bomb" component - B:<totalTicks>:<spellID>:<bombAmount>:target1,target2:<amount>:<isMulti>:<tickInterval>:target1,target2...
|
||
|
|
elseif( commType == "B" and arg1 and arg6 ) then
|
||
|
|
parseHotHeal(casterGUID, false, spellID, tonumber(arg3), tonumber(extraArg), tonumber(arg5), strsplit(",", arg6))
|
||
|
|
parseHotBomb(casterGUID, false, spellID, tonumber(arg1), strsplit(",", arg2))
|
||
|
|
-- New hot - H:<totalTicks>:<spellID>:<amount>:<isMulti>:<tickInterval>:target1,target2...
|
||
|
|
elseif( commType == "H" and arg1 and arg4 ) then
|
||
|
|
parseHotHeal(casterGUID, false, spellID, tonumber(arg1), tonumber(extraArg), tonumber(arg3), strsplit(",", arg4))
|
||
|
|
-- New updated heal somehow before ending - U:<totalTicks>:<spellID>:<amount>:<tickInterval>:target1,target2...
|
||
|
|
elseif( commType == "U" and arg1 and arg3 ) then
|
||
|
|
parseHotHeal(casterGUID, true, spellID, tonumber(arg1), tonumber(extraArg), tonumber(arg2), strsplit(",", arg3))
|
||
|
3 years ago
|
-- New variable tick hot - VH::<spellID>:<amount>:<isMulti>:<tickInterval>:target1,target2...
|
||
|
|
elseif( commType == "VH" and arg1 and arg4 ) then
|
||
|
|
parseHotHeal(casterGUID, false, spellID, arg1, tonumber(arg3), tonumber(extraArg), string.split(",", arg4))
|
||
|
|
-- New updated variable tick hot - U::<spellID>:amount1@amount2@amount3:<tickTotal>:target1,target2...
|
||
|
|
elseif( commType == "VU" and arg1 and arg3 ) then
|
||
|
|
parseHotHeal(casterGUID, true, spellID, arg1, tonumber(arg2), tonumber(extraArg), string.split(",", arg3))
|
||
|
4 years ago
|
-- New updated bomb hot - UB:<totalTicks>:<spellID>:<bombAmount>:target1,target2:<amount>:<tickInterval>:target1,target2...
|
||
|
|
elseif( commType == "UB" and arg1 and arg5 ) then
|
||
|
|
parseHotHeal(casterGUID, true, spellID, tonumber(arg3), tonumber(extraArg), tonumber(arg4), strsplit(",", arg5))
|
||
|
|
parseHotBomb(casterGUID, true, spellID, tonumber(arg1), strsplit(",", arg2))
|
||
|
|
-- Heal stopped - S:<extra>:<spellID>:<ended early: 0/1>:target1,target2...
|
||
|
|
elseif( commType == "S" or commType == "HS" ) then
|
||
|
|
local interrupted = arg1 == "1" and true or false
|
||
|
|
local checkType = commType == "HS" and "id" or "name"
|
||
|
|
local pending = commType == "HS" and pendingHots[casterGUID] and pendingHots[casterGUID][GetSpellInfo(spellID)]
|
||
|
|
|
||
|
|
if( arg2 and arg2 ~= "" ) then
|
||
|
|
parseHealEnd(casterGUID, pending, checkType, spellID, interrupted, strsplit(",", arg2))
|
||
|
|
else
|
||
|
|
parseHealEnd(casterGUID, pending, checkType, spellID, interrupted)
|
||
|
|
end
|
||
|
|
elseif commType == "F" then
|
||
|
|
parseHealDelayed(casterGUID, tonumber(arg1), tonumber(arg2), spellID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Bucketing reduces the number of events triggered for heals such as Tranquility that hit multiple targets
|
||
|
|
-- instead of firing 5 events * ticks it will fire 1 (maybe 2 depending on lag) events
|
||
|
|
HealComm.bucketHeals = HealComm.bucketHeals or {}
|
||
|
|
local bucketHeals = HealComm.bucketHeals
|
||
|
|
local BUCKET_FILLED = 0.30
|
||
|
|
|
||
|
|
HealComm.bucketFrame = HealComm.bucketFrame or CreateFrame("Frame")
|
||
|
|
HealComm.bucketFrame:Hide()
|
||
|
|
|
||
|
|
HealComm.bucketFrame:SetScript("OnUpdate", function(self, elapsed)
|
||
|
|
local totalLeft = 0
|
||
|
|
for casterGUID, spells in pairs(bucketHeals) do
|
||
|
|
for _, data in pairs(spells) do
|
||
|
|
if( data.timeout ) then
|
||
|
|
data.timeout = data.timeout - elapsed
|
||
|
|
if( data.timeout <= 0 ) then
|
||
|
|
-- This shouldn't happen, on the offhand chance it does then don't bother sending an event
|
||
|
|
if( #(data) == 0 or not data.spellID or not data.spellName ) then
|
||
|
|
wipe(data)
|
||
|
|
-- We're doing a bucket for a tick heal like Tranquility or Wild Growth
|
||
|
|
elseif( data.type == "tick" ) then
|
||
|
|
local pending = pendingHots[casterGUID] and pendingHots[casterGUID][data.spellName]
|
||
|
|
if( pending and pending.bitType ) then
|
||
|
|
local endTime = select(3, getRecord(pending, data[1]))
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealUpdated", casterGUID, pending.spellID, pending.bitType, endTime, unpack(data))
|
||
|
|
end
|
||
|
|
|
||
|
|
wipe(data)
|
||
|
|
-- We're doing a bucket for a cast thats a multi-target heal like Wild Growth or Prayer of Healing
|
||
|
|
elseif( data.type == "heal" ) then
|
||
|
3 years ago
|
local bitType, amount, totalTicks, tickInterval, _, hasVariableTicks = CalculateHotHealing(data[1], data.spellID)
|
||
|
4 years ago
|
if( bitType ) then
|
||
|
3 years ago
|
local targets = GetHealTargets(bitType, data[1], data.spellID, data)
|
||
|
|
parseHotHeal(playerGUID, false, data.spellID, amount, totalTicks, tickInterval, strsplit(",", targets))
|
||
|
|
|
||
|
|
if( not hasVariableTicks ) then
|
||
|
|
sendMessage(format("H:%d:%d:%d::%d:%s", totalTicks, data.spellID, amount, tickInterval, targets))
|
||
|
|
else
|
||
|
|
sendMessage(format("VH:%d:%d:%s::%d:%s", tickInterval, data.spellID, table.concat(amount, "@"), totalTicks, targets))
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
wipe(data)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
totalLeft = totalLeft + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( totalLeft <= 0 ) then
|
||
|
|
self:Hide()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- Monitor aura changes as well as new hots being cast
|
||
|
|
local eventRegistered = {
|
||
|
|
SPELL_HEAL = true,
|
||
|
|
SPELL_PERIODIC_HEAL = true,
|
||
|
|
SPELL_AURA_REMOVED = true,
|
||
|
|
SPELL_AURA_APPLIED = true,
|
||
|
|
SPELL_AURA_REFRESH = true,
|
||
|
|
SPELL_AURA_APPLIED_DOSE = true,
|
||
|
|
SPELL_AURA_REMOVED_DOSE = true,
|
||
|
|
}
|
||
|
|
|
||
|
|
function HealComm:COMBAT_LOG_EVENT_UNFILTERED(...)
|
||
|
|
local timestamp, eventType, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = ...
|
||
|
|
if( not eventRegistered[eventType] ) then return end
|
||
|
|
|
||
|
|
local _, spellName = select(12, ...)
|
||
|
|
local destUnit = guidToUnit[destGUID]
|
||
|
|
local spellID = destUnit and select(10, unitHasAura(destUnit, spellName)) or select(7, GetSpellInfo(spellName))
|
||
|
|
|
||
|
|
-- Heal or hot ticked that the library is tracking
|
||
|
|
-- It's more efficient/accurate to have the library keep track of this locally, spamming the comm channel would not be a very good thing especially when a single player can have 4 - 8 hots/channels going on them.
|
||
|
|
if( eventType == "SPELL_HEAL" or eventType == "SPELL_PERIODIC_HEAL" ) then
|
||
|
|
local pending = sourceGUID and pendingHots[sourceGUID] and pendingHots[sourceGUID][spellName]
|
||
|
|
if( pending and pending[destGUID] and pending.bitType and bit.band(pending.bitType, OVERTIME_HEALS) > 0 ) then
|
||
|
|
local amount, stack, _, ticksLeft = getRecord(pending, destGUID)
|
||
|
|
ticksLeft = ticksLeft - 1
|
||
|
|
local endTime = GetTime() + pending.tickInterval * ticksLeft
|
||
|
|
|
||
|
|
updateRecord(pending, destGUID, amount, stack, endTime, ticksLeft)
|
||
|
|
|
||
|
4 years ago
|
if( pending.isMultiTarget and sourceGUID ) then
|
||
|
4 years ago
|
bucketHeals[sourceGUID] = bucketHeals[sourceGUID] or {}
|
||
|
4 years ago
|
bucketHeals[sourceGUID][spellName] = bucketHeals[sourceGUID][spellName] or {}
|
||
|
4 years ago
|
|
||
|
4 years ago
|
local spellBucket = bucketHeals[sourceGUID][spellName]
|
||
|
4 years ago
|
if( not spellBucket[destGUID] ) then
|
||
|
|
spellBucket.timeout = BUCKET_FILLED
|
||
|
|
spellBucket.type = "tick"
|
||
|
|
spellBucket.spellName = spellName
|
||
|
|
spellBucket.spellID = spellID
|
||
|
|
spellBucket[destGUID] = true
|
||
|
|
tinsert(spellBucket, destGUID)
|
||
|
|
|
||
|
|
self.bucketFrame:Show()
|
||
|
|
end
|
||
|
|
else
|
||
|
|
HealComm.callbacks:Fire("HealComm_HealUpdated", sourceGUID, spellID, pending.bitType, endTime, destGUID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- New hot was applied
|
||
|
|
elseif( ( eventType == "SPELL_AURA_APPLIED" or eventType == "SPELL_AURA_REFRESH" or eventType == "SPELL_AURA_APPLIED_DOSE" ) and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) == COMBATLOG_OBJECT_AFFILIATION_MINE ) then
|
||
|
|
if( hotData[spellName] ) then
|
||
|
|
-- Single target so we can just send it off now thankfully
|
||
|
3 years ago
|
local bitType, amount, totalTicks, tickInterval, bombAmount, hasVariableTicks = CalculateHotHealing(destGUID, spellID)
|
||
|
4 years ago
|
if( bitType ) then
|
||
|
3 years ago
|
local targets = GetHealTargets(type, destGUID, spellID)
|
||
|
4 years ago
|
if targets then
|
||
|
3 years ago
|
parseHotHeal(sourceGUID, false, spellID, amount, totalTicks, tickInterval, strsplit(",", targets))
|
||
|
4 years ago
|
|
||
|
|
-- Hot with a bomb!
|
||
|
|
if( bombAmount ) then
|
||
|
3 years ago
|
local bombTargets = GetHealTargets(BOMB_HEALS, destGUID, spellName)
|
||
|
4 years ago
|
parseHotBomb(sourceGUID, false, spellID, bombAmount, strsplit(",", bombTargets))
|
||
|
|
sendMessage(format("B:%d:%d:%d:%s:%d::%d:%s", totalTicks, spellID, bombAmount, bombTargets, amount, tickInterval, targets))
|
||
|
3 years ago
|
elseif( hasVariableTicks ) then
|
||
|
|
sendMessage(format("VH:%d:%d:%s::%d:%s", tickInterval, spellID, table.concat(amount, "@"), totalTicks, targets))
|
||
|
4 years ago
|
else
|
||
|
|
sendMessage(format("H:%d:%d:%d::%d:%s", totalTicks, spellID, amount, tickInterval, targets))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
-- Single stack of a hot was removed, this only applies when going from 2 -> 1, when it goes from 1 -> 0 it fires SPELL_AURA_REMOVED
|
||
|
|
elseif( eventType == "SPELL_AURA_REMOVED_DOSE" and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) == COMBATLOG_OBJECT_AFFILIATION_MINE ) then
|
||
|
|
local pending = sourceGUID and pendingHeals[sourceGUID] and pendingHeals[sourceGUID][spellID]
|
||
|
|
if( pending and pending.bitType ) then
|
||
|
|
local amount = getRecord(pending, destGUID)
|
||
|
|
if( amount ) then
|
||
|
|
parseHotHeal(sourceGUID, true, spellID, amount, pending.totalTicks, pending.tickInterval, compressGUID[destGUID])
|
||
|
|
|
||
|
|
-- Plant the bomb
|
||
|
|
local bombPending = pending.hasBomb and pendingHeals[sourceGUID][spellName]
|
||
|
|
if( bombPending and bombPending.bitType ) then
|
||
|
|
local bombAmount = getRecord(bombPending, destGUID)
|
||
|
|
if( bombAmount ) then
|
||
|
|
parseHotBomb(sourceGUID, true, spellID, bombAmount, compressGUID[destGUID])
|
||
|
|
|
||
|
|
sendMessage(format("UB:%s:%d:%d:%s:%d:%d:%s", pending.totalTicks, spellID, bombAmount, compressGUID[destGUID], amount, pending.tickInterval, compressGUID[destGUID]))
|
||
|
|
return
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
if( pending.hasVariableTicks ) then
|
||
|
|
sendMessage(format("VU:%d:%d:%s:%d:%s", pending.tickInterval, spellID, amount, pending.totalTicks, compressGUID[destGUID]))
|
||
|
|
else
|
||
|
|
sendMessage(format("U:%s:%d:%d:%d:%s", spellID, amount, pending.totalTicks, pending.tickInterval, compressGUID[destGUID]))
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
end
|
||
|
|
-- Aura faded
|
||
|
|
elseif( eventType == "SPELL_AURA_REMOVED" and bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) == COMBATLOG_OBJECT_AFFILIATION_MINE ) then
|
||
|
|
if compressGUID[destGUID] then
|
||
|
|
-- Hot faded that we cast
|
||
|
|
local pending = pendingHots[playerGUID] and pendingHots[playerGUID][spellName]
|
||
|
|
if hotData[spellName] then
|
||
|
|
parseHealEnd(sourceGUID, pending, "id", spellID, false, compressGUID[destGUID])
|
||
|
|
sendMessage(format("HS::%d::%s", spellID, compressGUID[destGUID]))
|
||
|
|
elseif spellData[spellName] and spellData[spellName]._isChanneled then
|
||
|
|
parseHealEnd(sourceGUID, pending, "id", spellID, false, compressGUID[destGUID])
|
||
|
|
sendMessage(format("S::%d:0:%s", spellID, compressGUID[destGUID]))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Spell cast magic
|
||
|
|
-- When auto self cast is on, the UNIT_SPELLCAST_SENT event will always come first followed by the funciton calls
|
||
|
|
-- Otherwise either SENT comes first then function calls, or some function calls then SENT then more function calls
|
||
|
|
local castTarget, mouseoverGUID, mouseoverName, hadTargetingCursor, lastSentID, lastTargetGUID, lastTargetName
|
||
|
|
local lastFriendlyGUID, lastFriendlyName, lastGUID, lastName, lastIsFriend
|
||
|
|
local castGUIDs, guidPriorities = {}, {}
|
||
|
|
|
||
|
|
-- Deals with the fact that functions are called differently
|
||
|
|
-- Why a table when you can only cast one spell at a time you ask? When you factor in lag and mash clicking it's possible to:
|
||
|
|
-- cast A, interrupt it, cast B and have A fire SUCEEDED before B does, the tables keeps it from bugging out
|
||
|
|
local function setCastData(priority, name, guid)
|
||
|
|
if( not guid or not lastSentID ) then return end
|
||
|
|
if( guidPriorities[lastSentID] and guidPriorities[lastSentID] >= priority ) then return end
|
||
|
|
|
||
|
|
-- This is meant as a way of locking a cast in because which function has accurate data can be called into question at times, one of them always does though
|
||
|
|
-- this means that as soon as it finds a name match it locks the GUID in until another SENT is fired. Technically it's possible to get a bad GUID but it first requires
|
||
|
|
-- the functions to return different data and it requires the messed up call to be for another name conflict.
|
||
|
|
if( castTarget and castTarget == name ) then priority = 99 end
|
||
|
|
|
||
|
|
castGUIDs[lastSentID] = guid
|
||
|
|
guidPriorities[lastSentID] = priority
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
-- Penance sends different spellIDs on UNIT_SPELLCAST_SENT and UNIT_SPELLCAST_START. Reported on beta / remove this once fixed.
|
||
|
|
local penanceIDs = {
|
||
|
|
[47540] = 47757,
|
||
|
|
[53005] = 52986,
|
||
|
|
[53006] = 52987,
|
||
|
|
[53007] = 52988,
|
||
|
|
}
|
||
|
4 years ago
|
-- When the game tries to figure out the UnitID from the name it will prioritize players over non-players
|
||
|
|
-- if there are conflicts in names it will pull the one with the least amount of current health
|
||
|
|
function HealComm:UNIT_SPELLCAST_SENT(unit, targetName, castGUID, spellID)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
if(unit ~= "player") then return end
|
||
|
|
|
||
|
|
if hotData[spellName] or spellData[spellName] then
|
||
|
|
targetName = targetName or UnitName("player")
|
||
|
|
|
||
|
|
castTarget = gsub(targetName, "(.-)%-(.*)$", "%1")
|
||
|
3 years ago
|
lastSentID = penanceIDs[spellID] or spellID
|
||
|
4 years ago
|
|
||
|
|
-- Self cast is off which means it's possible to have a spell waiting for a target.
|
||
|
|
-- It's possible that it's the mouseover unit, but if a Target, TargetLast or AssistUnit call comes right after it means it's casting on that instead instead.
|
||
|
|
if( hadTargetingCursor ) then
|
||
|
|
hadTargetingCursor = nil
|
||
|
|
self.resetFrame:Show()
|
||
|
|
|
||
|
|
guidPriorities[lastSentID] = nil
|
||
|
|
setCastData(5, mouseoverName, mouseoverGUID)
|
||
|
|
else
|
||
|
|
-- If the player is ungrouped and healing, you can't take advantage of the name -> "unit" map, look in the UnitIDs that would most likely contain the information that's needed.
|
||
|
|
local guid = UnitGUID(targetName)
|
||
|
|
if( not guid ) then
|
||
|
|
guid = UnitName("target") == castTarget and UnitGUID("target") or UnitName("focus") == castTarget and UnitGUID("focus") or UnitName("mouseover") == castTarget and UnitGUID("mouseover") or UnitName("targettarget") == castTarget and UnitGUID("target") or UnitName("focustarget") == castTarget and UnitGUID("focustarget")
|
||
|
|
end
|
||
|
|
|
||
|
|
guidPriorities[lastSentID] = nil
|
||
|
|
setCastData(0, nil, guid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
local PlayerTargetSpells = {
|
||
|
|
[GetSpellInfo(689)] = true, -- Drain Life
|
||
|
|
[GetSpellInfo(740)] = true, -- Tranquility
|
||
|
|
[GetSpellInfo(331)] = true, -- Healing Wave
|
||
|
|
}
|
||
|
|
if isTBC or isWrath then
|
||
|
|
PlayerTargetSpells[GetSpellInfo(32546)] = true -- Binding Heal
|
||
|
|
end
|
||
|
4 years ago
|
function HealComm:UNIT_SPELLCAST_START(unit, cast, spellID)
|
||
|
|
if( unit ~= "player") then return end
|
||
|
|
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
3 years ago
|
|
||
|
4 years ago
|
if (not spellData[spellName] or UnitIsCharmed("player") or not UnitPlayerControlled("player") ) then return end
|
||
|
|
|
||
|
|
local castGUID = castGUIDs[spellID]
|
||
|
|
local castUnit = guidToUnit[castGUID]
|
||
|
3 years ago
|
if (isTBC or isWrath) and not castUnit and PlayerTargetSpells[spellName] then
|
||
|
4 years ago
|
castGUID = UnitGUID("player")
|
||
|
|
castUnit = "player"
|
||
|
3 years ago
|
elseif glyphCache[55440] and not castUnit and PlayerTargetSpells[spellName] then -- Glyph of Healing Wave
|
||
|
|
castUnit = unit
|
||
|
4 years ago
|
end
|
||
|
|
if( not castGUID or not castUnit ) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Figure out who we are healing and for how much
|
||
|
|
local bitType, amount, ticks, tickInterval = CalculateHealing(castGUID, spellID, castUnit)
|
||
|
|
if not amount then return end
|
||
|
|
|
||
|
3 years ago
|
local targets, fakeAmount = GetHealTargets(bitType, castGUID, spellID, amount)
|
||
|
|
amount = fakeAmount or amount
|
||
|
4 years ago
|
if not targets then return end -- only here until I compress/decompress npcs
|
||
|
|
|
||
|
|
if( bitType == DIRECT_HEALS ) then
|
||
|
|
local startTime, endTime = select(4, CastingInfo())
|
||
|
3 years ago
|
parseDirectHeal(playerGUID, spellID, amount, (endTime - startTime) / 1000, strsplit(",", targets))
|
||
|
|
sendMessage(format("D:%.3f:%d:%d:%s", (endTime - startTime) / 1000, spellID or 0, amount or "", targets))
|
||
|
4 years ago
|
elseif( bitType == CHANNEL_HEALS ) then
|
||
|
3 years ago
|
parseChannelHeal(playerGUID, spellID, amount, ticks, string.split(",", targets))
|
||
|
|
if spellName == GetSpellInfo(740) or spellName == GetSpellInfo(746) then
|
||
|
|
sendMessage(string.format("C::%d:%d:%s:%s", spellID, amount, ticks, targets))
|
||
|
|
else
|
||
|
|
-- Penance has its first tick already done by the time this arrives
|
||
|
|
sendMessage(string.format("C::%d:%d:%s:%s", spellID, amount, ticks - 1, targets))
|
||
|
|
end
|
||
|
4 years ago
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.UNIT_SPELLCAST_CHANNEL_START = HealComm.UNIT_SPELLCAST_START
|
||
|
|
|
||
|
|
local spellCastSucceeded = {}
|
||
|
|
local function hasNS()
|
||
|
|
local i=1
|
||
|
|
repeat
|
||
|
|
local spellID = select(10, UnitBuff("player", i))
|
||
|
|
if spellID == 17116 or spellID == 16188 then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
i = i + 1
|
||
|
|
until not spellID
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:UNIT_SPELLCAST_SUCCEEDED(unit, cast, spellID)
|
||
|
|
if( unit ~= "player") then return end
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
|
||
|
|
if spellID == 20216 then
|
||
|
|
hasDivineFavor = true
|
||
|
|
end
|
||
|
|
|
||
|
|
if spellData[spellName] and not spellData[spellName]._isChanneled and not hasNS() then
|
||
|
|
hasDivineFavor = nil
|
||
|
|
parseHealEnd(playerGUID, nil, "name", spellID, false)
|
||
|
|
sendMessage(format("S::%d:0", spellID or 0))
|
||
|
|
spellCastSucceeded[spellID] = true
|
||
|
|
elseif spellName == GetSpellInfo(20473) then -- Holy Shock
|
||
|
|
hasDivineFavor = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:UNIT_SPELLCAST_STOP(unit, castGUID, spellID)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
if( unit ~= "player" or not spellData[spellName] or spellData[spellName]._isChanneled ) then return end
|
||
|
|
|
||
|
|
if not spellCastSucceeded[spellID] then
|
||
|
|
parseHealEnd(playerGUID, nil, "name", spellID, true)
|
||
|
|
sendMessage(format("S::%d:1", spellID or 0))
|
||
|
|
end
|
||
|
|
|
||
|
|
spellCastSucceeded[spellID] = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Cast didn't go through, recheck any charge data if necessary
|
||
|
|
function HealComm:UNIT_SPELLCAST_INTERRUPTED(unit, castGUID, spellID)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
if( unit ~= "player" or not spellData[spellName] ) then return end
|
||
|
|
|
||
|
|
local guid = castGUIDs[spellID]
|
||
|
|
if( guid ) then
|
||
|
|
ResetChargeData(guid, spellID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:UNIT_SPELLCAST_DELAYED(unit, castGUID, spellID)
|
||
|
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
local casterGUID = UnitGUID(unit)
|
||
|
|
if( unit ~= "player" or not pendingHeals[casterGUID] or not pendingHeals[casterGUID][spellName] ) then return end
|
||
|
|
|
||
|
|
-- Direct heal delayed
|
||
|
|
if( pendingHeals[casterGUID][spellName].bitType == DIRECT_HEALS ) then
|
||
|
|
local startTime, endTime = select(4, CastingInfo())
|
||
|
|
if( startTime and endTime ) then
|
||
|
|
local startTimeRelative = startTime / 1000 - GetTime()
|
||
|
|
local endTimeRelative = endTime / 1000 - GetTime()
|
||
|
|
parseHealDelayed(casterGUID, startTimeRelative, endTimeRelative, spellID)
|
||
|
|
sendMessage(format("F::%d:%.3f:%.3f", spellID, startTimeRelative, endTimeRelative))
|
||
|
|
end
|
||
|
|
-- Channel heal delayed
|
||
|
|
elseif( pendingHeals[casterGUID][spellName].bitType == CHANNEL_HEALS ) then
|
||
|
|
local startTime, endTime = select(4, ChannelInfo())
|
||
|
|
if( startTime and endTime ) then
|
||
|
|
local startTimeRelative = startTime / 1000 - GetTime()
|
||
|
|
local endTimeRelative = endTime / 1000 - GetTime()
|
||
|
|
parseHealDelayed(casterGUID, startTimeRelative, endTimeRelative, spellID)
|
||
|
|
sendMessage(format("F::%d:%.3f:%.3f", spellID, startTimeRelative, endTimeRelative))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.UNIT_SPELLCAST_CHANNEL_UPDATE = HealComm.UNIT_SPELLCAST_DELAYED
|
||
|
|
|
||
|
|
-- Need to keep track of mouseover as it can change in the split second after/before casts
|
||
|
|
function HealComm:UPDATE_MOUSEOVER_UNIT()
|
||
|
|
mouseoverGUID = UnitCanAssist("player", "mouseover") and UnitGUID("mouseover")
|
||
|
|
mouseoverName = UnitCanAssist("player", "mouseover") and UnitName("mouseover")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Keep track of our last target/friendly target for the sake of /targetlast and /targetlastfriend
|
||
|
|
function HealComm:PLAYER_TARGET_CHANGED()
|
||
|
|
if( lastGUID and lastName ) then
|
||
|
|
if( lastIsFriend ) then
|
||
|
|
lastFriendlyGUID, lastFriendlyName = lastGUID, lastName
|
||
|
|
end
|
||
|
|
|
||
|
|
lastTargetGUID, lastTargetName = lastGUID, lastName
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Despite the fact that it's called target last friend, UnitIsFriend won't actually work
|
||
|
|
lastGUID = UnitGUID("target")
|
||
|
|
lastName = UnitName("target")
|
||
|
|
lastIsFriend = UnitCanAssist("player", "target")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Unit was targeted through a function
|
||
|
|
function HealComm:Target(unit)
|
||
|
|
if( self.resetFrame:IsShown() and UnitCanAssist("player", unit) ) then
|
||
|
|
setCastData(6, UnitName(unit), UnitGUID(unit))
|
||
|
|
end
|
||
|
|
|
||
|
|
self.resetFrame:Hide()
|
||
|
|
hadTargetingCursor = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- This is only needed when auto self cast is off, in which case this is called right after UNIT_SPELLCAST_SENT
|
||
|
|
-- because the player got a waiting-for-cast icon up and they pressed a key binding to target someone
|
||
|
|
HealComm.TargetUnit = HealComm.Target
|
||
|
|
|
||
|
|
-- Works the same as the above except it's called when you have a cursor icon and you click on a secure frame with a target attribute set
|
||
|
|
HealComm.SpellTargetUnit = HealComm.Target
|
||
|
|
|
||
|
|
-- Used in /assist macros
|
||
|
|
function HealComm:AssistUnit(unit)
|
||
|
|
if( self.resetFrame:IsShown() and UnitCanAssist("player", unit .. "target") ) then
|
||
|
|
setCastData(6, UnitName(unit .. "target"), UnitGUID(unit .. "target"))
|
||
|
|
end
|
||
|
|
|
||
|
|
self.resetFrame:Hide()
|
||
|
|
hadTargetingCursor = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Target last was used, the only reason this is called with reset frame being shown is we're casting on a valid unit
|
||
|
|
-- don't have to worry about the GUID no longer being invalid etc
|
||
|
|
function HealComm:TargetLast(guid, name)
|
||
|
|
if( name and guid and self.resetFrame:IsShown() ) then
|
||
|
|
setCastData(6, name, guid)
|
||
|
|
end
|
||
|
|
|
||
|
|
self.resetFrame:Hide()
|
||
|
|
hadTargetingCursor = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:TargetLastFriend()
|
||
|
|
self:TargetLast(lastFriendlyGUID, lastFriendlyName)
|
||
|
|
end
|
||
|
|
|
||
|
|
function HealComm:TargetLastTarget()
|
||
|
|
self:TargetLast(lastTargetGUID, lastTargetName)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Spell was cast somehow
|
||
|
|
function HealComm:CastSpell(arg, unit)
|
||
|
|
-- If the spell is waiting for a target and it's a spell action button then we know that the GUID has to be mouseover or a key binding cast.
|
||
|
|
if( unit and UnitCanAssist("player", unit) ) then
|
||
|
|
setCastData(4, UnitName(unit), UnitGUID(unit))
|
||
|
|
-- No unit, or it's a unit we can't assist
|
||
|
|
elseif( not SpellIsTargeting() ) then
|
||
|
|
if( UnitCanAssist("player", "target") ) then
|
||
|
|
setCastData(4, UnitName("target"), UnitGUID("target"))
|
||
|
|
else
|
||
|
|
setCastData(4, playerName, playerGUID)
|
||
|
|
end
|
||
|
|
|
||
|
|
hadTargetingCursor = nil
|
||
|
|
else
|
||
|
|
hadTargetingCursor = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
HealComm.CastSpellByName = HealComm.CastSpell
|
||
|
|
HealComm.CastSpellByID = HealComm.CastSpell
|
||
|
|
HealComm.UseAction = HealComm.CastSpell
|
||
|
|
|
||
|
|
-- Make sure we don't have invalid units in this
|
||
|
|
local function sanityCheckMapping()
|
||
|
|
for guid, unit in pairs(guidToUnit) do
|
||
|
|
-- Unit no longer exists, remove all healing for them
|
||
|
|
if guid ~= UnitGUID(unit) then
|
||
|
|
-- Check for (and remove) any active heals
|
||
|
|
for _, tbl in pairs({ pendingHeals, pendingHots }) do
|
||
|
|
if tbl[guid] then
|
||
|
|
for _, pending in pairs(tbl[guid]) do
|
||
|
|
if( pending.bitType ) then
|
||
|
|
parseHealEnd(guid, pending, nil, pending.spellID, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
pendingHeals[guid] = nil
|
||
|
|
pendingHots[guid] = nil
|
||
|
|
|
||
|
|
-- Remove any heals that are on them
|
||
|
|
removeAllRecords(guid)
|
||
|
|
|
||
|
|
guidToUnit[guid] = nil
|
||
|
|
guidToGroup[guid] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- 5s poll that tries to solve the problem of X running out of range while a HoT is ticking
|
||
|
|
-- this is not really perfect far from it in fact. If I can find a better solution I will switch to that.
|
||
|
|
if( not HealComm.hotMonitor ) then
|
||
|
|
HealComm.hotMonitor = CreateFrame("Frame")
|
||
|
|
HealComm.hotMonitor:Hide()
|
||
|
|
HealComm.hotMonitor.timeElapsed = 0
|
||
|
|
HealComm.hotMonitor:SetScript("OnUpdate", function(self, elapsed)
|
||
|
|
self.timeElapsed = self.timeElapsed + elapsed
|
||
|
|
if( self.timeElapsed < 5 ) then return end
|
||
|
|
self.timeElapsed = self.timeElapsed - 5
|
||
|
|
|
||
|
|
-- For the time being, it will only remove them if they don't exist and it found a valid unit
|
||
|
|
-- units that leave the raid are automatically removed
|
||
|
|
local found
|
||
|
|
for guid in pairs(activeHots) do
|
||
|
|
if( guidToUnit[guid] and not UnitIsVisible(guidToUnit[guid]) ) then
|
||
|
|
removeAllRecords(guid)
|
||
|
|
else
|
||
|
|
found = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if( not found ) then
|
||
|
|
self:Hide()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- After the player leaves a group, tables are wiped out or released for GC
|
||
|
|
local function clearGUIDData()
|
||
|
|
clearPendingHeals()
|
||
|
|
|
||
|
|
wipe(compressGUID)
|
||
|
|
wipe(decompressGUID)
|
||
|
|
wipe(activePets)
|
||
|
|
|
||
|
|
playerGUID = playerGUID or UnitGUID("player")
|
||
|
|
HealComm.guidToUnit = {[playerGUID] = "player"}
|
||
|
|
guidToUnit = HealComm.guidToUnit
|
||
|
|
|
||
|
|
HealComm.guidToGroup = {}
|
||
|
|
guidToGroup = HealComm.guidToGroup
|
||
|
|
|
||
|
|
HealComm.activeHots = {}
|
||
|
|
activeHots = HealComm.activeHots
|
||
|
|
|
||
|
|
HealComm.pendingHeals = {}
|
||
|
|
pendingHeals = HealComm.pendingHeals
|
||
|
|
|
||
|
|
HealComm.pendingHots = {}
|
||
|
|
pendingHots = HealComm.pendingHots
|
||
|
|
|
||
|
|
HealComm.bucketHeals = {}
|
||
|
|
bucketHeals = HealComm.bucketHeals
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Keeps track of pet GUIDs, as pets are considered vehicles this will also map vehicle GUIDs to unit
|
||
|
|
function HealComm:UNIT_PET(unit)
|
||
|
|
local guid = UnitGUID(unit)
|
||
|
|
unit = guidToUnit[guid]
|
||
|
|
|
||
|
|
if not unit then return end
|
||
|
|
|
||
|
|
local pet = self.unitToPet[unit]
|
||
|
|
local petGUID = pet and UnitGUID(pet)
|
||
|
|
|
||
|
|
-- We have an active pet guid from this user and it's different, kill it
|
||
|
|
local activeGUID = activePets[unit]
|
||
|
3 years ago
|
if activeGUID and petGUID and activeGUID ~= petGUID then
|
||
|
4 years ago
|
removeAllRecords(activeGUID)
|
||
|
|
|
||
|
|
rawset(self.compressGUID, activeGUID, nil)
|
||
|
3 years ago
|
rawset(self.decompressGUID, "p-"..strsub(guid,8), nil)
|
||
|
4 years ago
|
guidToUnit[activeGUID] = nil
|
||
|
|
guidToGroup[activeGUID] = nil
|
||
|
|
activePets[unit] = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Add the new record
|
||
|
|
if petGUID then
|
||
|
|
guidToUnit[petGUID] = pet
|
||
|
|
guidToGroup[petGUID] = guidToGroup[guid]
|
||
|
|
activePets[unit] = petGUID
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Keep track of raid GUIDs
|
||
|
|
function HealComm:GROUP_ROSTER_UPDATE()
|
||
|
|
updateDistributionChannel()
|
||
|
|
|
||
|
|
wipe(activePets)
|
||
|
|
|
||
|
|
local function update(unit)
|
||
|
|
local guid = UnitGUID(unit)
|
||
|
|
|
||
|
|
if guid then
|
||
|
|
local raidID = UnitInRaid(unit)
|
||
|
|
local group = raidID and select(3, GetRaidRosterInfo(raidID)) or 1
|
||
|
|
|
||
|
|
guidToUnit[guid] = unit
|
||
|
|
guidToGroup[guid] = group
|
||
|
|
|
||
|
|
local pet = self.unitToPet[unit]
|
||
|
|
local petGUID = pet and UnitGUID(pet)
|
||
|
|
|
||
|
|
activePets[unit] = petGUID
|
||
|
|
|
||
|
|
if petGUID then
|
||
|
|
guidToUnit[petGUID] = pet
|
||
|
|
guidToGroup[petGUID] = group
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if GetNumGroupMembers() == 0 then
|
||
|
|
clearGUIDData()
|
||
|
|
update("player")
|
||
|
|
elseif not IsInRaid() then
|
||
|
|
update("player")
|
||
|
|
|
||
|
|
for i = 1, MAX_PARTY_MEMBERS do
|
||
|
|
update(format("party%d", i))
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for i = 1, MAX_RAID_MEMBERS do
|
||
|
|
update(format("raid%d", i))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
sanityCheckMapping()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- PLAYER_ALIVE = got talent data
|
||
|
|
function HealComm:PLAYER_ALIVE()
|
||
|
|
self:CHARACTER_POINTS_CHANGED()
|
||
|
|
self.eventFrame:UnregisterEvent("PLAYER_ALIVE")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Initialize the library
|
||
|
|
function HealComm:OnInitialize()
|
||
|
|
-- If another instance already loaded then the tables should be wiped to prevent old data from persisting
|
||
|
|
-- in case of a spell being removed later on, only can happen if a newer LoD version is loaded
|
||
|
|
wipe(spellData)
|
||
|
|
wipe(hotData)
|
||
|
|
wipe(itemSetsData)
|
||
|
|
wipe(talentData)
|
||
|
|
|
||
|
|
-- Load all of the classes formulas and such
|
||
|
|
if LoadClassData then
|
||
|
|
LoadClassData()
|
||
|
|
end
|
||
|
|
|
||
|
|
do
|
||
|
|
local FirstAid = GetSpellInfo(746)
|
||
|
|
|
||
|
|
spellData[FirstAid] = {
|
||
|
3 years ago
|
_isChanneled = true,
|
||
|
|
ticks = {6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8},
|
||
|
4 years ago
|
interval = 1,
|
||
|
3 years ago
|
averages = {66, 114, 161, 301, 400, 640, 800, 1104, 1360, 2000, 2800, 3400, 4800, 5800}
|
||
|
4 years ago
|
}
|
||
|
|
|
||
|
|
local _GetHealTargets = GetHealTargets
|
||
|
|
|
||
|
3 years ago
|
GetHealTargets = function(bitType, guid, spellID, amount)
|
||
|
4 years ago
|
local spellName = GetSpellInfo(spellID)
|
||
|
|
|
||
|
|
if spellName == FirstAid then
|
||
|
3 years ago
|
return compressGUID[guid]
|
||
|
4 years ago
|
end
|
||
|
|
|
||
|
|
if _GetHealTargets then
|
||
|
3 years ago
|
return _GetHealTargets(bitType, guid, spellID, amount)
|
||
|
4 years ago
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local _CalculateHealing = CalculateHealing
|
||
|
|
|
||
|
|
CalculateHealing = function(guid, spellID, unit)
|
||
|
|
local spellName, spellRank = GetSpellInfo(spellID), SpellIDToRank[spellID]
|
||
|
|
|
||
|
|
if spellName == FirstAid then
|
||
|
|
local healAmount = spellData[spellName].averages[spellRank]
|
||
|
|
if not healAmount then return end
|
||
|
|
|
||
|
|
local ticks = spellData[spellName].ticks[spellRank]
|
||
|
|
|
||
|
|
return CHANNEL_HEALS, ceil(healAmount / ticks), ticks, spellData[spellName].interval
|
||
|
|
end
|
||
|
|
|
||
|
|
if _CalculateHealing then
|
||
|
|
return _CalculateHealing(guid, spellID, unit)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
3 years ago
|
-- Cache glyphs initially
|
||
|
|
for id=1, GetNumGlyphSockets() do
|
||
|
|
local enabled, _, glyphID = GetGlyphSocketInfo(id)
|
||
|
|
if( enabled and glyphID ) then
|
||
|
|
glyphCache[glyphID] = true
|
||
|
|
glyphCache[id] = glyphID
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
4 years ago
|
self:PLAYER_EQUIPMENT_CHANGED()
|
||
|
|
|
||
|
|
-- When first logging in talent data isn't available until at least PLAYER_ALIVE, so if we don't have data
|
||
|
|
-- will wait for that event otherwise will just cache it right now
|
||
|
|
if( GetNumTalentTabs() == 0 ) then
|
||
|
|
self.eventFrame:RegisterEvent("PLAYER_ALIVE")
|
||
|
|
else
|
||
|
|
self:CHARACTER_POINTS_CHANGED()
|
||
|
|
end
|
||
|
|
|
||
|
|
if( ResetChargeData ) then
|
||
|
|
HealComm.eventFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Finally, register it all
|
||
|
|
self.eventFrame:RegisterEvent("CHAT_MSG_ADDON")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_SENT")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_START")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_DELAYED")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
|
||
|
|
self.eventFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
||
|
|
self.eventFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
|
||
|
|
self.eventFrame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||
|
|
self.eventFrame:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||
|
|
self.eventFrame:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
|
||
|
|
self.eventFrame:RegisterEvent("PLAYER_LEVEL_UP")
|
||
|
|
self.eventFrame:RegisterEvent("CHARACTER_POINTS_CHANGED")
|
||
|
3 years ago
|
self.eventFrame:RegisterUnitEvent("UNIT_AURA", 'player')
|
||
|
|
if isWrath then
|
||
|
|
self.eventFrame:RegisterEvent("GLYPH_ADDED")
|
||
|
|
self.eventFrame:RegisterEvent("GLYPH_REMOVED")
|
||
|
|
self.eventFrame:RegisterEvent("GLYPH_UPDATED")
|
||
|
|
end
|
||
|
4 years ago
|
|
||
|
|
if( self.initialized ) then return end
|
||
|
|
self.initialized = true
|
||
|
|
|
||
|
|
self.resetFrame = CreateFrame("Frame")
|
||
|
|
self.resetFrame:Hide()
|
||
|
|
self.resetFrame:SetScript("OnUpdate", function(self) self:Hide() end)
|
||
|
|
|
||
|
|
-- You can't unhook secure hooks after they are done, so will hook once and the HealComm table will update with the latest functions
|
||
|
|
-- automagically. If a new function is ever used it'll need a specific variable to indicate those set of hooks.
|
||
|
|
-- By default most of these are mapped to a more generic function, but I call separate ones so I don't have to rehook
|
||
|
|
-- if it turns out I need to know something specific
|
||
|
|
hooksecurefunc("TargetUnit", function(...) HealComm:TargetUnit(...) end)
|
||
|
|
hooksecurefunc("SpellTargetUnit", function(...) HealComm:SpellTargetUnit(...) end)
|
||
|
|
hooksecurefunc("AssistUnit", function(...) HealComm:AssistUnit(...) end)
|
||
|
|
hooksecurefunc("UseAction", function(...) HealComm:UseAction(...) end)
|
||
|
|
hooksecurefunc("TargetLastFriend", function(...) HealComm:TargetLastFriend(...) end)
|
||
|
|
hooksecurefunc("TargetLastTarget", function(...) HealComm:TargetLastTarget(...) end)
|
||
|
|
hooksecurefunc("CastSpellByName", function(...) HealComm:CastSpellByName(...) end)
|
||
|
|
hooksecurefunc("CastSpellByID", function(...) HealComm:CastSpellByID(...) end)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- General event handler
|
||
|
|
local function OnEvent(self, event, ...)
|
||
|
|
if event == 'COMBAT_LOG_EVENT_UNFILTERED' then
|
||
|
|
HealComm[event](HealComm, CombatLogGetCurrentEventInfo())
|
||
|
|
else
|
||
|
|
HealComm[event](HealComm, ...)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Event handler
|
||
|
|
HealComm.eventFrame = HealComm.frame or HealComm.eventFrame or CreateFrame("Frame")
|
||
|
|
HealComm.eventFrame:UnregisterAllEvents()
|
||
|
|
HealComm.eventFrame:RegisterEvent("UNIT_PET")
|
||
|
|
HealComm.eventFrame:SetScript("OnEvent", OnEvent)
|
||
|
|
HealComm.frame = nil
|
||
|
|
|
||
|
|
-- At PLAYER_LEAVING_WORLD (Actually more like MIRROR_TIMER_STOP but anyway) UnitGUID("player") returns nil, delay registering
|
||
|
|
-- events and set a playerGUID/playerName combo for all players on PLAYER_LOGIN not just the healers.
|
||
|
|
function HealComm:PLAYER_LOGIN()
|
||
|
|
playerGUID = UnitGUID("player")
|
||
|
|
playerName = UnitName("player")
|
||
|
|
playerLevel = UnitLevel("player")
|
||
|
|
|
||
|
|
-- Oddly enough player GUID is not available on file load, so keep the map of player GUID to themselves too
|
||
|
|
guidToUnit[playerGUID] = "player"
|
||
|
|
|
||
|
|
self:OnInitialize()
|
||
|
|
|
||
|
|
self.eventFrame:UnregisterEvent("PLAYER_LOGIN")
|
||
|
|
self.eventFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
||
|
|
self.eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
|
|
self.eventFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
|
||
|
|
|
||
|
|
self:ZONE_CHANGED_NEW_AREA()
|
||
|
|
self:GROUP_ROSTER_UPDATE()
|
||
|
|
end
|
||
|
|
|
||
|
|
if( not IsLoggedIn() ) then
|
||
|
|
HealComm.eventFrame:RegisterEvent("PLAYER_LOGIN")
|
||
|
|
else
|
||
|
|
HealComm:PLAYER_LOGIN()
|
||
|
|
end
|