--[[ Healers Assist by Kiki - European Cho'gall TODO : GESTION/CONFIG : - Handle Talent/Armor bonus in _HA_GetDuration function - Handle pets (with config option to show them in emergency) - For the buf request, add an option to refuse requests when in combat (for some bufs, maybe configurable per buf with 3 options, deny:not-in-combat:always) GUI : - Possibility to configure color used in the HA_GUI_Process_Announce function - Possibility to only show hots I can cast in emergency list - Emergency list : Show in another color, people out of my healing range (CheckInteractDistance() is 30yards max, maybe show in yellow) - Show an estimated time of remaining regen time for a healer in Resting mode (dans le champ de casting) (new field EndRegenTime to compute in HA_Healers struct) - Show remaining effect time of a cooldown spell casted on a healer SORTING : [Quote from Freddy] Some feature ideas I heard about the emergency list: - Mark players which are in your party (different color of HP bar?) + priority option - Option to limit filter the estimated heal value that is added to the HP by casting time (e.g. only add heal which should have finished in <1.5sek) - Make an option which allowes it to set a fixed value of HP difference to show up the player in the list (not percentage) - Add something which put's the tanks at a higher priorty (class specific settings for casting tiem filter?) TODO WARRIOR PLUGIN : - Show for "YOU", current casts on yourself, with casting times (sorted by casting time) -> Prints a warning to yourself (use healing potion !! for example) if no spell is coming, and you have the agro, or gonna die soon ^^ BUGS : - Unit IDs changing during a SpellRequest (raid changes while the SpellRequest popup is shown -> possible wrong unitid) (NOT SURE IF THIS IS POSSIBLE) -> In _HA_SetRaiderVariables function (and GA callback) -> Callback a function in GUI to update all internal variables using IDs (like SpellRequest) - RaiderDeath not always correct (maybe check with hp==0 ?) - Hunter that Feign death will be counted as "dead/rezzed" - Druid shifting back to humain with full mana, will show empty mana (need to grab mp/mpmax after a shift) - Without GroupAnalyse, if I'm in Group Mode (not raid) all members are set as GroupLeader - Heal on an external player or NPC (out of raid), will trigger overheal status after spell complete -> Well... nevermind right now ChangeLog : - 2006/09/12 : 1.1 - Fixed incorrect healing debuff detection of Nefarius encounter - 2006/08/31 : - Fixed Item Bonuses not correct with latest BonusScanner addon installed (Scan not done, if no other addon ask BS for a scan) - 2006/08/30 : - Fixed minor xml error (minimap button not highlighting correctly (thanks Kagar) - Fixed initialization issue, if not using GroupAnalyse addon [Full ChangeLog in readme.txt file] ]] --------------- Shared Constantes --------------- HA_MODE_NONE = 0; HA_MODE_SOLO = 1 HA_MODE_GROUP = 2 HA_MODE_RAID = 3; --------------- Shared variables --------------- HA_VERSION = "1.1"; HA_PlayerName = nil; HA_CurrentTarget = nil; HA_LibramItem = nil; HA_Healers = {}; HA_Raiders = {}; HA_RaidersByID = {}; HA_CurrentGroupMode = HA_MODE_NONE; HA_MyselfHealer = nil; HA_MyselfRaider = nil; HA_CurrentZone = nil; HA_SpellCooldowns = {}; HA_AFK_Mode = false; HA_ClassesID = { ["DRUID"] = 1; ["HUNTER"] = 2; ["MAGE"] = 3; ["PRIEST"] = 4; ["ROGUE"] = 5; ["WARLOCK"] = 6; ["WARRIOR"] = 7; ["PALADIN"] = 8; ["SHAMAN"] = 8; }; --------------- Local Constantes --------------- local HA_COOLDOWN_SPELLS_UPDATE_DELAY = 60; -- Every 60sec local HA_VERSION_SEND_DELAY = 60; -- Every 60sec local HA_MAX_HEAL_UPDATES = 10; -- Keep max 10 incoming heal per raider local _HA_LastTimeEmergency = 0; --------------- Local variables --------------- local HA_NeedInit = true; local HA_CastingSpell = nil; local HA_HookActionName = nil; local HA_HookActionRank = 0; local HA_StopCommandSent = true; local HA_StopCommandLastTime = 0; local HA_CastingInstantSpell = nil; local HA_CastingInstantRank = 0; local HA_SpellTargetName = nil; local HA_PotentialSpellTargetName = nil; local HA_LastRegrowthRank = 0; local HA_MaxRanks = {}; local _HA_UseBonusScannerAddon = false; local _HA_BonusScanScheduled = false; local _HA_LastSendVersion = 0; --------------- Internal functions --------------- function HA_SetNewLock(printmsg) if(HA_Config.Lock) then HealersAssistMainFrame:EnableMouse("false"); if(printmsg) then HA_ChatPrint(HA_CHAT_LOCK_ON); end else HealersAssistMainFrame:EnableMouse("true"); if(printmsg) then HA_ChatPrint(HA_CHAT_LOCK_OFF); end end end local function _HA_GetMaxSpellRanks() local maxRanks = {}; local index = 1; while(1) do local spellName, spellRank = GetSpellName(index, BOOKTYPE_SPELL) if(not spellName) then break; elseif(HA_Spells[spellName]) then local _,_,ranknum = string.find(spellRank,HA_RANK.." (%d+)"); ranknum = tonumber(ranknum,10); if(not maxRanks[spellName]) then maxRanks[spellName] = ranknum; elseif(ranknum > maxRanks[spellName]) then maxRanks[spellName] = ranknum; end end index = index + 1 end return maxRanks; end local function _HA_SetRaiderAsHealer(raider) if(not raider.ishealer) then raider.ishealer = true; -- Call plugins for n,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_HEALER_JOINED,{raider.name}); end end end end local function _HA_UpdateOtherVariables(raider) raider.percent = floor(raider.hp / raider.hpmax * 100); raider.mppercent = floor(raider.mp / raider.mpmax * 100); local healer = HA_Healers[raider.name]; if(healer and (raider.class == "PRIEST" or raider.class == "DRUID" or raider.class == "PALADIN" or raider.class == "SHAMAN")) -- This is a healer then _HA_SetRaiderAsHealer(raider); healer.id = raider.id; healer.Raider = raider; end end local function _HA_SetMinimalRaiderVariables(raider,raidid,rank,subgrp) raider.id = raidid; HA_RaidersByID[raidid] = raider; raider.rank = rank; raider.subgrp = subgrp; end local function _HA_SetFullRaiderVariables(raider,raidid,rank,subgrp) _HA_SetMinimalRaiderVariables(raider,raidid,rank,subgrp); _,raider.class = UnitClass(raider.id); raider.classid = HA_ClassesID[raider.class]; raider.isdead = UnitIsDeadOrGhost(raider.id); raider.oldisdead = raider.isdead; raider.hp_real = UnitHealth(raider.id); raider.hpmax = UnitHealthMax(raider.id); raider.ischarmed = UnitIsCharmed(raider.id); raider.mp = UnitMana(raider.id); raider.mpmax = UnitManaMax(raider.id); raider.isconnected = UnitIsConnected(raider.id); raider.hp = raider.hp_real; raider.hp_estim = raider.hp_real; raider.ignore_next_wound = 0; raider.life_updates = {}; raider.heal_updates = {}; _HA_UpdateOtherVariables(raider); end local function _HA_CreateRaider(name) local infos = {}; infos.name = name; infos.count = 0; infos.estimates = {}; infos.estimate_ratio = 1; infos.overtime = {}; return infos; end local function _HA_AddHealer(pl_name) if(HA_Healers[pl_name] == nil) then local infos = {}; local raider = HA_Raiders[pl_name]; infos.Cooldown = {}; infos.Name = pl_name; infos.State = HA_STATE_NOTHING; infos.OverHealPercent = 0; infos.EstimateRatio = 1; infos.Estimate = 0; infos.StartTime = 0; infos.Raider = raider; if(raider) then local class = raider.class; infos.id = raider.id; if(class == "PRIEST" or class == "DRUID" or class == "PALADIN" or class == "SHAMAN") then HA_ChatDebug(HA_DEBUG_MEMBERS,"HA_CheckPlayerJoined : "..pl_name.." is in my Raiders list and is a Healer"); _HA_SetRaiderAsHealer(raider); else HA_ChatDebug(HA_DEBUG_MEMBERS,"HA_CheckPlayerJoined : "..pl_name.." is in my Raiders list but is not a Healer"); end else HA_ChatDebug(HA_DEBUG_MEMBERS,"HA_CheckPlayerJoined : "..pl_name.." is not in my Raiders list"); end HA_Healers[pl_name] = infos; if(pl_name == HA_PlayerName) then HA_MyselfHealer = infos; HA_ChatDebug(HA_DEBUG_GLOBAL,"Setting MYSELF as HEALER : "..tostring(infos)); HA_GUI_Process_Version(HA_PlayerName,HA_VERSION); end end end local function _HA_AddRaider(name,tab) HA_Raiders[name] = tab; HA_RaidersByID[tab.id] = tab; HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AddRaider : Adding raider "..name.." with id="..tab.id); if(name == HA_PlayerName) -- Myself then HA_MyselfRaider = tab; HA_ChatDebug(HA_DEBUG_GLOBAL,"Setting MYSELF as RAIDER : "..tostring(tab)); end -- Call plugins for _,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_RAIDER_JOINED,{name}); end end if(tab.class == "PRIEST" or tab.class == "DRUID" or tab.class == "PALADIN" or tab.class == "SHAMAN") then _HA_AddHealer(name); end end local function _HA_RemoveHealer(pl_name) if(HA_Healers[pl_name]) then -- Call plugins for n,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_HEALER_LEFT,{pl_name}); end end HA_Healers[pl_name] = nil; end end local function _HA_RemoveRaider(name) local raider = HA_Raiders[name]; if(raider) then HA_RaidersByID[raider.id] = nil; end HA_Raiders[name] = nil; HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_RemoveRaider : Removed raider "..name); -- Call plugins for _,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_RAIDER_LEFT,{name}); end end _HA_RemoveHealer(name); -- Check for healer removal end local function _HA_AnalyseGroupRaidMembers() local newmode = HA_MODE_NONE; local new_raiders = {}; HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : Start Analyse"); if(GetNumRaidMembers() ~= 0) -- In a raid then if(HA_CurrentGroupMode ~= HA_MODE_RAID) -- But was not then HealersAssistMainFrame:UnregisterEvent("PARTY_MEMBERS_CHANGED"); end HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : I'm in a RAID"); newmode = HA_MODE_RAID; local count = GetNumRaidMembers(); for i = 1, count do local name,rank,subgrp = GetRaidRosterInfo(i); if(name) then new_raiders[name] = _HA_CreateRaider(name); _HA_SetMinimalRaiderVariables(new_raiders[name],"raid"..i,rank,subgrp); end end else if(HA_CurrentGroupMode == HA_MODE_RAID) -- Was in a RAID then HealersAssistMainFrame:RegisterEvent("PARTY_MEMBERS_CHANGED"); end new_raiders[HA_PlayerName] = _HA_CreateRaider(HA_PlayerName); _HA_SetMinimalRaiderVariables(new_raiders[HA_PlayerName],"player",2,1); if(GetNumPartyMembers() ~= 0) -- In a group then HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : I'm in a GROUP"); newmode = HA_MODE_GROUP; for i = 1,4 do local name = UnitName("party"..i); if(name and (name ~= UNKNOWNOBJECT) and (name ~= UKNOWNBEING)) then new_raiders[name] = _HA_CreateRaider(name); _HA_SetMinimalRaiderVariables(new_raiders[name],"party"..i,2,1); end end else HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : I'm not grouped"); newmode = HA_MODE_SOLO; end end -- Update list of Raiders for n,tab in HA_Raiders -- Remove old raiders do if(new_raiders[n] == nil) -- No longer in the raid then _HA_RemoveRaider(n); end end HA_RaidersByID = {}; for n,tab in new_raiders -- Add new raiders do local raider = HA_Raiders[n]; if(raider == nil) -- New member then _HA_SetFullRaiderVariables(tab,tab.id,tab.rank,tab.subgrp); _HA_AddRaider(n,tab); else -- Was already here -> Update variables _HA_SetMinimalRaiderVariables(raider,tab.id,tab.rank,tab.subgrp); _HA_UpdateOtherVariables(raider); end end if(HA_Config.Auto and newmode ~= HA_CurrentGroupMode) then if(newmode == HA_MODE_SOLO) then HealersAssistMainFrame:Hide(); else HealersAssistMainFrame:Show(); end end HA_CurrentGroupMode = newmode; HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_AnalyseGroupRaidMembers : Analyse Completed"); end local function _HA_GroupAnalyseCallback(event,param) local infos; if(event == GA_EVENT_INFOS_CHANGED) then HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_GroupAnalyseCallback : Start members data update"); HA_RaidersByID = {}; debugprofilestart(); for name,member in GA_Members do infos = HA_Raiders[name]; if(infos) then infos.id = member.unitid; HA_RaidersByID[member.unitid] = infos; infos.rank = member.rank; infos.subgrp = member.subgrp; infos.ischarmed = member.ischarmed; infos.isconnected = member.isconnected; _HA_UpdateOtherVariables(infos); end end HA_PROFILE_AnalyseRaidersRoutine = debugprofilestop(); HA_ChatDebug(HA_DEBUG_RAIDERS,"_HA_GroupAnalyseCallback : Update Completed"); elseif(event == GA_EVENT_MEMBER_JOINED) then infos = _HA_CreateRaider(param); local member = GA_Members[param]; _HA_SetFullRaiderVariables(infos,member.unitid,member.rank,member.subgrp); _HA_AddRaider(param,infos); elseif(event == GA_EVENT_MEMBER_LEFT) then _HA_RemoveRaider(param); elseif(event == GA_EVENT_GROUP_MODE_CHANGED) then HA_ChatDebug(HA_DEBUG_GLOBAL,"_HA_GroupAnalyseCallback : Group mode changed to "..param); if(HA_Config.Auto) then if(param == GA_MODE_SOLO) then HealersAssistMainFrame:Hide(); else HealersAssistMainFrame:Show(); end end HA_CurrentGroupMode = param; end end local function _HA_CooldownUpdateScheduleRoutine() local serv_time = GetTime(); local tim = time(); -- Update my cooldown status if(HA_MyselfHealer) then for spell,infos in HA_SpellCooldowns do -- Update my cooldown local startTime,cd_value = GetSpellCooldown(infos.id,"spell"); local Cooldown = 0; if(startTime ~= 0 and cd_value > 30) -- (Prevent global cooldown and silence spells to interfere) then Cooldown = floor((startTime+cd_value) - serv_time); end -- Check for update send if(not HA_AFK_Mode and ((Cooldown == 0 and infos.last ~= 0) or -- Was in cooldown, but not anymore (Cooldown ~= 0 and infos.last == 0) or -- Was up, but in cooldown now (tim > (infos.lastSend+HA_COOLDOWN_SPELLS_UPDATE_DELAY)))) -- Delay expired then HA_GUI_Process_CooldownUpdate(HA_PlayerName,infos.ispell,Cooldown); HA_COM_CooldownUpdate(infos.ispell,Cooldown); infos.lastSend = tim; infos.last = Cooldown; end end -- Check for HA version send if(not HA_AFK_Mode and (tim > (_HA_LastSendVersion+HA_VERSION_SEND_DELAY))) -- Delay expired then HA_ChatDebug(HA_DEBUG_GLOBAL,"Too long since last Version send"); HA_COM_SendVersion(); _HA_LastSendVersion = tim; end end -- Update other cooldown status for name,tab in HA_Healers do for spell,infos in tab.Cooldown do if(infos.Remain ~= 0) -- Player having a Cooldown spell then infos.Remain = infos.Remain - (serv_time - infos.Start); if(infos.Remain <= 0) then infos.Remain = 0; end infos.Start = serv_time; end end end -- Re schedule HASystem_Schedule(1,_HA_CooldownUpdateScheduleRoutine); end local function _HA_SetDefaultConfig(param,value) if(HA_Config[param] == nil) then HA_Config[param] = value; end end local function _HA_ScheduledInit() HA_InitializeCooldownSpells(); HASystem_Schedule(1,_HA_CooldownUpdateScheduleRoutine); -- Special cooldown routine HASystem_Schedule(1,HA_OvertimeScheduleRoutine); -- Special overtime routine HASystem_Schedule(1,HA_StatusScheduleRoutine); -- Special status routine end local function HA_StartupInitVars() local playerName = UnitName("player"); if((playerName) and (playerName ~= UNKNOWNOBJECT) and (playerName ~= UKNOWNBEING)) then -- Initialize Toon specific stuff HA_PlayerName = playerName; HA_NeedInit = false; _HA_SetDefaultConfig("MinEmergencyPercent",90); _HA_SetDefaultConfig("KeepValue",3); _HA_SetDefaultConfig("ShowInstants",false); _HA_SetDefaultConfig("ShowHoT",false); _HA_SetDefaultConfig("ButtonPosition",190); _HA_SetDefaultConfig("EmergencyGroups",{ true,true,true,true,true,true,true,true }); _HA_SetDefaultConfig("EmergencyClasses",{ true,true,true,true,true,true,true,true }); _HA_SetDefaultConfig("HealersClasses",{ true,true,true,true,true,true,true,true }); _HA_SetDefaultConfig("AllowSpellRequest",{}); _HA_SetDefaultConfig("HealersLines",10); _HA_SetDefaultConfig("EmergLines",5); _HA_SetDefaultConfig("Scale",100); _HA_SetDefaultConfig("Alpha",100); _HA_SetDefaultConfig("BackdropAlpha",100); _HA_SetDefaultConfig("GUIRefresh",0.1); _HA_SetDefaultConfig("Plugins",{}); _HA_SetDefaultConfig("PluginOrder",{}); _HA_SetDefaultConfig("PluginAuto",{}); _HA_SetDefaultConfig("UseEstimatedHealth",true); HA_CheckLoadPlugins(); HA_SetNewLock(false); HASystem_Schedule(5,_HA_ScheduledInit); HA_MoveMinimapButton(); -- Setup Default function if(CastParty_OnClickByUnit and type(CastParty_OnClickByUnit) == "function") -- CastParty then HA_CustomOnClickFunction = CastParty_OnClickByUnit; end if(WatchDog_OnClick and type(WatchDog_OnClick) == "function") -- WatchDog then HA_CustomOnClickFunction = WatchDog_OnClick; end if(JC_CatchKeyBinding and type(JC_CatchKeyBinding) == "function") -- JustClick then HA_CustomOnClickFunction = JC_CatchKeyBinding; end end end function HA_PlayerHasBuf(texture) for i=1,16 do local t = UnitBuff("player",i); if(t == nil) then break; end; if(t == texture) then return true; end end return false; end local function HA_BuildRankString(ranknum) local rankstr = ""; if(ranknum and ranknum > 1) then for i=1,ranknum do rankstr = rankstr.."I"; end end return rankstr; end local function HA_Commands(command) local i,j, cmd, param = string.find(command, "^([^ ]+) (.+)$"); if(not cmd) then cmd = command; end if(not cmd) then cmd = ""; end if(not param) then param = ""; end if((cmd == "") or (cmd == "help")) then local lock = "off"; if(HA_Config.Lock) then lock = "on"; end local auto = "off"; if(HA_Config.Auto) then auto = "on"; end HA_ChatPrint("Usage:"); HA_ChatPrint(" |cffffffff/ha show|r - "..HA_CHAT_HELP_SHOW); HA_ChatPrint(" |cffffffff/ha lock (on|off)|r |cff2040ff["..lock.."]|r - "..HA_CHAT_HELP_LOCK); HA_ChatPrint(" |cffffffff/ha auto (on|off)|r |cff2040ff["..auto.."]|r - "..HA_CHAT_HELP_AUTO); HA_ChatPrint(" |cffffffff/ha versions |r - "..HA_CHAT_HELP_VERSIONS); HA_ChatPrint(" |cffffffff/ha msg |r - "..HA_CHAT_HELP_MSG); HA_ChatPrint(" ---- DEBUG ----"); HA_ChatPrint(" |cffffffff/ha debug (on|off)|r - "..HA_CHAT_HELP_DEBUG); elseif(cmd == "show") then HealersAssistMainFrame:Show(); elseif(cmd == "lock") then if(param == "off") then HA_Config.Lock = nil; elseif(param == "on") then HA_Config.Lock = true; else HA_ChatPrint(HA_CHAT_CMD_PARAM_ERROR.."lock"); return; end HA_SetNewLock(true); elseif(cmd == "auto") then if(param == "off") then HA_Config.Auto = nil; elseif(param == "on") then HA_Config.Auto = true; else HA_ChatPrint(HA_CHAT_CMD_PARAM_ERROR.."auto"); return; end elseif(cmd == "debug") then if(param == "off") then HA_DBG_SetDebugMode(0); elseif(param == "on") then HA_DBG_SetDebugMode(1) else HA_ChatPrint(HA_CHAT_CMD_PARAM_ERROR.."debug"); return; end elseif(cmd == "versions") then HA_ShowVersions(); elseif(cmd == "msg" and param and param ~= "") then HA_GUI_Process_Announce(HA_PlayerName,param); HA_COM_Announce(param); else HA_ChatPrint(HA_CHAT_CMD_UNKNOWN); end end function CheckSpellTarget(FunctionHook) if(SpellIsTargeting()) then HA_PotentialSpellTargetName = UnitName("mouseover"); HA_ChatDebug(HA_DEBUG_ACTIONS,FunctionHook.." : PotentialTarget="..tostring(HA_PotentialSpellTargetName)); elseif(UnitIsFriend("player","target")) then HA_SpellTargetName = UnitName("target"); HA_ChatDebug(HA_DEBUG_ACTIONS,FunctionHook.." : Target="..tostring(HA_SpellTargetName)); end end --------------- Hooked functions --------------- function HealersAssist_SpellTargetUnit(unit) if(SpellIsTargeting() or not HA_SpellTargetName) then HA_SpellTargetName = UnitName(unit); HA_ChatDebug(HA_DEBUG_ACTIONS,"SpellTargetUnit : Target="..tostring(HA_SpellTargetName)); end return HA_Old_SpellTargetUnit(unit); end HA_Old_SpellTargetUnit = SpellTargetUnit; SpellTargetUnit = HealersAssist_SpellTargetUnit; -- function HealersAssist_CastShapeshiftForm(index) local ret_val = HA_Old_CastShapeshiftForm(index); HA_ChatDebug(HA_DEBUG_ACTIONS,"CastShapeshiftForm : Index="..tostring(index)); HA_CastingInstantSpell = nil; HA_CastingInstantRank = 0; return ret_val; end HA_Old_CastShapeshiftForm = CastShapeshiftForm; CastShapeshiftForm = HealersAssist_CastShapeshiftForm; -- function HealersAssist_UseAction(id, val, onSelf) HealersAssistTooltip:SetOwner(UIParent, "ANCHOR_NONE"); HealersAssistTooltip:ClearLines(); HealersAssistTooltip:SetAction(id); local spellName = tostring(HealersAssistTooltipTextLeft1:GetText()); local rank = HealersAssistTooltipTextRight1:GetText(); local rankstr = ""; HealersAssistTooltip:Hide(); local ret_val = HA_Old_UseAction(id,val,onSelf); if(rank and rank ~= "") then local _,_,ranknum = string.find(rank,HA_RANK.." (%d+)"); rank = tonumber(ranknum,10); elseif(HA_MaxRanks[spellName]) then rank = HA_MaxRanks[spellName]; else rank = 1; end -- Call plugins for n,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_USE_ACTION,{id,val,onSelf,spellName,rank}); end end if(HA_Spells[spellName]) then CheckSpellTarget("UseAction"); HA_ChatDebug(HA_DEBUG_ACTIONS,"UseAction : SpellName="..tostring(spellName).." Rank="..tostring(rank)); HA_HookActionName = spellName; HA_HookActionRank = rank; end if(HA_InstantSpells[spellName]) then CheckSpellTarget("UseAction"); HA_CastingInstantSpell = spellName; HA_CastingInstantRank = rank; end -- If onSelf if(onSelf and onSelf == 1) then HA_SpellTargetName = HA_PlayerName; HA_ChatDebug(HA_DEBUG_ACTIONS,"UseAction on Self : Target="..tostring(HA_SpellTargetName)); end return ret_val; end HA_Old_UseAction = UseAction; UseAction = HealersAssist_UseAction; -- function HealersAssist_CastSpell(id,spellBook) local ret_val = HealersAssist_Old_CastSpell(id,spellBook); local spellName,rank = GetSpellName(id,spellBook); if(rank and rank ~= "") then local _,_,ranknum = string.find(rank,HA_RANK.." (%d+)"); rank = tonumber(ranknum,10); elseif(HA_MaxRanks[spellName]) then rank = HA_MaxRanks[spellName]; else rank = 1; end -- Call plugins for n,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_CAST_SPELL,{id,spellBook,spellName,rank}); end end if(HA_Spells[spellName]) then CheckSpellTarget("CastSpell"); HA_ChatDebug(HA_DEBUG_ACTIONS,"CastSpell : SpellName="..tostring(spellName).." Rank="..tostring(rank)); HA_HookActionName = spellName; HA_HookActionRank = rank; end if(HA_InstantSpells[spellName]) then CheckSpellTarget("CastSpell"); HA_CastingInstantSpell = spellName; HA_CastingInstantRank = rank; end return ret_val; end HealersAssist_Old_CastSpell = CastSpell; CastSpell = HealersAssist_CastSpell; function HealersAssist_CastSpellByName(spell, onSelf) local ret_val = HealersAssist_Old_CastSpellByName(spell, onSelf); if(spell) then local _,_,spellName,rank = string.find(spell,"(.+)%("..HA_RANK.." (%d+)%)"); if(spellName == nil) -- Spell without rank then spellName = spell; end if(rank) then rank = tonumber(rank,10); elseif(HA_MaxRanks[spellName]) then rank = HA_MaxRanks[spellName]; else rank = 1; end -- Call plugins for n,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_CAST_SPELL_BY_NAME,{spell,onSelf,spellName,rank}); end end if(HA_Spells[spellName]) then CheckSpellTarget("CastSpellByName"); HA_ChatDebug(HA_DEBUG_ACTIONS,"CastSpellByName : SpellName="..tostring(spellName).." Rank="..tostring(rank)); HA_HookActionName = spellName; HA_HookActionRank = rank; end if(HA_InstantSpells[spellName]) then CheckSpellTarget("CastSpellByName"); HA_CastingInstantSpell = spellName; HA_CastingInstantRank = rank; end end -- If onSelf if(onSelf and onSelf == 1) then HA_SpellTargetName = HA_PlayerName; HA_ChatDebug(HA_DEBUG_ACTIONS,"CastSpellByName on Self : Target="..tostring(HA_SpellTargetName)); end return ret_val; end HealersAssist_Old_CastSpellByName = CastSpellByName; CastSpellByName = HealersAssist_CastSpellByName; -- local function _HA_GetISpell(SpellName) if(HA_Spells[SpellName]) then return HA_Spells[SpellName].iname; elseif(HA_InstantSpells[SpellName]) then return HA_InstantSpells[SpellName].iname; end return nil; end function HA_UnitHasBlessingOfLight(TargetName) local id = nil; if(TargetName) then if(HA_Raiders[TargetName] and HA_Raiders[TargetName].id) then id = HA_Raiders[TargetName].id; elseif(UnitName("target") == TargetName) then id = "target"; end end if(id) then for i=1,16 do local t = UnitBuff(id,i,1); if(t == nil) then break; end; if(t == HA_BLESSING_OF_LIGHT_TEXTURE or t == HA_GREATER_BLESSING_OF_LIGHT_TEXTURE) then return true; end end end return false; end local function _HA_GetEstimated(ISpell,IHookName,HookRank,TargetName,CastTime) local estimated = 0; local spiritadd = 0; local willcrit = false; if(IHookName and HookRank and ISpell == IHookName and HA_SpellRanks[ISpell] and HA_SpellRanks[ISpell][HookRank]) then local infos = HA_SpellRanks[ISpell][HookRank]; estimated = infos.base; -- Add Talent values local tinfos = HA_SpellTalents[ISpell]; if(tinfos) then if(tinfos.ratios) -- Ratio then --[[ -- Old code (if talent bonus are applied one after another, and not all at once) for _,tid in tinfos.ratios do if(HA_Talents[tid] and HA_Talents[tid].rank) -- Talent found, and player's rank set then if(HA_Talents[tid].rankratio) -- Direct ratios then estimated = estimated * HA_Talents[tid].rankratio[HA_Talents[tid].rank]; elseif(HA_Talents[tid].spiritratio) -- Spirit based ratios then spiritadd = HA_Talents[tid].spiritratio[HA_Talents[tid].rank] * UnitStat("player",5); end end end ]] local total_percent = 1.0; for _,tid in tinfos.ratios do if(HA_Talents[tid] and HA_Talents[tid].rank) -- Talent found, and player's rank set then if(HA_Talents[tid].rankratio) -- Direct ratios then total_percent = total_percent + HA_Talents[tid].rankratio[HA_Talents[tid].rank]; elseif(HA_Talents[tid].spiritratio) -- Spirit based ratios then spiritadd = HA_Talents[tid].spiritratio[HA_Talents[tid].rank] * UnitStat("player",5); end end end estimated = estimated * total_percent; end end -- Add +heal values local itembonus; if(_HA_UseBonusScannerAddon) then itembonus = tonumber(BonusScanner.bonuses["HEAL"]); else itembonus = tonumber(HA_BonusScanner_bonuses["HEAL"]); end if(itembonus) then if(HA_LibramItem) -- Check for librams then if(ISpell == HA_SPELL_FLASH_OF_LIGHT and HA_LibramItem == 23201) -- Libram of Divinity then itembonus = itembonus + 53; elseif(ISpell == HA_SPELL_FLASH_OF_LIGHT and HA_LibramItem == 23006) -- Libram of Light then itembonus = itembonus + 83; elseif(ISpell == HA_SPELL_LESSER_HEALING_WAVE and HA_LibramItem == 23200) -- Totem of Sustaining then itembonus = itembonus + 53; elseif(ISpell == HA_SPELL_LESSER_HEALING_WAVE and HA_LibramItem == 22396) -- Totem of Life then itembonus = itembonus + 80; elseif(ISpell == HA_SPELL_REJUVENATION and HA_LibramItem == 22398) -- Idol of Rejuvenation then itembonus = itembonus + 62.5; -- Real value is 50, but this value will be 80% applied below, but the game does not apply the 80% rule on the idol end end estimated = estimated + itembonus * infos.castratio * infos.levelratio; end -- Add + Blessing of light value if(HA_SpellTalents[ISpell] and HA_SpellTalents[ISpell].blessing) -- Check for Blessing of light then if(HA_UnitHasBlessingOfLight(TargetName)) then estimated = estimated + HA_SpellTalents[ISpell].blessing; end end -- Check for WillCrit local index = 0; local texture; while(GetPlayerBuffTexture(index)) do texture = GetPlayerBuffTexture(index); applications = GetPlayerBuffApplications(index); if(texture == HA_DIVINE_FAVOR_TEXTURE) then willcrit = true; elseif(texture == HA_UNSTABLE_POWER_TEXTURE) then estimated = estimated + (70 * applications * infos.castratio * infos.levelratio); elseif(texture == HA_HEALING_OF_THE_AGES_TEXTURE) then estimated = estimated + (350 * infos.castratio * infos.levelratio); end index = index + 1; end if(willcrit) then estimated = estimated * 1.5; end end return floor(estimated+spiritadd),willcrit; end local function _HA_GetDuration(ISpell) local duration = 0; if(HA_SpellOvertime[ISpell]) then local infos = HA_SpellOvertime[ISpell]; duration = infos.duration; -- Add Talent values for duration -- Add armor values for duration end return duration; end local function _HA_DoStop(resetSpell,msg) HA_ChatDebug(HA_DEBUG_ACTIONS,"HA_DoStop : Event="..msg.." CmdSend="..tostring(HA_StopCommandSent).." LastTime="..HA_StopCommandLastTime.." CurTime="..HA_CurrentTime); if(not HA_StopCommandSent and (HA_StopCommandLastTime+0.10 < HA_CurrentTime)) then HA_GUI_Process_SpellStop(HA_PlayerName); HA_COM_SpellStop(); HA_StopCommandSent = true; HA_StopCommandLastTime = HA_CurrentTime; if(resetSpell) then HA_CastingSpell = nil; end end end local function _HA_RegExpr_CallBack(event, Source, Target, SpellName, Value, Crit) -- Check for HoT Ticks if(event == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" or event == "CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS" or event == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS") then local ispell = HA_HotSpells[SpellName]; if(ispell) then HA_ChatDebug(HA_DEBUG_SPELLS,"Callback HOT : Source="..tostring(Source).." Target="..tostring(Target).." Spell="..tostring(SpellName).." ("..ispell..") Value="..tostring(Value)); HA_GUI_Process_SpellHotTick(Source,ispell,Target,Value); HA_COM_SpellHotTick(ispell,Target,Value); end else -- Ok, check for direct heal spells local spell = HA_Spells[SpellName]; local instaspell = false; if(spell == nil) -- Not casted, check instant then spell = HA_InstantSpells[SpellName]; instaspell = true; end if(spell) then if(event == "CHAT_MSG_SPELL_SELF_BUFF") -- Heal from self then local ispell = _HA_GetISpell(SpellName); if(not instaspell) then _HA_DoStop(true,"HIT"); end HA_GUI_Process_SpellHit(Source,ispell,Target,Value,Crit); HA_COM_SpellComplete(ispell,Target,Value,Crit); -- Special HA_SPELL_REGROWTH case if(ispell == HA_SPELL_REGROWTH) then ispell = HA_SPELL_REGROWTH_HOT; local estimated = _HA_GetEstimated(ispell,ispell,HA_LastRegrowthRank,Target,0); local duration = _HA_GetDuration(ispell); if(duration ~= 0) then HA_GUI_Process_SpellOvertime(HA_PlayerName,ispell,Target,duration,estimated,HA_LastRegrowthRank); HA_COM_SpellOvertime(ispell,Target,duration,estimated,HA_LastRegrowthRank); end end else -- Heal from other if(not spell.group) then local ispell = _HA_GetISpell(SpellName); HA_GUI_Process_SpellHit(Source,ispell,Target,Value,Crit); end end end end end local function _HA_CheckRegenModeFromCast() local healer = HA_MyselfHealer; if(healer and healer.State == HA_STATE_RESTING) then HA_SetRegenMode(false); end end local function _HA_ScanScheduleRoutine() HA_ChatDebug(HA_DEBUG_GLOBAL,"_HA_ScanScheduleRoutine : Scanning bonuses"); -- Check for Items bonuses if(not _HA_UseBonusScannerAddon) then HA_ChatDebug(HA_DEBUG_GLOBAL,"_HA_ScanScheduleRoutine : Not using BonusScanner addon - Scanning equipment !"); HA_BonusScanner_ScanAll(); end -- Check for Relic local libramlink = GetInventoryItemLink("player",18); if(libramlink) then for iItemId, iEnchantId, iPropertieId, sItemName in string.gfind(libramlink, "|c%x+|Hitem:(%d+):(%d+):(%d+):%d+|h%[(.-)%]|h|r") do HA_LibramItem = tonumber(iItemId); end else HA_LibramItem = nil; end -- Ok done _HA_BonusScanScheduled = false; end local function _HA_GetReasonCode(Reason) for i,n in HA_FailReasons do if(Reason == n) then return i,""; end end return 0,Reason; end local function _HA_CheckForBonusScan() if(not _HA_BonusScanScheduled) then _HA_BonusScanScheduled = true; HASystem_Schedule(2,_HA_ScanScheduleRoutine); -- Schedule scan in 2 sec end end local function _HA_RegisterEvents() HealersAssistMainFrame:RegisterEvent("UNIT_INVENTORY_CHANGED"); end local function _HA_UnregisterEvents() HealersAssistMainFrame:UnregisterEvent("UNIT_INVENTORY_CHANGED"); end local function _HA_CheckLifeUpdates(raider,new_hp) if(HA_Config.UseEstimatedHealth) then local estim = raider.hp_real; local updates = raider.life_updates; --[[if(updates[1] == nil and new_hp == estim) then HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_HEALTH Diff OK : NewHP="..new_hp.." - Got no updates"); end]] while(updates[1]) do estim = estim + updates[1].value; table.remove(updates,1); if(estim == new_hp) then --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_HEALTH Diff Matching ("..table.getn(updates).." remaning updates) : NewHP="..new_hp); break; end end if(estim > raider.hpmax) then estim = raider.hpmax; end if(new_hp ~= estim) then --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_HEALTH Diff Error : NewHP="..new_hp.." Ignoring next Wound Event"); raider.ignore_next_wound = HA_CurrentTime; end end raider.hp_estim = new_hp; end -- Currently don't handle "STOP because of failure" for casted request-spell local function _HA_CheckSpellRequestSuccess(spell,instant) if(HA_SpellRequest and HA_SpellRequest.spell == spell) then HA_ChatDebug(HA_DEBUG_ACTIONS,"_HA_CheckSpellRequestSuccess : SpellRequest successfully casted (or stopped for casted ones) : "..tostring(spell).." : "..tostring(instant)); HA_SpellRequest = nil; StaticPopup_Hide("HA_REQUEST_FOR_SPELL"); end end local function _HA_CheckSpellRequestFailed(spell,reason) if(HA_SpellRequest and HA_SpellRequest.spell == spell) then HA_ChatDebug(HA_DEBUG_ACTIONS,"_HA_CheckSpellRequestFailed : SpellRequest failed for "..tostring(spell).." : "..tostring(reason)); HA_SpellRequest.failure = reason; end end local function _HA_ProcessEvent_SpellCastStart() _HA_CheckRegenModeFromCast(); if(HA_Spells[arg1]) then _HA_DoStop(false,"START"); HA_CastingSpell = arg1; HA_StopCommandSent = false; -- Set *waiting for stop* state HA_StopCommandLastTime = HA_CurrentTime; -- And reset time if(HA_SpellTargetName == nil) then HA_SpellTargetName = HA_PotentialSpellTargetName; HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_START : Target="..tostring(HA_SpellTargetName)); end local casttime = tonumber(arg2,10) local target = HA_SpellTargetName; local ispell = _HA_GetISpell(HA_CastingSpell); local ihook = _HA_GetISpell(HA_HookActionName); local estimated,willcrit = _HA_GetEstimated(ispell,ihook,HA_HookActionRank,target,casttime); HA_GUI_Process_SpellStart(HA_PlayerName,ispell,target,casttime,estimated,willcrit,HA_HookActionRank); HA_COM_SpellStart(ispell,target,casttime,estimated,willcrit,HA_HookActionRank); -- Special HA_SPELL_REGROWTH case if(ispell == HA_SPELL_REGROWTH) then HA_LastRegrowthRank = HA_HookActionRank; end end HA_SpellTargetName = nil; end local function _HA_ProcessEvent_SpellCastStop() _HA_CheckRegenModeFromCast(); if(HA_CastingInstantSpell) -- An instant spell ? then if(HA_SpellTargetName == nil) then HA_SpellTargetName = HA_PotentialSpellTargetName; HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_STOP (INSTANT SPELL) : Target="..tostring(HA_SpellTargetName)); end local target = HA_SpellTargetName; local ispell = _HA_GetISpell(HA_CastingInstantSpell); if(HA_InstantSpells[HA_CastingInstantSpell].cooldown) -- A cooldown instant spell then HA_GUI_Process_SpellCooldown(HA_PlayerName,ispell,target); HA_COM_SpellCooldown(ispell,target); elseif(HA_InstantSpells[HA_CastingInstantSpell].nonheal) -- A non-heal spell then HA_GUI_Process_SpellInstant(HA_PlayerName,ispell,target,HA_CastingInstantRank); HA_COM_SpellInstant(ispell,target,HA_CastingInstantRank); else local estimated = _HA_GetEstimated(ispell,ispell,HA_CastingInstantRank,target,0); local duration = _HA_GetDuration(ispell); if(duration ~= 0) then HA_GUI_Process_SpellOvertime(HA_PlayerName,ispell,target,duration,estimated,HA_CastingInstantRank); HA_COM_SpellOvertime(ispell,target,duration,estimated,HA_CastingInstantRank); end end _HA_CheckSpellRequestSuccess(HA_CastingInstantSpell,true); HA_CastingInstantSpell = nil; elseif(HA_CastingSpell) then HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_STOP : Spell="..tostring(HA_CastingSpell)); _HA_CheckSpellRequestSuccess(HA_CastingSpell,false); _HA_DoStop(false,"STOP"); end HA_SpellTargetName = nil; end --------------- Shared functions --------------- function HA_GetSpellIDFromName(spellName,spellRank) local i = 1; local name,rank = GetSpellName(i,BOOKTYPE_SPELL); local searched; if(spellRank) -- With rank then if(type(spellRank) == "number") -- Number then searched = spellName.."("..HA_RANK.." "..tostring(spellRank)..")"; else -- String searched = spellName.."("..spellRank..")"; end else -- No rank searched = spellName; end while(name) do local current; if(rank ~= "") -- With rank then current = name.."("..rank..")"; else -- No rank current = name; end if(searched == current) -- Found then return i; end i = i + 1; name,rank = GetSpellName(i,BOOKTYPE_SPELL); end return 0; end local function HA_CheckForEmergencyListUpdate() -- Update Emergency every 30 msec if(HA_CurrentTime > (_HA_LastTimeEmergency+0.030)) then HA_UpdateListEmergency(); _HA_LastTimeEmergency = HA_CurrentTime; end end --------------- XML functions --------------- function HA_OnEvent() if(event == "VARIABLES_LOADED") then if(HA_NeedInit) then HA_StartupInitVars(); end return; elseif(event == "PLAYER_LOGIN") then HA_MaxRanks = _HA_GetMaxSpellRanks(); elseif(event == "PLAYER_ENTERING_WORLD") then _HA_AnalyseGroupRaidMembers(); -- Force group analyse now HA_CurrentZone = GetRealZoneText(); -- Get it here too, because when you /reloadui the ZONE_CHANGED is not called - Thanks blizzard _HA_RegisterEvents(); _HA_CheckForBonusScan(); HA_CurrentTarget = nil if(HA_Config.Auto and HA_CurrentGroupMode ~= HA_MODE_SOLO) then HealersAssistMainFrame:Show(); end elseif(event == "PLAYER_LEAVING_WORLD") then _HA_UnregisterEvents(); elseif(event == "ZONE_CHANGED_NEW_AREA") then HA_CurrentZone = GetRealZoneText(); end if(event == "UNIT_COMBAT") then if(HA_Config.UseEstimatedHealth) then local raider = HA_RaidersByID[arg1]; if(raider and arg1 ~= "target") then if(arg2 == "WOUND") then if(HA_CurrentTime > (raider.ignore_next_wound+0.5)) then --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : DEGATS : "..tostring(arg4).." : UnitHealth="..UnitHealth(arg1).." hp="..raider.hp_real); raider.hp_estim = raider.hp_estim - arg4; if(raider.hp_estim < 0) then raider.hp_estim = 0; end tinsert(raider.life_updates,{ value=-arg4; stamp=HA_CurrentTime }); --[[else HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : DEGATS : "..tostring(arg4).." : UnitHealth="..UnitHealth(arg1).." hp="..raider.hp_real.." - IGNORED");]] end raider.ignore_next_wound = 0; elseif(arg2 == "HEAL") then --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : SOINS : "..tostring(arg4)); tinsert(raider.heal_updates,{ value=arg4, oldhp=raider.hp_estim }); -- Store health before applying heal raider.hp_estim = raider.hp_estim + arg4; if(raider.hp_estim > raider.hpmax) then raider.hp_estim = raider.hpmax; end tinsert(raider.life_updates,{ value=arg4; stamp=HA_CurrentTime }); if(getn(raider.heal_updates) > HA_MAX_HEAL_UPDATES) -- Remove a heal update, if too many in the array then table.remove(raider.heal_updates,1); end else --HA_ChatDebug(HA_DEBUG_GLOBAL,"UNIT_COMBAT '"..tostring(arg1).."' ("..tostring(raider.name)..") : "..tostring(arg2).." : "..tostring(arg4).." : "..tostring(arg3)); end raider.hp = raider.hp_estim; -- Update HP value end end return; -- Health/Mana events elseif(event == "UNIT_HEALTH") then local raider = HA_RaidersByID[arg1]; if(raider) then local new_hp = UnitHealth(arg1); if(arg1 ~= "target") then _HA_CheckLifeUpdates(raider,new_hp); -- Parse "updates" array, and update hp_estim to 'new_hp' value end raider.hp_real = new_hp; raider.hp = new_hp; raider.percent = floor(raider.hp / raider.hpmax * 100); HA_CheckForEmergencyListUpdate(); end return; elseif(event == "UNIT_MAXHEALTH") then local raider = HA_RaidersByID[arg1]; if(raider) then raider.hpmax = UnitHealthMax(arg1); raider.percent = floor(raider.hp / raider.hpmax * 100); HA_CheckForEmergencyListUpdate(); end return; elseif(event == "UNIT_MANA") then local raider = HA_RaidersByID[arg1]; if(raider and raider.ishealer) then raider.mp = UnitMana(arg1); raider.mppercent = floor(raider.mp / raider.mpmax * 100); end return; elseif(event == "UNIT_MAXMANA") then local raider = HA_RaidersByID[arg1]; if(raider and raider.ishealer) then raider.mpmax = UnitManaMax(arg1); raider.mppercent = floor(raider.mp / raider.mpmax * 100); end return; elseif(event == "PLAYER_AURAS_CHANGED") then if(HA_MyselfHealer and not HA_RaiderInfused(HA_PlayerName) and HA_PlayerHasBuf(HA_POWER_INFUSION_TEXTURE)) then HA_GUI_Process_GotPowerInfusion(HA_PlayerName); HA_COM_GotPowerInfusion(); end return; end -- Group events if(event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE") then HA_ChatDebug(HA_DEBUG_RAIDERS,event.." : Party members changed. Analysing Raiders..."); debugprofilestart(); _HA_AnalyseGroupRaidMembers(); HA_PROFILE_AnalyseRaidersRoutine = debugprofilestop(); return; end -- Inventory events if(HA_NeedInit == false and event == "UNIT_INVENTORY_CHANGED") then _HA_CheckForBonusScan(); return; end -- Mouse over event - Get target if(event == "UPDATE_MOUSEOVER_UNIT") then if(SpellIsTargeting() and UnitIsFriend("player", "mouseover")) then HA_PotentialSpellTargetName = UnitName("mouseover"); HA_ChatDebug(HA_DEBUG_ACTIONS,"MouseOverUpdate : PotentialTarget="..tostring(HA_PotentialSpellTargetName)); end return; end -- Target changed if(event == "PLAYER_TARGET_CHANGED" ) then if(UnitExists("target") and UnitIsFriend("player","target")) -- Add target if don't exist then local name = UnitName("target"); local raider = HA_Raiders[name]; if(raider == nil) -- Don't exist, add it as subgrp 0 then raider = _HA_CreateRaider(name); HA_Raiders[name] = raider; _HA_SetFullRaiderVariables(raider,"target",0,0); HA_UpdateListEmergency(); -- Call plugins for n,pl in HA_ActivePlugins do if(pl.OnEvent) then pl.OnEvent(HA_EVENT_RAIDER_JOINED,{name}); end end end HA_RaidersByID["target"] = raider; HA_CurrentTarget = name; else -- No valid target HA_RaidersByID["target"] = nil; HA_CurrentTarget = nil end HA_PotentialSpellTargetName = HA_CurrentTarget; return; end -- System events if(event == "CHAT_MSG_SYSTEM") then if(arg1 == MARKED_AFK or string.find(arg1,string.format(MARKED_AFK_MESSAGE,".*"))) then HA_AFK_Mode = true; end if(arg1 == CLEARED_AFK) then HA_AFK_Mode = false; end elseif(event == "CHAT_MSG_ADDON" and (arg3 == "RAID" or arg3 == "PARTY") and arg1 == HA_ADDON_PREFIX) then HA_COM_ParseMessage(arg4,arg2); -- Spell Events elseif(event == "SPELLCAST_START") then _HA_ProcessEvent_SpellCastStart(); elseif(event == "SPELLCAST_STOP") then _HA_ProcessEvent_SpellCastStop(); elseif(event == "SPELLCAST_DELAYED") then if(HA_CastingSpell) then HA_GUI_Process_SpellDelayed(HA_PlayerName,arg1); HA_COM_SpellDelayed(arg1); end elseif(event == "CHAT_MSG_SPELL_FAILED_LOCALPLAYER") then local _,_,spell,reason = string.find(arg1,HA_PARSE_SPELL_FAILED_REASON); if(reason == nil) then reason = "???"; end; HA_ChatDebug(HA_DEBUG_ACTIONS,"SPELLCAST_FAILED : ParsedSpell="..tostring(spell).." Casting="..tostring(HA_CastingSpell).." Instant="..tostring(HA_CastingInstantSpell).." Reason="..reason); if(reason ~= SPELL_FAILED_SPELL_IN_PROGRESS and HA_CastingSpell and HA_CastingSpell == spell) then local ispell = _HA_GetISpell(HA_CastingSpell); local ireason,sreason = _HA_GetReasonCode(reason); _HA_DoStop(true,"FAILED"); if(HA_StopCommandSent) then HA_GUI_Process_SpellFailed(HA_PlayerName,ispell,ireason,sreason); HA_COM_SpellFailed(ispell,ireason,sreason); end end _HA_CheckSpellRequestFailed(spell,reason); HA_CastingInstantSpell = nil; else HA_findEventMatch(event,_HA_RegExpr_CallBack); -- Check remaing events end end --------------- Exported functions --------------- --[[ Function HA_QuerySpell - SpellName : String - Spell to request. - PlayerName : String - Player to request spell to. Sends a request for spell to this player. Returns true is spell was found, ready, and queried. False otherwise. ]] function HA_QuerySpell(SpellName,PlayerName) local healer = HA_Healers[PlayerName]; if(healer) then local spell = healer.Cooldown[SpellName]; if(spell and spell.Remain == 0) then HA_COM_SpellRequest(spell.ispell,PlayerName); return true; else if(spell == nil) then HA_ChatPrint("HA_QuerySpell Failed : "..SpellName.." is not in "..PlayerName.."'s list of spells"); else HA_ChatPrint("HA_QuerySpell Failed : "..SpellName.." is not ready ("..spell.Remain.." sec remaining)"); end end else HA_ChatPrint("HA_QuerySpell Failed : "..PlayerName.." is not in my group/raid"); end return false; end --[[ Function HA_ShowVersions Prints HA's version for all Healers in your group/raid (if available). ]] function HA_ShowVersions() for name,raider in HA_Raiders do if(raider.Version) then HA_ChatPrint(name.." : "..raider.Version); end end end --------------- Init functions --------------- local function _HA_CheckSpellCooldown(spell,ispell,rank) local id = HA_GetSpellIDFromName(spell,rank); if(id == 0 and type(rank) == "number") then while(id == 0) do rank = rank - 1; if(rank == 0) -- Really don't have this spell then break; end id = HA_GetSpellIDFromName(spell,rank); end end if(id ~= 0) then HA_SpellCooldowns[spell] = { ispell=ispell; id=id; last=0; lastSend=0 }; HA_ChatDebug(HA_DEBUG_SPELLS,"_HA_CheckSpellCooldown : Found spell "..spell.." rank "..tostring(rank).." as ID "..id); return true; end return false; end local function _HA_CheckTalents(talentids) local numTabs = GetNumTalentTabs(); for t=1, numTabs do local numTalents = GetNumTalents(t); for i=1, numTalents do local name,icon,x,y,rank,max = GetTalentInfo(t,i); for _,tid in talentids do if(HA_Talents[tid] and HA_Talents[tid].texture == icon) then HA_Talents[tid].rank = rank; HA_ChatDebug(HA_DEBUG_SPELLS,"_HA_CheckTalents : Found talent "..name.." at rank "..rank.."/"..max); end end end end end function HA_InitializeCooldownSpells() -- Reset infos HA_SpellCooldowns = {}; for id,tab in HA_Talents do tab.rank = nil; end -- Fill infos local _,clas = UnitClass("player"); if(clas == "DRUID") then _HA_CheckSpellCooldown(HA_INNERVATE,HA_SPELL_INNERVATE); _HA_CheckSpellCooldown(HA_REBIRTH,HA_SPELL_REBIRTH,5); _HA_CheckTalents({HA_TALENT_GIF_OF_NATURE,HA_TALENT_IMPROVED_REJUVINATION}); elseif(clas == "PALADIN") then _HA_CheckSpellCooldown(HA_DIVINE_INTERVENTION,HA_SPELL_DIVINE_INTERVENTION); _HA_CheckSpellCooldown(HA_BLESSING_OF_PROTECTION,HA_SPELL_BLESSING_OF_PROTECTION,3); _HA_CheckTalents({HA_TALENT_HEALING_LIGHT}); elseif(clas == "SHAMAN") then _HA_CheckSpellCooldown(HA_REINCARNATION,HA_SPELL_REINCARNATION,HA_PASSIVE); _HA_CheckSpellCooldown(HA_MANA_TIDE,HA_SPELL_MANA_TIDE,2); _HA_CheckTalents({HA_TALENT_PURIFICATION}); elseif(clas == "PRIEST") then _HA_CheckSpellCooldown(HA_LIGHTWELL,HA_SPELL_LIGHTWELL,3); _HA_CheckSpellCooldown(HA_POWER_INFUSION,HA_SPELL_POWER_INFUSION); _HA_CheckTalents({HA_TALENT_SPIRITUAL_HEALING,HA_TALENT_IMPROVED_RENEW,HA_TALENT_SPIRITUAL_GUIDANCE}); end end function HA_OnLoad() -- Print init message HA_ChatPrint("Version "..HA_VERSION.." "..HA_CHAT_MISC_LOADED); -- Register events this:RegisterEvent("VARIABLES_LOADED"); this:RegisterEvent("PLAYER_LOGIN"); this:RegisterEvent("PLAYER_ENTERING_WORLD"); this:RegisterEvent("PLAYER_LEAVING_WORLD"); this:RegisterEvent("ZONE_CHANGED_NEW_AREA"); this:RegisterEvent("UNIT_COMBAT"); this:RegisterEvent("UNIT_HEALTH"); this:RegisterEvent("UNIT_MAXHEALTH"); this:RegisterEvent("UNIT_MANA"); this:RegisterEvent("UNIT_MAXMANA"); this:RegisterEvent("PLAYER_AURAS_CHANGED"); this:RegisterEvent("UPDATE_MOUSEOVER_UNIT"); this:RegisterEvent("PLAYER_TARGET_CHANGED"); this:RegisterEvent("CHAT_MSG_SYSTEM"); this:RegisterEvent("CHAT_MSG_ADDON"); this:RegisterEvent("SPELLCAST_START"); this:RegisterEvent("SPELLCAST_STOP"); this:RegisterEvent("SPELLCAST_DELAYED"); this:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF"); this:RegisterEvent("CHAT_MSG_SPELL_PARTY_BUFF"); this:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF"); this:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"); this:RegisterEvent("CHAT_MSG_SPELL_FAILED_LOCALPLAYER"); this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"); -- Self hots this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS"); -- Group hots this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS"); -- Raid hots HealersAssistTitle:SetText("HealersAssist v"..HA_VERSION); -- Initialize Slash commands SLASH_HA1 = "/ha"; SlashCmdList["HA"] = function(msg) HA_Commands(msg); end tinsert(UISpecialFrames, "HAConfFrame"); -- Initialiaze RegExprs HA_CreateRegexFromGlobals(); -- Build ISpell structure HA_BuildLocalNames(); -- Check for GroupAnalyse AddOn if(GA_RegisterForEvents ~= nil) then GA_RegisterForEvents(_HA_GroupAnalyseCallback); else this:RegisterEvent("PARTY_MEMBERS_CHANGED"); this:RegisterEvent("RAID_ROSTER_UPDATE"); end -- Check for BonusScanner AddOn if(BonusScanner and BONUSSCANNER_VERSION and BONUSSCANNER_VERSION >= "v1.0") then _HA_UseBonusScannerAddon = true; end end --/script HA_RequestSpell("Blessing of Protection") --/script SendAddonMessage("HealAss","09"..'\30'.."16"..'\30'.."Kiki","RAID")