WoWInterface SVN oUF_Smee2

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /trunk/oUF_Smee2/Libs
    from Rev 4 to Rev 6
    Reverse comparison

Rev 4 → Rev 6

LibHealComm-3.0.lua New file
0,0 → 1,1396
local MAJOR_VERSION = "LibHealComm-3.0";
local MINOR_VERSION = 90000 + tonumber(("$Revision: 48 $"):match("%d+"));
 
local lib = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION);
if not lib then return end
 
local playerName = UnitName('player');
local playerRealm = GetRealmName();
local playerClass = select(2, UnitClass('player'));
local isHealer = (playerClass == "PRIEST") or (playerClass == "SHAMAN") or (playerClass == "DRUID") or (playerClass == "PALADIN");
 
 
-----------------
-- Event Frame --
-----------------
 
lib.EventFrame = lib.EventFrame or CreateFrame("Frame");
lib.EventFrame:SetScript("OnEvent", function (this, event, ...) lib[event](lib, ...) end);
lib.EventFrame:UnregisterAllEvents();
 
-- Register Events
lib.EventFrame:RegisterEvent("PLAYER_ALIVE");
lib.EventFrame:RegisterEvent("LEARNED_SPELL_IN_TAB");
lib.EventFrame:RegisterEvent("CHAT_MSG_ADDON");
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_DELAYED");
lib.EventFrame:RegisterEvent("UNIT_AURA");
lib.EventFrame:RegisterEvent("UNIT_TARGET");
lib.EventFrame:RegisterEvent("PLAYER_TARGET_CHANGED");
lib.EventFrame:RegisterEvent("PLAYER_FOCUS_CHANGED");
lib.EventFrame:RegisterEvent("GLYPH_ADDED");
lib.EventFrame:RegisterEvent("GLYPH_REMOVED");
lib.EventFrame:RegisterEvent("GLYPH_UPDATED");
 
-- For keeping track of versions
lib.EventFrame:RegisterEvent("PARTY_MEMBERS_CHANGED");
lib.EventFrame:RegisterEvent("RAID_ROSTER_UPDATE");
 
-- Prune data at zone change
lib.EventFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
 
-- Only listen to these events if player is healing class
if (isHealer) then
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_SENT");
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_START");
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED");
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_STOP");
end
 
 
----------------------
-- Scanning Tooltip --
----------------------
 
if (not lib.Tooltip) then
lib.Tooltip = CreateFrame("GameTooltip");
lib.Tooltip:SetOwner(UIParent, "ANCHOR_NONE");
for i = 1, 4 do
lib["TooltipTextLeft" .. i] = lib.Tooltip:CreateFontString();
lib["TooltipTextRight" .. i] = lib.Tooltip:CreateFontString();
lib.Tooltip:AddFontStrings(lib["TooltipTextLeft" .. i], lib["TooltipTextRight" .. i]);
end
end
 
 
-------------------------------
-- Embed CallbackHandler-1.0 --
-------------------------------
 
lib.Callbacks = LibStub("CallbackHandler-1.0"):New(lib);
 
 
-----------------
-- Static Data --
-----------------
 
-- Cache of spells and heal sizes
local SpellCache = {};
 
-- Cache of glyphs
local GlyphCache = {};
 
-- Info about spells being cast by other players
local HealTime = {};
local HealTarget = {};
local HealSize = {};
 
-- Healing Modifiers (by name)
local HealModifier = {};
 
-- Last target name from UNIT_SPELLCAST_SENT
local SentTargetName;
 
-- Last spellCastIndex from UNIT_SPELLCAST_STOP
local LastSpellCastIndex;
 
-- Info about the spell being cast by the player
local CastInfoIsCasting;
local CastInfoHealingTargetUnitID;
local CastInfoHealingTargetNames;
local CastInfoHealingSize;
local CastInfoEndTime;
 
-- Latency Measurement
local SentTime = 0;
local Latency = 0;
 
-- Version Information Table
local Versions = {};
 
-- Battleground/Arena/Group Indicators
local InBattlegroundOrArena;
local InRaidOrParty;
 
 
---------------------------------
-- Frequently Accessed Globals --
---------------------------------
 
local type = type;
local tonumber = tonumber;
local math = math;
local string = string;
local select = select;
local pairs = pairs;
local unpack = unpack;
local UnitName = UnitName;
local SendAddonMessage = SendAddonMessage;
local IsInInstance = IsInInstance;
local UnitBuff = UnitBuff;
local UnitDebuff = UnitDebuff;
local UnitLevel = UnitLevel;
local GetInventoryItemLink = GetInventoryItemLink;
local GetTime = GetTime;
local UnitCastingInfo = UnitCastingInfo;
local GetSpellBonusHealing = GetSpellBonusHealing;
local GetTalentInfo = GetTalentInfo;
local UnitExists = UnitExists;
local tinsert = table.insert;
local tconcat = table.concat;
 
 
---------------
-- Utilities --
---------------
 
local function unitFullName(unit)
local name, realm = UnitName(unit);
if (realm and realm ~= "") then
return name .. "-" .. realm;
else
return name;
end
end
 
local function extractRealm(fullName)
return fullName:match("^[^%-]+%-(.+)$");
end
 
-- Convert a remotely generated fully qualified name to
-- a local fully qualified name.
local function convertRealm(fullName, remoteRealm)
if (remoteRealm) then
local name, realm = fullName:match("^([^%-]+)%-(.+)$");
if (not realm) then
-- Apply remote realm if there is no realm on the target
return fullName .. "-" .. remoteRealm;
elseif (realm == playerRealm) then
-- Strip realm if it is equal to the local realm
return name;
end
end
return fullName;
end
 
local function commSend(contents, distribution, target)
SendAddonMessage("HealComm", contents, distribution or (InBattlegroundOrArena and "BATTLEGROUND" or "RAID"), target);
end
 
-- Spellbook Scanner --
local function getBaseHealSize(name)
 
-- Check if info is already cached
if (SpellCache[name]) then
return SpellCache[name];
end
 
SpellCache[name] = {};
 
-- Gather info (only done if not in cache)
local i = 1;
 
while true do
 
local spellName, spellRank = GetSpellName(i, BOOKTYPE_SPELL);
 
if (not spellName) then
break
end
 
if (spellName == name) then
-- This is the spell we're looking for, gather info
 
-- Determine rank
spellRank = tonumber(spellRank:match("(%d+)"));
lib.Tooltip:SetSpell(i, BOOKTYPE_SPELL);
 
-- Determine healing
local HealMin, HealMax = select(3, string.find(lib.TooltipTextLeft4:GetText() or lib.TooltipTextLeft3:GetText() or "", "(%d+) ?[\195\160tobisa到~\-]+ ?(%d+)"));
HealMin, HealMax = tonumber(HealMin) or 0, tonumber(HealMax) or 0;
local Heal = (HealMin + HealMax) / 2;
 
SpellCache[spellName][spellRank] = Heal;
end
i = i + 1;
end
 
return SpellCache[name];
end
 
local function detectGlyph(id)
 
-- Check if info is already cached
if (GlyphCache[id] ~= nil) then
return GlyphCache[id];
end
 
GlyphCache[id] = false;
 
-- Gather info (only done if not in cache)
for i = 1, GetNumGlyphSockets() do
local enabled, _, glyphId = GetGlyphSocketInfo(i);
if (enabled and glyphId) then
GlyphCache[glyphId] = true;
end
end
 
return GlyphCache[id];
end
 
-- Detects if a buff is present on the unit and returns the application number.
-- Optionally, if the third argument is provided and is true, then only return
-- true if the buff was placed by the player.
local function detectBuff(unit, buffName, mineOnly)
local name, _, _, count, _, _, _, isMine = UnitBuff(unit, buffName);
return name and (not mineOnly or isMine) and count or false;
end
 
--[[
[GetSpellInfo(604)] = -20, -- Dampen Magic (Rank 1)
[GetSpellInfo(8450)] = -40, -- Dampen Magic (Rank 2)
[GetSpellInfo(8451)] = -80, -- Dampen Magic (Rank 3)
[GetSpellInfo(10173)] = -120, -- Dampen Magic (Rank 4)
[GetSpellInfo(10174)] = -180, -- Dampen Magic (Rank 5)
[GetSpellInfo(33944)] = -240, -- Dampen Magic (Rank 6)
[GetSpellInfo(1008)] = 30, -- Amplify Magic (Rank 1)
[GetSpellInfo(8455)] = 60, -- Amplify Magic (Rank 2)
[GetSpellInfo(10169)] = 100, -- Amplify Magic (Rank 3)
[GetSpellInfo(10170)] = 150, -- Amplify Magic (Rank 4)
[GetSpellInfo(27130)] = 180, -- Amplify Magic (Rank 5)
[GetSpellInfo(33946)] = 240, -- Amplify Magic (Rank 6)
[GetSpellInfo(32858)] = -345 -- Touch of the Forgotten (Auchenai Crypts)
[GetSpellInfo(38377)] = -690 -- Touch of the Forgotten (Auchenai Crypts)
]]--
 
local healingBuffs =
{
[GetSpellInfo(706)] = 1.20, -- Demon Armor
[GetSpellInfo(45234)] = function (count, rank) return (1.0 + (0.04 + 0.03 * (rank - 1)) * count) end, -- Focused Will
[GetSpellInfo(34123)] = 1.06, -- Tree of Life
[GetSpellInfo(58549)] = function (count, rank, texture) return ((texture == "Interface\\Icons\\Ability_Warrior_StrengthOfArms") and (1.18 ^ count) or 1.0) end, -- Tenacity (Wintergrasp)
}
 
local healingDebuffs =
{
[GetSpellInfo(25646)] = function (count) return (1.0 - count * 0.10) end, -- Mortal Wound (Temporus - The Black Morass)
[GetSpellInfo(45347)] = function (count) return (1.0 - count * 0.04) end, -- Dark Touched (Grand Warlock Alythess - Sunwell Plateau)
[GetSpellInfo(30423)] = function (count) return (1.0 - count * 0.01) end, -- Nether Portal - Dominance (Netherspite - Karazhan)
[GetSpellInfo(13218)] = function (count) return (1.0 - count * 0.10) end, -- Wound Poison
[GetSpellInfo(19434)] = 0.50, -- Aimed Shot
[GetSpellInfo(12294)] = 0.50, -- Mortal Strike
[GetSpellInfo(40599)] = 0.50, -- Arcing Smash (Gurtogg Bloodboil)
[GetSpellInfo(23169)] = 0.50, -- Brood Affliction: Green (Chromaggus)
[GetSpellInfo(34073)] = 0.85, -- Curse of the Bleeding Hollow (Hellfire Peninsula)
[GetSpellInfo(13583)] = 0.50, -- Curse of the Deadwood (Deadwood Furbolgs - Felwood)
[GetSpellInfo(36023)] = 0.50, -- Deathblow
[GetSpellInfo(34625)] = 0.25, -- Demolish (Negatron - Netherstorm)
[GetSpellInfo(34366)] = 0.75, -- Ebon Poison (Black Morass)
[GetSpellInfo(32378)] = 0.50, -- Filet (Spectral Chef - Karazhan)
[GetSpellInfo(19716)] = 0.25, -- Gehennas' Curse (Gehennas - Molten Core)
[GetSpellInfo(36917)] = 0.50, -- Magma-Thrower's Curse (Sulfuron Magma-Thrower - The Arcatraz)
[GetSpellInfo(22859)] = 0.50, -- Mortal Cleave (High Priestess Thekal - Zul'Gurub)
[GetSpellInfo(38572)] = 0.50, -- Mortal Cleave (High Priestess Thekal - Zul'Gurub)
[GetSpellInfo(39595)] = 0.50, -- Mortal Cleave (High Priestess Thekal - Zul'Gurub)
[GetSpellInfo(28776)] = 0.10, -- Necrotic Poison (Maexxna - Naxxramas)
[GetSpellInfo(35189)] = 0.50, -- Solar Strike (The Mechanar)
[GetSpellInfo(32315)] = 0.50, -- Soul Strike (Ethereal Crypt Raiders - Mana-Tombs)
[GetSpellInfo(7068)] = 0.25, -- Veil of Shadow (Nefarian - Blackwing Lair)
[GetSpellInfo(38387)] = 1.50, -- Bane of Infinity (CoT: Escape from Durholde)
[GetSpellInfo(31977)] = 1.50, -- Curse of Infinity (CoT: Escape from Durholde)
[GetSpellInfo(41292)] = 0.00, -- Aura of Suffering (Essence of Souls - Black Temple)
[GetSpellInfo(41350)] = 2.00, -- Aura of Desire (Essence of Souls - Black Temple)
[GetSpellInfo(30843)] = 0.00, -- Enfeeble (Prince Malchezaar - Karazhan)
}
 
local function calculateHealModifier(unit)
local modifier = 1.0;
 
for i = 1, 40 do
local name, rank, texture, count = UnitDebuff(unit, i);
if (not name) then
break;
end
local mark = healingDebuffs[name];
if (mark) then
if (type(mark) == "function") then
mark = mark(count);
end
if (mark < modifier) then
modifier = mark;
end
end
end
for i = 1, 40 do
local name, rank, texture, count = UnitBuff(unit, i);
if (not name) then
break;
end
local mark = healingBuffs[name];
if (mark) then
if (type(mark) == "function") then
mark = mark(count, rank and tonumber(rank:match("(%d+)")), texture);
end
modifier = modifier * mark;
end
end
 
return modifier;
end
 
local function getDownrankingFactor(spellLevel, playerLevel)
local factor = 0.05 * ((spellLevel + 7) - playerLevel) + 1;
if (factor > 1.0) then
return 1;
elseif (factor < 0.0) then
return 0;
else
return factor;
end
end
 
local relicSlotNumber = GetInventorySlotInfo("RangedSlot");
local function getEquippedRelicID()
local itemLink = GetInventoryItemLink('player', relicSlotNumber);
if (itemLink) then
return tonumber(itemLink:match("(%d+):"));
end
end
 
 
-----------------------------
-- Healing Data Management --
-----------------------------
 
local function entryDelete(healerName)
local targetNames = HealTarget[healerName];
HealTime[healerName] = nil;
HealTarget[healerName] = nil;
if (type(targetNames) == "table") then
for i, targetName in pairs(targetNames) do
if HealSize[targetName] then
HealSize[targetName][healerName] = nil;
end
end
elseif (targetNames and HealSize[targetNames]) then
HealSize[targetNames][healerName] = nil;
end
end
 
local function entryUpdate(healerName, targetNames, healSize, healTime)
entryDelete(healerName);
HealTime[healerName] = healTime;
HealTarget[healerName] = targetNames;
if (type(targetNames) == "table") then
for i, targetName in pairs(targetNames) do
if (not HealSize[targetName]) then
HealSize[targetName] = {};
end
HealSize[targetName][healerName] = healSize;
end
elseif (targetNames) then
if (not HealSize[targetNames]) then
HealSize[targetNames] = {};
end
HealSize[targetNames][healerName] = healSize;
end
end
 
local function entryRetrieve(healerName)
local healTime = HealTime[healerName];
if (healTime) then
local targetNames = HealTarget[healerName];
if (type(targetNames) == "table") then
return targetNames, HealSize[targetNames[1]][healerName], healTime;
elseif (targetNames) then
return targetNames, HealSize[targetNames][healerName], healTime;
end
end
end
 
 
----------------------
-- Public Functions --
----------------------
 
--[[ UnitIncomingHealGet(unit, time)
 
Description: Retrieve info about the incoming heals to a specific
target. The second argument specifies a boundary time,
relative to the current time. Examples:
 
UnitIncomingHealGet("Kaki", GetTime() + 3)
UnitIncomingHealGet("Kaki-Emerald Dream", GetTime() + 3)
UnitIncomingHealGet("player", GetTime() + 3)
UnitIncomingHealGet("raid10", GetTime() + 3)
UnitIncomingHealGet("target", GetTime() + 3)
 
Retrieves info about the incoming heals on the specified
target. incomingHealBefore will contain the sum of heals
that will land within the next 3 seconds, and
incomingHealAfter will contain the sum of heals that will
land after 3 seconds.
 
Input:
unit - The exact name or UnitID of the unit to retrieve information about.
time - the desired boundary time of the inquiry.
 
Output:
incomingHealBefore - The total size of the incoming heals before the boundary time.
incomingHealAfter - The total size of the incoming heals after the boundary time.
nextTime - the time left until the next incoming heal will land.
nextSize - the size of the next incoming heal.
nextName - the name of the healer casting the next incoming heal.
 
]]--
 
function lib:UnitIncomingHealGet(unit, time)
if (type(unit) ~= "string") then return end
if (type(time) ~= "number") then return end
 
local targetName = unitFullName(unit);
if (HealSize[targetName]) then
local now = GetTime();
local incomingHealBefore, incomingHealAfter = 0, 0;
local nextTime, nextSize, nextName;
for healerName, size in pairs(HealSize[targetName]) do
local healTime = HealTime[healerName];
if (size and healTime) then
healTime = healTime + Latency;
if (healTime > now) then
if (healTime < time) then
-- Due before boundary time
incomingHealBefore = incomingHealBefore + size;
else
-- Due after boundary time
incomingHealAfter = incomingHealAfter + size;
end
if ((not nextTime) or (healTime < nextTime)) then
nextTime = healTime;
nextSize = size;
nextName = healerName;
end
end
end
end
if ((incomingHealBefore > 0) or (incomingHealAfter > 0)) then
return incomingHealBefore, incomingHealAfter, nextTime, nextSize, nextName;
end
end
end
 
--[[ UnitCastingHealGet(unit)
 
Description: Retrieve info about the direct healing spell
currently being cast by any unit. Examples:
 
UnitCastingHealGet("Kaki");
UnitCastingHealGet("Kaki-Emerald Dream");
UnitCastingHealGet("player")
UnitCastingHealGet("raid10")
UnitCastingHealGet("target")
 
Input:
unit - The name or UnitID of the unit to retrieve information about.
 
Output:
healSize - Size of the healing being cast.
endTime - The time when the healing completes.
targetName - Name of the unit(s) being targeted for heal.
 
]]--
 
function lib:UnitCastingHealGet(unit)
if (type(unit) ~= "string") then return end
local healerName = unitFullName(unit);
 
if (healerName == playerName) then
if (CastInfoIsCasting) then
return CastInfoHealingSize, CastInfoEndTime, CastInfoHealingTargetNames;
end
else
local targetNames, healSize, endTime = entryRetrieve(healerName);
if (targetNames) then
return healSize, endTime, targetNames;
end
end
end
 
--[[ UnitHealModifierGet(unit)
 
Description: Returns the modifier to healing (as a factor)
caused by buffs and debuffs. Examples:
 
UnitHealModifierGet("Kaki");
UnitHealModifierGet("Kaki-Emerald Dream");
UnitHealModifierGet("player", 3)
UnitHealModifierGet("raid10", 3)
UnitHealModifierGet("target", 3)
 
Input:
unit - The name or UnitID of the unit to retrieve information about.
 
Output:
factor - Always a fractional number - will be 1.0 if no buffs/debuffs
affect healing.
 
]]--
 
function lib:UnitHealModifierGet(unit)
if (type(unit) ~= "string") then return end
 
local targetName = unitFullName(unit);
return HealModifier[targetName] or calculateHealModifier(unit);
end
 
 
function lib:GetRaidOrPartyVersions()
local tab = {};
 
if (GetNumRaidMembers() > 0) then
for i = 1, GetNumRaidMembers() do
local name = unitFullName('raid' .. i);
if (not (name == playerName)) then
tab[name] = Versions[name] or false;
end
end
elseif (GetNumPartyMembers() > 0) then
for i = 1, GetNumPartyMembers() do
local name = unitFullName('party' .. i);
tab[name] = Versions[name] or false;
end
end
 
tab[playerName] = MINOR_VERSION;
 
return tab;
end
 
function lib:GetGuildVersions()
local tab = {};
 
if (IsInGuild()) then
GuildRoster();
 
for i = 1, GetNumGuildMembers(false) do
local name, rank, rankIndex, level, class, zone, note, officernote, online, status = GetGuildRosterInfo(i);
if (online and not (name == playerName)) then
tab[name] = Versions[name] or false;
end
end
end
 
tab[playerName] = MINOR_VERSION;
 
return tab;
end
 
function lib:GetUnitVersion(unit)
if (type(unit) ~= "string") then return end
 
local targetName = unitFullName(unit);
if (targetName == playerName) then return MINOR_VERSION end
return Versions[targetName] or false;
end
 
 
--------------------
-- Class Specific --
--------------------
 
local HealingSpells;
--local HotSpells;
local GetHealSize;
 
-- Druid --
 
-- TODO:
-- Talent: Empowered Rejuvenation. Increase effect of all HOTs by 4%-20%
-- Idol: Idol of Rejuvenation
 
if (playerClass == "DRUID") then
 
local tHealingTouch = GetSpellInfo(5185);
local tRegrowth = GetSpellInfo(8936);
local tNourish = GetSpellInfo(50464);
local tRejuvenation = GetSpellInfo(774);
local tLifebloom = GetSpellInfo(33763);
local tWildGrowth = GetSpellInfo(48438);
 
--[[HotSpells =
{
[tRegrowth] =
{
Level = {17, 23, 29, 35, 41, 47, 53, 59, 65, 70, 76, 80},
Duration = 21,
Ticks = 7,
Pattern = "(%d+)[^0-9]+%d+[^0-9]+$",
Type = "HoT",
},
[tRejuvenation] =
{
Level = {9, 15, 21, 27, 33, 39, 45, 51, 57, 59, 62, 68, 74, 79, 80},
Duration = 12,
Ticks = 4,
Pattern = "(%d+)",
Type = "HoT",
},
[tLifebloom] =
{
Level = {71, 79, 80},
Duration = 7,
Ticks = 7,
Pattern = "(%d+)"
Type = "Lifebloom",
},
}]]--
 
HealingSpells =
{
[tHealingTouch] =
{
Level = {1, 8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 62, 69, 74, 79},
Type = "Direct",
},
[tRegrowth] =
{
Level = {12, 18, 24, 30, 36, 42, 48, 54, 60, 65, 71, 77},
Type = "Direct",
},
[tNourish] =
{
Level = {80};
Type = "Direct",
}
}
 
local idolsHealingTouch =
{
[28568] = 136, -- Idol of the Avian Heart
[22399] = 100, -- Idol of Health
}
 
GetHealSize = function(name, rank, target)
local i, effectiveHeal;
 
-- Get static spell info
local baseHealSize = getBaseHealSize(name)[rank];
local nBonus = 0;
local effectiveHealModifier = 1.0;
 
if (not baseHealSize) then
return nil;
end
 
-- Get +healing bonus
local bonus = GetSpellBonusHealing();
 
local spellTab = HealingSpells[name];
 
-- Gift of Nature Talent - Increases effective healing by 2% per rank on all spells
effectiveHealModifier = effectiveHealModifier * (2 * select(5, GetTalentInfo(3, 13)) / 100 + 1);
 
-- Process individual spells
if (name == tHealingTouch) then
local idolBonus = idolsHealingTouch[getEquippedRelicID()] or 0;
baseHealSize = baseHealSize + idolBonus;
 
-- Glyph of Healing Touch (decreases amount healed by 50%)
if (detectGlyph(54825)) then
effectiveHealModifier = effectiveHealModifier * 0.5
end
 
-- Empowered Touch Talent (increases bonus healing effects by 20% per rank)
local talentEmpoweredTouch = 20 * select(5, GetTalentInfo(3, 15)) / 100;
 
if (rank < 5) then
nBonus = bonus * (1.88 * (1.0 + rank * 0.5) / 3.5 + talentEmpoweredTouch);
else
nBonus = bonus * (1.88 + talentEmpoweredTouch);
end
elseif (name == tRegrowth) then
-- Glyph of Regrowth (increases effective healing by 20% if player's Regrowth is on target)
if (detectGlyph(54743) and detectBuff(target, tRegrowth, true)) then
effectiveHealModifier = effectiveHealModifier * 1.2;
end
nBonus = bonus * 1.88 * (2.0 / 3.5) * 0.5;
elseif (name == tNourish) then
-- Nourish heals for 20% more if player's HoT is on the target.
if (detectBuff(target, tRejuvenation, true) or detectBuff(target, tRegrowth, true) or detectBuff(target, tLifebloom, true) or detectBuff(target, tWildGrowth, true)) then
effectiveHealModifier = effectiveHealModifier * 1.2
end
nBonus = bonus * 1.88 * (1.5 / 3.5);
end
 
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player')));
return effectiveHeal;
end
 
end
 
-- Paladin --
-- TODO: Track Beacon of Light (GetSpellInfo(53563) for name).
if (playerClass == "PALADIN") then
 
local tHolyLight = GetSpellInfo(635);
local tFlashOfLight = GetSpellInfo(19750);
local tDivineFavor = GetSpellInfo(20216);
local tSealOfLight = GetSpellInfo(20167);
local tAvengingWrath = GetSpellInfo(31884);
local tDivinePlea = GetSpellInfo(54428);
 
HealingSpells =
{
[tHolyLight] =
{
Level = {1, 6, 14, 22, 30, 38, 46, 54, 60, 62, 70, 75, 80},
Type = "Direct",
},
[tFlashOfLight] =
{
Level = {20, 26, 34, 42, 50, 58, 66, 74, 79},
Type = "Direct",
},
}
 
local libramsFlashOfLight =
{
[42614] = 267, -- Deadly Gladiator's Libram of Justice
[42613] = 236, -- Hateful Gladiator's Libram of Justice
[42612] = 204, -- Savage Gladiator's Libram of Justice
[28592] = 89, -- Libram of Souls Redeemed (TODO: may be changed to affect Holy Light in 3.0.3)
[25644] = 79, -- Blessed Book of Nagrand
[23006] = 43, -- Libram of Light
[23201] = 28, -- Libram of Divinity
}
 
local libramsHolyLight =
{
[40268] = 141, -- Libram of Tolerance
[28296] = 47, -- Libram of the Lightbringer
}
 
GetHealSize = function(name, rank, target)
local i, effectiveHeal;
 
-- Get static spell info
local baseHealSize = getBaseHealSize(name)[rank];
local nBonus = 0;
local effectiveHealModifier = 1.0;
 
if (not baseHealSize) then
return nil;
end
 
-- Get +healing bonus
local bonus = GetSpellBonusHealing();
 
local spellTab = HealingSpells[name];
 
-- Divine Favor (100% crit chance on heal spell)
if (detectBuff('player', tDivineFavor)) then
effectiveHealModifier = effectiveHealModifier * 1.5;
end
 
-- Avenging Wrath (increase all healing by 20%)
if (detectBuff('player', tAvengingWrath)) then
effectiveHealModifier = effectiveHealModifier * 1.2;
end
 
-- Divine Plea (decrease all healing by 50%)
if (detectBuff('player', tDivinePlea)) then
effectiveHealModifier = effectiveHealModifier * 0.5;
end
 
-- Glyph of Seal of Light (increases healing by 5% if Seal of Light is active)
if (detectGlyph(54943) and detectBuff('player', tSealOfLight)) then
effectiveHealModifier = effectiveHealModifier * 1.05;
end
 
-- Healing Light - Increases healing by 4% per rank on all spells
effectiveHealModifier = effectiveHealModifier * (4 * select(5, GetTalentInfo(1, 3)) / 100 + 1);
 
-- Process individual spells
if (name == tFlashOfLight) then
local libramBonus = libramsFlashOfLight[getEquippedRelicID()] or 0;
nBonus = (bonus + libramBonus) * 1.88 * (1.5 / 3.5) * 1.25;
elseif (name == tHolyLight) then
local libramBonus = libramsHolyLight[getEquippedRelicID()] or 0;
nBonus = (bonus + libramBonus) * 1.88 * (2.5 / 3.5) * 1.25;
end
 
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player')));
return effectiveHeal;
end
 
end
 
-- Priest --
 
-- TODO: Talent: Improved Renew: increases renew by 5%-10%-15%
-- Healing_Done = (Renew_Base + (Healbonus * Downrankfactor) ) * Improved_Renew * Spiritual_Healing
if (playerClass == "PRIEST") then
 
local tLesserHeal = GetSpellInfo(2050);
local tHeal = GetSpellInfo(2054);
local tGreaterHeal = GetSpellInfo(2060);
local tFlashHeal = GetSpellInfo(2061);
local tBindingHeal = GetSpellInfo(32546);
local tPrayerOfHealing = GetSpellInfo(596);
local tPowerWordFortitude = GetSpellInfo(1243);
--local tRenew = GetSpellInfo(139);
local tGrace = GetSpellInfo(47930);
 
--[[HotSpells =
{
[tRenew] =
{
Level = {8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 65, 74, 79, 80},
Duration = 15,
Ticks = 5,
Pattern = "(%d+)",
Type = "HoT",
},
}]]--
 
HealingSpells =
{
[tLesserHeal] =
{
Level = {1, 4, 10},
Type = "Direct"
},
[tHeal] =
{
Level = {16, 22, 28, 34},
Type = "Direct"
},
[tGreaterHeal] =
{
Level = {40, 46, 52, 58, 60, 63, 68, 73, 78},
Type = "Direct",
},
[tFlashHeal] =
{
Level = {20, 26, 32, 38, 44, 50, 56, 61, 67, 73, 79},
Type = "Direct",
},
[tBindingHeal] =
{
Level = {64, 72, 78},
Type = "Binding"
},
[tPrayerOfHealing] =
{
Level = {30, 40, 50, 60, 60, 68, 76},
Type = "Party",
InRange = function(unit) return IsSpellInRange(tPowerWordFortitude, unit) == 1 end
},
}
 
GetHealSize = function(name, rank, target)
local i, effectiveHeal;
 
-- Get static spell info
local baseHealSize = getBaseHealSize(name)[rank];
local nBonus = 0;
local effectiveHealModifier = 1.0;
 
if (not baseHealSize) then
return nil;
end
 
-- Get +healing bonus
local bonus = GetSpellBonusHealing();
 
local spellTab = HealingSpells[name];
 
-- Focused Power - Increases healing by 2% per rank on all spells
effectiveHealModifier = effectiveHealModifier * (2 * select(5, GetTalentInfo(1, 16)) / 100 + 1);
 
-- Spiritual Healing - Increases healing by 2% per rank on all spells
effectiveHealModifier = effectiveHealModifier * (2 * select(5, GetTalentInfo(2, 16)) / 100 + 1);
 
-- Grace (increases healing by 2% per application on target if buff was placed by the player)
if (target) then
local grace = detectBuff(target, tGrace, true);
if (grace) then
effectiveHealModifier = effectiveHealModifier * (1.0 + 0.02 * grace);
end
end
 
-- Process individual spells
if (name == tLesserHeal) then
nBonus = bonus * 1.88 * (1.0 + rank * 0.5) / 3.5;
elseif (name == tHeal) then
nBonus = bonus * 1.88 * (3.0 / 3.5);
elseif (name == tGreaterHeal) then
local empoweredHealing = 8 * select(5, GetTalentInfo(2, 20)) / 100;
nBonus = bonus * (1.88 * (3.0 / 3.5) + empoweredHealing);
elseif (name == tFlashHeal) then
local empoweredHealing = 4 * select(5, GetTalentInfo(2, 20)) / 100;
nBonus = bonus * (1.88 * (1.5 / 3.5) + empoweredHealing);
elseif (name == tBindingHeal) then
local empoweredHealing = 4 * select(5, GetTalentInfo(2, 20)) / 100;
nBonus = bonus * (1.88 * (1.5 / 3.5) + empoweredHealing);
elseif (name == tPrayerOfHealing) then
nBonus = bonus * 1.88 * (3.0 / 3.5) * 0.5;
end
 
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player')));
return effectiveHeal;
end
 
end
 
-- Shaman --
-- TODO: Nature's Blessing (GetTalentInfo(3, 21)) is probably not accounted for automatically anymore (or is it?)
-- TODO: Riptide 51point resto spell (instant cast regrowth (direct + hot))
-- TODO: Glyph of Healing Wave (binding heal, but self-heal is percentage of actual target healed)
if (playerClass == "SHAMAN") then
 
local tLesserHealingWave = GetSpellInfo(8004);
local tHealingWave = GetSpellInfo(331);
local tChainHeal = GetSpellInfo(1064);
local tHealingWay = GetSpellInfo(29206);
local tTidalWaves = GetSpellInfo(51562);
local tRiptide = GetSpellInfo(61295);
local tEarthShield = GetSpellInfo(974);
 
--[[HotSpells =
{
[tRiptide] =
{
Level = {60, 70, 75, 80},
Duration = 15,
Ticks = 5,
Pattern = "(%d+)",
Type = "HoT",
},
}]]--
 
HealingSpells =
{
[tLesserHealingWave] =
{
Level = {20, 28, 36, 44, 52, 60, 66, 72, 77},
Type = "Direct",
},
[tHealingWave] =
{
Level = {1, 6, 12, 18, 24, 32, 40, 48, 56, 60, 63, 70, 75, 80},
Type = "Direct",
},
[tChainHeal] =
{
Level = {40, 46, 54, 61, 68, 74, 80},
Type = "Direct",
},
}
 
local totemsLesserHealingWave =
{
[42597] = 267, -- Deadly Gladiator's Totem of the Third Wind
[42596] = 236, -- Hateful Gladiator's Totem of the Third Wind
[42595] = 204, -- Savage Gladiator's Totem of the Third Wind
[25645] = 79, -- Totem of The Plains
[22396] = 80, -- Totem of Life
[23200] = 53, -- Totem of Sustaining
}
 
local totemsHealingWave =
{
[27544] = 88, -- Totem of Spontaneous Regrowth
}
 
local totemsChainHeal =
{
[38368] = 102, -- Totem of the Bay
[28523] = 87, -- Totem of Healing Rains
}
 
GetHealSize = function(name, rank, target)
local i, effectiveHeal;
 
-- Get static spell info
local baseHealSize = getBaseHealSize(name)[rank];
local nBonus = 0;
local effectiveHealModifier = 1.0;
 
if (not baseHealSize) then
return nil;
end
 
-- Get +healing bonus
local bonus = GetSpellBonusHealing();
 
-- Purification Talent (increases healing by 2% per rank).
-- This is factored into effectiveHealModifier in the individual spell section below due to interaction with Improved Chain Heal.
local talentPurification = 2 * select(5, GetTalentInfo(3, 15)) / 100 + 1;
 
local spellTab = HealingSpells[name];
 
-- Process individual spells
if (name == tLesserHealingWave) then
local totemBonus = totemsLesserHealingWave[getEquippedRelicID()] or 0;
effectiveHealModifier = effectiveHealModifier * talentPurification;
 
-- Glyph of Lesser Healing Wave (increases effective healing by 20% if player's Earth Shield is on target)
if (detectGlyph(55438) and detectBuff(target, tEarthShield, true)) then
effectiveHealModifier = effectiveHealModifier * 1.2;
end
 
-- Tidal Waves Talent (increases bonus healing effects by 2% per rank)
local talentTidalWaves = 2 * select(5, GetTalentInfo(3, 25)) / 100;
 
nBonus = (bonus + totemBonus) * (1.88 * (1.5 / 3.5) + talentTidalWaves);
elseif (name == tHealingWave) then
local totemBonus = totemsHealingWave[getEquippedRelicID()] or 0;
effectiveHealModifier = effectiveHealModifier * talentPurification;
 
-- Healing Way Buff (target buff that increases effective healing by 18%)
if (detectBuff(target, tHealingWay)) then
effectiveHealModifier = effectiveHealModifier * 1.18;
end;
 
-- Tidal Waves Talent (increases bonus healing effects by 4% per rank)
local talentTidalWaves = 4 * select(5, GetTalentInfo(3, 25)) / 100;
 
-- Determine normalisation
if (rank < 4) then
nBonus = (bonus + totemBonus) * (1.88 * (1.0 + rank * 0.5) / 3.5 + talentTidalWaves);
else
nBonus = (bonus + totemBonus) * (1.88 * (3.0 / 3.5) + talentTidalWaves);
end
elseif (name == tChainHeal) then
local totemBonus = totemsChainHeal[getEquippedRelicID()] or 0;
baseHealSize = baseHealSize + totemBonus;
 
-- Improved Chain Heal Talent (increases healing by 10% per rank)
local talentImprovedChainHeal = 10 * select(5, GetTalentInfo(3, 20)) / 100;
 
effectiveHealModifier = effectiveHealModifier * (talentPurification + talentImprovedChainHeal);
 
-- Riptide Buff (target buff that increases effective healing by 25%)
if (detectBuff(target, tRiptide, true)) then
effectiveHealModifier = effectiveHealModifier * 1.25;
end
 
nBonus = bonus * 1.88 * (2.5 / 3.5);
end
 
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player')));
return effectiveHeal;
end
 
end
 
 
--------------------
-- Event Handlers --
--------------------
 
function lib:PLAYER_FOCUS_CHANGED()
if (UnitExists('focus')) then
self:UNIT_AURA('focus');
end
if (UnitExists('focustarget')) then
self:UNIT_AURA('focustarget');
end
end
 
function lib:PLAYER_TARGET_CHANGED()
if (UnitExists('target')) then
self:UNIT_AURA('target');
end
if (UnitExists('targettarget')) then
self:UNIT_AURA('targettarget');
end
end
 
function lib:UNIT_TARGET(unit)
if ((unit == 'target') or (unit == 'focus')) then
local unitTarget = unit .. "target";
if (UnitExists(unitTarget)) then
self:UNIT_AURA(unitTarget);
end
end
end
 
function lib:UNIT_AURA(unit)
local targetName = unitFullName(unit);
 
local oldModifier = HealModifier[targetName];
local newModifier = calculateHealModifier(unit);
if (oldModifier) then
if (newModifier == oldModifier) then
return
end
else
if (newModifier == 1.0) then
return
end
end
HealModifier[targetName] = newModifier;
 
self.Callbacks:Fire("HealComm_HealModifierUpdate", unit, targetName, newModifier);
end
 
function lib:LEARNED_SPELL_IN_TAB()
-- Invalidate cached spell data when learning new spells
SpellCache = {};
end
 
function lib:GLYPH_ADDED()
-- Invalidate cached glyph data when updating glyphs
GlyphCache = {};
end
 
function lib:GLYPH_REMOVED()
-- Invalidate cached glyph data when updating glyphs
GlyphCache = {};
end
 
function lib:GLYPH_UPDATED()
-- Invalidate cached glyph data when updating glyphs
GlyphCache = {};
end
 
function lib:UNIT_SPELLCAST_SENT(unit, spellName, spellRank, targetName)
if (unit ~= 'player') then return end
 
-- Latency measurement
SentTime = GetTime();
 
SentTargetName = targetName;
end
 
function lib:UNIT_SPELLCAST_START(unit, spellName, spellRank)
if (unit ~= 'player') then return end
 
-- Latency measurement
local currentLatency = GetTime() - SentTime;
if (currentLatency > 1) then -- Limit to 1 sec
currentLatency = 1;
end
Latency = 0.5 * Latency + 0.70 * currentLatency;
 
local spellInfo = HealingSpells[spellName];
 
-- Only process healing spells
if (spellInfo) then
if (spellInfo.Type == "Direct") then
CastInfoHealingTargetNames = SentTargetName;
CastInfoHealingSize = GetHealSize(spellName, tonumber(spellRank:match("(%d+)")), SentTargetName) or 0;
CastInfoIsCasting = true;
CastInfoEndTime = (select(6, UnitCastingInfo('player')) or 0) / 1000;
self.Callbacks:Fire("HealComm_DirectHealStart", playerName, CastInfoHealingSize, CastInfoEndTime, SentTargetName);
commSend(string.format("000%05d%s", math.min(CastInfoHealingSize, 99999), SentTargetName));
elseif (spellInfo.Type == "Binding") then
CastInfoHealingTargetNames = {playerName, SentTargetName};
CastInfoHealingSize = GetHealSize(spellName, tonumber(spellRank:match("(%d+)")), SentTargetName) or 0;
CastInfoIsCasting = true;
CastInfoEndTime = (select(6, UnitCastingInfo('player')) or 0) / 1000;
self.Callbacks:Fire("HealComm_DirectHealStart", playerName, CastInfoHealingSize, CastInfoEndTime, unpack(CastInfoHealingTargetNames));
commSend(string.format("002%05d%s", math.min(CastInfoHealingSize, 99999), SentTargetName));
elseif (spellInfo.Type == "Party") then
CastInfoHealingTargetNames = {};
if (spellInfo.InRange('party1')) then tinsert(CastInfoHealingTargetNames, unitFullName('party1')) end
if (spellInfo.InRange('party2')) then tinsert(CastInfoHealingTargetNames, unitFullName('party2')) end
if (spellInfo.InRange('party3')) then tinsert(CastInfoHealingTargetNames, unitFullName('party3')) end
if (spellInfo.InRange('party4')) then tinsert(CastInfoHealingTargetNames, unitFullName('party4')) end
CastInfoHealingSize = GetHealSize(spellName, tonumber(spellRank:match("(%d+)"))) or 0;
CastInfoIsCasting = true;
CastInfoEndTime = (select(6, UnitCastingInfo('player')) or 0) / 1000;
commSend(string.format("002%05d%s", math.min(CastInfoHealingSize, 99999), tconcat(CastInfoHealingTargetNames, ":")));
tinsert(CastInfoHealingTargetNames, 1, playerName);
self.Callbacks:Fire("HealComm_DirectHealStart", playerName, CastInfoHealingSize, CastInfoEndTime, unpack(CastInfoHealingTargetNames));
end
end
end
 
function lib:CHAT_MSG_ADDON(prefix, msg, distribution, sender)
if (prefix ~= "HealComm") then return end
if (sender == playerName) then return end
 
-- Workaround: Sometimes in battlegrounds the sender argument is not a
-- fully qualified name (the realm is missing), even though the sender is
-- from a different realm.
if (distribution == "BATTLEGROUND") then
sender = unitFullName(sender) or sender;
end
 
-- Get message type
local msgtype = tonumber(msg:sub(1, 3));
if (not msgtype) then return end
 
if (msgtype == 0) then -- DirectHealStart
local healSize = tonumber(msg:sub(4, 8));
local targetName = msg:sub(9, -1);
 
if (healSize and targetName) then
local endTime = select(6, UnitCastingInfo(sender));
 
if (endTime) then
if (distribution == "BATTLEGROUND") then
targetName = convertRealm(targetName, extractRealm(sender));
end
endTime = endTime / 1000;
entryUpdate(sender, targetName, healSize, endTime);
self.Callbacks:Fire("HealComm_DirectHealStart", sender, healSize, endTime, targetName);
end
end
elseif (msgtype == 1) then -- HealStop
local targetNames, healSize = entryRetrieve(sender);
entryDelete(sender);
if (type(targetNames) == "table") then
self.Callbacks:Fire("HealComm_DirectHealStop", sender, healSize, msg:sub(4, 4) == "S", unpack(targetNames));
elseif (targetNames) then
self.Callbacks:Fire("HealComm_DirectHealStop", sender, healSize, msg:sub(4, 4) == "S", targetNames);
end
elseif (msgtype == 2) then -- MultiTargetHealStart
local healSize = tonumber(msg:sub(4, 8));
local targetNames = {strsplit(":", msg:sub(9, -1))};
 
if (healSize) then
local endTime = select(6, UnitCastingInfo(sender));
 
if (endTime) then
if (distribution == "BATTLEGROUND") then
local senderRealm = extractRealm(sender);
for k, targetName in pairs(targetNames) do
targetNames[k] = convertRealm(targetName, senderRealm);
end
end
endTime = endTime / 1000;
tinsert(targetNames, 1, sender);
entryUpdate(sender, targetNames, healSize, endTime);
self.Callbacks:Fire("HealComm_DirectHealStart", sender, healSize, endTime, unpack(targetNames));
end
end
elseif (msgtype >= 998) then -- AnnounceVersion
local version = tonumber(msg:sub(4, -1));
if (version) then
Versions[sender] = version;
 
if (msgtype == 999) then -- RequestVersion
if (distribution ~= "BATTLEGROUND") then
-- Reply in whisper if possible
commSend("998" .. tostring(MINOR_VERSION), "WHISPER", sender);
else
-- Reply to inbound distribution channel
commSend("998" .. tostring(MINOR_VERSION), distribution);
end
end
end
end
end
 
function lib:UNIT_SPELLCAST_DELAYED(unit)
if (unit == 'player') then
if (CastInfoIsCasting) then
local endTime = select(6, UnitCastingInfo('player'));
if (endTime) then
CastInfoEndTime = endTime / 1000;
if (type(CastInfoHealingTargetNames) == "table") then
self.Callbacks:Fire("HealComm_DirectHealDelayed", playerName, CastInfoHealingSize, CastInfoEndTime, unpack(CastInfoHealingTargetNames));
elseif (CastInfoHealingTargetNames) then
self.Callbacks:Fire("HealComm_DirectHealDelayed", playerName, CastInfoHealingSize, CastInfoEndTime, CastInfoHealingTargetNames);
end
end
end
elseif (unit ~= 'target' and unit ~= 'focus') then
local healerName = unitFullName(unit);
local targetNames, healSize = entryRetrieve(healerName)
if (targetNames) then
local endTime = select(6, UnitCastingInfo(healerName));
if (endTime) then
endTime = endTime / 1000;
HealTime[healerName] = endTime;
if (type(targetNames) == "table") then
self.Callbacks:Fire("HealComm_DirectHealDelayed", healerName, healSize, endTime, unpack(targetNames));
elseif (targetNames) then
self.Callbacks:Fire("HealComm_DirectHealDelayed", healerName, healSize, endTime, targetNames);
end
end
end
end
end
 
function lib:UNIT_SPELLCAST_SUCCEEDED(unit, spellName, spellRank, spellCastIndex)
if (unit ~= 'player') then return end
 
if (CastInfoIsCasting) then
CastInfoIsCasting = false;
commSend("001S");
 
if (type(CastInfoHealingTargetNames) == "table") then
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, true, unpack(CastInfoHealingTargetNames));
elseif (CastInfoHealingTargetNames) then
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, true, CastInfoHealingTargetNames);
end
else
if (LastSpellCastIndex ~= spellCastIndex) then
-- Instant Cast Spells
end
end
end
 
function lib:UNIT_SPELLCAST_STOP(unit, spellName, spellRank, spellCastIndex)
if (unit == 'player' and CastInfoIsCasting) then
LastSpellCastIndex = spellCastIndex;
CastInfoIsCasting = false;
commSend("001F");
if (type(CastInfoHealingTargetNames) == "table") then
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, false, unpack(CastInfoHealingTargetNames));
elseif (CastInfoHealingTargetNames) then
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, false, CastInfoHealingTargetNames);
end
end
end
 
function lib:PLAYER_ALIVE()
-- This event is only fired at initial login, not at reloadui or load-on-demand loading.
-- The initialisation is triggered again, since none of the initialisation had any effect
-- prior to this event firing (no messages sent and InBattlegroundOrArena and InRaidOrParty
-- are probably not correctly initialised).
lib:Initialise();
 
-- Only receive once
self.EventFrame:UnregisterEvent("PLAYER_ALIVE");
end
 
function lib:PLAYER_ENTERING_WORLD()
HealTime = {};
HealTarget = {};
HealSize = {};
HealModifier = {};
end
 
function lib:PARTY_MEMBERS_CHANGED()
local wasInRaidOrParty = InRaidOrParty;
InRaidOrParty = (GetNumRaidMembers() > 0) or (GetNumPartyMembers() > 0);
 
-- Announce and request version when joining a group
if (not wasInRaidOrParty and InRaidOrParty) then
commSend("999" .. tostring(MINOR_VERSION));
end
end
 
function lib:RAID_ROSTER_UPDATE()
self:PARTY_MEMBERS_CHANGED();
end
 
function lib:Initialise()
local it = select(2, IsInInstance());
InBattlegroundOrArena = (it == "pvp") or (it == "arena");
 
InRaidOrParty = (GetNumRaidMembers() > 0) or (GetNumPartyMembers() > 0);
 
-- Announce and request version in group and in guild
commSend("999" .. tostring(MINOR_VERSION));
if (IsInGuild()) then
commSend("999" .. tostring(MINOR_VERSION), "GUILD");
end
end
 
lib:Initialise();