├── Bindings.xml ├── HealingBar.xml ├── QuickClick.lua ├── QuickHeal.lua ├── QuickHeal.toc ├── QuickHeal.xml ├── QuickHealDruid.lua ├── QuickHealPaladin.lua ├── QuickHealPriest.lua ├── QuickHealShaman.lua ├── README.md ├── libs ├── AceAddon-2.0 │ └── AceAddon-2.0.lua ├── AceConsole-2.0 │ └── AceConsole-2.0.lua ├── AceEvent-2.0 │ └── AceEvent-2.0.lua ├── HealComm-1.0 │ └── HealComm-1.0.lua └── RosterLib-2.0 │ └── RosterLib-2.0.lua ├── localization.de.lua ├── localization.en.lua └── localization.fr.lua /Bindings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | if QUICKHEAL_LOADED then QuickHeal() end 4 | 5 | 6 | if QUICKHEAL_LOADED then QuickHeal('subgroup') end 7 | 8 | 9 | if QUICKHEAL_LOADED then QuickHeal('party') end 10 | 11 | 12 | if QUICKHEAL_LOADED then QuickHeal('mt') end 13 | 14 | 15 | if QUICKHEAL_LOADED then QuickHeal('nonmt') end 16 | 17 | 18 | if QUICKHEAL_LOADED then QuickHeal('player') end 19 | 20 | 21 | if QUICKHEAL_LOADED then QuickHeal('target') end 22 | 23 | 24 | if QUICKHEAL_LOADED then QuickHeal('targettarget') end 25 | 26 | 27 | if QUICKHEAL_LOADED then QuickHeal_Toggle_Healthy_Threshold() end 28 | 29 | 30 | if QUICKHEAL_LOADED then ToggleDownrankWindow() end 31 | 32 | -------------------------------------------------------------------------------- /HealingBar.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | this:SetMinMaxValues(0,1); 73 | this:SetValue(0.5); 74 | this:SetFrameLevel(this:GetFrameLevel() - 1) 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | this:SetMinMaxValues(0,2); 112 | this:SetValue(1); 113 | this:SetFrameLevel(this:GetFrameLevel() - 2) 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | if QuickHealConfig:IsVisible() and (arg1 == "LeftButton") then 123 | this:StartMoving(); 124 | end 125 | 126 | 127 | if QuickHealConfig:IsVisible() then 128 | if (arg1 == "LeftButton") then 129 | this:StopMovingOrSizing(); 130 | elseif (arg1 == "RightButton") then 131 | QuickHealHealingBar:ClearAllPoints(); 132 | QuickHealHealingBar:SetPoint("CENTER",CastingBarFrame,"CENTER",0,30) 133 | end 134 | end 135 | 136 | 137 | if QuickHealConfig:IsVisible() then 138 | GameTooltip:SetOwner(this,"ANCHOR_TOPLEFT"); 139 | GameTooltip:SetText("Healing Bar Configuration\nLeft-click to drag.\nRight-click to re-attach to Casting Bar."); 140 | this.tooltipShown = true; 141 | end 142 | 143 | 144 | GameTooltip:Hide(); 145 | this.tooltipShown = false; 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /QuickClick.lua: -------------------------------------------------------------------------------- 1 | local IsQuickClickKeyDown = IsControlKeyDown; 2 | local MouseButton = "LeftButton"; 3 | local BlizzardFrames = {"PlayerFrame","PetFrame","TargetFrame","TargetofTarget","PartyMemberFrame","PartyMemberPetFrame"}; 4 | QuickClick_OldOnClick = {}; 5 | 6 | --[ Click event handlers ]-- 7 | 8 | local function QuickClick(button,unit) 9 | if IsQuickClickKeyDown() and button == MouseButton then 10 | QuickHeal(unit); 11 | return true; 12 | else return false end 13 | end 14 | 15 | -- PlayerFrame 16 | function QuickClick_PlayerFrame_OnClick(button) 17 | if not QuickClick(button,this.unit) then QuickClick_OldOnClick.PlayerFrame(button) end 18 | end 19 | -- PetFrame 20 | function QuickClick_PetFrame_OnClick(button) 21 | if not QuickClick(button,this.unit) then QuickClick_OldOnClick.PetFrame(button) end 22 | end 23 | -- TargetFrame 24 | function QuickClick_TargetFrame_OnClick(button) 25 | if not QuickClick(button,this.unit) then QuickClick_OldOnClick.TargetFrame(button) end 26 | end 27 | -- TargetofTarget 28 | function QuickClick_TargetofTarget_OnClick(button) 29 | if not QuickClick(button,this.unit) then QuickClick_OldOnClick.TargetofTarget(button) end 30 | end 31 | -- PartyMemberFrame 32 | function QuickClick_PartyMemberFrame_OnClick(partyFrame) 33 | if not QuickClick(arg1,this.unit) then QuickClick_OldOnClick.PartyMemberFrame(partyFrame) end 34 | end 35 | -- PartyMemberPetFrame 36 | function QuickClick_PartyMemberPetFrame_OnClick() 37 | if not QuickClick(arg1,this.unit) then QuickClick_OldOnClick.PartyMemberPetFrame() end 38 | end 39 | 40 | -- EasyRaid 41 | function QuickClick_ER_RaidPulloutButton_OnClick() 42 | if not QuickClick(arg1, this.unit or this:GetParent().unit) then QuickClick_OldOnClick.ER_RaidPulloutButton() end 43 | end 44 | function QuickClick_ER_MainTankButton_OnClick() 45 | if not QuickClick(arg1, this.unit or this:GetParent().unit) then QuickClick_OldOnClick.ER_MainTankButton() end 46 | end 47 | 48 | -- Discord Unit Frames 49 | function QuickClick_DUF_UnitFrame_OnClick(button) 50 | if not QuickClick(button, this.unit) then QuickClick_OldOnClick.DUF_UnitFrame(button) end 51 | end 52 | function QuickClick_DUF_Element_OnClick(button) 53 | if not QuickClick(button, this.unit or this:GetParent().unit) then QuickClick_OldOnClick.DUF_Element(button) end 54 | end 55 | 56 | --[[ Loading and Unloading ]]-- 57 | 58 | function QuickClick_Load() 59 | -- Hook all Blizzard provided player frames 60 | for i,v in ipairs(BlizzardFrames) do 61 | loadstring("if type("..v.."_OnClick) == \"function\" then QuickClick_OldOnClick."..v..","..v.."_OnClick = "..v.."_OnClick,QuickClick_"..v.."_OnClick end")(); 62 | end 63 | 64 | -- Hook CT_RA_CustomOnClickFunction 65 | QuickClick_OldOnClick.CT_RA_CustomOnClickFunction,CT_RA_CustomOnClickFunction = CT_RA_CustomOnClickFunction,QuickClick; 66 | 67 | -- Hook EasyRaid 68 | if IsAddOnLoaded("EasyRaid") then 69 | if (type(ER_RaidPulloutButton_OnClick) == "function") and (type(ER_MainTankButton_OnClick) == "function") then 70 | QuickClick_OldOnClick.ER_RaidPulloutButton,ER_RaidPulloutButton_OnClick = ER_RaidPulloutButton_OnClick,QuickClick_ER_RaidPulloutButton_OnClick; 71 | QuickClick_OldOnClick.ER_MainTankButton,ER_MainTankButton_OnClick = ER_MainTankButton_OnClick,QuickClick_ER_MainTankButton_OnClick; 72 | end 73 | end 74 | 75 | -- Hook Perl Classic Unit Frames and X-Perl 76 | QuickClick_OldOnClick.Perl_Custom_ClickFunction,Perl_Custom_ClickFunction = Perl_Custom_ClickFunction,QuickClick; 77 | 78 | -- Hook Discord Unit Frames 79 | if IsAddOnLoaded("DiscordUnitFrames") then 80 | if (type(DUF_UnitFrame_OnClick) == "function") and (type(DUF_Element_OnClick) == "function") then 81 | QuickClick_OldOnClick.DUF_UnitFrame,DUF_UnitFrame_OnClick = DUF_UnitFrame_OnClick,QuickClick_DUF_UnitFrame_OnClick; 82 | QuickClick_OldOnClick.DUF_Element,DUF_Element_OnClick = DUF_Element_OnClick,QuickClick_DUF_Element_OnClick; 83 | end 84 | end 85 | end 86 | 87 | function QuickClick_Unload() 88 | -- Unhook all Blizzard provided player frames 89 | for i,v in ipairs(BlizzardFrames) do 90 | loadstring("if type(QuickClick_OldOnClick."..v..") == \"function\" then "..v.."_OnClick = QuickClick_OldOnClick."..v.." end")(); 91 | end 92 | 93 | -- Unhook CT_RA_CustomOnClickFunction 94 | CT_RA_CustomOnClickFunction = QuickClick_OldOnClick.CT_RA_CustomOnClickFunction; 95 | 96 | -- Unhook EasyRaid 97 | if IsAddOnLoaded("EasyRaid") then 98 | if (type(QuickClick_OldOnClick.ER_RaidPulloutButton) == "function") and (type(QuickClick_OldOnClick.ER_MainTankButton) == "function") then 99 | ER_RaidPulloutButton_OnClick = QuickClick_OldOnClick.ER_RaidPulloutButton; 100 | ER_MainTankButton_OnClick = QuickClick_OldOnClick.ER_MainTankButton; 101 | end 102 | end 103 | 104 | -- Unhook Perl Classic Unit Frames and X-Perl 105 | Perl_Custom_ClickFunction = QuickClick_OldOnClick.Perl_Custom_ClickFunction; 106 | 107 | -- Unhook Discord Unit Frames 108 | if IsAddOnLoaded("DiscordUnitFrames") then 109 | if (type(QuickClick_OldOnClick.DUF_UnitFrame) == "function") and (type(QuickClick_OldOnClick.DUF_Element) == "function") then 110 | DUF_UnitFrame_OnClick = QuickClick_OldOnClick.DUF_UnitFrame; 111 | DUF_Element_OnClick = QuickClick_OldOnClick.DUF_Element; 112 | end 113 | end 114 | 115 | QuickClick_OldOnClick = {}; 116 | end 117 | 118 | 119 | -------------------------------------------------------------------------------- /QuickHeal.toc: -------------------------------------------------------------------------------- 1 | ## Interface: 11200 2 | ## Title: QuickHeal 3 | ## Author: Thomas Thorsen, Scott Geeding and Kostas Karachalios 4 | ## Notes: Quick healing of party/raid members 5 | ## OptionalDeps: BonusScanner,Titan Panel,myAddOns,CT_RaidAssist 6 | ## SavedVariables: 7 | ## SavedVariablesPerCharacter: QuickHealVariables 8 | 9 | libs\RosterLib-2.0\RosterLib-2.0.lua 10 | 11 | libs\HealComm-1.0\HealComm-1.0.lua 12 | 13 | QuickHeal.xml 14 | HealingBar.xml 15 | -------------------------------------------------------------------------------- /QuickHealDruid.lua: -------------------------------------------------------------------------------- 1 | 2 | function QuickHeal_Druid_GetRatioHealthyExplanation() 3 | local RatioHealthy = QuickHeal_GetRatioHealthy(); 4 | local RatioFull = QuickHealVariables["RatioFull"]; 5 | 6 | if RatioHealthy >= RatioFull then 7 | return QUICKHEAL_SPELL_REGROWTH .. " will always be used in combat, and " .. QUICKHEAL_SPELL_HEALING_TOUCH .. " will be used when out of combat. "; 8 | else 9 | if RatioHealthy > 0 then 10 | return QUICKHEAL_SPELL_REGROWTH .. " will be used in combat if the target has less than " .. RatioHealthy*100 .. "% life, and " .. QUICKHEAL_SPELL_HEALING_TOUCH .. " will be used otherwise. "; 11 | else 12 | return QUICKHEAL_SPELL_REGROWTH .. " will never be used. " .. QUICKHEAL_SPELL_HEALING_TOUCH .. " will always be used in and out of combat. "; 13 | end 14 | end 15 | end 16 | 17 | function QuickHeal_Druid_FindSpellToUse(Target) 18 | local SpellID = nil; 19 | local HealSize = 0; 20 | 21 | -- +Healing-PenaltyFactor = (1-((20-LevelLearnt)*0.0375)) for all spells learnt before level 20 22 | local PF1 = 0.2875; 23 | local PF8 = 0.55; 24 | local PFRG1 = 0.7 * 1.042; -- Rank 1 of RG (1.041 compensates for the 0.50 factor that should be 0.48 for RG1) 25 | local PF14 = 0.775; 26 | local PFRG2 = 0.925; 27 | 28 | -- Local aliases to access main module functionality and settings 29 | local RatioFull = QuickHealVariables["RatioFull"]; 30 | local RatioHealthy = QuickHeal_GetRatioHealthy(); 31 | local UnitHasHealthInfo = QuickHeal_UnitHasHealthInfo; 32 | local EstimateUnitHealNeed = QuickHeal_EstimateUnitHealNeed; 33 | local GetSpellIDs = QuickHeal_GetSpellIDs; 34 | local debug = QuickHeal_debug; 35 | 36 | -- Return immediately if no player needs healing 37 | if not Target then 38 | return SpellID,HealSize; 39 | end 40 | 41 | -- Determine health and heal need of target 42 | local healneed; 43 | local Health; 44 | if UnitHasHealthInfo(Target) then 45 | -- Full info available 46 | healneed = UnitHealthMax(Target) - UnitHealth(Target) - HealComm:getHeal(UnitName(Target)); -- Implementatio for HealComm 47 | Health = UnitHealth(Target) / UnitHealthMax(Target); 48 | else 49 | -- Estimate target health 50 | healneed = EstimateUnitHealNeed(Target,true); 51 | Health = UnitHealth(Target)/100; 52 | end 53 | 54 | -- if BonusScanner is running, get +Healing bonus 55 | local Bonus = 0; 56 | if (BonusScanner) then 57 | Bonus = tonumber(BonusScanner:GetBonus("HEAL")); 58 | debug(string.format("Equipment Healing Bonus: %d", Bonus)); 59 | end 60 | 61 | -- Calculate healing bonus 62 | local healMod15 = (1.5/3.5) * Bonus; 63 | local healMod20 = (2.0/3.5) * Bonus; 64 | local healMod25 = (2.5/3.5) * Bonus; 65 | local healMod30 = (3.0/3.5) * Bonus; 66 | local healMod35 = Bonus; 67 | local healModRG = (2.0/3.5) * Bonus * 0.5; -- The 0.5 factor is calculated as DirectHeal/(DirectHeal+HoT) 68 | debug("Final Healing Bonus (1.5,2.0,2.5,3.0,3.5,Regrowth)", healMod15,healMod20,healMod25,healMod30,healMod35,healModRG); 69 | 70 | local InCombat = UnitAffectingCombat('player') or UnitAffectingCombat(Target); 71 | 72 | -- Gift of Nature - Increases healing by 2% per rank 73 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12); 74 | local gnMod = 2*talentRank/100 + 1; 75 | debug(string.format("Gift of Nature modifier: %f", gnMod)); 76 | 77 | -- Tranquil Spirit - Decreases mana usage by 2% per rank on HT only 78 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,9); 79 | local tsMod = 1 - 2*talentRank/100; 80 | debug(string.format("Tranquil Spirit modifier: %f", tsMod)); 81 | 82 | -- Moonglow - Decrease mana usage by 3% per rank 83 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,14); 84 | local mgMod = 1 - 3*talentRank/100; 85 | debug(string.format("Moonglow modifier: %f", mgMod)); 86 | 87 | -- Improved Rejuvenation -- Increases Rejuvenation effects by 5% per rank 88 | --local _,_,_,_,talentRank,_ = GetTalentInfo(3,10); 89 | --local irMod = 5*talentRank/100 + 1; 90 | --debug(string.format("Improved Rejuvenation modifier: %f", irMod)); 91 | 92 | local TargetIsHealthy = Health >= RatioHealthy; 93 | local ManaLeft = UnitMana('player'); 94 | 95 | if TargetIsHealthy then 96 | debug("Target is healthy ",Health); 97 | end 98 | 99 | -- Detect Clearcasting (from Omen of Clarity, talent(1,9)) 100 | if QuickHeal_DetectBuff('player',"Spell_Shadow_ManaBurn",1) then -- Spell_Shadow_ManaBurn (1) 101 | ManaLeft = UnitManaMax('player'); -- set to max mana so max spell rank will be cast 102 | healneed = 10^6; -- deliberate overheal (mana is free) 103 | debug("BUFF: Clearcasting (Omen of Clarity)"); 104 | end 105 | 106 | -- Detect Nature's Swiftness (next nature spell is instant cast) 107 | if QuickHeal_DetectBuff('player',"Spell_Nature_RavenForm") then 108 | debug("BUFF: Nature's Swiftness (out of combat healing forced)"); 109 | InCombat = false; 110 | end 111 | 112 | -- Detect proc of 'Hand of Edward the Odd' mace (next spell is instant cast) 113 | if QuickHeal_DetectBuff('player',"Spell_Holy_SearingLight") then 114 | debug("BUFF: Hand of Edward the Odd (out of combat healing forced)"); 115 | InCombat = false; 116 | end 117 | 118 | -- Get total healing modifier (factor) caused by healing target debuffs 119 | local HDB = QuickHeal_GetHealModifier(Target); 120 | debug("Target debuff healing modifier",HDB); 121 | healneed = healneed/HDB; 122 | 123 | -- Get a list of ranks available for all spells 124 | local SpellIDsHT = GetSpellIDs(QUICKHEAL_SPELL_HEALING_TOUCH); 125 | local SpellIDsRG = GetSpellIDs(QUICKHEAL_SPELL_REGROWTH); 126 | --local SpellIDsRJ = GetSpellIDs(QUICKHEAL_SPELL_REJUVENATION); 127 | 128 | local maxRankHT = table.getn(SpellIDsHT); 129 | local maxRankRG = table.getn(SpellIDsRG); 130 | --local maxRankRJ = table.getn(SpellIDsRJ); 131 | 132 | debug(string.format("Found HT up to rank %d, RG up to rank %d", maxRankHT, maxRankRG)); 133 | 134 | -- Compensation for health lost during combat 135 | local k=1.0; 136 | local K=1.0; 137 | if InCombat then 138 | k=0.9; 139 | K=0.8; 140 | end 141 | 142 | -- Find suitable SpellID based on the defined criteria 143 | if not InCombat or TargetIsHealthy or maxRankRG<1 then 144 | -- Not in combat or target is healthy so use the closest available mana efficient healing 145 | debug(string.format("Not in combat or target healthy or no Regrowth available, will use Healing Touch")) 146 | if Health < RatioFull then 147 | SpellID = SpellIDsHT[1]; HealSize = 44*gnMod+healMod15*PF1; -- Default to rank 1 148 | if healneed > ( 100*gnMod+healMod20*PF8 )*k and ManaLeft >= 55*tsMod*mgMod and maxRankHT >= 2 then SpellID = SpellIDsHT[2]; HealSize = 100*gnMod+healMod20*PF8 end 149 | if healneed > ( 219*gnMod+healMod25*PF14)*K and ManaLeft >= 110*tsMod*mgMod and maxRankHT >= 3 then SpellID = SpellIDsHT[3]; HealSize = 219*gnMod+healMod25*PF14 end 150 | if healneed > ( 404*gnMod+healMod30)*K and ManaLeft >= 185*tsMod*mgMod and maxRankHT >= 4 then SpellID = SpellIDsHT[4]; HealSize = 404*gnMod+healMod30 end 151 | if healneed > ( 633*gnMod+healMod35)*K and ManaLeft >= 270*tsMod*mgMod and maxRankHT >= 5 then SpellID = SpellIDsHT[5]; HealSize = 633*gnMod+healMod35 end 152 | if healneed > ( 818*gnMod+healMod35)*K and ManaLeft >= 335*tsMod*mgMod and maxRankHT >= 6 then SpellID = SpellIDsHT[6]; HealSize = 818*gnMod+healMod35 end 153 | if healneed > (1028*gnMod+healMod35)*K and ManaLeft >= 405*tsMod*mgMod and maxRankHT >= 7 then SpellID = SpellIDsHT[7]; HealSize = 1028*gnMod+healMod35 end 154 | if healneed > (1313*gnMod+healMod35)*K and ManaLeft >= 495*tsMod*mgMod and maxRankHT >= 8 then SpellID = SpellIDsHT[8]; HealSize = 1313*gnMod+healMod35 end 155 | if healneed > (1656*gnMod+healMod35)*K and ManaLeft >= 600*tsMod*mgMod and maxRankHT >= 9 then SpellID = SpellIDsHT[9]; HealSize = 1656*gnMod+healMod35 end 156 | if healneed > (2060*gnMod+healMod35)*K and ManaLeft >= 720*tsMod*mgMod and maxRankHT >= 10 then SpellID = SpellIDsHT[10]; HealSize = 2060*gnMod+healMod35 end 157 | if healneed > (2472*gnMod+healMod35)*K and ManaLeft >= 800*tsMod*mgMod and maxRankHT >= 11 then SpellID = SpellIDsHT[11]; HealSize = 2472*gnMod+healMod35 end 158 | end 159 | else 160 | -- In combat and target is unhealthy and player has Regrowth 161 | debug(string.format("In combat and target unhealthy and Regrowth available, will use Regrowth")); 162 | if Health < RatioFull then 163 | SpellID = SpellIDsRG[1]; HealSize = 91*gnMod+healModRG*PFRG1; -- Default to rank 1 164 | if healneed > ( 176*gnMod+healModRG*PFRG2)*k and ManaLeft >= 205*mgMod and maxRankRG >= 2 then SpellID = SpellIDsRG[2]; HealSize = 176*gnMod+healModRG*PFRG2 end 165 | if healneed > ( 257*gnMod+healModRG)*k and ManaLeft >= 280*mgMod and maxRankRG >= 3 then SpellID = SpellIDsRG[3]; HealSize = 257*gnMod+healModRG end 166 | if healneed > ( 339*gnMod+healModRG)*k and ManaLeft >= 350*mgMod and maxRankRG >= 4 then SpellID = SpellIDsRG[4]; HealSize = 339*gnMod+healModRG end 167 | if healneed > ( 431*gnMod+healModRG)*k and ManaLeft >= 420*mgMod and maxRankRG >= 5 then SpellID = SpellIDsRG[5]; HealSize = 431*gnMod+healModRG end 168 | if healneed > ( 543*gnMod+healModRG)*k and ManaLeft >= 510*mgMod and maxRankRG >= 6 then SpellID = SpellIDsRG[6]; HealSize = 543*gnMod+healModRG end 169 | if healneed > ( 686*gnMod+healModRG)*k and ManaLeft >= 615*mgMod and maxRankRG >= 7 then SpellID = SpellIDsRG[7]; HealSize = 686*gnMod+healModRG end 170 | if healneed > ( 857*gnMod+healModRG)*k and ManaLeft >= 740*mgMod and maxRankRG >= 8 then SpellID = SpellIDsRG[8]; HealSize = 857*gnMod+healModRG end 171 | if healneed > (1061*gnMod+healModRG)*k and ManaLeft >= 880*mgMod and maxRankRG >= 9 then SpellID = SpellIDsRG[9]; HealSize = 1061*gnMod+healModRG end 172 | end 173 | end 174 | 175 | return SpellID,HealSize*HDB; 176 | end 177 | -------------------------------------------------------------------------------- /QuickHealPaladin.lua: -------------------------------------------------------------------------------- 1 | 2 | function QuickHeal_Paladin_GetRatioHealthyExplanation() 3 | local RatioHealthy = QuickHeal_GetRatioHealthy(); 4 | local RatioFull = QuickHealVariables["RatioFull"]; 5 | 6 | if RatioHealthy >= RatioFull then 7 | return QUICKHEAL_SPELL_HOLY_LIGHT .. " will never be used in combat. "; 8 | else 9 | if RatioHealthy > 0 then 10 | return QUICKHEAL_SPELL_HOLY_LIGHT .. " will only be used in combat if the target has more than " .. RatioHealthy*100 .. "% life, and only if the healing done is greater than the greatest " .. QUICKHEAL_SPELL_FLASH_OF_LIGHT .. " available. "; 11 | else 12 | return QUICKHEAL_SPELL_HOLY_LIGHT .. " will only be used in combat if the healing done is greater than the greatest " .. QUICKHEAL_SPELL_FLASH_OF_LIGHT .. " available. "; 13 | end 14 | end 15 | end 16 | 17 | function QuickHeal_Paladin_FindSpellToUse(Target) 18 | local SpellID = nil; 19 | local HealSize = 0; 20 | 21 | -- +Healing-PenaltyFactor = (1-((20-LevelLearnt)*0.0375)) for all spells learnt before level 20 22 | local PF1 = 0.2875; 23 | local PF6 = 0.475; 24 | local PF14 = 0.775; 25 | 26 | -- Local aliases to access main module functionality and settings 27 | local RatioFull = QuickHealVariables["RatioFull"]; 28 | local RatioHealthy = QuickHeal_GetRatioHealthy(); 29 | local UnitHasHealthInfo = QuickHeal_UnitHasHealthInfo; 30 | local EstimateUnitHealNeed = QuickHeal_EstimateUnitHealNeed; 31 | local GetSpellIDs = QuickHeal_GetSpellIDs; 32 | local debug = QuickHeal_debug; 33 | 34 | -- Return immediatly if no player needs healing 35 | if not Target then 36 | return SpellID,HealSize; 37 | end 38 | 39 | -- Determine health and healneed of target 40 | local healneed; 41 | local Health; 42 | 43 | if UnitHasHealthInfo(Target) then 44 | -- Full info available 45 | healneed = UnitHealthMax(Target) - UnitHealth(Target) - HealComm:getHeal(UnitName(Target)); -- Implementatio for HealComm 46 | Health = UnitHealth(Target) / UnitHealthMax(Target); 47 | else 48 | -- Estimate target health 49 | healneed = EstimateUnitHealNeed(Target,true); 50 | Health = UnitHealth(Target)/100; 51 | end 52 | 53 | -- if BonusScanner is running, get +Healing bonus 54 | local Bonus = 0; 55 | if (BonusScanner) then 56 | Bonus = tonumber(BonusScanner:GetBonus("HEAL")); 57 | debug(string.format("Equipment Healing Bonus: %d", Bonus)); 58 | end 59 | 60 | -- Calculate healing bonus 61 | local healMod15 = (1.5/3.5) * Bonus; 62 | local healMod25 = (2.5/3.5) * Bonus; 63 | debug("Final Healing Bonus (1.5,2.5)", healMod15,healMod25); 64 | 65 | local InCombat = UnitAffectingCombat('player') or UnitAffectingCombat(Target); 66 | 67 | -- Healing Light Talent (increases healing by 4% per rank) 68 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5); 69 | local hlMod = 4*talentRank/100 + 1; 70 | debug(string.format("Healing Light talentmodification: %f", hlMod)) 71 | 72 | local TargetIsHealthy = Health >= RatioHealthy; 73 | local ManaLeft = UnitMana('player'); 74 | 75 | if TargetIsHealthy then 76 | debug("Target is healthy",Health); 77 | end 78 | 79 | -- Detect proc of 'Hand of Edward the Odd' mace (next spell is instant cast) 80 | if QuickHeal_DetectBuff('player',"Spell_Holy_SearingLight") then 81 | debug("BUFF: Hand of Edward the Odd (out of combat healing forced)"); 82 | InCombat = false; 83 | end 84 | 85 | -- Get total healing modifier (factor) caused by healing target debuffs 86 | local HDB = QuickHeal_GetHealModifier(Target); 87 | debug("Target debuff healing modifier",HDB); 88 | healneed = healneed/HDB; 89 | 90 | -- Get a list of ranks available of 'Lesser Healing Wave' and 'Healing Wave' 91 | local SpellIDsHL = GetSpellIDs(QUICKHEAL_SPELL_HOLY_LIGHT); 92 | local SpellIDsFL = GetSpellIDs(QUICKHEAL_SPELL_FLASH_OF_LIGHT); 93 | local maxRankHL = table.getn(SpellIDsHL); 94 | local maxRankFL = table.getn(SpellIDsFL); 95 | local NoFL = maxRankFL < 1; 96 | debug(string.format("Found HL up to rank %d, and found FL up to rank %d", maxRankHL, maxRankFL)) 97 | 98 | --Get max HealRanks that are allowed to be used 99 | local downRankFH = QuickHealVariables.DownrankValueFH -- rank for 1.5 sec heals 100 | local downRankNH = QuickHealVariables.DownrankValueNH -- rank for < 1.5 sec heals 101 | 102 | 103 | -- below changed to not differentiate between in or out if combat. Original code down below 104 | -- Find suitable SpellID based on the defined criteria 105 | local k = 1; 106 | local K = 1; 107 | if InCombat then 108 | local k = 0.9; -- In combat means that target is loosing life while casting, so compensate 109 | local K = 0.8; -- k for fast spells (LHW and HW Rank 1 and 2) and K for slow spells (HW) 3 = 4 | 3 < 4 | 3 > 4 110 | end 111 | 112 | if Health < RatioFull then 113 | if maxRankFL >=1 then SpellID = SpellIDsFL[1]; HealSize = 67*hlMod+healMod15 else SpellID = SpellIDsHL[1]; HealSize = 43*hlMod+healMod25*PF1 end -- Default to rank 1 of FL or HL 114 | if healneed > ( 83*hlMod+healMod25*PF6 )*K and ManaLeft >= 60 and maxRankHL >=2 and (TargetIsHealthy and maxRankFL <= 1 or NoFL) then SpellID = SpellIDsHL[2]; HealSize = 83*hlMod+healMod25*PF6 end 115 | if healneed > (103*hlMod+healMod15) *k and ManaLeft >= 50 and maxRankFL >=2 and downRankFH >= 2 then SpellID = SpellIDsFL[2]; HealSize = 103*hlMod+healMod15 end 116 | if healneed > (154*hlMod+healMod15) *k and ManaLeft >= 70 and maxRankFL >=3 and downRankFH >= 3 then SpellID = SpellIDsFL[3]; HealSize = 154*hlMod+healMod15 end 117 | if healneed > (173*hlMod+healMod25*PF14)*K and ManaLeft >= 110 and maxRankHL >=3 and (TargetIsHealthy and maxRankFL <= 3 or NoFL) then SpellID = SpellIDsHL[3]; HealSize = 173*hlMod+healMod25*PF14 end 118 | if healneed > (209*hlMod+healMod15) *k and ManaLeft >= 90 and maxRankFL >=4 and downRankFH >= 4 then SpellID = SpellIDsFL[4]; HealSize = 209*hlMod+healMod15 end 119 | if healneed > (283*hlMod+healMod15) *k and ManaLeft >= 115 and maxRankFL >=5 and downRankFH >= 5 then SpellID = SpellIDsFL[5]; HealSize = 283*hlMod+healMod15 end 120 | if healneed > (333*hlMod+healMod25) *K and ManaLeft >= 190 and maxRankHL >=4 and (TargetIsHealthy and maxRankFL <= 5 or NoFL) then SpellID = SpellIDsHL[4]; HealSize = 333*hlMod+healMod25 end 121 | if healneed > (363*hlMod+healMod15) *k and ManaLeft >= 140 and maxRankFL >=6 and downRankFH >= 6 then SpellID = SpellIDsFL[6]; HealSize = 363*hlMod+healMod15 end 122 | if healneed > (522*hlMod+healMod25) *K and ManaLeft >= 275 and maxRankHL >=5 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[5]; HealSize = 522*hlMod+healMod25 end 123 | if healneed > (739*hlMod+healMod25) *K and ManaLeft >= 365 and maxRankHL >=6 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[6]; HealSize = 739*hlMod+healMod25 end 124 | if healneed > (999*hlMod+healMod25) *K and ManaLeft >= 465 and maxRankHL >=7 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[7]; HealSize = 999*hlMod+healMod25 end 125 | if healneed > (1317*hlMod+healMod25) *K and ManaLeft >= 580 and maxRankHL >=8 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[8]; HealSize = 1317*hlMod+healMod25 end 126 | if healneed > (1680*hlMod+healMod25) *K and ManaLeft >= 660 and maxRankHL >=9 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[9]; HealSize = 1680*hlMod+healMod25 end 127 | end 128 | return SpellID,HealSize*HDB; 129 | end 130 | 131 | 132 | 133 | 134 | --Original Code 135 | -- -- Find suitable SpellID based on the defined criteria 136 | -- if InCombat then 137 | -- local k = 0.9; -- In combat means that target is loosing life while casting, so compensate 138 | -- local K = 0.8; -- k for fast spells (LHW and HW Rank 1 and 2) and K for slow spells (HW) 139 | -- if Health < RatioFull then 140 | -- if maxRankFL >=1 then SpellID = SpellIDsFL[1]; HealSize = 67*hlMod+healMod15 else SpellID = SpellIDsHL[1]; HealSize = 43*hlMod+healMod25*PF1 end -- Default to rank 1 of FL or HL 141 | -- if healneed > ( 83*hlMod+healMod25*PF6 )*K and ManaLeft >= 60 and maxRankHL >=2 and (TargetIsHealthy and maxRankFL <= 1 or NoFL) then SpellID = SpellIDsHL[2]; HealSize = 83*hlMod+healMod25*PF6 end 142 | -- if healneed > (103*hlMod+healMod15) *k and ManaLeft >= 50 and maxRankFL >=2 then SpellID = SpellIDsFL[2]; HealSize = 103*hlMod+healMod15 end 143 | -- if healneed > (154*hlMod+healMod15) *k and ManaLeft >= 70 and maxRankFL >=3 then SpellID = SpellIDsFL[3]; HealSize = 154*hlMod+healMod15 end 144 | -- if healneed > (173*hlMod+healMod25*PF14)*K and ManaLeft >= 110 and maxRankHL >=3 and (TargetIsHealthy and maxRankFL <= 3 or NoFL) then SpellID = SpellIDsHL[3]; HealSize = 173*hlMod+healMod25*PF14 end 145 | -- if healneed > (209*hlMod+healMod15) *k and ManaLeft >= 90 and maxRankFL >=4 then SpellID = SpellIDsFL[4]; HealSize = 209*hlMod+healMod15 end 146 | -- if healneed > (283*hlMod+healMod15) *k and ManaLeft >= 115 and maxRankFL >=5 then SpellID = SpellIDsFL[5]; HealSize = 283*hlMod+healMod15 end 147 | -- if healneed > (333*hlMod+healMod25) *K and ManaLeft >= 190 and maxRankHL >=4 and (TargetIsHealthy and maxRankFL <= 5 or NoFL) then SpellID = SpellIDsHL[4]; HealSize = 333*hlMod+healMod25 end 148 | -- if healneed > (363*hlMod+healMod15) *k and ManaLeft >= 140 and maxRankFL >=6 then SpellID = SpellIDsFL[6]; HealSize = 363*hlMod+healMod15 end 149 | -- if healneed > (522*hlMod+healMod25) *K and ManaLeft >= 275 and maxRankHL >=5 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[5]; HealSize = 522*hlMod+healMod25 end 150 | -- if healneed > (739*hlMod+healMod25) *K and ManaLeft >= 365 and maxRankHL >=6 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[6]; HealSize = 739*hlMod+healMod25 end 151 | -- if healneed > (999*hlMod+healMod25) *K and ManaLeft >= 465 and maxRankHL >=7 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[7]; HealSize = 999*hlMod+healMod25 end 152 | -- if healneed > (1317*hlMod+healMod25) *K and ManaLeft >= 580 and maxRankHL >=8 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[8]; HealSize = 1317*hlMod+healMod25 end 153 | -- if healneed > (1680*hlMod+healMod25) *K and ManaLeft >= 660 and maxRankHL >=9 and (TargetIsHealthy and maxRankFL <= 6 or NoFL) then SpellID = SpellIDsHL[9]; HealSize = 1680*hlMod+healMod25 end 154 | -- end 155 | -- else 156 | -- -- Not in combat 157 | -- if Health < RatioFull then 158 | -- if maxRankFL >=1 then SpellID = SpellIDsFL[1]; HealSize = 67*hlMod+healMod15 else SpellID = SpellIDsHL[1]; HealSize = 43*hlMod+healMod25*PF1 end -- Default to rank 1 of FL or HL 159 | -- if healneed > ( 83*hlMod+healMod25*PF6 ) and ManaLeft >= 60 and maxRankHL >=2 and maxRankFL <= 1 then SpellID = SpellIDsHL[2]; HealSize = 83*hlMod+healMod25*PF6 end 160 | -- if healneed > (103*hlMod+healMod15) and ManaLeft >= 50 and maxRankFL >=2 then SpellID = SpellIDsFL[2]; HealSize = 103*hlMod+healMod15 end 161 | -- if healneed > (154*hlMod+healMod15) and ManaLeft >= 70 and maxRankFL >=3 then SpellID = SpellIDsFL[3]; HealSize = 154*hlMod+healMod15 end 162 | -- if healneed > (173*hlMod+healMod25*PF14) and ManaLeft >= 110 and maxRankHL >=3 and maxRankFL <= 3 then SpellID = SpellIDsHL[3]; HealSize = 173*hlMod+healMod25*PF14 end 163 | -- if healneed > (209*hlMod+healMod15) and ManaLeft >= 90 and maxRankFL >=4 then SpellID = SpellIDsFL[4]; HealSize = 209*hlMod+healMod15 end 164 | -- if healneed > (283*hlMod+healMod15) and ManaLeft >= 115 and maxRankFL >=5 then SpellID = SpellIDsFL[5]; HealSize = 283*hlMod+healMod15 end 165 | -- if healneed > (333*hlMod+healMod25) and ManaLeft >= 190 and maxRankHL >=4 and maxRankFL <= 5 then SpellID = SpellIDsHL[4]; HealSize = 333*hlMod+healMod25 end 166 | -- if healneed > (363*hlMod+healMod15) and ManaLeft >= 140 and maxRankFL >=6 then SpellID = SpellIDsFL[6]; HealSize = 363*hlMod+healMod15 end 167 | -- if healneed > (522*hlMod+healMod25) and ManaLeft >= 275 and maxRankHL >=5 then SpellID = SpellIDsHL[5]; HealSize = 522*hlMod+healMod25 end 168 | -- if healneed > (739*hlMod+healMod25) and ManaLeft >= 365 and maxRankHL >=6 then SpellID = SpellIDsHL[6]; HealSize = 739*hlMod+healMod25 end 169 | -- if healneed > (999*hlMod+healMod25) and ManaLeft >= 465 and maxRankHL >=7 then SpellID = SpellIDsHL[7]; HealSize = 999*hlMod+healMod25 end 170 | -- if healneed > (1317*hlMod+healMod25) and ManaLeft >= 580 and maxRankHL >=8 then SpellID = SpellIDsHL[8]; HealSize = 1317*hlMod+healMod25 end 171 | -- if healneed > (1680*hlMod+healMod25) and ManaLeft >= 660 and maxRankHL >=9 then SpellID = SpellIDsHL[9]; HealSize = 1680*hlMod+healMod25 end 172 | -- end 173 | -- end 174 | -- return SpellID,HealSize*HDB; 175 | --end -------------------------------------------------------------------------------- /QuickHealPriest.lua: -------------------------------------------------------------------------------- 1 | local function writeLine(s,r,g,b) 2 | if DEFAULT_CHAT_FRAME then 3 | DEFAULT_CHAT_FRAME:AddMessage(s, r or 1, g or 1, b or 0.5) 4 | end 5 | end 6 | 7 | function QuickHeal_Priest_GetRatioHealthyExplanation() 8 | if QuickHealVariables.RatioHealthyPriest >= QuickHealVariables.RatioFull then 9 | return QUICKHEAL_SPELL_FLASH_HEAL .. " will always be used in combat, and " .. QUICKHEAL_SPELL_LESSER_HEAL .. ", " .. QUICKHEAL_SPELL_HEAL .. " or " .. QUICKHEAL_SPELL_GREATER_HEAL .. " will be used when out of combat. "; 10 | else 11 | if QuickHealVariables.RatioHealthyPriest > 0 then 12 | return QUICKHEAL_SPELL_FLASH_HEAL .. " will be used in combat if the target has less than " .. QuickHealVariables.RatioHealthyPriest*100 .. "% life, and " .. QUICKHEAL_SPELL_LESSER_HEAL .. ", " .. QUICKHEAL_SPELL_HEAL .. " or " .. QUICKHEAL_SPELL_GREATER_HEAL .. " will be used otherwise. "; 13 | else 14 | return QUICKHEAL_SPELL_FLASH_HEAL .. " will never be used. " .. QUICKHEAL_SPELL_LESSER_HEAL .. ", " .. QUICKHEAL_SPELL_HEAL .. " or " .. QUICKHEAL_SPELL_GREATER_HEAL .. " will always be used in and out of combat. "; 15 | end 16 | end 17 | end 18 | 19 | function QuickHeal_Priest_FindSpellToUse(Target) 20 | local SpellID = nil; 21 | local HealSize = 0; 22 | -- Return immediatly if no player needs healing 23 | if not Target then 24 | return SpellID,HealSize; 25 | end 26 | 27 | -- +Healing-PenaltyFactor = (1-((20-LevelLearnt)*0.0375)) for all spells learnt before level 20 28 | local PF1 = 0.2875; 29 | local PF4 = 0.4; 30 | local PF10 = 0.625; 31 | local PF18 = 0.925; 32 | 33 | -- Determine health and healneed of target 34 | local healneed; 35 | local Health; 36 | 37 | if QuickHeal_UnitHasHealthInfo(Target) then 38 | -- Full info available 39 | healneed = UnitHealthMax(Target) - UnitHealth(Target) - HealComm:getHeal(UnitName(Target)); -- Implementatin for HealComm 40 | Health = UnitHealth(Target) / UnitHealthMax(Target); 41 | else 42 | -- Estimate target health 43 | healneed = QuickHeal_EstimateUnitHealNeed(Target,true); -- needs HealComm implementation maybe 44 | Health = UnitHealth(Target)/100; 45 | end 46 | 47 | -- if BonusScanner is running, get +Healing bonus 48 | local Bonus = 0; 49 | if (BonusScanner) then 50 | Bonus = tonumber(BonusScanner:GetBonus("HEAL")); 51 | QuickHeal_debug(string.format("Equipment Healing Bonus: %d", Bonus)); 52 | end 53 | 54 | -- Spiritual Guidance - Increases spell damage and healing by up to 5% (per rank) of your total Spirit. 55 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14); 56 | local _,Spirit,_,_ = UnitStat('player',5); 57 | local sgMod = Spirit * 5*talentRank/100; 58 | QuickHeal_debug(string.format("Spiritual Guidance Bonus: %f", sgMod)); 59 | 60 | -- Calculate healing bonus 61 | local healMod15 = (1.5/3.5) * (sgMod + Bonus); 62 | local healMod20 = (2.0/3.5) * (sgMod + Bonus); 63 | local healMod25 = (2.5/3.5) * (sgMod + Bonus); 64 | local healMod30 = (3.0/3.5) * (sgMod + Bonus); 65 | QuickHeal_debug("Final Healing Bonus (1.5,2.0,2.5,3.0)", healMod15,healMod20,healMod25,healMod30); 66 | 67 | local InCombat = UnitAffectingCombat('player') or UnitAffectingCombat(Target); 68 | 69 | -- Spiritual Healing - Increases healing by 2% per rank on all spells 70 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,15); 71 | local shMod = 2*talentRank/100 + 1; 72 | QuickHeal_debug(string.format("Spiritual Healing modifier: %f", shMod)); 73 | 74 | -- Improved Healing - Decreases mana usage by 5% per rank on LH,H and GH 75 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,10); 76 | local ihMod = 1 - 5*talentRank/100; 77 | QuickHeal_debug(string.format("Improved Healing modifier: %f", ihMod)); 78 | 79 | local TargetIsHealthy = Health >= QuickHealVariables.RatioHealthyPriest; 80 | local ManaLeft = UnitMana('player'); 81 | 82 | if TargetIsHealthy then 83 | QuickHeal_debug("Target is healthy",Health); 84 | end 85 | 86 | -- Detect proc of 'Hand of Edward the Odd' mace (next spell is instant cast) 87 | if QuickHeal_DetectBuff('player',"Spell_Holy_SearingLight") then 88 | QuickHeal_debug("BUFF: Hand of Edward the Odd (out of combat healing forced)"); 89 | InCombat = false; 90 | end 91 | 92 | -- Detect Inner Focus or Spirit of Redemption (hack ManaLeft and healneed) 93 | if QuickHeal_DetectBuff('player',"Spell_Frost_WindWalkOn",1) or QuickHeal_DetectBuff('player',"Spell_Holy_GreaterHeal") then 94 | QuickHeal_debug("Inner Focus or Spirit of Redemption active"); 95 | ManaLeft = UnitManaMax('player'); -- Infinite mana 96 | healneed = 10^6; -- Deliberate overheal (mana is free) 97 | end 98 | 99 | -- Get total healing modifier (factor) caused by healing target debuffs 100 | local HDB = QuickHeal_GetHealModifier(Target); 101 | QuickHeal_debug("Target debuff healing modifier",HDB); 102 | healneed = healneed/HDB; 103 | 104 | -- Get a list of ranks available for all spells 105 | local SpellIDsLH = QuickHeal_GetSpellIDs(QUICKHEAL_SPELL_LESSER_HEAL); 106 | local SpellIDsH = QuickHeal_GetSpellIDs(QUICKHEAL_SPELL_HEAL); 107 | local SpellIDsGH = QuickHeal_GetSpellIDs(QUICKHEAL_SPELL_GREATER_HEAL); 108 | local SpellIDsFH = QuickHeal_GetSpellIDs(QUICKHEAL_SPELL_FLASH_HEAL); 109 | 110 | local maxRankLH = table.getn(SpellIDsLH); 111 | local maxRankH = table.getn(SpellIDsH); 112 | local maxRankGH = table.getn(SpellIDsGH); 113 | local maxRankFH = table.getn(SpellIDsFH); 114 | 115 | QuickHeal_debug(string.format("Found LH up to rank %d, H up top rank %d, GH up to rank %d, and FH up to rank %d", maxRankLH, maxRankH, maxRankGH, maxRankFH)); 116 | 117 | --Get max HealRanks that are allowed to be used 118 | local downRankFH = QuickHealVariables.DownrankValueFH -- rank for 1.5 sec heals 119 | local downRankNH = QuickHealVariables.DownrankValueNH -- rank for < 1.5 sec heals 120 | 121 | -- Compensation for health lost during combat 122 | local k=1.0; 123 | local K=1.0; 124 | if InCombat then 125 | k=0.9; 126 | K=0.8; 127 | end 128 | 129 | -- Find suitable SpellID based on the defined criteria 130 | if not InCombat or TargetIsHealthy or maxRankFH<1 then 131 | -- Not in combat or target is healthy so use the closest available mana efficient healing 132 | QuickHeal_debug(string.format("Not in combat or target healthy or no flash heal available, will use closest available LH, H or GH (not FH)")) 133 | if Health < QuickHealVariables.RatioFull then 134 | SpellID = SpellIDsLH[1]; HealSize = 53*shMod+healMod15*PF1; -- Default to LH 135 | if healneed > ( 84*shMod+healMod20*PF4) *k and ManaLeft >= 45*ihMod and maxRankLH >=2 and downRankNH >= 2 then SpellID = SpellIDsLH[2]; HealSize = 84*shMod+healMod20*PF4 end 136 | if healneed > ( 154*shMod+healMod25*PF10)*K and ManaLeft >= 75*ihMod and maxRankLH >=3 and downRankNH >= 3 then SpellID = SpellIDsLH[3]; HealSize = 154*shMod+healMod25*PF10 end 137 | if healneed > ( 318*shMod+healMod30*PF18)*K and ManaLeft >= 155*ihMod and maxRankH >=1 and downRankNH >= 4 then SpellID = SpellIDsH[1] ; HealSize = 318*shMod+healMod30*PF18 end 138 | if healneed > ( 460*shMod+healMod30)*K and ManaLeft >= 205*ihMod and maxRankH >=2 and downRankNH >= 5 then SpellID = SpellIDsH[2] ; HealSize = 460*shMod+healMod30 end 139 | if healneed > ( 604*shMod+healMod30)*K and ManaLeft >= 255*ihMod and maxRankH >=3 and downRankNH >= 6 then SpellID = SpellIDsH[3] ; HealSize = 604*shMod+healMod30 end 140 | if healneed > ( 758*shMod+healMod30)*K and ManaLeft >= 305*ihMod and maxRankH >=4 and downRankNH >= 7 then SpellID = SpellIDsH[4] ; HealSize = 758*shMod+healMod30 end 141 | if healneed > ( 956*shMod+healMod30)*K and ManaLeft >= 370*ihMod and maxRankGH >=1 and downRankNH >= 8 then SpellID = SpellIDsGH[1]; HealSize = 956*shMod+healMod30 end 142 | if healneed > (1219*shMod+healMod30)*K and ManaLeft >= 455*ihMod and maxRankGH >=2 and downRankNH >= 9 then SpellID = SpellIDsGH[2]; HealSize = 1219*shMod+healMod30 end 143 | if healneed > (1523*shMod+healMod30)*K and ManaLeft >= 545*ihMod and maxRankGH >=3 and downRankNH >= 10 then SpellID = SpellIDsGH[3]; HealSize = 1523*shMod+healMod30 end 144 | if healneed > (1902*shMod+healMod30)*K and ManaLeft >= 655*ihMod and maxRankGH >=4 and downRankNH >= 11 then SpellID = SpellIDsGH[4]; HealSize = 1902*shMod+healMod30 end 145 | if healneed > (2080*shMod+healMod30)*K and ManaLeft >= 710*ihMod and maxRankGH >=5 and downRankNH >= 12 then SpellID = SpellIDsGH[5]; HealSize = 2080*shMod+healMod30 end 146 | end 147 | else 148 | -- In combat and target is unhealthy and player has flash heal 149 | QuickHeal_debug(string.format("In combat and target unhealthy and player has flash heal, will only use FH")); 150 | if Health < QuickHealVariables.RatioFull then 151 | SpellID = SpellIDsFH[1]; HealSize = 215*shMod+healMod15; -- Default to FH 152 | if healneed > (286*shMod+healMod15)*k and ManaLeft >= 155 and maxRankFH >=2 and downRankFH >= 2 then SpellID = SpellIDsFH[2]; HealSize = 286*shMod+healMod15 end 153 | if healneed > (360*shMod+healMod15)*k and ManaLeft >= 185 and maxRankFH >=3 and downRankFH >= 3 then SpellID = SpellIDsFH[3]; HealSize = 360*shMod+healMod15 end 154 | if healneed > (439*shMod+healMod15)*k and ManaLeft >= 215 and maxRankFH >=4 and downRankFH >= 4 then SpellID = SpellIDsFH[4]; HealSize = 439*shMod+healMod15 end 155 | if healneed > (567*shMod+healMod15)*k and ManaLeft >= 265 and maxRankFH >=5 and downRankFH >= 5 then SpellID = SpellIDsFH[5]; HealSize = 567*shMod+healMod15 end 156 | if healneed > (704*shMod+healMod15)*k and ManaLeft >= 315 and maxRankFH >=6 and downRankFH >= 6 then SpellID = SpellIDsFH[6]; HealSize = 704*shMod+healMod15 end 157 | if healneed > (885*shMod+healMod15)*k and ManaLeft >= 380 and maxRankFH >=7 and downRankFH >= 7 then SpellID = SpellIDsFH[7]; HealSize = 885*shMod+healMod15 end 158 | end 159 | end 160 | 161 | return SpellID,HealSize*HDB; 162 | end 163 | -------------------------------------------------------------------------------- /QuickHealShaman.lua: -------------------------------------------------------------------------------- 1 | 2 | function QuickHeal_Shaman_GetRatioHealthyExplanation() 3 | local RatioHealthy = QuickHeal_GetRatioHealthy(); 4 | local RatioFull = QuickHealVariables["RatioFull"]; 5 | 6 | if RatioHealthy >= RatioFull then 7 | return QUICKHEAL_SPELL_HEALING_WAVE .. " will never be used in combat. "; 8 | else 9 | if RatioHealthy > 0 then 10 | return QUICKHEAL_SPELL_HEALING_WAVE .. " will only be used in combat if the target has more than " .. RatioHealthy*100 .. "% life, and only if the healing done is greater than the greatest " .. QUICKHEAL_SPELL_LESSER_HEALING_WAVE .. " available. "; 11 | else 12 | return QUICKHEAL_SPELL_HEALING_WAVE .. " will only be used in combat if the healing done is greater than the greatest " .. QUICKHEAL_SPELL_LESSER_HEALING_WAVE .. " available. "; 13 | end 14 | end 15 | end 16 | 17 | function QuickHeal_Shaman_FindSpellToUse(Target) 18 | local SpellID = nil; 19 | local HealSize = 0; 20 | 21 | -- +Healing-PenaltyFactor = (1-((20-LevelLearnt)*0.0375)) for all spells learnt before level 20 22 | local PF1 = 0.2875; 23 | local PF6 = 0.475; 24 | local PF12 = 0.7; 25 | local PF18 = 0.925; 26 | 27 | -- Local aliases to access main module functionality and settings 28 | local RatioFull = QuickHealVariables["RatioFull"]; 29 | local RatioHealthy = QuickHeal_GetRatioHealthy(); 30 | local UnitHasHealthInfo = QuickHeal_UnitHasHealthInfo; 31 | local EstimateUnitHealNeed = QuickHeal_EstimateUnitHealNeed; 32 | local GetSpellIDs = QuickHeal_GetSpellIDs; 33 | local debug = QuickHeal_debug; 34 | 35 | -- Return immediatly if no player needs healing 36 | if not Target then 37 | return SpellID,HealSize; 38 | end 39 | 40 | -- Determine health and healneed of target 41 | local healneed; 42 | local Health; 43 | if UnitHasHealthInfo(Target) then 44 | -- Full info available 45 | healneed = UnitHealthMax(Target) - UnitHealth(Target) - HealComm:getHeal(UnitName(Target)); -- Implementatio for HealComm 46 | Health = UnitHealth(Target) / UnitHealthMax(Target); 47 | else 48 | -- Estimate target health 49 | healneed = EstimateUnitHealNeed(Target,true); 50 | Health = UnitHealth(Target)/100; 51 | end 52 | 53 | -- if BonusScanner is running, get +Healing bonus 54 | local Bonus = 0; 55 | if (BonusScanner) then 56 | Bonus = tonumber(BonusScanner:GetBonus("HEAL")); 57 | debug(string.format("Equipment Healing Bonus: %d", Bonus)); 58 | end 59 | 60 | -- Calculate healing bonus 61 | local healModLHW = (1.5/3.5) * Bonus; 62 | local healMod15 = (1.5/3.5) * Bonus; 63 | local healMod20 = (2.0/3.5) * Bonus; 64 | local healMod25 = (2.5/3.5) * Bonus; 65 | local healMod30 = (3.0/3.5) * Bonus; 66 | debug("Final Healing Bonus (1.5,2.0,2.5,3.0,LHW)", healMod15,healMod20,healMod25,healMod30,healModLHW); 67 | 68 | local InCombat = UnitAffectingCombat('player') or UnitAffectingCombat(Target); 69 | 70 | -- Purification Talent (increases healing by 2% per rank) 71 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14); 72 | local pMod = 2*talentRank/100 + 1; 73 | debug(string.format("Purification modifier: %f", pMod)) 74 | 75 | -- Tidal Focus - Decreases mana usage by 1% per rank on healing 76 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,2); 77 | local tfMod = 1 - talentRank/100; 78 | debug(string.format("Improved Healing modifier: %f", tfMod)); 79 | 80 | local TargetIsHealthy = Health >= RatioHealthy; 81 | local ManaLeft = UnitMana('player'); 82 | 83 | if TargetIsHealthy then 84 | debug("Target is healthy",Health) 85 | end 86 | 87 | -- Detect Nature's Swiftness (next nature spell is instant cast) 88 | if QuickHeal_DetectBuff('player',"Spell_Nature_RavenForm") then 89 | debug("BUFF: Nature's Swiftness (out of combat healing forced)"); 90 | InCombat = false; 91 | end 92 | 93 | -- Detect proc of 'Hand of Edward the Odd' mace (next spell is instant cast) 94 | if QuickHeal_DetectBuff('player',"Spell_Holy_SearingLight") then 95 | debug("BUFF: Hand of Edward the Odd (out of combat healing forced)"); 96 | InCombat = false; 97 | end 98 | 99 | -- Get total healing modifier (factor) caused by healing target debuffs 100 | local HDB = QuickHeal_GetHealModifier(Target); 101 | debug("Target debuff healing modifier",HDB); 102 | healneed = healneed/HDB; 103 | 104 | -- Detect healing way on target 105 | local hwMod = QuickHeal_DetectBuff(Target,"Spell_Nature_HealingWay"); 106 | if hwMod then hwMod = 1+0.06*hwMod else hwMod = 1 end; 107 | debug("Healing Way healing modifier",hwMod); 108 | 109 | -- Get a list of ranks available of 'Lesser Healing Wave' and 'Healing Wave' 110 | local SpellIDsHW = GetSpellIDs(QUICKHEAL_SPELL_HEALING_WAVE); 111 | local SpellIDsLHW = GetSpellIDs(QUICKHEAL_SPELL_LESSER_HEALING_WAVE); 112 | local maxRankHW = table.getn(SpellIDsHW); 113 | local maxRankLHW = table.getn(SpellIDsLHW); 114 | local NoLHW = maxRankLHW < 1; 115 | debug(string.format("Found HW up to rank %d, and found LHW up to rank %d", maxRankHW, maxRankLHW)) 116 | 117 | -- Find suitable SpellID based on the defined criteria 118 | if InCombat then 119 | -- In combat so use LHW unless: 120 | -- Target is healthy (health > RatioHealthy) 121 | -- AND The HW in question is larger than any available LHW 122 | -- OR LHW is unavailable (sub level 20 characters) 123 | debug(string.format("In combat, will prefer LHW")) 124 | if Health < RatioFull then 125 | local k = 0.9; -- In combat means that target is losing life while casting, so compensate 126 | local K = 0.8; -- k for fast spells (LHW and HW Rank 1 and 2) and K for slow spells (HW) 127 | if maxRankLHW >=1 then SpellID = SpellIDsLHW[1]; HealSize = 174*pMod+healModLHW else SpellID = SpellIDsHW[1]; HealSize = 39*pMod*hwMod+healMod15*PF1 end -- Default to HW or LHW 128 | if healneed > ( 71*pMod*hwMod+healMod20*PF6 )*k and ManaLeft >= 45*tfMod and maxRankHW >=2 and NoLHW then SpellID = SpellIDsHW[2]; HealSize = 71*pMod*hwMod+healMod20*PF6 end 129 | if healneed > ( 142*pMod*hwMod+healMod25*PF12)*K and ManaLeft >= 80*tfMod and maxRankHW >=3 and NoLHW then SpellID = SpellIDsHW[3]; HealSize = 142*pMod*hwMod+healMod25*PF12 end 130 | if healneed > (174*pMod+healModLHW)*k and ManaLeft >= 105*tfMod and maxRankLHW >=1 then SpellID = SpellIDsLHW[1]; HealSize = 174*pMod+healModLHW end 131 | if healneed > (264*pMod+healModLHW)*k and ManaLeft >= 145*tfMod and maxRankLHW >=2 then SpellID = SpellIDsLHW[2]; HealSize = 264*pMod+healModLHW end 132 | if healneed > ( 292*pMod*hwMod+healMod30*PF18)*K and ManaLeft >= 155*tfMod and maxRankHW >=4 and (TargetIsHealthy and maxRankLHW <= 2 or NoLHW) then SpellID = SpellIDsHW[4]; HealSize = 292*pMod*hwMod+healMod30*PF18 end 133 | if healneed > (359*pMod+healModLHW)*k and ManaLeft >= 185*tfMod and maxRankLHW >=3 then SpellID = SpellIDsLHW[3]; HealSize = 359*pMod+healModLHW end 134 | if healneed > ( 408*pMod*hwMod+healMod30)*K and ManaLeft >= 200*tfMod and maxRankHW >=5 and (TargetIsHealthy and maxRankLHW <= 3 or NoLHW) then SpellID = SpellIDsHW[5]; HealSize = 408*pMod*hwMod+healMod30 end 135 | if healneed > (486*pMod+healModLHW)*k and ManaLeft >= 235*tfMod and maxRankLHW >=4 then SpellID = SpellIDsLHW[4]; HealSize = 486*pMod+healModLHW end 136 | if healneed > ( 579*pMod*hwMod+healMod30)*K and ManaLeft >= 265*tfMod and maxRankHW >=6 and (TargetIsHealthy and maxRankLHW <= 4 or NoLHW) then SpellID = SpellIDsHW[6]; HealSize = 579*pMod*hwMod+healMod30 end 137 | if healneed > (668*pMod+healModLHW)*k and ManaLeft >= 305*tfMod and maxRankLHW >=5 then SpellID = SpellIDsLHW[5]; HealSize = 668*pMod+healModLHW end 138 | if healneed > ( 797*pMod*hwMod+healMod30)*K and ManaLeft >= 340*tfMod and maxRankHW >=7 and (TargetIsHealthy and maxRankLHW <= 5 or NoLHW) then SpellID = SpellIDsHW[7]; HealSize = 797*pMod*hwMod+healMod30 end 139 | if healneed > (880*pMod+healModLHW)*k and ManaLeft >= 380*tfMod and maxRankLHW >=6 then SpellID = SpellIDsLHW[6]; HealSize = 880*pMod+healModLHW end 140 | if healneed > (1092*pMod*hwMod+healMod30)*K and ManaLeft >= 440*tfMod and maxRankHW >=8 and (TargetIsHealthy and maxRankLHW <= 6 or NoLHW) then SpellID = SpellIDsHW[8]; HealSize = 1092*pMod*hwMod+healMod30 end 141 | if healneed > (1464*pMod*hwMod+healMod30)*K and ManaLeft >= 560*tfMod and maxRankHW >=9 and (TargetIsHealthy and maxRankLHW <= 6 or NoLHW) then SpellID = SpellIDsHW[9]; HealSize = 1464*pMod*hwMod+healMod30 end 142 | if healneed > (1735*pMod*hwMod+healMod30)*K and ManaLeft >= 620*tfMod and maxRankHW >=10 and (TargetIsHealthy and maxRankLHW <= 6 or NoLHW) then SpellID = SpellIDsHW[10]; HealSize = 1735*pMod*hwMod+healMod30 end 143 | end 144 | else 145 | -- Not in combat so use the closest available healing 146 | debug(string.format("Not in combat, will use closest available HW or LHW")) 147 | if Health < RatioFull then 148 | SpellID = SpellIDsHW[1]; HealSize = 39*pMod*hwMod+healMod15*PF1; 149 | if healneed > ( 71*pMod*hwMod+healMod20*PF6 ) and ManaLeft >= 45*tfMod and maxRankHW >=2 then SpellID = SpellIDsHW[2]; HealSize = 71*pMod*hwMod+healMod20*PF6 end 150 | if healneed > (142*pMod*hwMod+healMod25*PF12) and ManaLeft >= 80*tfMod and maxRankHW >=3 then SpellID = SpellIDsHW[3]; HealSize = 142*pMod*hwMod+healMod25*PF12 end 151 | if healneed > (174*pMod+healModLHW) and ManaLeft >= 105*tfMod and maxRankLHW >=1 then SpellID = SpellIDsLHW[1]; HealSize = 174*pMod+healModLHW end 152 | if healneed > (264*pMod+healModLHW) and ManaLeft >= 145*tfMod and maxRankLHW >=2 then SpellID = SpellIDsLHW[2]; HealSize = 264*pMod+healModLHW end 153 | if healneed > (292*pMod*hwMod+healMod30*PF18) and ManaLeft >= 155*tfMod and maxRankHW >=4 then SpellID = SpellIDsHW[4]; HealSize = 292*pMod*hwMod+healMod30*PF18 end 154 | if healneed > (359*pMod+healModLHW) and ManaLeft >= 185*tfMod and maxRankLHW >=3 then SpellID = SpellIDsLHW[3]; HealSize = 359*pMod+healModLHW end 155 | if healneed > (408*pMod*hwMod+healMod30) and ManaLeft >= 200*tfMod and maxRankHW >=5 then SpellID = SpellIDsHW[5]; HealSize = 408*pMod*hwMod+healMod30 end 156 | if healneed > (486*pMod+healModLHW) and ManaLeft >= 235*tfMod and maxRankLHW >=4 then SpellID = SpellIDsLHW[4]; HealSize = 486*pMod+healModLHW end 157 | if healneed > (579*pMod*hwMod+healMod30) and ManaLeft >= 265*tfMod and maxRankHW >=6 then SpellID = SpellIDsHW[6]; HealSize = 579*pMod*hwMod+healMod30 end 158 | if healneed > (668*pMod+healModLHW) and ManaLeft >= 305*tfMod and maxRankLHW >=5 then SpellID = SpellIDsLHW[5]; HealSize = 668*pMod+healModLHW end 159 | if healneed > (797*pMod*hwMod+healMod30) and ManaLeft >= 340*tfMod and maxRankHW >=7 then SpellID = SpellIDsHW[7]; HealSize = 797*pMod*hwMod+healMod30 end 160 | if healneed > (880*pMod+healModLHW) and ManaLeft >= 380*tfMod and maxRankLHW >=6 then SpellID = SpellIDsLHW[6]; HealSize = 880*pMod+healModLHW end 161 | if healneed > (1092*pMod*hwMod+healMod30) and ManaLeft >= 440*tfMod and maxRankHW >=8 then SpellID = SpellIDsHW[8]; HealSize = 1092*pMod*hwMod+healMod30 end 162 | if healneed > (1464*pMod*hwMod+healMod30) and ManaLeft >= 560*tfMod and maxRankHW >=9 then SpellID = SpellIDsHW[9]; HealSize = 1464*pMod*hwMod+healMod30 end 163 | if healneed > (1735*pMod*hwMod+healMod30) and ManaLeft >= 620*tfMod and maxRankHW >=10 then SpellID = SpellIDsHW[10]; HealSize = 1735*pMod*hwMod+healMod30 end 164 | end 165 | end 166 | 167 | return SpellID,HealSize*HDB; 168 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuickHeal 2 | QuickHeal for Vanilla WoW 1.12. with integration for HealComm (Luna unit Frames). 3 | 4 | I'm not the author of QuickHeal. 5 | 6 | I just did the implementation for QuckHeal to use Healcom to heal smarter. 7 | I also did some other additions and improvements. 8 | 9 | I got the original version of QuickHeal from https://wow.curseforge.com/projects/project-2800. 10 | 11 | # Installation 12 | - Download QuickHeal from this repository into your Interface folder and remove the "-master" in the folder name 13 | - Download HealComm or Luna unit Frames: https://github.com/Aviana/LunaUnitFrames 14 | - Download Bonusscanner (Makes QhickHeal and HealComm (Luna unit Frames) more accurate by taking gear and +Heal into account: http://www.vanilla-addons.com/dls/bonusscanner/ 15 | 16 | # What this version of QuickHeal does: 17 | 18 | **Integration of HealComm** 19 | 20 | - QuickHeal is now using the incomming heal information, broadcasted by HealComm (Luna unit Frames), through the addonchannel to reduce overhealing and making this addon used by multible raidmembers more effective. 21 | - Also there is the option to downrank the spells being used, so healing can be more effitient. 22 | 23 | **How to use** 24 | - Find the original description here: https://wow.curseforge.com/projects/project-2800 25 | 26 | - Help 27 | "/qh help" displays all a 28 | 29 | - Heal: 30 | To do a heal make a macro with "/qh" or set Keybind: 31 | 32 | ![Keybind Quick Heal](https://www2.pic-upload.de/img/35189646/1.png) 33 | 34 | - Downrank 35 | To conserve mana and heal more effitiently you can limit the max rank that Quick Heal will use. It is done by moving the slider. on the Downrank Window. "/qh dr" to open the Downrank Window. 36 | ![Open Downrank Window](https://www2.pic-upload.de/img/35189791/2.png) 37 | ![Downrank Window](https://www2.pic-upload.de/img/35189979/3.png) 38 | 39 | - Change Heal mode 40 | There are 3 modes for healing. 41 | 42 | "/qh hm 1" - Default 43 | Priest: Will only cast Greater Heal, Heal and Lesser Heal. 44 | Paladin: Will only use Flash of Light 45 | 46 | "/qh hm 2" 47 | Priest: Will only cast Flash Heal 48 | Paladin: Will only cast Holy Light 49 | 50 | "/qh hm 3" - Most HPS 51 | Will cast the heal with the most HPS. 52 | Priest: Will cast Prayer of Healing if possible 53 | 54 | The Heal mode can tbe set with macro or Key bind. The ">" indicate the selected mode. 55 | ![Keybinds Healmode](https://www2.pic-upload.de/img/35190067/4.png) 56 | 57 | # Changes: 58 | 59 | hc05: (get from Branch) 60 | - Healmodes for Paladins and Priests now controll the spells used. See How to Use 61 | - Priests: in healmode 3 Prayer of Heal is cast if possible 62 | - Finalization of the addon, for the possibility of localization [@shikulja](https://github.com/shikulja). Special thanks, for the help and instructions Lichery. 63 | - Fixed overhealing sound playing 64 | - Added colored target names (enabled by default) 65 | 66 | hc04: 67 | - new command: /qh healpct - Will prioritise the player with the lowest percentage of health. (override changes of hc03, where priests heal the target with the most healing needed and paladins the lowest target) 68 | 69 | 70 | hc03: 71 | - /qh downrank or /qh dr toggles a window where the maximum spell rank to be used for healspells can be set. (hold rightclick on frame to drag the window) 72 | - Keybind for toggeling downrank window 73 | - /qh toggle will change between Flash heals and Normal heals on Priest 74 | - Keybind to toggle between Flash heals and Normal heals on Priest 75 | - Heal sent from other players via HealComm will now be considered in selv preservation 76 | - Heal sent from other players via HealComm will now be considered for player that is target of target prioity healing 77 | - Priests will now heal the player with the most health missing 78 | - Paladin will now heal the player with the lowest amount of health 79 | - For Paladins healing out of fight will now use the same spells as infight 80 | -------------------------------------------------------------------------------- /libs/AceAddon-2.0/AceAddon-2.0.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulpitz/QuickHeal/33a3172bbcfa930b57bb3f0b9002eb09f99d4760/libs/AceAddon-2.0/AceAddon-2.0.lua -------------------------------------------------------------------------------- /libs/AceEvent-2.0/AceEvent-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: AceEvent-2.0 3 | Revision: $Rev: 17638 $ 4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) 5 | Inspired By: Ace 1.x by Turan (turan@gryphon.com) 6 | Website: http://www.wowace.com/ 7 | Documentation: http://www.wowace.com/index.php/AceEvent-2.0 8 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0 9 | Description: Mixin to allow for event handling, scheduling, and inter-addon 10 | communication. 11 | Dependencies: AceLibrary, AceOO-2.0 12 | ]] 13 | 14 | local MAJOR_VERSION = "AceEvent-2.0" 15 | local MINOR_VERSION = "$Revision: 17638 $" 16 | 17 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end 18 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 19 | 20 | if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check 21 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end 22 | 23 | local AceOO = AceLibrary:GetInstance("AceOO-2.0") 24 | local Mixin = AceOO.Mixin 25 | local AceEvent = Mixin { 26 | "RegisterEvent", 27 | "RegisterAllEvents", 28 | "UnregisterEvent", 29 | "UnregisterAllEvents", 30 | "TriggerEvent", 31 | "ScheduleEvent", 32 | "ScheduleRepeatingEvent", 33 | "CancelScheduledEvent", 34 | "CancelAllScheduledEvents", 35 | "IsEventRegistered", 36 | "IsEventScheduled", 37 | "RegisterBucketEvent", 38 | "UnregisterBucketEvent", 39 | "UnregisterAllBucketEvents", 40 | "IsBucketEventRegistered", 41 | } 42 | 43 | local table_setn 44 | do 45 | local version = GetBuildInfo() 46 | if string.find(version, "^2%.") then 47 | -- 2.0.0 48 | table_setn = function() end 49 | else 50 | table_setn = table.setn 51 | end 52 | end 53 | 54 | local weakKey = {__mode="k"} 55 | local new, del 56 | do 57 | local list = setmetatable({}, weakKey) 58 | function new() 59 | local t = next(list) 60 | if t then 61 | list[t] = nil 62 | return t 63 | else 64 | return {} 65 | end 66 | end 67 | 68 | function del(t) 69 | setmetatable(t, nil) 70 | for k in pairs(t) do 71 | t[k] = nil 72 | end 73 | list[t] = true 74 | end 75 | end 76 | 77 | local FAKE_NIL 78 | local RATE 79 | 80 | local eventsWhichHappenOnce = { 81 | PLAYER_LOGIN = true, 82 | AceEvent_FullyInitialized = true, 83 | VARIABLES_LOADED = true, 84 | PLAYER_LOGOUT = true, 85 | } 86 | 87 | local registeringFromAceEvent 88 | function AceEvent:RegisterEvent(event, method, once) 89 | AceEvent:argCheck(event, 2, "string") 90 | if self == AceEvent and not registeringFromAceEvent then 91 | AceEvent:argCheck(method, 3, "function") 92 | self = method 93 | else 94 | AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number") 95 | if type(method) == "boolean" or type(method) == "number" then 96 | AceEvent:argCheck(once, 4, "nil") 97 | once, method = method, event 98 | end 99 | end 100 | AceEvent:argCheck(once, 4, "number", "boolean", "nil") 101 | if eventsWhichHappenOnce[event] then 102 | once = true 103 | end 104 | local throttleRate 105 | if type(once) == "number" then 106 | throttleRate, once = once 107 | end 108 | if not method then 109 | method = event 110 | end 111 | if type(method) == "string" and type(self[method]) ~= "function" then 112 | AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) 113 | else 114 | assert(type(method) == "function" or type(method) == "string") 115 | end 116 | 117 | local AceEvent_registry = AceEvent.registry 118 | if not AceEvent_registry[event] then 119 | AceEvent_registry[event] = new() 120 | AceEvent.frame:RegisterEvent(event) 121 | end 122 | 123 | local remember = true 124 | if AceEvent_registry[event][self] then 125 | remember = false 126 | end 127 | AceEvent_registry[event][self] = method 128 | 129 | local AceEvent_onceRegistry = AceEvent.onceRegistry 130 | if once then 131 | if not AceEvent_onceRegistry then 132 | AceEvent.onceRegistry = new() 133 | AceEvent_onceRegistry = AceEvent.onceRegistry 134 | end 135 | if not AceEvent_onceRegistry[event] then 136 | AceEvent_onceRegistry[event] = new() 137 | end 138 | AceEvent_onceRegistry[event][self] = true 139 | else 140 | if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then 141 | AceEvent_onceRegistry[event][self] = nil 142 | if not next(AceEvent_onceRegistry[event]) then 143 | AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) 144 | end 145 | end 146 | end 147 | 148 | local AceEvent_throttleRegistry = AceEvent.throttleRegistry 149 | if throttleRate then 150 | if not AceEvent_throttleRegistry then 151 | AceEvent.throttleRegistry = new() 152 | AceEvent_throttleRegistry = AceEvent.throttleRegistry 153 | end 154 | if not AceEvent_throttleRegistry[event] then 155 | AceEvent_throttleRegistry[event] = new() 156 | end 157 | if AceEvent_throttleRegistry[event][self] then 158 | AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) 159 | end 160 | AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey) 161 | local t = AceEvent_throttleRegistry[event][self] 162 | t[RATE] = throttleRate 163 | else 164 | if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then 165 | if AceEvent_throttleRegistry[event][self] then 166 | AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) 167 | end 168 | if not next(AceEvent_throttleRegistry[event]) then 169 | AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) 170 | end 171 | end 172 | end 173 | 174 | if remember then 175 | AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event) 176 | end 177 | end 178 | 179 | local ALL_EVENTS 180 | 181 | function AceEvent:RegisterAllEvents(method) 182 | if self == AceEvent then 183 | AceEvent:argCheck(method, 1, "function") 184 | self = method 185 | else 186 | AceEvent:argCheck(method, 1, "string", "function") 187 | if type(method) == "string" and type(self[method]) ~= "function" then 188 | AceEvent:error("Cannot register all events to method %q, it does not exist", method) 189 | end 190 | end 191 | 192 | local AceEvent_registry = AceEvent.registry 193 | if not AceEvent_registry[ALL_EVENTS] then 194 | AceEvent_registry[ALL_EVENTS] = new() 195 | AceEvent.frame:RegisterAllEvents() 196 | end 197 | 198 | AceEvent_registry[ALL_EVENTS][self] = method 199 | end 200 | 201 | local _G = getfenv(0) 202 | local memstack, timestack = {}, {} 203 | local memdiff, timediff 204 | function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 205 | AceEvent:argCheck(event, 2, "string") 206 | local AceEvent_registry = AceEvent.registry 207 | if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then 208 | return 209 | end 210 | local _G_event = _G.event 211 | _G.event = event 212 | local lastEvent = AceEvent.currentEvent 213 | AceEvent.currentEvent = event 214 | 215 | local AceEvent_onceRegistry = AceEvent.onceRegistry 216 | local AceEvent_debugTable = AceEvent.debugTable 217 | if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then 218 | local tmp = new() 219 | for obj, method in pairs(AceEvent_onceRegistry[event]) do 220 | tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil 221 | end 222 | local obj = next(tmp) 223 | while obj do 224 | local mem, time 225 | if AceEvent_debugTable then 226 | if not AceEvent_debugTable[event] then 227 | AceEvent_debugTable[event] = new() 228 | end 229 | if not AceEvent_debugTable[event][obj] then 230 | AceEvent_debugTable[event][obj] = new() 231 | AceEvent_debugTable[event][obj].mem = 0 232 | AceEvent_debugTable[event][obj].time = 0 233 | AceEvent_debugTable[event][obj].count = 0 234 | end 235 | if memdiff then 236 | table.insert(memstack, memdiff) 237 | table.insert(timestack, timediff) 238 | end 239 | memdiff, timediff = 0, 0 240 | mem, time = gcinfo(), GetTime() 241 | end 242 | local method = tmp[obj] 243 | AceEvent.UnregisterEvent(obj, event) 244 | if type(method) == "string" then 245 | local obj_method = obj[method] 246 | if obj_method then 247 | obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 248 | end 249 | elseif method then -- function 250 | method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 251 | end 252 | if AceEvent_debugTable then 253 | local dmem, dtime = memdiff, timediff 254 | mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff 255 | AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem 256 | AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time 257 | AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 258 | 259 | memdiff, timediff = table.remove(memstack), table.remove(timestack) 260 | if memdiff then 261 | memdiff = memdiff + mem + dmem 262 | timediff = timediff + time + dtime 263 | end 264 | end 265 | tmp[obj] = nil 266 | obj = next(tmp) 267 | end 268 | del(tmp) 269 | end 270 | 271 | local AceEvent_throttleRegistry = AceEvent.throttleRegistry 272 | local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] 273 | if AceEvent_registry[event] then 274 | local tmp = new() 275 | for obj, method in pairs(AceEvent_registry[event]) do 276 | tmp[obj] = method 277 | end 278 | local obj = next(tmp) 279 | while obj do 280 | local method = tmp[obj] 281 | local continue = false 282 | if throttleTable and throttleTable[obj] then 283 | local a1 = a1 284 | if a1 == nil then 285 | a1 = FAKE_NIL 286 | end 287 | if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then 288 | throttleTable[obj][a1] = GetTime() 289 | else 290 | continue = true 291 | end 292 | end 293 | if not continue then 294 | local mem, time 295 | if AceEvent_debugTable then 296 | if not AceEvent_debugTable[event] then 297 | AceEvent_debugTable[event] = new() 298 | end 299 | if not AceEvent_debugTable[event][obj] then 300 | AceEvent_debugTable[event][obj] = new() 301 | AceEvent_debugTable[event][obj].mem = 0 302 | AceEvent_debugTable[event][obj].time = 0 303 | AceEvent_debugTable[event][obj].count = 0 304 | end 305 | if memdiff then 306 | table.insert(memstack, memdiff) 307 | table.insert(timestack, timediff) 308 | end 309 | memdiff, timediff = 0, 0 310 | mem, time = gcinfo(), GetTime() 311 | end 312 | if type(method) == "string" then 313 | local obj_method = obj[method] 314 | if obj_method then 315 | obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 316 | end 317 | elseif method then -- function 318 | method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 319 | end 320 | if AceEvent_debugTable then 321 | local dmem, dtime = memdiff, timediff 322 | mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff 323 | AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem 324 | AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time 325 | AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 326 | 327 | memdiff, timediff = table.remove(memstack), table.remove(timestack) 328 | if memdiff then 329 | memdiff = memdiff + mem + dmem 330 | timediff = timediff + time + dtime 331 | end 332 | end 333 | end 334 | tmp[obj] = nil 335 | obj = next(tmp) 336 | end 337 | del(tmp) 338 | end 339 | if AceEvent_registry[ALL_EVENTS] then 340 | local tmp = new() 341 | for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do 342 | tmp[obj] = method 343 | end 344 | local obj = next(tmp) 345 | while obj do 346 | local method = tmp[obj] 347 | local mem, time 348 | if AceEvent_debugTable then 349 | if not AceEvent_debugTable[event] then 350 | AceEvent_debugTable[event] = new() 351 | end 352 | if not AceEvent_debugTable[event][obj] then 353 | AceEvent_debugTable[event][obj] = new() 354 | AceEvent_debugTable[event][obj].mem = 0 355 | AceEvent_debugTable[event][obj].time = 0 356 | AceEvent_debugTable[event][obj].count = 0 357 | end 358 | if memdiff then 359 | table.insert(memstack, memdiff) 360 | table.insert(timestack, timediff) 361 | end 362 | memdiff, timediff = 0, 0 363 | mem, time = gcinfo(), GetTime() 364 | end 365 | if type(method) == "string" then 366 | local obj_method = obj[method] 367 | if obj_method then 368 | obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 369 | end 370 | elseif method then -- function 371 | method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 372 | end 373 | if AceEvent_debugTable then 374 | local dmem, dtime = memdiff, timediff 375 | mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff 376 | AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem 377 | AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time 378 | AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 379 | 380 | memdiff, timediff = table.remove(memstack), table.remove(timestack) 381 | if memdiff then 382 | memdiff = memdiff + mem + dmem 383 | timediff = timediff + time + dtime 384 | end 385 | end 386 | tmp[obj] = nil 387 | obj = next(tmp) 388 | end 389 | del(tmp) 390 | end 391 | _G.event = _G_event 392 | AceEvent.currentEvent = lastEvent 393 | end 394 | 395 | -- local accessors 396 | local getn = table.getn 397 | local tinsert = table.insert 398 | local tremove = table.remove 399 | local floor = math.floor 400 | local GetTime = GetTime 401 | local next = next 402 | local pairs = pairs 403 | local unpack = unpack 404 | 405 | local delayRegistry 406 | local tmp = {} 407 | local function OnUpdate() 408 | local t = GetTime() 409 | for k,v in pairs(delayRegistry) do 410 | tmp[k] = true 411 | end 412 | for k in pairs(tmp) do 413 | local v = delayRegistry[k] 414 | if v then 415 | local v_time = v.time 416 | if not v_time then 417 | delayRegistry[k] = del(v) 418 | elseif v_time <= t then 419 | local v_repeatDelay = v.repeatDelay 420 | if v_repeatDelay then 421 | -- use the event time, not the current time, else timing inaccuracies add up over time 422 | v.time = v_time + v_repeatDelay 423 | end 424 | local event = v.event 425 | local mem, time 426 | if AceEvent_debugTable then 427 | mem, time = gcinfo(), GetTime() 428 | end 429 | if type(event) == "function" then 430 | event(unpack(v)) 431 | else 432 | AceEvent:TriggerEvent(event, unpack(v)) 433 | end 434 | if AceEvent_debugTable then 435 | mem, time = gcinfo() - mem, GetTime() - time 436 | v.mem = v.mem + mem 437 | v.timeSpent = v.timeSpent + time 438 | v.count = v.count + 1 439 | end 440 | if not v_repeatDelay then 441 | local x = delayRegistry[k] 442 | if x and x.time == v_time then -- check if it was manually reset 443 | delayRegistry[k] = del(v) 444 | end 445 | end 446 | end 447 | end 448 | end 449 | for k in pairs(tmp) do 450 | tmp[k] = nil 451 | end 452 | if not next(delayRegistry) then 453 | AceEvent.frame:Hide() 454 | end 455 | end 456 | 457 | local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 458 | local id 459 | if type(event) == "string" or type(event) == "table" then 460 | if type(event) == "table" then 461 | if not delayRegistry or not delayRegistry[event] then 462 | AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") 463 | end 464 | end 465 | if type(delay) ~= "number" then 466 | id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 467 | AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number") 468 | AceEvent:argCheck(delay, 4, "number") 469 | self:CancelScheduledEvent(id) 470 | end 471 | else 472 | AceEvent:argCheck(event, 2, "string", "function") 473 | AceEvent:argCheck(delay, 3, "number") 474 | end 475 | 476 | if not delayRegistry then 477 | AceEvent.delayRegistry = new() 478 | delayRegistry = AceEvent.delayRegistry 479 | AceEvent.frame:SetScript("OnUpdate", OnUpdate) 480 | end 481 | local t 482 | if type(id) == "table" then 483 | for k in pairs(id) do 484 | id[k] = nil 485 | end 486 | t = id 487 | else 488 | t = new() 489 | end 490 | t[1] = a1 491 | t[2] = a2 492 | t[3] = a3 493 | t[4] = a4 494 | t[5] = a5 495 | t[6] = a6 496 | t[7] = a7 497 | t[8] = a8 498 | t[9] = a9 499 | t[10] = a10 500 | t[11] = a11 501 | t[12] = a12 502 | t[13] = a13 503 | t[14] = a14 504 | t[15] = a15 505 | t[16] = a16 506 | t[17] = a17 507 | t[18] = a18 508 | t[19] = a19 509 | t[20] = a20 510 | table_setn(t, 20) 511 | t.event = event 512 | t.time = GetTime() + delay 513 | t.self = self 514 | t.id = id or t 515 | t.repeatDelay = repeating and delay 516 | if AceEvent_debugTable then 517 | t.mem = 0 518 | t.count = 0 519 | t.timeSpent = 0 520 | end 521 | delayRegistry[t.id] = t 522 | AceEvent.frame:Show() 523 | return t.id 524 | end 525 | 526 | function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 527 | if type(event) == "string" or type(event) == "table" then 528 | if type(event) == "table" then 529 | if not delayRegistry or not delayRegistry[event] then 530 | AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") 531 | end 532 | end 533 | if type(delay) ~= "number" then 534 | AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") 535 | AceEvent:argCheck(a1, 4, "number") 536 | end 537 | else 538 | AceEvent:argCheck(event, 2, "string", "function") 539 | AceEvent:argCheck(delay, 3, "number") 540 | end 541 | 542 | return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 543 | end 544 | 545 | function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 546 | if type(event) == "string" or type(event) == "table" then 547 | if type(event) == "table" then 548 | if not delayRegistry or not delayRegistry[event] then 549 | AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") 550 | end 551 | end 552 | if type(delay) ~= "number" then 553 | AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") 554 | AceEvent:argCheck(a1, 4, "number") 555 | end 556 | else 557 | AceEvent:argCheck(event, 2, "string", "function") 558 | AceEvent:argCheck(delay, 3, "number") 559 | end 560 | 561 | return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) 562 | end 563 | 564 | function AceEvent:CancelScheduledEvent(t) 565 | AceEvent:argCheck(t, 2, "string", "table") 566 | if delayRegistry then 567 | local v = delayRegistry[t] 568 | if v then 569 | delayRegistry[t] = del(v) 570 | if not next(delayRegistry) then 571 | AceEvent.frame:Hide() 572 | end 573 | return true 574 | end 575 | end 576 | return false 577 | end 578 | 579 | function AceEvent:IsEventScheduled(t) 580 | AceEvent:argCheck(t, 2, "string", "table") 581 | if delayRegistry then 582 | local v = delayRegistry[t] 583 | if v then 584 | return true, v.time - GetTime() 585 | end 586 | end 587 | return false, nil 588 | end 589 | 590 | function AceEvent:UnregisterEvent(event) 591 | AceEvent:argCheck(event, 2, "string") 592 | local AceEvent_registry = AceEvent.registry 593 | if AceEvent_registry[event] and AceEvent_registry[event][self] then 594 | AceEvent_registry[event][self] = nil 595 | local AceEvent_onceRegistry = AceEvent.onceRegistry 596 | if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then 597 | AceEvent_onceRegistry[event][self] = nil 598 | if not next(AceEvent_onceRegistry[event]) then 599 | AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) 600 | end 601 | end 602 | local AceEvent_throttleRegistry = AceEvent.throttleRegistry 603 | if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then 604 | AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) 605 | if not next(AceEvent_throttleRegistry[event]) then 606 | AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) 607 | end 608 | end 609 | if not next(AceEvent_registry[event]) then 610 | AceEvent_registry[event] = del(AceEvent_registry[event]) 611 | if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then 612 | AceEvent.frame:UnregisterEvent(event) 613 | end 614 | end 615 | else 616 | if self == AceEvent then 617 | error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2) 618 | else 619 | AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self) 620 | end 621 | end 622 | AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) 623 | end 624 | 625 | function AceEvent:UnregisterAllEvents() 626 | local AceEvent_registry = AceEvent.registry 627 | if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then 628 | AceEvent_registry[ALL_EVENTS][self] = nil 629 | if not next(AceEvent_registry[ALL_EVENTS]) then 630 | del(AceEvent_registry[ALL_EVENTS]) 631 | AceEvent.frame:UnregisterAllEvents() 632 | for k,v in pairs(AceEvent_registry) do 633 | if k ~= ALL_EVENTS then 634 | AceEvent.frame:RegisterEvent(k) 635 | end 636 | end 637 | AceEvent_registry[event] = nil 638 | end 639 | end 640 | local first = true 641 | for event, data in pairs(AceEvent_registry) do 642 | if first then 643 | if AceEvent_registry.AceEvent_EventUnregistered then 644 | event = "AceEvent_EventUnregistered" 645 | else 646 | first = false 647 | end 648 | end 649 | local x = data[self] 650 | data[self] = nil 651 | if x and event ~= ALL_EVENTS then 652 | if not next(data) then 653 | del(data) 654 | if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then 655 | AceEvent.frame:UnregisterEvent(event) 656 | end 657 | AceEvent_registry[event] = nil 658 | end 659 | AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) 660 | end 661 | if first then 662 | event = nil 663 | end 664 | end 665 | if AceEvent.onceRegistry then 666 | for event, data in pairs(AceEvent.onceRegistry) do 667 | data[self] = nil 668 | end 669 | end 670 | end 671 | 672 | function AceEvent:CancelAllScheduledEvents() 673 | if delayRegistry then 674 | for k,v in pairs(delayRegistry) do 675 | if v.self == self then 676 | delayRegistry[k] = del(v) 677 | end 678 | end 679 | if not next(delayRegistry) then 680 | AceEvent.frame:Hide() 681 | end 682 | end 683 | end 684 | 685 | function AceEvent:IsEventRegistered(event) 686 | AceEvent:argCheck(event, 2, "string") 687 | local AceEvent_registry = AceEvent.registry 688 | if self == AceEvent then 689 | return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false 690 | end 691 | if AceEvent_registry[event] and AceEvent_registry[event][self] then 692 | return true, AceEvent_registry[event][self] 693 | end 694 | return false, nil 695 | end 696 | 697 | local bucketfunc 698 | function AceEvent:RegisterBucketEvent(event, delay, method) 699 | AceEvent:argCheck(event, 2, "string", "table") 700 | if type(event) == "table" then 701 | for k,v in pairs(event) do 702 | if type(k) ~= "number" then 703 | AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.") 704 | elseif type(v) ~= "string" then 705 | AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.") 706 | end 707 | end 708 | end 709 | AceEvent:argCheck(delay, 3, "number") 710 | if AceEvent == self then 711 | AceEvent:argCheck(method, 4, "function") 712 | self = method 713 | else 714 | if type(event) == "string" then 715 | AceEvent:argCheck(method, 4, "string", "function", "nil") 716 | if not method then 717 | method = event 718 | end 719 | else 720 | AceEvent:argCheck(method, 4, "string", "function") 721 | end 722 | 723 | if type(method) == "string" and type(self[method]) ~= "function" then 724 | AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) 725 | end 726 | end 727 | if not AceEvent.buckets then 728 | AceEvent.buckets = new() 729 | end 730 | if not AceEvent.buckets[event] then 731 | AceEvent.buckets[event] = new() 732 | end 733 | if not AceEvent.buckets[event][self] then 734 | AceEvent.buckets[event][self] = new() 735 | AceEvent.buckets[event][self].current = new() 736 | AceEvent.buckets[event][self].self = self 737 | else 738 | AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id) 739 | end 740 | local bucket = AceEvent.buckets[event][self] 741 | bucket.method = method 742 | 743 | local func = function(arg1) 744 | bucket.run = true 745 | if arg1 then 746 | bucket.current[arg1] = true 747 | end 748 | end 749 | AceEvent.buckets[event][self].func = func 750 | if type(event) == "string" then 751 | AceEvent.RegisterEvent(self, event, func) 752 | else 753 | for _,v in ipairs(event) do 754 | AceEvent.RegisterEvent(self, v, func) 755 | end 756 | end 757 | if not bucketfunc then 758 | bucketfunc = function(bucket) 759 | local current = bucket.current 760 | local method = bucket.method 761 | local self = bucket.self 762 | if bucket.run then 763 | if type(method) == "string" then 764 | self[method](self, current) 765 | elseif method then -- function 766 | method(current) 767 | end 768 | for k in pairs(current) do 769 | current[k] = nil 770 | k = nil 771 | end 772 | bucket.run = false 773 | end 774 | end 775 | end 776 | bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket) 777 | end 778 | 779 | function AceEvent:IsBucketEventRegistered(event) 780 | AceEvent:argCheck(event, 2, "string", "table") 781 | return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self] 782 | end 783 | 784 | function AceEvent:UnregisterBucketEvent(event) 785 | AceEvent:argCheck(event, 2, "string", "table") 786 | if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then 787 | AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self) 788 | end 789 | 790 | local bucket = AceEvent.buckets[event][self] 791 | 792 | if type(event) == "string" then 793 | AceEvent.UnregisterEvent(self, event) 794 | else 795 | for _,v in ipairs(event) do 796 | AceEvent.UnregisterEvent(self, v) 797 | end 798 | end 799 | AceEvent:CancelScheduledEvent(bucket.id) 800 | 801 | del(bucket.current) 802 | AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self]) 803 | if not next(AceEvent.buckets[event]) then 804 | AceEvent.buckets[event] = del(AceEvent.buckets[event]) 805 | end 806 | end 807 | 808 | function AceEvent:UnregisterAllBucketEvents() 809 | if not AceEvent.buckets or not next(AceEvent.buckets) then 810 | return 811 | end 812 | for k,v in pairs(AceEvent.buckets) do 813 | if v == self then 814 | AceEvent.UnregisterBucketEvent(self, k) 815 | k = nil 816 | end 817 | end 818 | end 819 | 820 | function AceEvent:OnEmbedDisable(target) 821 | self.UnregisterAllEvents(target) 822 | 823 | self.CancelAllScheduledEvents(target) 824 | 825 | self.UnregisterAllBucketEvents(target) 826 | end 827 | 828 | function AceEvent:EnableDebugging() 829 | if not self.debugTable then 830 | self.debugTable = new() 831 | 832 | if delayRegistry then 833 | for k,v in pairs(self.delayRegistry) do 834 | if not v.mem then 835 | v.mem = 0 836 | v.count = 0 837 | v.timeSpent = 0 838 | end 839 | end 840 | end 841 | end 842 | end 843 | 844 | function AceEvent:IsFullyInitialized() 845 | return self.postInit or false 846 | end 847 | 848 | function AceEvent:IsPostPlayerLogin() 849 | return self.playerLogin or false 850 | end 851 | 852 | function AceEvent:activate(oldLib, oldDeactivate) 853 | AceEvent = self 854 | 855 | if oldLib then 856 | self.onceRegistry = oldLib.onceRegistry 857 | self.throttleRegistry = oldLib.throttleRegistry 858 | self.delayRegistry = oldLib.delayRegistry 859 | self.buckets = oldLib.buckets 860 | self.registry = oldLib.registry 861 | self.frame = oldLib.frame 862 | self.debugTable = oldLib.debugTable 863 | self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true 864 | self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true 865 | self.ALL_EVENTS = oldLib.ALL_EVENTS 866 | self.FAKE_NIL = oldLib.FAKE_NIL 867 | self.RATE = oldLib.RATE 868 | end 869 | if not self.registry then 870 | self.registry = {} 871 | end 872 | if not self.frame then 873 | self.frame = CreateFrame("Frame", "AceEvent20Frame") 874 | end 875 | if not self.ALL_EVENTS then 876 | self.ALL_EVENTS = {} 877 | end 878 | if not self.FAKE_NIL then 879 | self.FAKE_NIL = {} 880 | end 881 | if not self.RATE then 882 | self.RATE = {} 883 | end 884 | ALL_EVENTS = self.ALL_EVENTS 885 | FAKE_NIL = self.FAKE_NIL 886 | RATE = self.RATE 887 | local inPlw = false 888 | local blacklist = { 889 | UNIT_INVENTORY_CHANGED = true, 890 | BAG_UPDATE = true, 891 | ITEM_LOCK_CHANGED = true, 892 | ACTIONBAR_SLOT_CHANGED = true, 893 | } 894 | self.frame:SetScript("OnEvent", function() 895 | local event = event 896 | if event == "PLAYER_ENTERING_WORLD" then 897 | inPlw = false 898 | elseif event == "PLAYER_LEAVING_WORLD" then 899 | inPlw = true 900 | end 901 | if event and (not inPlw or not blacklist[event]) then 902 | self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) 903 | end 904 | end) 905 | if self.delayRegistry then 906 | delayRegistry = self.delayRegistry 907 | self.frame:SetScript("OnUpdate", OnUpdate) 908 | end 909 | 910 | self:UnregisterAllEvents() 911 | self:CancelAllScheduledEvents() 912 | 913 | registeringFromAceEvent = true 914 | self:RegisterEvent("LOOT_OPENED", function() 915 | SendAddonMessage("LOOT_OPENED", "", "RAID") 916 | end) 917 | registeringFromAceEvent = nil 918 | 919 | if not self.playerLogin then 920 | registeringFromAceEvent = true 921 | self:RegisterEvent("PLAYER_LOGIN", function() 922 | self.playerLogin = true 923 | end, true) 924 | registeringFromAceEvent = nil 925 | end 926 | 927 | if not self.postInit then 928 | local isReload = true 929 | local function func() 930 | self.postInit = true 931 | self:TriggerEvent("AceEvent_FullyInitialized") 932 | if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then 933 | self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") 934 | end 935 | if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then 936 | self:UnregisterEvent("MEETINGSTONE_CHANGED") 937 | end 938 | if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then 939 | self:UnregisterEvent("MINIMAP_ZONE_CHANGED") 940 | end 941 | if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then 942 | self:UnregisterEvent("LANGUAGE_LIST_CHANGED") 943 | end 944 | end 945 | registeringFromAceEvent = true 946 | local f = function() 947 | self.playerLogin = true 948 | self:ScheduleEvent("AceEvent_FullyInitialized", func, 1) 949 | end 950 | self:RegisterEvent("MEETINGSTONE_CHANGED", f, true) 951 | self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function() 952 | self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05) 953 | end) 954 | self:RegisterEvent("LANGUAGE_LIST_CHANGED", function() 955 | if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then 956 | self:UnregisterEvent("MEETINGSTONE_CHANGED") 957 | self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true) 958 | end 959 | end) 960 | registeringFromAceEvent = nil 961 | end 962 | 963 | self.super.activate(self, oldLib, oldDeactivate) 964 | if oldLib then 965 | oldDeactivate(oldLib) 966 | end 967 | end 968 | 969 | AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate) 970 | AceEvent = AceLibrary(MAJOR_VERSION) 971 | -------------------------------------------------------------------------------- /libs/HealComm-1.0/HealComm-1.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: HealComm-1.0 3 | Revision: $Rev: 11430 $ 4 | Author(s): aviana 5 | Website: https://github.com/Aviana 6 | Description: A library to provide communication of heals and resurrections. 7 | Dependencies: AceLibrary, AceEvent-2.0, RosterLib-2.0 8 | ]] 9 | 10 | local MAJOR_VERSION = "HealComm-1.0" 11 | local MINOR_VERSION = "$Revision: 11430 $" 12 | 13 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end 14 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 15 | if not AceLibrary:HasInstance("RosterLib-2.0") then error(MAJOR_VERSION .. " requires RosterLib-2.0") end 16 | if not AceLibrary:HasInstance("AceEvent-2.0") then error(MAJOR_VERSION .. " requires AceEvent-2.0") end 17 | if not AceLibrary:HasInstance("AceHook-2.1") then error(MAJOR_VERSION .. " requires AceHook-2.1") end 18 | 19 | local roster = AceLibrary("RosterLib-2.0") 20 | local console = AceLibrary("AceConsole-2.0") --HCSpy 21 | local HealComm = {} 22 | 23 | ------------------------------------------------ 24 | -- Locales 25 | ------------------------------------------------ 26 | 27 | local L = {} 28 | if( GetLocale() == "deDE" ) then 29 | L["Renew"] = "Erneuerung" 30 | L["Rejuvenation"] = "Verj\195\188ngung" 31 | L["Holy Light"] = "Heiliges Licht" 32 | L["Flash of Light"] = "Lichtblitz" 33 | L["Healing Wave"] = "Welle der Heilung" 34 | L["Lesser Healing Wave"] = "Geringe Welle der Heilung" 35 | L["Chain Heal"] = "Kettenheilung" 36 | L["Lesser Heal"] = "Geringes Heilen" 37 | L["Heal"] = "Heilen" 38 | L["Flash Heal"] = "Blitzheilung" 39 | L["Greater Heal"] = "Große Heilung" 40 | L["Prayer of Healing"] = "Gebet der Heilung" 41 | L["Healing Touch"] = "Heilende Ber\195\188hrung" 42 | L["Regrowth"] = "Nachwachsen" 43 | L["Resurrection"] = "Wiederbelebung" 44 | L["Rebirth"] = "Wiedergeburt" 45 | L["Redemption"] = "Erl\195\182sung" 46 | L["Ancestral Spirit"] = "Geist der Ahnen" 47 | L["Libram of Divinity"] = "Buchband der Offenbarung" 48 | L["Libram of Light"] = "Buchband des Lichts" 49 | L["Totem of Sustaining"] = "Totem der Erhaltung" 50 | L["Totem of Life"] = "Totem des Lebens" 51 | L["Power Infusion"] = "Seele der Macht" 52 | L["Divine Favor"] = "G\195\182ttliche Gunst" 53 | L["Nature Aligned"] = "Naturverbundenheit" 54 | L["Crusader's Wrath"] = "Zorn des Kreuzfahrers" 55 | L["The Furious Storm"] = "Der wilde Sturm" 56 | L["Holy Power"] = "Heilige Kraft" 57 | L["Prayer Beads Blessing"] = "Segen der Gebetsperlen" 58 | L["Chromatic Infusion"] = "Erf\195\188llt mit chromatischer Macht" 59 | L["Ascendance"] = "\154berlegenheit" 60 | L["Ephemeral Power"] = "Ephemere Macht" 61 | L["Unstable Power"] = "Instabile Macht" 62 | L["Healing of the Ages"] = "Heilung der Urzeiten" 63 | L["Essence of Sapphiron"] = "Essenz Saphirons" 64 | L["The Eye of the Dead"] = "Das Auge des Todes" 65 | L["Mortal Strike"] = "T\195\182dlicher Stoß" 66 | L["Wound Poison"] = "Wundgift" 67 | L["Curse of the Deadwood"] = "Fluch der Totenwaldfelle" 68 | L["Veil of Shadow"] = "Schattenschleier" 69 | L["Gehennas' Curse"] = "Gehennas' Fluch" 70 | L["Mortal Wound"] = "Trauma" 71 | L["Necrotic Poison"] = "Nekrotisches Gift" 72 | L["Necrotic Aura"] = "Nekrotische Aura" 73 | L["Healing Way"] = "Pfad der Heilung" 74 | L["Warsong Gulch"] = "Kriegshymnenschlucht" 75 | L["Arathi Basin"] = "Arathibecken" 76 | L["Alterac Valley"] = "Alteractal" 77 | L["Blessing of Light"] = "Segen des Lichts" 78 | L["Blood Fury"] = "Kochendes Blut" 79 | L["Set: Increases the duration of your Rejuvenation spell by 3 sec."] = "Set: Erh\195\182ht die Dauer Eures Zaubers \'Verj\195\188ngung\' um 3 Sek." 80 | L["Set: Increases the duration of your Renew spell by 3 sec."] = "Set: Erh\195\182ht die Dauer Eures Zaubers 'Erneuerung' um 3 Sek." 81 | L["^Corpse of (.+)$"] = "^Leichnam von (.+)$" 82 | elseif ( GetLocale() == "frFR" ) then 83 | L["Renew"] = "R\195\169novation" 84 | L["Rejuvenation"] = "R\195\169cup\195\169ration" 85 | L["Holy Light"] = "Lumi\195\168re sacr\195\169e" 86 | L["Flash of Light"] = "Eclair lumineux" 87 | L["Healing Wave"] = "Vague de soins" 88 | L["Lesser Healing Wave"] = "Vague de soins inf\195\169rieurs" 89 | L["Chain Heal"] = "Salve de gu\195\169rison" 90 | L["Lesser Heal"] = "Soins inf\195\169rieurs" 91 | L["Heal"] = "Soins" 92 | L["Flash Heal"] = "Soins rapides" 93 | L["Greater Heal"] = "Soins sup\195\169rieurs" 94 | L["Prayer of Healing"] = "Pri\195\168re de soins" 95 | L["Healing Touch"] = "Toucher gu\195\169risseur" 96 | L["Regrowth"] = "R\195\169tablissement" 97 | L["Resurrection"] = "R\195\169surrection" 98 | L["Rebirth"] = "Renaissance" 99 | L["Redemption"] = "R\195\169demption" 100 | L["Ancestral Spirit"] = "Esprit ancestral" 101 | L["Libram of Divinity"] = "Libram de divinit\195\169" 102 | L["Libram of Light"] = "Libram de lumi\195\168re" 103 | L["Totem of Sustaining"] = "Totem de soutien" 104 | L["Totem of Life"] = "Totem de vie" 105 | L["Power Infusion"] = "Infusion de puissance" 106 | L["Divine Favor"] = "Faveur divine" 107 | L["Nature Aligned"] = "Alignement sur la nature" 108 | L["Crusader's Wrath"] = "Col\195\168re du crois\195\169" 109 | L["The Furious Storm"] = "La temp\195\170te furieuse" 110 | L["Holy Power"] = "Puissance sacr\195\169e" 111 | L["Prayer Beads Blessing"] = "B\195\169n\195\169diction du chapelet" 112 | L["Chromatic Infusion"] = "Infusion chromatique" 113 | L["Ascendance"] = "Ascendance" 114 | L["Ephemeral Power"] = "Puissance \195\169ph\195\169m\195\168re" 115 | L["Unstable Power"] = "Puissance instable" 116 | L["Healing of the Ages"] = "Soins des \195\162ges" 117 | L["Essence of Sapphiron"] = "Essence de Saphiron" 118 | L["The Eye of the Dead"] = "L'Oeil du mort" 119 | L["Mortal Strike"] = "Frappe mortelle" 120 | L["Wound Poison"] = "Poison douloureux" 121 | L["Curse of the Deadwood"] = "Mal\195\169diction des Mort-bois" 122 | L["Veil of Shadow"] = "Voile de l'ombre" 123 | L["Gehennas' Curse"] = "Mal\195\169diction de Gehennas" 124 | L["Mortal Wound"] = "Blessures mortelles" 125 | L["Necrotic Poison"] = "Poison n\195\169crotique" 126 | L["Necrotic Aura"] = "Aura n\195\169crotique" 127 | L["Healing Way"] = "Flots de soins" 128 | L["Warsong Gulch"] = "Goulet des Warsong" 129 | L["Arathi Basin"] = "Bassin d'Arathi" 130 | L["Alterac Valley"] = "Vall\195\169e d'Alterac" 131 | L["Blood Fury"] = "Fureur sanguinaire" 132 | L["Blessing of Light"] = "B\195\169n\195\169diction de lumi\195\168re" 133 | L["Set: Increases the duration of your Rejuvenation spell by 3 sec."] = "Set: Augmente la dur\195\169e de votre sort R\195\169cup\195\169ration de 3 s." 134 | L["Set: Increases the duration of your Renew spell by 3 sec."] = "Set: Augmente la dur\195\169e de votre sort R\195\169novation de 3 s." 135 | L["^Corpse of (.+)$"] = "^Cadavre |2 (.+)$" 136 | elseif GetLocale() == "zhCN" then 137 | if AceLibrary:HasInstance("Babble-Spell-2.2") then 138 | L.BS = AceLibrary("Babble-Spell-2.2") 139 | end 140 | if AceLibrary:HasInstance("Babble-Zone-2.2") then 141 | L.BZ = AceLibrary("Babble-Zone-2.2") 142 | end 143 | setmetatable(L, {__index = function(table, key) 144 | if table.BS and table.BS:HasTranslation(key) then 145 | return table.BS[key] 146 | elseif table.BZ and table.BZ:HasTranslation(key) then 147 | return table.BZ[key] 148 | end 149 | end}) 150 | L["Libram of Divinity"] = "神性圣契" 151 | L["Libram of Light"] = "光明圣契" 152 | L["Necrotic Aura"] = "死灵光环" -- 这个技能不知道是否存在 153 | L["Set: Increases the duration of your Rejuvenation spell by 3 sec."] = "套装:使你的回春术的持续时间延长3秒。" -- T2 154 | L["Set: Increases the duration of your Renew spell by 3 sec."] = "套装:使你的恢复术的持续时间延长3秒。" -- T2.5 155 | L["Totem of Life"] = "生命图腾" 156 | L["Totem of Sustaining"] = "持久图腾" 157 | L["^Corpse of (.+)$"] = "(.+)的尸体" 158 | else 159 | L["Renew"] = "Renew" 160 | L["Rejuvenation"] = "Rejuvenation" 161 | L["Holy Light"] = "Holy Light" 162 | L["Flash of Light"] = "Flash of Light" 163 | L["Healing Wave"] = "Healing Wave" 164 | L["Lesser Healing Wave"] = "Lesser Healing Wave" 165 | L["Chain Heal"] = "Chain Heal" 166 | L["Lesser Heal"] = "Lesser Heal" 167 | L["Heal"] = "Heal" 168 | L["Flash Heal"] = "Flash Heal" 169 | L["Greater Heal"] = "Greater Heal" 170 | L["Prayer of Healing"] = "Prayer of Healing" 171 | L["Healing Touch"] = "Healing Touch" 172 | L["Regrowth"] = "Regrowth" 173 | L["Resurrection"] = "Resurrection" 174 | L["Rebirth"] = "Rebirth" 175 | L["Redemption"] = "Redemption" 176 | L["Ancestral Spirit"] = "Ancestral Spirit" 177 | L["Libram of Divinity"] = "Libram of Divinity" 178 | L["Libram of Light"] = "Libram of Light" 179 | L["Totem of Sustaining"] = "Totem of Sustaining" 180 | L["Totem of Life"] = "Totem of Life" 181 | L["Power Infusion"] = "Power Infusion" 182 | L["Divine Favor"] = "Divine Favor" 183 | L["Nature Aligned"] = "Nature Aligned" 184 | L["Crusader's Wrath"] = "Crusader's Wrath" 185 | L["The Furious Storm"] = "The Furious Storm" 186 | L["Holy Power"] = "Holy Power" 187 | L["Prayer Beads Blessing"] = "Prayer Beads Blessing" 188 | L["Chromatic Infusion"] = "Chromatic Infusion" 189 | L["Ascendance"] = "Ascendance" 190 | L["Ephemeral Power"] = "Ephemeral Power" 191 | L["Unstable Power"] = "Unstable Power" 192 | L["Healing of the Ages"] = "Healing of the Ages" 193 | L["Essence of Sapphiron"] = "Essence of Sapphiron" 194 | L["The Eye of the Dead"] = "The Eye of the Dead" 195 | L["Mortal Strike"] = "Mortal Strike" 196 | L["Wound Poison"] = "Wound Poison" 197 | L["Curse of the Deadwood"] = "Curse of the Deadwood" 198 | L["Veil of Shadow"] = "Veil of Shadow" 199 | L["Gehennas' Curse"] = "Gehennas' Curse" 200 | L["Mortal Wound"] = "Mortal Wound" 201 | L["Necrotic Poison"] = "Necrotic Poison" 202 | L["Necrotic Aura"] = "Necrotic Aura" 203 | L["Healing Way"] = "Healing Way" 204 | L["Warsong Gulch"] = "Warsong Gulch" 205 | L["Arathi Basin"] = "Arathi Basin" 206 | L["Alterac Valley"] = "Alterac Valley" 207 | L["Blessing of Light"] = "Blessing of Light" 208 | L["Blood Fury"] = "Blood Fury" 209 | L["Set: Increases the duration of your Rejuvenation spell by 3 sec."] = "Set: Increases the duration of your Rejuvenation spell by 3 sec." 210 | L["Set: Increases the duration of your Renew spell by 3 sec."] = "Set: Increases the duration of your Renew spell by 3 sec." 211 | L["^Corpse of (.+)$"] = "^Corpse of (.+)$" 212 | end 213 | 214 | ------------------------------------------------ 215 | -- activate, enable, disable 216 | ------------------------------------------------ 217 | 218 | local function activate(self, oldLib, oldDeactivate) 219 | HealComm = self 220 | if oldLib then 221 | self.Heals = oldLib.Heals 222 | self.GrpHeals = oldLib.GrpHeals 223 | self.Lookup = oldLib.Lookup 224 | self.pendingResurrections = oldlib.pendingResurrections 225 | self.Hots = oldLib.Hots 226 | self.SpellCastInfo = oldLib.SpellCastInfo 227 | oldLib:UnregisterAllEvents() 228 | oldLib:CancelAllScheduledEvents() 229 | end 230 | if not self.Heals then 231 | self.Heals = {} 232 | end 233 | if not self.GrpHeals then 234 | self.GrpHeals = {} 235 | end 236 | if not self.Lookup then 237 | self.Lookup = {} 238 | end 239 | if not self.pendingResurrections then 240 | self.pendingResurrections = {} 241 | end 242 | if not self.Hots then 243 | self.Hots = {} 244 | end 245 | if not self.SpellCastInfo then 246 | self.SpellCastInfo = {} 247 | end 248 | if oldDeactivate then oldDeactivate(oldLib) end 249 | end 250 | 251 | local function external(self, major, instance) 252 | if major == "AceEvent-2.0" then 253 | local AceEvent = instance 254 | AceEvent:embed(self) 255 | self:RegisterEvent("SPELLCAST_START") 256 | self:RegisterEvent("SPELLCAST_INTERRUPTED", "SPELLCAST_FAILED") 257 | self:RegisterEvent("SPELLCAST_FAILED") 258 | self:RegisterEvent("SPELLCAST_DELAYED") 259 | self:RegisterEvent("SPELLCAST_STOP") 260 | self:RegisterEvent("CHAT_MSG_ADDON") 261 | self:RegisterEvent("UNIT_AURA") 262 | self:RegisterEvent("UNIT_HEALTH") 263 | self:RegisterEvent("PLAYER_LOGIN") 264 | self:TriggerEvent("HealComm_Enabled") 265 | end 266 | if major == "AceHook-2.1" then 267 | local AceHook = instance 268 | AceHook:embed(self) 269 | end 270 | end 271 | 272 | function HealComm:PLAYER_LOGIN() 273 | self:HookScript(WorldFrame, "OnMouseDown", "OnMouseDown") 274 | self:Hook("CastSpell") 275 | self:Hook("CastSpellByName") 276 | self:Hook("UseAction") 277 | self:Hook("SpellTargetUnit") 278 | self:Hook("SpellStopTargeting") 279 | self:Hook("TargetUnit") 280 | end 281 | 282 | function HealComm:Enable() 283 | -- not used anymore, but as addons still might be calling this method, we're keeping it. 284 | end 285 | 286 | 287 | function HealComm:Disable() 288 | -- not used anymore, but as addons still might be calling this method, we're keeping it. 289 | end 290 | 291 | ------------------------------------------------ 292 | -- Addon Code 293 | ------------------------------------------------ 294 | 295 | function strmatch(str, pat, init) 296 | local a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a13,a14,a15,a16,a17,a18,a19,a20 = string.find(str, pat, init) 297 | return a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a13,a14,a15,a16,a17,a18,a19,a20 298 | end 299 | 300 | HealComm.Spells = { 301 | [L["Holy Light"]] = { 302 | [1] = function (SpellPower) 303 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 304 | local hlMod = 4*talentRank/100 + 1 305 | return (44*hlMod+(((2.5/3.5) * SpellPower)*0.1)) 306 | end; 307 | [2] = function (SpellPower) 308 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 309 | local hlMod = 4*talentRank/100 + 1 310 | return (88*hlMod+(((2.5/3.5) * SpellPower)*0.224)) 311 | end; 312 | [3] = function (SpellPower) 313 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 314 | local hlMod = 4*talentRank/100 + 1 315 | return (174*hlMod+(((2.5/3.5) * SpellPower)*0.476)) 316 | end; 317 | [4] = function (SpellPower) 318 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 319 | local hlMod = 4*talentRank/100 + 1 320 | return (334*hlMod+((2.5/3.5) * SpellPower)) 321 | end; 322 | [5] = function (SpellPower) 323 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 324 | local hlMod = 4*talentRank/100 + 1 325 | return (522*hlMod+((2.5/3.5) * SpellPower)) 326 | end; 327 | [6] = function (SpellPower) 328 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 329 | local hlMod = 4*talentRank/100 + 1 330 | return (740*hlMod+((2.5/3.5) * SpellPower)) 331 | end; 332 | [7] = function (SpellPower) 333 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 334 | local hlMod = 4*talentRank/100 + 1 335 | return (1000*hlMod+((2.5/3.5) * SpellPower)) 336 | end; 337 | [8] = function (SpellPower) 338 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 339 | local hlMod = 4*talentRank/100 + 1 340 | return (1318*hlMod+((2.5/3.5) * SpellPower)) 341 | end; 342 | [9] = function (SpellPower) 343 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 344 | local hlMod = 4*talentRank/100 + 1 345 | return (1681*hlMod+((2.5/3.5) * SpellPower)) 346 | end; 347 | }; 348 | [L["Flash of Light"]] = { 349 | [1] = function (SpellPower) 350 | local lp = 0 351 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 352 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 353 | local name = GetItemInfo(itemstring) 354 | if name == "Libram of Divinity" then 355 | lp = 53 356 | elseif name == "Libram of Light" then 357 | lp = 83 358 | end 359 | end 360 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 361 | local hlMod = 4*talentRank/100 + 1 362 | return (68*hlMod+lp+((1.5/3.5) * SpellPower)) 363 | end; 364 | [2] = function (SpellPower) 365 | local lp = 0 366 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 367 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 368 | local name = GetItemInfo(itemstring) 369 | if name == "Libram of Divinity" then 370 | lp = 53 371 | elseif name == "Libram of Light" then 372 | lp = 83 373 | end 374 | end 375 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 376 | local hlMod = 4*talentRank/100 + 1 377 | return (104*hlMod+lp+((1.5/3.5) * SpellPower)) 378 | end; 379 | [3] = function (SpellPower) 380 | local lp = 0 381 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 382 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 383 | local name = GetItemInfo(itemstring) 384 | if name == "Libram of Divinity" then 385 | lp = 53 386 | elseif name == "Libram of Light" then 387 | lp = 83 388 | end 389 | end 390 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 391 | local hlMod = 4*talentRank/100 + 1 392 | return (155*hlMod+lp+((1.5/3.5) * SpellPower)) 393 | end; 394 | [4] = function (SpellPower) 395 | local lp = 0 396 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 397 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 398 | local name = GetItemInfo(itemstring) 399 | if name == "Libram of Divinity" then 400 | lp = 53 401 | elseif name == "Libram of Light" then 402 | lp = 83 403 | end 404 | end 405 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 406 | local hlMod = 4*talentRank/100 + 1 407 | return (210*hlMod+lp+((1.5/3.5) * SpellPower)) 408 | end; 409 | [5] = function (SpellPower) 410 | local lp = 0 411 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 412 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 413 | local name = GetItemInfo(itemstring) 414 | if name == "Libram of Divinity" then 415 | lp = 53 416 | elseif name == "Libram of Light" then 417 | lp = 83 418 | end 419 | end 420 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 421 | local hlMod = 4*talentRank/100 + 1 422 | return (284*hlMod+lp+((1.5/3.5) * SpellPower)) 423 | end; 424 | [6] = function (SpellPower) 425 | local lp = 0 426 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 427 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 428 | local name = GetItemInfo(itemstring) 429 | if name == "Libram of Divinity" then 430 | lp = 53 431 | elseif name == "Libram of Light" then 432 | lp = 83 433 | end 434 | end 435 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 436 | local hlMod = 4*talentRank/100 + 1 437 | return (364*hlMod+lp+((1.5/3.5) * SpellPower)) 438 | end; 439 | [7] = function (SpellPower) 440 | local lp = 0 441 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 442 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 443 | local name = GetItemInfo(itemstring) 444 | if name == "Libram of Divinity" then 445 | lp = 53 446 | elseif name == "Libram of Light" then 447 | lp = 83 448 | end 449 | end 450 | local _,_,_,_,talentRank,_ = GetTalentInfo(1,5) 451 | local hlMod = 4*talentRank/100 + 1 452 | return (481*hlMod+lp+((1.5/3.5) * SpellPower)) 453 | end; 454 | }; 455 | [L["Healing Wave"]] = { 456 | [1] = function (SpellPower) 457 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 458 | local pMod = 2*talentRank/100 + 1 459 | return (40*pMod+(((1.5/3.5) * SpellPower)*0.22)) 460 | end; 461 | [2] = function (SpellPower) 462 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 463 | local pMod = 2*talentRank/100 + 1 464 | return (72*pMod+(((2/3.5) * SpellPower)*0.38)) 465 | end; 466 | [3] = function (SpellPower) 467 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 468 | local pMod = 2*talentRank/100 + 1 469 | return (143*pMod+(((2.5/3.5) * SpellPower)*0.446)) 470 | end; 471 | [4] = function (SpellPower) 472 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 473 | local pMod = 2*talentRank/100 + 1 474 | return (293*pMod+(((3/3.5) * SpellPower)*0.7)) 475 | end; 476 | [5] = function (SpellPower) 477 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 478 | local pMod = 2*talentRank/100 + 1 479 | return (409*pMod+((3/3.5) * SpellPower)) 480 | end; 481 | [6] = function (SpellPower) 482 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 483 | local pMod = 2*talentRank/100 + 1 484 | return (580*pMod+((3/3.5) * SpellPower)) 485 | end; 486 | [7] = function (SpellPower) 487 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 488 | local pMod = 2*talentRank/100 + 1 489 | return (798*pMod+((3/3.5) * SpellPower)) 490 | end; 491 | [8] = function (SpellPower) 492 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 493 | local pMod = 2*talentRank/100 + 1 494 | return (1093*pMod+((3/3.5) * SpellPower)) 495 | end; 496 | [9] = function (SpellPower) 497 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 498 | local pMod = 2*talentRank/100 + 1 499 | return (1465*pMod+((3/3.5) * SpellPower)) 500 | end; 501 | [10] = function (SpellPower) 502 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 503 | local pMod = 2*talentRank/100 + 1 504 | return (1736*pMod+((3/3.5) * SpellPower)) 505 | end; 506 | }; 507 | [L["Lesser Healing Wave"]] = { 508 | [1] = function (SpellPower) 509 | local tp = 0 510 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 511 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 512 | local name = GetItemInfo(itemstring) 513 | if name == "Totem of Sustaining" then 514 | tp = 53 515 | elseif name == "Totem of Life" then 516 | tp = 80 517 | end 518 | end 519 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 520 | local pMod = 2*talentRank/100 + 1 521 | return (175*pMod+tp+((1.5/3.5) * SpellPower)) 522 | end; 523 | [2] = function (SpellPower) 524 | local tp = 0 525 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 526 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 527 | local name = GetItemInfo(itemstring) 528 | if name == "Totem of Sustaining" then 529 | tp = 53 530 | elseif name == "Totem of Life" then 531 | tp = 80 532 | end 533 | end 534 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 535 | local pMod = 2*talentRank/100 + 1 536 | return (265*pMod+tp+((1.5/3.5) * SpellPower)) 537 | end; 538 | [3] = function (SpellPower) 539 | local tp = 0 540 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 541 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 542 | local name = GetItemInfo(itemstring) 543 | if name == "Totem of Sustaining" then 544 | tp = 53 545 | elseif name == "Totem of Life" then 546 | tp = 80 547 | end 548 | end 549 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 550 | local pMod = 2*talentRank/100 + 1 551 | return (360*pMod+tp+((1.5/3.5) * SpellPower)) 552 | end; 553 | [4] = function (SpellPower) 554 | local tp = 0 555 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 556 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 557 | local name = GetItemInfo(itemstring) 558 | if name == "Totem of Sustaining" then 559 | tp = 53 560 | elseif name == "Totem of Life" then 561 | tp = 80 562 | end 563 | end 564 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 565 | local pMod = 2*talentRank/100 + 1 566 | return (487*pMod+tp+((1.5/3.5) * SpellPower)) 567 | end; 568 | [5] = function (SpellPower) 569 | local tp = 0 570 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 571 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 572 | local name = GetItemInfo(itemstring) 573 | if name == "Totem of Sustaining" then 574 | tp = 53 575 | elseif name == "Totem of Life" then 576 | tp = 80 577 | end 578 | end 579 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 580 | local pMod = 2*talentRank/100 + 1 581 | return (669*pMod+tp+((1.5/3.5) * SpellPower)) 582 | end; 583 | [6] = function (SpellPower) 584 | local tp = 0 585 | if GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")) then 586 | local _,_,itemstring = string.find(GetInventoryItemLink("player",GetInventorySlotInfo("RangedSlot")), "|H(.+)|h") 587 | local name = GetItemInfo(itemstring) 588 | if name == "Totem of Sustaining" then 589 | tp = 53 590 | elseif name == "Totem of Life" then 591 | tp = 80 592 | end 593 | end 594 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 595 | local pMod = 2*talentRank/100 + 1 596 | return (881*pMod+tp+((1.5/3.5) * SpellPower)) 597 | end; 598 | }; 599 | [L["Chain Heal"]] = { 600 | [1] = function (SpellPower) 601 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 602 | local pMod = 2*talentRank/100 + 1 603 | return (344*pMod+((2.5/3.5) * SpellPower)) 604 | end; 605 | [2] = function (SpellPower) 606 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 607 | local pMod = 2*talentRank/100 + 1 608 | return (435*pMod+((2.5/3.5) * SpellPower)) 609 | end; 610 | [3] = function (SpellPower) 611 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,14) 612 | local pMod = 2*talentRank/100 + 1 613 | return (591*pMod+((2.5/3.5) * SpellPower)) 614 | end; 615 | }; 616 | [L["Lesser Heal"]] = { 617 | [1] = function (SpellPower) 618 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 619 | local _,Spirit,_,_ = UnitStat("player",5) 620 | local sgMod = Spirit * 5*talentRank/100 621 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 622 | local shMod = 2*talentRank2/100 + 1 623 | return (52*shMod+((1.5/3.5) * (SpellPower+sgMod))*0.19) 624 | end; 625 | [2] = function (SpellPower) 626 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 627 | local _,Spirit,_,_ = UnitStat("player",5) 628 | local sgMod = Spirit * 5*talentRank/100 629 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 630 | local shMod = 2*talentRank2/100 + 1 631 | return (79*shMod+((2/3.5) * (SpellPower+sgMod))*0.34) 632 | end; 633 | [3] = function (SpellPower) 634 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 635 | local _,Spirit,_,_ = UnitStat("player",5) 636 | local sgMod = Spirit * 5*talentRank/100 637 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 638 | local shMod = 2*talentRank2/100 + 1 639 | return (147*shMod+((2.5/3.5) * (SpellPower+sgMod))*0.6) 640 | end; 641 | }; 642 | [L["Heal"]] = { 643 | [1] = function (SpellPower) 644 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 645 | local _,Spirit,_,_ = UnitStat("player",5) 646 | local sgMod = Spirit * 5*talentRank/100 647 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 648 | local shMod = 2*talentRank2/100 + 1 649 | return (319*shMod+((3/3.5) * (SpellPower+sgMod))*0.586) 650 | end; 651 | [2] = function (SpellPower) 652 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 653 | local _,Spirit,_,_ = UnitStat("player",5) 654 | local sgMod = Spirit * 5*talentRank/100 655 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 656 | local shMod = 2*talentRank2/100 + 1 657 | return (471*shMod+((3/3.5) * (SpellPower+sgMod))) 658 | end; 659 | [3] = function (SpellPower) 660 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 661 | local _,Spirit,_,_ = UnitStat("player",5) 662 | local sgMod = Spirit * 5*talentRank/100 663 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 664 | local shMod = 2*talentRank2/100 + 1 665 | return (610*shMod+((3/3.5) * (SpellPower+sgMod))) 666 | end; 667 | [4] = function (SpellPower) 668 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 669 | local _,Spirit,_,_ = UnitStat("player",5) 670 | local sgMod = Spirit * 5*talentRank/100 671 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 672 | local shMod = 2*talentRank2/100 + 1 673 | return (759*shMod+((3/3.5) * (SpellPower+sgMod))) 674 | end; 675 | }; 676 | [L["Flash Heal"]] = { 677 | [1] = function (SpellPower) 678 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 679 | local _,Spirit,_,_ = UnitStat("player",5) 680 | local sgMod = Spirit * 5*talentRank/100 681 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 682 | local shMod = 2*talentRank2/100 + 1 683 | return (216*shMod+((1.5/3.5) * (SpellPower+sgMod))) 684 | end; 685 | [2] = function (SpellPower) 686 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 687 | local _,Spirit,_,_ = UnitStat("player",5) 688 | local sgMod = Spirit * 5*talentRank/100 689 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 690 | local shMod = 2*talentRank2/100 + 1 691 | return (287*shMod+((1.5/3.5) * (SpellPower+sgMod))) 692 | end; 693 | [3] = function (SpellPower) 694 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 695 | local _,Spirit,_,_ = UnitStat("player",5) 696 | local sgMod = Spirit * 5*talentRank/100 697 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 698 | local shMod = 2*talentRank2/100 + 1 699 | return (361*shMod+((1.5/3.5) * (SpellPower+sgMod))) 700 | end; 701 | [4] = function (SpellPower) 702 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 703 | local _,Spirit,_,_ = UnitStat("player",5) 704 | local sgMod = Spirit * 5*talentRank/100 705 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 706 | local shMod = 2*talentRank2/100 + 1 707 | return (440*shMod+((1.5/3.5) * (SpellPower+sgMod))) 708 | end; 709 | [5] = function (SpellPower) 710 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 711 | local _,Spirit,_,_ = UnitStat("player",5) 712 | local sgMod = Spirit * 5*talentRank/100 713 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 714 | local shMod = 2*talentRank2/100 + 1 715 | return (568*shMod+((1.5/3.5) * (SpellPower+sgMod))) 716 | end; 717 | [6] = function (SpellPower) 718 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 719 | local _,Spirit,_,_ = UnitStat("player",5) 720 | local sgMod = Spirit * 5*talentRank/100 721 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 722 | local shMod = 2*talentRank2/100 + 1 723 | return (705*shMod+((1.5/3.5) * (SpellPower+sgMod))) 724 | end; 725 | [7] = function (SpellPower) 726 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 727 | local _,Spirit,_,_ = UnitStat("player",5) 728 | local sgMod = Spirit * 5*talentRank/100 729 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 730 | local shMod = 2*talentRank2/100 + 1 731 | return (886*shMod+((1.5/3.5) * (SpellPower+sgMod))) 732 | end; 733 | }; 734 | [L["Greater Heal"]] = { 735 | [1] = function (SpellPower) 736 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 737 | local _,Spirit,_,_ = UnitStat("player",5) 738 | local sgMod = Spirit * 5*talentRank/100 739 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 740 | local shMod = 2*talentRank2/100 + 1 741 | return (957*shMod+((3/3.5) * (SpellPower+sgMod))) 742 | end; 743 | [2] = function (SpellPower) 744 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 745 | local _,Spirit,_,_ = UnitStat("player",5) 746 | local sgMod = Spirit * 5*talentRank/100 747 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 748 | local shMod = 2*talentRank2/100 + 1 749 | return (1220*shMod+((3/3.5) * (SpellPower+sgMod))) 750 | end; 751 | [3] = function (SpellPower) 752 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 753 | local _,Spirit,_,_ = UnitStat("player",5) 754 | local sgMod = Spirit * 5*talentRank/100 755 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 756 | local shMod = 2*talentRank2/100 + 1 757 | return (1524*shMod+((3/3.5) * (SpellPower+sgMod))) 758 | end; 759 | [4] = function (SpellPower) 760 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 761 | local _,Spirit,_,_ = UnitStat("player",5) 762 | local sgMod = Spirit * 5*talentRank/100 763 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 764 | local shMod = 2*talentRank2/100 + 1 765 | return (1903*shMod+((3/3.5) * (SpellPower+sgMod))) 766 | end; 767 | [5] = function (SpellPower) 768 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 769 | local _,Spirit,_,_ = UnitStat("player",5) 770 | local sgMod = Spirit * 5*talentRank/100 771 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 772 | local shMod = 2*talentRank2/100 + 1 773 | return (2081*shMod+((3/3.5) * (SpellPower+sgMod))) 774 | end; 775 | }; 776 | [L["Prayer of Healing"]] = { 777 | [1] = function (SpellPower) 778 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 779 | local _,Spirit,_,_ = UnitStat("player",5) 780 | local sgMod = Spirit * 5*talentRank/100 781 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 782 | local shMod = 2*talentRank2/100 + 1 783 | return (311*shMod+((3/3.5/3) * (SpellPower+sgMod))) 784 | end; 785 | [2] = function (SpellPower) 786 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 787 | local _,Spirit,_,_ = UnitStat("player",5) 788 | local sgMod = Spirit * 5*talentRank/100 789 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 790 | local shMod = 2*talentRank2/100 + 1 791 | return (460*shMod+((3/3.5/3) * (SpellPower+sgMod))) 792 | end; 793 | [3] = function (SpellPower) 794 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 795 | local _,Spirit,_,_ = UnitStat("player",5) 796 | local sgMod = Spirit * 5*talentRank/100 797 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 798 | local shMod = 2*talentRank2/100 + 1 799 | return (676*shMod+((3/3.5/3) * (SpellPower+sgMod))) 800 | end; 801 | [4] = function (SpellPower) 802 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 803 | local _,Spirit,_,_ = UnitStat("player",5) 804 | local sgMod = Spirit * 5*talentRank/100 805 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 806 | local shMod = 2*talentRank2/100 + 1 807 | return (965*shMod+((3/3.5/3) * (SpellPower+sgMod))) 808 | end; 809 | [5] = function (SpellPower) 810 | local _,_,_,_,talentRank,_ = GetTalentInfo(2,14) 811 | local _,Spirit,_,_ = UnitStat("player",5) 812 | local sgMod = Spirit * 5*talentRank/100 813 | local _,_,_,_,talentRank2,_ = GetTalentInfo(2,15) 814 | local shMod = 2*talentRank2/100 + 1 815 | return (1070*shMod+((3/3.5/3) * (SpellPower+sgMod))) 816 | end; 817 | }; 818 | [L["Healing Touch"]] = { 819 | [1] = function (SpellPower) 820 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 821 | local gnMod = 2*talentRank/100 + 1 822 | return (43*gnMod+((1.5/3.5) * SpellPower * (1-((20-4)*0.0375)))) 823 | end; 824 | [2] = function (SpellPower) 825 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 826 | local gnMod = 2*talentRank/100 + 1 827 | return (101*gnMod+((2/3.5) * SpellPower * (1-((20-13)*0.0375)))) 828 | end; 829 | [3] = function (SpellPower) 830 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 831 | local gnMod = 2*talentRank/100 + 1 832 | return (220*gnMod+((2.5/3.5) * SpellPower)) 833 | end; 834 | [4] = function (SpellPower) 835 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 836 | local gnMod = 2*talentRank/100 + 1 837 | return (435*gnMod+((3/3.5) * SpellPower)) 838 | end; 839 | [5] = function (SpellPower) 840 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 841 | local gnMod = 2*talentRank/100 + 1 842 | return ((634*gnMod)+SpellPower) 843 | end; 844 | [6] = function (SpellPower) 845 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 846 | local gnMod = 2*talentRank/100 + 1 847 | return ((819*gnMod)+SpellPower) 848 | end; 849 | [7] = function (SpellPower) 850 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 851 | local gnMod = 2*talentRank/100 + 1 852 | return ((1029*gnMod)+SpellPower) 853 | end; 854 | [8] = function (SpellPower) 855 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 856 | local gnMod = 2*talentRank/100 + 1 857 | return ((1314*gnMod)+SpellPower) 858 | end; 859 | [9] = function (SpellPower) 860 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 861 | local gnMod = 2*talentRank/100 + 1 862 | return ((1657*gnMod)+SpellPower) 863 | end; 864 | [10] = function (SpellPower) 865 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 866 | local gnMod = 2*talentRank/100 + 1 867 | return ((2061*gnMod)+SpellPower) 868 | end; 869 | [11] = function (SpellPower) 870 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 871 | local gnMod = 2*talentRank/100 + 1 872 | return ((2473*gnMod)+SpellPower) 873 | end; 874 | }; 875 | [L["Regrowth"]] = { 876 | [1] = function (SpellPower) 877 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 878 | local gnMod = 2*talentRank/100 + 1 879 | return ((91*gnMod)+(((2/3.5)*SpellPower)*0.5*0.38)) 880 | end; 881 | [2] = function (SpellPower) 882 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 883 | local gnMod = 2*talentRank/100 + 1 884 | return ((177*gnMod)+(((2/3.5)*SpellPower)*0.5*0.513)) 885 | end; 886 | [3] = function (SpellPower) 887 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 888 | local gnMod = 2*talentRank/100 + 1 889 | return ((258*gnMod)+(((2/3.5)*SpellPower)*0.5)) 890 | end; 891 | [4] = function (SpellPower) 892 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 893 | local gnMod = 2*talentRank/100 + 1 894 | return ((340*gnMod)+(((2/3.5)*SpellPower)*0.5)) 895 | end; 896 | [5] = function (SpellPower) 897 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 898 | local gnMod = 2*talentRank/100 + 1 899 | return ((432*gnMod)+(((2/3.5)*SpellPower)*0.5)) 900 | end; 901 | [6] = function (SpellPower) 902 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 903 | local gnMod = 2*talentRank/100 + 1 904 | return ((544*gnMod)+(((2/3.5)*SpellPower)*0.5)) 905 | end; 906 | [7] = function (SpellPower) 907 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 908 | local gnMod = 2*talentRank/100 + 1 909 | return ((686*gnMod)+(((2/3.5)*SpellPower)*0.5)) 910 | end; 911 | [8] = function (SpellPower) 912 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 913 | local gnMod = 2*talentRank/100 + 1 914 | return ((858*gnMod)+(((2/3.5)*SpellPower)*0.5)) 915 | end; 916 | [9] = function (SpellPower) 917 | local _,_,_,_,talentRank,_ = GetTalentInfo(3,12) 918 | local gnMod = 2*talentRank/100 + 1 919 | return ((1062*gnMod)+(((2/3.5)*SpellPower)*0.5)) 920 | end; 921 | }; 922 | } 923 | 924 | local Resurrections = { 925 | [L["Resurrection"]] = true; 926 | [L["Rebirth"]] = true; 927 | [L["Redemption"]] = true; 928 | [L["Ancestral Spirit"]] = true; 929 | } 930 | 931 | local function strsplit(pString, pPattern) 932 | local Table = {} 933 | local fpat = "(.-)" .. pPattern 934 | local last_end = 1 935 | local s, e, cap = strfind(pString, fpat, 1) 936 | while s do 937 | if s ~= 1 or cap ~= "" then 938 | table.insert(Table,cap) 939 | end 940 | last_end = e+1 941 | s, e, cap = strfind(pString, fpat, last_end) 942 | end 943 | if last_end <= strlen(pString) then 944 | cap = strfind(pString, last_end) 945 | table.insert(Table, cap) 946 | end 947 | return Table 948 | end 949 | 950 | local healcommTip = CreateFrame("GameTooltip", "healcommTip", nil, "GameTooltipTemplate") 951 | healcommTip:SetOwner(WorldFrame, "ANCHOR_NONE") 952 | 953 | HealComm.Buffs = { 954 | [L["Power Infusion"]] = {amount = 0, mod = 0.2, icon = "Interface\\Icons\\Spell_Holy_PowerInfusion"}; 955 | [L["Divine Favor"]] = {amount = 0, mod = 0.5, icon = "Interface\\Icons\\Spell_Holy_Heal"}; 956 | [L["Nature Aligned"]] = {amount = 0, mod = 0.2, icon = "Interface\\Icons\\Spell_Nature_SpiritArmor"}; 957 | [L["Crusader's Wrath"]] = {amount = 95, mod = 0, icon = "Interface\\Icons\\Spell_Nature_GroundingTotem"}; 958 | [L["The Furious Storm"]] = {amount = 95, mod = 0, icon = "Interface\\Icons\\Spell_Nature_CallStorm"}; 959 | [L["Holy Power"]] = {amount = 80, mod = 0, icon = "Interface\\Icons\\Spell_Holy_HolyNova"}; 960 | [L["Prayer Beads Blessing"]] = {amount = 190, mod = 0, icon = "Interface\\Icons\\Inv_Jewelry_Necklace_11"}; 961 | [L["Chromatic Infusion"]] = {amount = 190, mod = 0, icon = "Interface\\Icons\\Spell_Holy_MindVision"}; 962 | [L["Ascendance"]] = {amount = 75, mod = 0, icon = "Interface\\Icons\\Spell_Lightning_LightningBolt01"}; 963 | [L["Ephemeral Power"]] = {amount = 175, mod = 0, icon = "Interface\\Icons\\Spell_Holy_MindVision"}; 964 | [L["Unstable Power"]] = {amount = 34, mod = 0, icon = "Interface\\Icons\\Spell_Lightning_LightningBolt01"}; 965 | [L["Healing of the Ages"]] = {amount = 350, mod = 0, icon = "Interface\\Icons\\Spell_Nature_HealingWaveGreater"}; 966 | [L["Essence of Sapphiron"]] = {amount = 130, mod = 0, icon = "Interface\\Icons\\Inv_Trinket_Naxxramas06"}; 967 | [L["The Eye of the Dead"]] = {amount = 450, mod = 0, icon = "Interface\\Icons\\Inv_Trinket_Naxxramas01"} 968 | } 969 | 970 | HealComm.Debuffs = { 971 | [L["Mortal Strike"]] = {amount = 0, mod = 0.5, icon = "Interface\\Icons\\Ability_Warrior_SavageBlow"}; 972 | [L["Wound Poison"]] = {amount = -135, mod = 0, icon = "Interface\\Icons\\Inv_Misc_Herb_16"}; 973 | [L["Curse of the Deadwood"]] = {amount = 0, mod = 0.5, icon = "Interface\\Icons\\Spell_Shadow_GatherShadows"}; 974 | [L["Veil of Shadow"]] = {amount = 0, mod = 0.75, icon = "Interface\\Icons\\Spell_Shadow_GatherShadows"}; 975 | [L["Gehennas' Curse"]] = {amount = 0, mod = 0.75, icon = "Interface\\Icons\\Spell_Shadow_GatherShadows"}; 976 | [L["Mortal Wound"]] = {amount = 0, mod = 0.1, icon = "Interface\\Icons\\Ability_CriticalStrike"}; 977 | [L["Necrotic Poison"]] = {amount = 0, mod = 0.9, icon = "Interface\\Icons\\Ability_Creature_Poison_03"}; 978 | [L["Blood Fury"]] = {amount = 0, mod = 0.5, icon = "Interface\\Icons\\Ability_Rogue_FeignDeath"}; 979 | [L["Necrotic Aura"]] = {amount = 0, mod = 1, icon = "Interface\\Icons\\Ability_Creature_Disease_05"} 980 | } 981 | 982 | local function getSetBonus() 983 | healcommTip:SetInventoryItem("player", 1) 984 | local text = getglobal("healcommTipTextLeft"..healcommTip:NumLines()):GetText() 985 | if text == L["Set: Increases the duration of your Rejuvenation spell by 3 sec."] or text == L["Set: Increases the duration of your Renew spell by 3 sec."] then 986 | return true 987 | else 988 | return nil 989 | end 990 | end 991 | 992 | function HealComm:GetBuffSpellPower() 993 | local Spellpower = 0 994 | local healmod = 1 995 | for i=1, 16 do 996 | local buffTexture, buffApplications = UnitBuff("player", i) 997 | if not buffTexture then 998 | return Spellpower, healmod 999 | end 1000 | healcommTip:SetUnitBuff("player", i) 1001 | local buffName = healcommTipTextLeft1:GetText() 1002 | if self.Buffs[buffName] and self.Buffs[buffName].icon == buffTexture then 1003 | Spellpower = (self.Buffs[buffName].amount * buffApplications) + Spellpower 1004 | healmod = (self.Buffs[buffName].mod * buffApplications) + healmod 1005 | end 1006 | end 1007 | return Spellpower, healmod 1008 | end 1009 | 1010 | function HealComm:GetTargetSpellPower(spell) 1011 | local targetpower = 0 1012 | local targetmod = 1 1013 | local buffTexture, buffApplications 1014 | local debuffTexture, debuffApplications 1015 | for i=1, 16 do 1016 | if UnitIsVisible("target") and UnitIsConnected("target") and UnitReaction("target", "player") > 4 then 1017 | buffTexture, buffApplications = UnitBuff("target", i) 1018 | healcommTip:SetUnitBuff("target", i) 1019 | else 1020 | buffTexture, buffApplications = UnitBuff("player", i) 1021 | healcommTip:SetUnitBuff("player", i) 1022 | end 1023 | if not buffTexture then 1024 | break 1025 | end 1026 | local buffName = healcommTipTextLeft1:GetText() 1027 | if buffName == L["Blessing of Light"] then 1028 | local HLBonus, FoLBonus = strmatch(healcommTipTextLeft2:GetText(),"(%d+).-(%d+)") 1029 | if (spell == L["Flash of Light"]) then 1030 | targetpower = FoLBonus + targetpower 1031 | elseif spell == L["Holy Light"] then 1032 | targetpower = HLBonus + targetpower 1033 | end 1034 | end 1035 | if buffName == L["Healing Way"] and spell == L["Healing Wave"] then 1036 | targetmod = targetmod * ((buffApplications * 0.06) + 1) 1037 | end 1038 | end 1039 | for i=1, 16 do 1040 | if UnitIsVisible("target") and UnitIsConnected("target") and UnitReaction("target", "player") > 4 then 1041 | debuffTexture, debuffApplications = UnitDebuff("target", i) 1042 | healcommTip:SetUnitDebuff("target", i) 1043 | else 1044 | debuffTexture, debuffApplications = UnitDebuff("player", i) 1045 | healcommTip:SetUnitDebuff("player", i) 1046 | end 1047 | if not debuffTexture then 1048 | break 1049 | end 1050 | local debuffName = healcommTipTextLeft1:GetText() 1051 | if self.Debuffs[debuffName] then 1052 | targetpower = (self.Debuffs[debuffName].amount * debuffApplications) + targetpower 1053 | targetmod = (1-(self.Debuffs[debuffName].mod * debuffApplications)) * targetmod 1054 | end 1055 | end 1056 | return targetpower, targetmod 1057 | end 1058 | 1059 | function HealComm:UNIT_HEALTH() 1060 | local name = UnitName(arg1) 1061 | if self.pendingResurrections[name] then 1062 | for k,v in pairs(self.pendingResurrections[name]) do 1063 | self.pendingResurrections[name][k] = nil 1064 | end 1065 | self:TriggerEvent("HealComm_Ressupdate", name) 1066 | end 1067 | end 1068 | 1069 | function HealComm:stopHeal(caster) 1070 | if self:IsEventScheduled("Healcomm_"..caster) then 1071 | self:CancelScheduledEvent("Healcomm_"..caster) 1072 | end 1073 | if self.Lookup[caster] then 1074 | self.Heals[self.Lookup[caster]][caster] = nil 1075 | self:TriggerEvent("HealComm_Healupdate", self.Lookup[caster]) 1076 | self.Lookup[caster] = nil 1077 | end 1078 | end 1079 | 1080 | function HealComm:startHeal(caster, target, size, casttime) 1081 | 1082 | -- HCSpy 1083 | --console:Print(caster) 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | self:ScheduleEvent("Healcomm_"..caster, self.stopHeal, (casttime/1000), self, caster) 1090 | if not self.Heals[target] then 1091 | self.Heals[target] = {} 1092 | end 1093 | if self.Lookup[caster] then 1094 | self.Heals[self.Lookup[caster]][caster] = nil 1095 | self.Lookup[caster] = nil 1096 | end 1097 | self.Heals[target][caster] = {amount = size, ctime = (casttime/1000)+GetTime()} 1098 | self.Lookup[caster] = target 1099 | self:TriggerEvent("HealComm_Healupdate", target) 1100 | end 1101 | 1102 | function HealComm:delayHeal(caster, delay) 1103 | self:CancelScheduledEvent("Healcomm_"..caster) 1104 | if self.Lookup[caster] and self.Heals[self.Lookup[caster]] then 1105 | self.Heals[self.Lookup[caster]][caster].ctime = self.Heals[self.Lookup[caster]][caster].ctime + (delay/1000) 1106 | self:ScheduleEvent("Healcomm_"..caster, self.stopHeal, (self.Heals[self.Lookup[caster]][caster].ctime-GetTime()), self, caster) 1107 | end 1108 | end 1109 | 1110 | function HealComm:startGrpHeal(caster, size, casttime, party1, party2, party3, party4, party5) 1111 | self:ScheduleEvent("Healcomm_"..caster, self.stopGrpHeal, (casttime/1000), self, caster) 1112 | self.GrpHeals[caster] = {amount = size, ctime = (casttime/1000)+GetTime(), targets = {party1, party2, party3, party4, party5}} 1113 | for i=1,getn(self.GrpHeals[caster].targets) do 1114 | self:TriggerEvent("HealComm_Healupdate", self.GrpHeals[caster].targets[i]) 1115 | end 1116 | end 1117 | 1118 | function HealComm:stopGrpHeal(caster) 1119 | if self:IsEventScheduled("Healcomm_"..caster) then 1120 | self:CancelScheduledEvent("Healcomm_"..caster) 1121 | end 1122 | local targets 1123 | if self.GrpHeals[caster] then 1124 | targets = self.GrpHeals[caster].targets 1125 | end 1126 | self.GrpHeals[caster] = nil 1127 | if targets then 1128 | for i=1,getn(targets) do 1129 | self:TriggerEvent("HealComm_Healupdate", targets[i]) 1130 | end 1131 | end 1132 | end 1133 | 1134 | function HealComm:delayGrpHeal(caster, delay) 1135 | self:CancelScheduledEvent("Healcomm_"..caster) 1136 | if self.GrpHeals[caster] then 1137 | self.GrpHeals[caster].ctime = self.GrpHeals[caster].ctime + (delay/1000) 1138 | self:ScheduleEvent("Healcomm_"..caster, self.stopGrpHeal, (self.GrpHeals[caster].ctime-GetTime()), self, caster) 1139 | end 1140 | end 1141 | 1142 | function HealComm:startResurrection(caster, target) 1143 | if not self.pendingResurrections[target] then 1144 | self.pendingResurrections[target] = {} 1145 | end 1146 | self.pendingResurrections[target][caster] = GetTime()+70 1147 | self:ScheduleEvent("Healcomm_"..caster..target, self.RessExpire, 70, self, caster, target) 1148 | self:TriggerEvent("HealComm_Ressupdate", target) 1149 | end 1150 | 1151 | function HealComm:cancelResurrection(caster) 1152 | for k,v in pairs(self.pendingResurrections) do 1153 | if v[caster] and (v[caster]-GetTime()) > 60 then 1154 | self.pendingResurrections[k][caster] = nil 1155 | self:TriggerEvent("HealComm_Ressupdate", k) 1156 | end 1157 | end 1158 | end 1159 | 1160 | function HealComm:RessExpire(caster, target) 1161 | self.pendingResurrections[target][caster] = nil 1162 | self:TriggerEvent("HealComm_Ressupdate", target) 1163 | end 1164 | 1165 | function HealComm:SendAddonMessage(msg) 1166 | local zone = GetRealZoneText() 1167 | if zone == L["Warsong Gulch"] or zone == L["Arathi Basin"] or zone == L["Alterac Valley"] then 1168 | SendAddonMessage("HealComm", msg, "BATTLEGROUND") 1169 | else 1170 | SendAddonMessage("HealComm", msg, "RAID") 1171 | end 1172 | end 1173 | 1174 | function HealComm:SPELLCAST_START() 1175 | 1176 | if ( self.SpellCastInfo and self.SpellCastInfo[1] == arg1 and self.Spells[arg1] ) then 1177 | local Bonus = 0 1178 | if BonusScanner then 1179 | Bonus = tonumber(BonusScanner:GetBonus("HEAL")) 1180 | end 1181 | local buffpower, buffmod = self:GetBuffSpellPower() 1182 | local targetpower, targetmod = self.SpellCastInfo[4], self.SpellCastInfo[5] 1183 | local Bonus = Bonus + buffpower 1184 | local amount = ((math.floor(self.Spells[self.SpellCastInfo[1]][tonumber(self.SpellCastInfo[2])](Bonus))+targetpower)*buffmod*targetmod) 1185 | if arg1 == L["Prayer of Healing"] then 1186 | local targets = {UnitName("player")} 1187 | local targetsstring = UnitName("player").."/" 1188 | for i=1,4 do 1189 | if CheckInteractDistance("party"..i, 4) then 1190 | table.insert(targets, i ,UnitName("party"..i)) 1191 | targetsstring = targetsstring..UnitName("party"..i).."/" 1192 | end 1193 | end 1194 | self:SendAddonMessage("GrpHeal/"..amount.."/"..arg2.."/"..targetsstring) 1195 | self:startGrpHeal(UnitName("player"), amount, arg2, targets[1], targets[2], targets[3], targets[4], targets[5]) 1196 | else 1197 | self:SendAddonMessage("Heal/"..self.SpellCastInfo[3].."/"..amount.."/"..arg2.."/") 1198 | self:startHeal(UnitName("player"), self.SpellCastInfo[3], amount, arg2) 1199 | end 1200 | elseif ( self.SpellCastInfo and self.SpellCastInfo[1] == arg1 and Resurrections[arg1] ) then 1201 | self:SendAddonMessage("Resurrection/"..self.SpellCastInfo[3].."/start/") 1202 | self:startResurrection(UnitName("player"), self.SpellCastInfo[3]) 1203 | end 1204 | self.spellIsCasting = arg1 1205 | end 1206 | 1207 | function HealComm:SPELLCAST_FAILED() 1208 | 1209 | if self:IsEventScheduled("TriggerRegrowthHot") then 1210 | self:CancelScheduledEvent("TriggerRegrowthHot") 1211 | end 1212 | 1213 | if self.Spells[self.spellIsCasting] then 1214 | if self.spellIsCasting == L["Prayer of Healing"] then 1215 | self:SendAddonMessage("GrpHealstop") 1216 | self:stopGrpHeal(UnitName("player")) 1217 | else 1218 | self:SendAddonMessage("Healstop") 1219 | self:stopHeal(UnitName("player")) 1220 | end 1221 | elseif Resurrections[self.spellIsCasting] then 1222 | self:SendAddonMessage("Resurrection/stop/") 1223 | self:cancelResurrection(UnitName("player")) 1224 | end 1225 | self.CurrentSpellRank = nil 1226 | self.CurrentSpellName = nil 1227 | self.spellIsCasting = nil 1228 | for key in pairs(self.SpellCastInfo) do 1229 | self.SpellCastInfo[key] = nil 1230 | end 1231 | end 1232 | 1233 | function HealComm:SPELLCAST_DELAYED() 1234 | if self.spellIsCasting == L["Prayer of Healing"] then 1235 | self:SendAddonMessage("GrpHealdelay/"..arg1.."/") 1236 | self:delayGrpHeal(UnitName("player"), arg1) 1237 | else 1238 | self:SendAddonMessage("Healdelay/"..arg1.."/") 1239 | self:delayHeal(UnitName("player"), arg1) 1240 | end 1241 | end 1242 | 1243 | function HealComm:TriggerRegrowthHot() 1244 | local dur = 21 1245 | self:SendAddonMessage("Regr/"..self.savetarget.."/"..dur.."/") 1246 | if not self.Hots[self.savetarget] then 1247 | self.Hots[self.savetarget] = {} 1248 | end 1249 | if not self.Hots[self.savetarget]["Regr"] then 1250 | self.Hots[self.savetarget]["Regr"]= {} 1251 | end 1252 | self.Hots[self.savetarget]["Regr"].start = GetTime() 1253 | self.Hots[self.savetarget]["Regr"].dur = dur 1254 | self:TriggerEvent("HealComm_Hotupdate", roster:GetUnitIDFromName(self.savetarget), "Regrowth") 1255 | end 1256 | 1257 | function HealComm:SPELLCAST_STOP() 1258 | if not self.SpellCastInfo then return end 1259 | local targetUnit = roster:GetUnitIDFromName(self.SpellCastInfo[3]) 1260 | if targetUnit then 1261 | if self.SpellCastInfo[1] == L["Renew"] then 1262 | local dur = getSetBonus() and 18 or 15 1263 | self:SendAddonMessage("Renew/"..self.SpellCastInfo[3].."/"..dur.."/") 1264 | if not self.Hots[self.SpellCastInfo[3]] then 1265 | self.Hots[self.SpellCastInfo[3]] = {} 1266 | end 1267 | if not self.Hots[self.SpellCastInfo[3]]["Renew"] then 1268 | self.Hots[self.SpellCastInfo[3]]["Renew"]= {} 1269 | end 1270 | self.Hots[self.SpellCastInfo[3]]["Renew"].start = GetTime() 1271 | self.Hots[self.SpellCastInfo[3]]["Renew"].dur = dur 1272 | self:TriggerEvent("HealComm_Hotupdate", targetUnit, "Renew") 1273 | elseif self.SpellCastInfo[1] == L["Rejuvenation"] then 1274 | local dur = getSetBonus() and 15 or 12 1275 | self:SendAddonMessage("Reju/"..self.SpellCastInfo[3].."/"..dur.."/") 1276 | if not self.Hots[self.SpellCastInfo[3]] then 1277 | self.Hots[self.SpellCastInfo[3]] = {} 1278 | end 1279 | if not self.Hots[self.SpellCastInfo[3]]["Reju"] then 1280 | self.Hots[self.SpellCastInfo[3]]["Reju"]= {} 1281 | end 1282 | self.Hots[self.SpellCastInfo[3]]["Reju"].start = GetTime() 1283 | self.Hots[self.SpellCastInfo[3]]["Reju"].dur = dur 1284 | self:TriggerEvent("HealComm_Hotupdate", targetUnit, "Rejuvenation") 1285 | elseif self.SpellCastInfo[1] == L["Regrowth"] then 1286 | self.savetarget = self.SpellCastInfo[3] 1287 | self:ScheduleEvent("TriggerRegrowthHot", self.TriggerRegrowthHot, 0.3, self) 1288 | end 1289 | end 1290 | self.CurrentSpellRank = nil 1291 | self.CurrentSpellName = nil 1292 | for key in pairs(self.SpellCastInfo) do 1293 | self.SpellCastInfo[key] = nil 1294 | end 1295 | end 1296 | 1297 | function HealComm:CHAT_MSG_ADDON() 1298 | if arg1 == "HealComm" and arg4 ~= UnitName("player") then 1299 | local result = strsplit(arg2,"/") 1300 | --console:Print("HealComm user has Startet casting: "..UnitName("player")) --HCSpy 1301 | if result[1] == "Heal" then 1302 | self:startHeal(arg4, result[2], result[3], result[4]) 1303 | elseif arg2 == "Healstop" then 1304 | self:stopHeal(arg4) 1305 | elseif result[1] == "Healdelay" then 1306 | self:delayHeal(arg4, result[2]) 1307 | elseif result[1] == "Resurrection" and result[2] == "stop" then 1308 | self:cancelResurrection(arg4) 1309 | elseif result[1] == "Resurrection" and result[3] == "start" then 1310 | self:startResurrection(arg4, result[2]) 1311 | elseif result[1] == "GrpHeal" then 1312 | self:startGrpHeal(arg4, result[2], result[3], result[4], result[5], result[6], result[7], result[8]) 1313 | elseif arg2 == "GrpHealstop" then 1314 | self:stopGrpHeal(arg4) 1315 | elseif result[1] == "GrpHealdelay" then 1316 | self:delayGrpHeal(arg4, result[2]) 1317 | elseif result[1] == "Renew" then 1318 | if not self.Hots[result[2]] then 1319 | self.Hots[result[2]] = {} 1320 | end 1321 | if not self.Hots[result[2]]["Renew"] then 1322 | self.Hots[result[2]]["Renew"]= {} 1323 | end 1324 | self.Hots[result[2]]["Renew"].dur = result[3] 1325 | self.Hots[result[2]]["Renew"].start = GetTime() 1326 | local targetUnit = roster:GetUnitIDFromName(result[2]) 1327 | self:TriggerEvent("HealComm_Hotupdate", targetUnit, "Renew") 1328 | elseif result[1] == "Reju" then 1329 | if not self.Hots[result[2]] then 1330 | self.Hots[result[2]] = {} 1331 | end 1332 | if not self.Hots[result[2]]["Reju"] then 1333 | self.Hots[result[2]]["Reju"]= {} 1334 | end 1335 | self.Hots[result[2]]["Reju"].dur = result[3] 1336 | self.Hots[result[2]]["Reju"].start = GetTime() 1337 | local targetUnit = roster:GetUnitIDFromName(result[2]) 1338 | self:TriggerEvent("HealComm_Hotupdate", targetUnit, "Rejuvenation") 1339 | elseif result[1] == "Regr" then 1340 | if not self.Hots[result[2]] then 1341 | self.Hots[result[2]] = {} 1342 | end 1343 | if not self.Hots[result[2]]["Regr"] then 1344 | self.Hots[result[2]]["Regr"]= {} 1345 | end 1346 | self.Hots[result[2]]["Regr"].dur = result[3] 1347 | self.Hots[result[2]]["Regr"].start = GetTime() 1348 | local targetUnit = roster:GetUnitIDFromName(result[2]) 1349 | self:TriggerEvent("HealComm_Hotupdate", targetUnit, "Regrowth") 1350 | end 1351 | end 1352 | end 1353 | 1354 | function HealComm:UNIT_AURA() 1355 | local name = UnitName(arg1) 1356 | if self.Hots[name] and (self.Hots[name]["Regr"] or self.Hots[name]["Reju"] or self.Hots[name]["Renew"]) then 1357 | local regr,reju,renew 1358 | for i=1,32 do 1359 | if not UnitBuff(arg1,i) then 1360 | break 1361 | end 1362 | healcommTip:ClearLines() 1363 | healcommTip:SetUnitBuff(arg1,i) 1364 | regr = regr or healcommTipTextLeft1:GetText() == L["Regrowth"] 1365 | reju = reju or healcommTipTextLeft1:GetText() == L["Rejuvenation"] 1366 | renew = renew or healcommTipTextLeft1:GetText() == L["Renew"] 1367 | end 1368 | if not regr then 1369 | self.Hots[name]["Regr"] = nil 1370 | self:TriggerEvent("HealComm_Hotupdate", arg1, "Regrowth") 1371 | end 1372 | if not reju then 1373 | self.Hots[name]["Reju"] = nil 1374 | self:TriggerEvent("HealComm_Hotupdate", arg1, "Rejuvenation") 1375 | end 1376 | if not renew then 1377 | self.Hots[name]["Renew"] = nil 1378 | self:TriggerEvent("HealComm_Hotupdate", arg1, "Renew") 1379 | end 1380 | end 1381 | end 1382 | 1383 | function HealComm:getRegrTime(unit) 1384 | if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then 1385 | return 1386 | end 1387 | local dbUnit = self.Hots[UnitName(unit)] 1388 | if dbUnit and dbUnit["Regr"] and (dbUnit["Regr"].start + dbUnit["Regr"].dur) > GetTime() then 1389 | return dbUnit["Regr"].start, dbUnit["Regr"].dur 1390 | else 1391 | return 1392 | end 1393 | end 1394 | 1395 | function HealComm:getRejuTime(unit) 1396 | if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then 1397 | return 1398 | end 1399 | local dbUnit = self.Hots[UnitName(unit)] 1400 | if dbUnit and dbUnit["Reju"] and (dbUnit["Reju"].start + dbUnit["Reju"].dur) > GetTime() then 1401 | return dbUnit["Reju"].start, dbUnit["Reju"].dur 1402 | else 1403 | return 1404 | end 1405 | end 1406 | 1407 | function HealComm:getRenewTime(unit) 1408 | if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then 1409 | return 1410 | end 1411 | local dbUnit = self.Hots[UnitName(unit)] 1412 | if dbUnit and dbUnit["Renew"] and (dbUnit["Renew"].start + dbUnit["Renew"].dur) > GetTime() then 1413 | return dbUnit["Renew"].start, dbUnit["Renew"].dur 1414 | else 1415 | return 1416 | end 1417 | end 1418 | 1419 | function HealComm:getHeal(unit) 1420 | if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then 1421 | return 0 1422 | end 1423 | local healamount = 0 1424 | if self.Heals[unit] then 1425 | for k,v in self.Heals[unit] do 1426 | healamount = healamount+v.amount 1427 | end 1428 | end 1429 | for k,v in pairs(self.GrpHeals) do 1430 | for j,c in pairs(v.targets) do 1431 | if unit == c then 1432 | healamount = healamount+v.amount 1433 | end 1434 | end 1435 | end 1436 | return healamount 1437 | end 1438 | 1439 | function HealComm:UnitisResurrecting(unit) 1440 | local resstime 1441 | if self.pendingResurrections[unit] then 1442 | for k,v in pairs(self.pendingResurrections[unit]) do 1443 | if v < GetTime() then 1444 | self.pendingResurrections[unit][k] = nil 1445 | elseif not resstime or resstime > v then 1446 | resstime = v 1447 | end 1448 | end 1449 | end 1450 | return resstime 1451 | end 1452 | 1453 | function HealComm:getNumHeals(unit) 1454 | if unit == UNKNOWNOBJECT or unit == UKNOWNBEING then 1455 | return 0 1456 | end 1457 | local heals = 0 1458 | if self.Heals[unit] then 1459 | for _ in self.Heals[unit] do 1460 | heals = heals + 1 1461 | end 1462 | end 1463 | for _,v in pairs(self.GrpHeals) do 1464 | for _,c in pairs(v.targets) do 1465 | if unit == c then 1466 | heals = heals + 1 1467 | end 1468 | end 1469 | end 1470 | return heals 1471 | end 1472 | 1473 | 1474 | function HealComm:CastSpell(spellId, spellbookTabNum) 1475 | self.hooks.CastSpell(spellId, spellbookTabNum) 1476 | local spellName, rank = GetSpellName(spellId, spellbookTabNum) 1477 | _,_,rank = string.find(rank,"(%d+)") 1478 | if ( SpellIsTargeting() ) then 1479 | -- Spell is waiting for a target 1480 | self.CurrentSpellName = spellName 1481 | self.CurrentSpellRank = rank 1482 | elseif ( UnitIsVisible("target") and UnitIsConnected("target") and UnitCanAssist("player", "target") ) then 1483 | -- Spell is being cast on the current target. 1484 | -- If ClearTarget() had been called, we'd be waiting target 1485 | if UnitIsPlayer("target") then 1486 | self:ProcessSpellCast(spellName, rank, UnitName("target")) 1487 | end 1488 | else 1489 | self:ProcessSpellCast(spellName, rank, UnitName("player")) 1490 | end 1491 | end 1492 | 1493 | function HealComm:CastSpellByName(spellName, onSelf) 1494 | self.hooks.CastSpellByName(spellName, onSelf) 1495 | local _,_,rank = string.find(spellName,"(%d+)") 1496 | local _, _, spellName = string.find(spellName, "^([^%(]+)") 1497 | if not rank then 1498 | local i = 1 1499 | while GetSpellName(i, BOOKTYPE_SPELL) do 1500 | local s, r = GetSpellName(i, BOOKTYPE_SPELL) 1501 | if s == spellName then 1502 | rank = r 1503 | end 1504 | i = i+1 1505 | end 1506 | if rank then 1507 | _,_,rank = string.find(rank,"(%d+)") 1508 | end 1509 | end 1510 | if ( spellName ) then 1511 | if ( SpellIsTargeting() ) then 1512 | self.CurrentSpellName = spellName 1513 | self.CurrentSpellRank = rank 1514 | else 1515 | if UnitIsVisible("target") and UnitIsConnected("target") and UnitCanAssist("player", "target") and onSelf ~= 1 then 1516 | if UnitIsPlayer("target") then 1517 | self:ProcessSpellCast(spellName, rank, UnitName("target")) 1518 | end 1519 | else 1520 | self:ProcessSpellCast(spellName, rank, UnitName("player")) 1521 | end 1522 | end 1523 | end 1524 | end 1525 | 1526 | function HealComm:OnMouseDown(object) 1527 | -- If we're waiting to target 1528 | local targetName 1529 | 1530 | if ( self.CurrentSpellName and UnitExists("mouseover") ) then 1531 | targetName = UnitName("mouseover") 1532 | elseif ( self.CurrentSpellName and GameTooltipTextLeft1:IsVisible() ) then 1533 | local _, _, name = string.find(GameTooltipTextLeft1:GetText(), L["^Corpse of (.+)$"]) 1534 | if ( name ) then 1535 | targetName = name 1536 | end 1537 | end 1538 | if ( self.hooks[object]["OnMouseDown"] ) then 1539 | self.hooks[object]["OnMouseDown"]() 1540 | end 1541 | if ( self.CurrentSpellName and targetName ) then 1542 | self:ProcessSpellCast(self.CurrentSpellName, self.CurrentSpellRank, targetName) 1543 | end 1544 | end 1545 | 1546 | function HealComm:UseAction(slot, checkCursor, onSelf) 1547 | healcommTip:ClearLines() 1548 | healcommTip:SetAction(slot) 1549 | local spellName = healcommTipTextLeft1:GetText() 1550 | 1551 | -- Test to see if this is a macro 1552 | if not GetActionText(slot) then 1553 | self.CurrentSpellName = spellName 1554 | end 1555 | 1556 | self.hooks.UseAction(slot, checkCursor, onSelf) 1557 | 1558 | -- Test to see if this is a macro 1559 | if ( not self.CurrentSpellName ) then 1560 | return 1561 | end 1562 | local rank = healcommTipTextRight1:GetText() 1563 | if rank then 1564 | _,_,rank = string.find(rank,"(%d+)") 1565 | end 1566 | if not rank then 1567 | rank = 1 1568 | end 1569 | self.CurrentSpellRank = rank 1570 | if ( SpellIsTargeting() ) then 1571 | -- Spell is waiting for a target 1572 | return 1573 | elseif ( UnitIsVisible("target") and UnitIsConnected("target") and UnitCanAssist("player", "target") and onSelf ~= 1) then 1574 | -- Spell is being cast on the current target 1575 | if UnitIsPlayer("target") then 1576 | self:ProcessSpellCast(spellName, rank, UnitName("target")) 1577 | end 1578 | else 1579 | -- Spell is being cast on the player 1580 | self:ProcessSpellCast(spellName, rank, UnitName("player")) 1581 | end 1582 | end 1583 | 1584 | function HealComm:SpellTargetUnit(unit) 1585 | local shallTargetUnit 1586 | if ( SpellIsTargeting() ) then 1587 | shallTargetUnit = true 1588 | end 1589 | self.hooks.SpellTargetUnit(unit) 1590 | if ( shallTargetUnit and self.CurrentSpellName and not SpellIsTargeting() ) then 1591 | if UnitIsPlayer(unit) then 1592 | self:ProcessSpellCast(self.CurrentSpellName, self.CurrentSpellRank, UnitName(unit)) 1593 | end 1594 | self.CurrentSpellName = nil 1595 | self.CurrentSpellRank = nil 1596 | end 1597 | end 1598 | 1599 | function HealComm:SpellStopTargeting() 1600 | self.hooks.SpellStopTargeting() 1601 | self.CurrentSpellName = nil 1602 | self.CurrentSpellRank = nil 1603 | end 1604 | 1605 | function HealComm:TargetUnit(unit) 1606 | -- Look to see if we're currently waiting for a target internally 1607 | -- If we are, then well glean the target info here. 1608 | if ( self.CurrentSpellName and UnitExists(unit) ) and UnitIsPlayer(unit) then 1609 | self:ProcessSpellCast(self.CurrentSpellName, self.CurrentSpellRank, UnitName(unit)) 1610 | end 1611 | self.hooks.TargetUnit(unit) 1612 | end 1613 | 1614 | function HealComm:ProcessSpellCast(spellName, rank, targetName) 1615 | local power, mod = self:GetTargetSpellPower(spellName) 1616 | self.SpellCastInfo[1] = self.SpellCastInfo[1] or spellName 1617 | self.SpellCastInfo[2] = self.SpellCastInfo[2] or rank 1618 | self.SpellCastInfo[3] = self.SpellCastInfo[3] or targetName 1619 | self.SpellCastInfo[4] = power 1620 | self.SpellCastInfo[5] = mod 1621 | end 1622 | 1623 | AceLibrary:Register(HealComm, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) 1624 | HealComm = nil -------------------------------------------------------------------------------- /libs/RosterLib-2.0/RosterLib-2.0.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: RosterLib-2.0 3 | Revision: $Revision: 16213 $ 4 | X-ReleaseDate: $Date: 2006-08-10 08:55:29 +0200 (Thu, 10 Aug 2006) $ 5 | Author: Maia (maia.proudmoore@gmail.com) 6 | Website: http://wiki.wowace.com/index.php/RosterLib-2.0 7 | Documentation: http://wiki.wowace.com/index.php/RosterLib-2.0_API_Documentation 8 | SVN: http://svn.wowace.com/root/trunk/RosterLib-2.0/ 9 | Description: party/raid roster management 10 | Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0 11 | ]] 12 | 13 | local MAJOR_VERSION = "RosterLib-2.0" 14 | local MINOR_VERSION = "$Revision: 16213 $" 15 | 16 | if not AceLibrary then error(vmajor .. " requires AceLibrary.") end 17 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end 18 | if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end 19 | if not AceLibrary:HasInstance("AceEvent-2.0") then error(MAJOR_VERSION .. " requires AceEvent-2.0") end 20 | 21 | local updatedUnits = {} 22 | local unknownUnits = {} 23 | local RosterLib = {} 24 | local roster 25 | 26 | ------------------------------------------------ 27 | -- activate, enable, disable 28 | ------------------------------------------------ 29 | 30 | local function print(text) 31 | ChatFrame3:AddMessage(text) 32 | end 33 | 34 | local function activate(self, oldLib, oldDeactivate) 35 | RosterLib = self 36 | if oldLib then 37 | self.roster = oldLib.roster 38 | oldLib:UnregisterAllEvents() 39 | oldLib:CancelAllScheduledEvents() 40 | end 41 | if not self.roster then self.roster = {} end 42 | if oldDeactivate then oldDeactivate(oldLib) end 43 | roster = self.roster 44 | end 45 | 46 | 47 | local function external(self, major, instance) 48 | if major == "AceEvent-2.0" then 49 | AceEvent = instance 50 | AceEvent:embed(self) 51 | self:UnregisterAllEvents() 52 | self:CancelAllScheduledEvents() 53 | if AceEvent:IsFullyInitialized() then 54 | self:AceEvent_FullyInitialized() 55 | else 56 | self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true) 57 | end 58 | elseif major == "Compost-2.0" then 59 | Compost = instance 60 | end 61 | end 62 | 63 | 64 | function RosterLib:Enable() 65 | -- not used anymore, but as addons still might be calling this method, we're keeping it. 66 | end 67 | 68 | 69 | function RosterLib:Disable() 70 | -- not used anymore, but as addons still might be calling this method, we're keeping it. 71 | end 72 | 73 | ------------------------------------------------ 74 | -- Internal functions 75 | ------------------------------------------------ 76 | 77 | function RosterLib:AceEvent_FullyInitialized() 78 | self:TriggerEvent("RosterLib_Enabled") 79 | self:RegisterEvent("RAID_ROSTER_UPDATE","ScanFullRoster") 80 | self:RegisterEvent("PARTY_MEMBERS_CHANGED","ScanFullRoster") 81 | self:RegisterEvent("UNIT_PET","ScanPet") 82 | self:ScanFullRoster() 83 | end 84 | 85 | 86 | ------------------------------------------------ 87 | -- Unit iterator 88 | ------------------------------------------------ 89 | 90 | local playersent, petsent, unitcount, petcount, pmem, rmem, unit 91 | 92 | local function NextUnit() 93 | -- STEP 1: pet 94 | if not petsent then 95 | petsent = true 96 | if rmem == 0 then 97 | unit = "pet" 98 | if UnitExists(unit) then return unit end 99 | end 100 | end 101 | -- STEP 2: player 102 | if not playersent then 103 | playersent = true 104 | if rmem == 0 then 105 | unit = "player" 106 | if UnitExists(unit) then return unit end 107 | end 108 | end 109 | -- STEP 3: raid units 110 | if rmem > 0 then 111 | -- STEP 3a: pet units 112 | for i = petcount, rmem do 113 | unit = string.format("raidpet%d", i) 114 | petcount = petcount + 1 115 | if UnitExists(unit) then return unit end 116 | end 117 | -- STEP 3b: player units 118 | for i = unitcount, rmem do 119 | unit = string.format("raid%d", i) 120 | unitcount = unitcount + 1 121 | if UnitExists(unit) then return unit end 122 | end 123 | -- STEP 4: party units 124 | elseif pmem > 0 then 125 | -- STEP 3a: pet units 126 | for i = petcount, pmem do 127 | unit = string.format("partypet%d", i) 128 | petcount = petcount + 1 129 | if UnitExists(unit) then return unit end 130 | end 131 | -- STEP 3b: player units 132 | for i = unitcount, pmem do 133 | unit = string.format("party%d", i) 134 | unitcount = unitcount + 1 135 | if UnitExists(unit) then return unit end 136 | end 137 | end 138 | end 139 | 140 | local function UnitIterator() 141 | playersent, petsent, unitcount, petcount, pmem, rmem = false, false, 1, 1, GetNumPartyMembers(), GetNumRaidMembers() 142 | return NextUnit 143 | end 144 | 145 | ------------------------------------------------ 146 | -- Roster code 147 | ------------------------------------------------ 148 | 149 | 150 | function RosterLib:ScanFullRoster() 151 | -- save all units we currently have, this way we can check who to remove from roster later. 152 | local temp = Compost and Compost:Acquire() or {} 153 | for name in pairs(roster) do 154 | temp[name] = true 155 | end 156 | -- update data 157 | for unitid in UnitIterator() do 158 | local name = self:CreateOrUpdateUnit(unitid) 159 | -- we successfully added a unit, so we don't need to remove it next step 160 | if name then temp[name] = nil end 161 | end 162 | -- clear units we had in roster that either left the raid or are unknown for some reason. 163 | for name in pairs(temp) do 164 | self:RemoveUnit(name) 165 | end 166 | if Compost then Compost:Reclaim(temp) end 167 | self:ProcessRoster() 168 | end 169 | 170 | 171 | function RosterLib:ScanPet(owner) 172 | local unitid = self:GetPetFromOwner(owner) 173 | if not unitid then 174 | return 175 | elseif not UnitExists(unitid) then 176 | unknownUnits[unitid] = nil 177 | -- find the pet in the roster we need to delete 178 | for _,u in pairs(roster) do 179 | if u.unitid == unitid then 180 | self:RemoveUnit(u.name) 181 | end 182 | end 183 | else 184 | self:CreateOrUpdateUnit(unitid) 185 | end 186 | self:ProcessRoster() 187 | end 188 | 189 | 190 | function RosterLib:GetPetFromOwner(id) 191 | -- convert party3 crap to raid IDs when in raid. 192 | local owner = self:GetUnitIDFromUnit(id) 193 | if not owner then 194 | return 195 | end 196 | -- get ID 197 | if string.find(owner,"raid") then 198 | return string.gsub(owner, "raid", "raidpet") 199 | elseif string.find(owner,"party") then 200 | return string.gsub(owner, "party", "partypet") 201 | elseif owner == "player" then 202 | return "pet" 203 | else 204 | return nil 205 | end 206 | end 207 | 208 | 209 | function RosterLib:ScanUnknownUnits() 210 | local name 211 | for unitid in pairs(unknownUnits) do 212 | if UnitExists(unitid) then 213 | name = self:CreateOrUpdateUnit(unitid) 214 | else 215 | unknownUnits[unitid] = nil 216 | end 217 | -- some pets never have a name. too bad for them, farewell! 218 | if not name and string.find(unitid,"pet") then 219 | unknownUnits[unitid] = nil 220 | end 221 | end 222 | self:ProcessRoster() 223 | end 224 | 225 | 226 | function RosterLib:ProcessRoster() 227 | if next(updatedUnits, nil) then 228 | self:TriggerEvent("RosterLib_RosterChanged", updatedUnits) 229 | for name in pairs(updatedUnits) do 230 | local u = updatedUnits[name] 231 | self:TriggerEvent("RosterLib_UnitChanged", u.unitid, u.name, u.class, u.subgroup, u.rank, u.oldname, u.oldunitid, u.oldclass, u.oldsubgroup, u.oldrank) 232 | if Compost then Compost:Reclaim(updatedUnits[name]) end 233 | updatedUnits[name] = nil 234 | end 235 | end 236 | if next(unknownUnits, nil) then 237 | self:CancelScheduledEvent("ScanUnknownUnits") 238 | self:ScheduleEvent("ScanUnknownUnits",self.ScanUnknownUnits, 1, self) 239 | end 240 | end 241 | 242 | 243 | function RosterLib:CreateOrUpdateUnit(unitid) 244 | local old = nil 245 | -- check for name 246 | local name = UnitName(unitid) 247 | if name and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING and not UnitIsCharmed(unitid) then 248 | -- clear stuff 249 | unknownUnits[unitid] = nil 250 | -- return if a pet attempts to replace a player name 251 | -- this doesnt fix the problem with 2 pets overwriting each other FIXME 252 | if string.find(unitid,"pet") then 253 | if roster[name] and roster[name].class ~= "pet" then 254 | return name 255 | end 256 | end 257 | -- save old data if existing 258 | if roster[name] then 259 | old = Compost and Compost:Acquire() or {} 260 | old.name = roster[name].name 261 | old.unitid = roster[name].unitid 262 | old.class = roster[name].class 263 | old.rank = roster[name].rank 264 | old.subgroup = roster[name].subgroup 265 | old.online = roster[name].online 266 | end 267 | -- object 268 | if not roster[name] then 269 | roster[name] = Compost and Compost:Acquire() or {} 270 | end 271 | -- name 272 | roster[name].name = name 273 | -- unitid 274 | roster[name].unitid = unitid 275 | -- class 276 | if string.find(unitid,"pet") then 277 | roster[name].class = "PET" 278 | else 279 | _,roster[name].class = UnitClass(unitid) 280 | end 281 | -- subgroup and rank 282 | if GetNumRaidMembers() > 0 then 283 | local _,_,num = string.find(unitid, "(%d+)") 284 | _,roster[name].rank,roster[name].subgroup = GetRaidRosterInfo(num) 285 | else 286 | roster[name].subgroup = 1 287 | roster[name].rank = 0 288 | end 289 | -- online/offline status 290 | roster[name].online = UnitIsConnected(unitid) 291 | 292 | -- compare data 293 | if not old 294 | or roster[name].name ~= old.name 295 | or roster[name].unitid ~= old.unitid 296 | or roster[name].class ~= old.class 297 | or roster[name].subgroup ~= old.subgroup 298 | or roster[name].rank ~= old.rank 299 | or roster[name].online ~= old.online 300 | then 301 | updatedUnits[name] = Compost and Compost:Acquire() or {} 302 | updatedUnits[name].oldname = (old and old.name) or nil 303 | updatedUnits[name].oldunitid = (old and old.unitid) or nil 304 | updatedUnits[name].oldclass = (old and old.class) or nil 305 | updatedUnits[name].oldsubgroup = (old and old.subgroup) or nil 306 | updatedUnits[name].oldrank = (old and old.rank) or nil 307 | updatedUnits[name].oldonline = (old and old.online) or nil 308 | updatedUnits[name].name = roster[name].name 309 | updatedUnits[name].unitid = roster[name].unitid 310 | updatedUnits[name].class = roster[name].class 311 | updatedUnits[name].subgroup = roster[name].subgroup 312 | updatedUnits[name].rank = roster[name].rank 313 | updatedUnits[name].online = roster[name].online 314 | end 315 | -- compost our table 316 | if old and Compost then 317 | Compost:Reclaim(old) 318 | end 319 | return name 320 | else 321 | unknownUnits[unitid] = true 322 | return false 323 | end 324 | end 325 | 326 | 327 | function RosterLib:RemoveUnit(name) 328 | updatedUnits[name] = Compost and Compost:Acquire() or {} 329 | updatedUnits[name].oldname = roster[name].name 330 | updatedUnits[name].oldunitid = roster[name].unitid 331 | updatedUnits[name].oldclass = roster[name].class 332 | updatedUnits[name].oldsubgroup = roster[name].subgroup 333 | updatedUnits[name].oldrank = roster[name].rank 334 | if Compost then Compost:Reclaim(roster[name]) end 335 | roster[name] = nil 336 | end 337 | 338 | 339 | ------------------------------------------------ 340 | -- API 341 | ------------------------------------------------ 342 | 343 | function RosterLib:GetUnitIDFromName(name) 344 | if roster[name] then 345 | return roster[name].unitid 346 | else 347 | return nil 348 | end 349 | end 350 | 351 | 352 | function RosterLib:GetUnitIDFromUnit(unit) 353 | local name = UnitName(unit) 354 | if name and roster[name] then 355 | return roster[name].unitid 356 | else 357 | return nil 358 | end 359 | end 360 | 361 | 362 | function RosterLib:GetUnitObjectFromName(name) 363 | if roster[name] then 364 | return roster[name] 365 | else 366 | return nil 367 | end 368 | end 369 | 370 | 371 | function RosterLib:GetUnitObjectFromUnit(unit) 372 | local name = UnitName(unit) 373 | if roster[name] then 374 | return roster[name] 375 | else 376 | return nil 377 | end 378 | end 379 | 380 | 381 | function RosterLib:IterateRoster(pets) 382 | local key 383 | return function() 384 | repeat 385 | key = next(roster, key) 386 | until (roster[key] == nil or pets or roster[key].class ~= "PET") 387 | 388 | return roster[key] 389 | end 390 | end 391 | 392 | 393 | AceLibrary:Register(RosterLib, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) 394 | -------------------------------------------------------------------------------- /localization.de.lua: -------------------------------------------------------------------------------- 1 | if (GetLocale() == "deDE") then 2 | 3 | -- Shaman 4 | QUICKHEAL_SPELL_LESSER_HEALING_WAVE = 'Geringe Welle der Heilung'; 5 | QUICKHEAL_SPELL_HEALING_WAVE = 'Welle der Heilung'; 6 | 7 | -- Priest 8 | QUICKHEAL_SPELL_LESSER_HEAL = 'Geringes Heilen'; 9 | QUICKHEAL_SPELL_HEAL = 'Heilen'; 10 | QUICKHEAL_SPELL_GREATER_HEAL = 'Gro\195\159e Heilung'; 11 | QUICKHEAL_SPELL_FLASH_HEAL = 'Blitzheilung'; 12 | 13 | -- Paladin 14 | QUICKHEAL_SPELL_HOLY_LIGHT = 'Heiliges Licht'; 15 | QUICKHEAL_SPELL_FLASH_OF_LIGHT = 'Lichtblitz'; 16 | 17 | -- Druid 18 | QUICKHEAL_SPELL_HEALING_TOUCH = 'Heilende Ber\195\188hrung'; 19 | QUICKHEAL_SPELL_REGROWTH = 'Nachwachsen'; 20 | 21 | end -------------------------------------------------------------------------------- /localization.en.lua: -------------------------------------------------------------------------------- 1 | -- Shaman 2 | QUICKHEAL_SPELL_LESSER_HEALING_WAVE = "Lesser Healing Wave"; 3 | QUICKHEAL_SPELL_HEALING_WAVE = "Healing Wave"; 4 | 5 | -- Priest 6 | QUICKHEAL_SPELL_LESSER_HEAL = 'Lesser Heal'; 7 | QUICKHEAL_SPELL_HEAL = 'Heal'; 8 | QUICKHEAL_SPELL_GREATER_HEAL = 'Greater Heal'; 9 | QUICKHEAL_SPELL_FLASH_HEAL = 'Flash Heal'; 10 | 11 | -- Paladin 12 | QUICKHEAL_SPELL_HOLY_LIGHT = 'Holy Light'; 13 | QUICKHEAL_SPELL_FLASH_OF_LIGHT = 'Flash of Light'; 14 | 15 | -- Druid 16 | QUICKHEAL_SPELL_HEALING_TOUCH = 'Healing Touch'; 17 | QUICKHEAL_SPELL_REGROWTH = 'Regrowth'; -------------------------------------------------------------------------------- /localization.fr.lua: -------------------------------------------------------------------------------- 1 | if (GetLocale() == "frFR") then 2 | 3 | -- Shaman 4 | QUICKHEAL_SPELL_LESSER_HEALING_WAVE = 'Vague de soins inf\195\169rieurs'; 5 | QUICKHEAL_SPELL_HEALING_WAVE = 'Vague de soins'; 6 | 7 | -- Priest 8 | QUICKHEAL_SPELL_LESSER_HEAL = 'Soins inf\195\169rieurs'; 9 | QUICKHEAL_SPELL_HEAL = 'Soins'; 10 | QUICKHEAL_SPELL_GREATER_HEAL = 'Soins sup\195\169rieurs'; 11 | QUICKHEAL_SPELL_FLASH_HEAL = 'Soins rapides'; 12 | 13 | -- Paladin 14 | QUICKHEAL_SPELL_HOLY_LIGHT = 'Lumi\195\168re sacr\195\169e'; 15 | QUICKHEAL_SPELL_FLASH_OF_LIGHT = 'Eclair lumineux'; 16 | 17 | -- Druid 18 | QUICKHEAL_SPELL_HEALING_TOUCH = 'Toucher gu\195\169risseur'; 19 | QUICKHEAL_SPELL_REGROWTH = 'R\195\169tablissement'; 20 | 21 | end --------------------------------------------------------------------------------