From 3ae3f26f7748bf91334bf0a814221a8470391f34 Mon Sep 17 00:00:00 2001 From: Bluewhale1337 <295648290+Bluewhale1337@users.noreply.github.com> Date: Thu, 25 Jun 2026 19:33:48 +0200 Subject: [PATCH] Further modularization of HealBot codebase by splitting core logic into dedicated controller and view components --- HealBot.lua | 1409 +-------------------------------- HealBotBlue.toc | 36 +- HealBot_Action.lua | 1048 +----------------------- HealBot_Controller_Aura.lua | 182 +++++ HealBot_Controller_Comms.lua | 114 +++ HealBot_Controller_Events.lua | 383 +++++++++ HealBot_Controller_Range.lua | 42 + HealBot_Controller_Spells.lua | 636 +++++++++++++++ HealBot_Model.lua | 2 +- HealBot_View_Layout.lua | 772 ++++++++++++++++++ HealBot_View_Tooltip.lua | 250 ++++++ README.md | 8 +- 12 files changed, 2429 insertions(+), 2453 deletions(-) create mode 100644 HealBot_Controller_Aura.lua create mode 100644 HealBot_Controller_Comms.lua create mode 100644 HealBot_Controller_Events.lua create mode 100644 HealBot_Controller_Range.lua create mode 100644 HealBot_Controller_Spells.lua create mode 100644 HealBot_View_Layout.lua create mode 100644 HealBot_View_Tooltip.lua diff --git a/HealBot.lua b/HealBot.lua index beecb03..06a2533 100644 --- a/HealBot.lua +++ b/HealBot.lua @@ -4,62 +4,16 @@ ]] -local _scale=0; -local CalcEquipBonus=false; -local HealBot_EquipChangeTimer = 0; -local HealValue=0; -local InitSpells=1; +-- local _scale=0; -- moved to HealBot_Controller_Range.lua +CalcEquipBonus=false; +HealBot_EquipChangeTimer = 0; +HealValue=0; +InitSpells=1; local DebugDebuff=false; -local Delay_RecalcParty=0; +Delay_RecalcParty=0; -function HealBot_AddChat(msg) - local chanid=HealBot_Get_DebugChan(); - if chanid and HealBot_SpamCnt < 3 then - HealBot_SpamCnt=HealBot_SpamCnt+1; - local hour,minute = GetGameTime(); - if minute==0 then - msg="["..hour..":00] "..msg; - elseif minute<10 then - msg="["..hour..":0"..minute.."] "..msg; - else - msg="["..hour..":"..minute.."] "..msg; - end - SendChatMessage(msg , "CHANNEL", nil, chanid); - elseif ( DEFAULT_CHAT_FRAME ) then - DEFAULT_CHAT_FRAME:AddMessage(msg); - end -end - - -function HealBot_AddDebug(msg) - local chanid=HealBot_Get_DebugChan(); - if chanid and HealBot_SpamCnt < 3 then - HealBot_SpamCnt=HealBot_SpamCnt+1; - local hour,minute = GetGameTime(); - if minute==0 then - msg="["..hour..":00] DEBUG: "..msg; - elseif minute<10 then - msg="["..hour..":0"..minute.."] DEBUG: "..msg; - else - msg="["..hour..":"..minute.."] DEBUG: "..msg; - end - SendChatMessage(msg , "CHANNEL", nil, chanid); - end -end - -function HealBot_Report_Error(msg) - if HealBot_ErrorCnt<28 then - HealBot_ErrorCnt=HealBot_ErrorCnt+1; - ShowUIPanel(HealBot_Error); - HealBot_ErrorsIn(msg,HealBot_ErrorCnt); - end -end - -function HealBot_AddError(msg) - UIErrorsFrame:AddMessage(msg, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME); - HealBot_AddDebug(msg); -end +-- Debugging and Error functions moved to HealBot_Controller_Comms.lua function HealBot_TogglePanel(panel) if (not panel) then return end @@ -104,7 +58,7 @@ function HealBot_SlashCmd(cmd) end if (cmd=="reset" or cmd=="recalc" or cmd=="defaults") then - initSpells=2; + InitSpells=2; HealBot_Options_Defaults_OnClick(HealBot_Options_Defaults); return end @@ -116,7 +70,7 @@ function HealBot_SlashCmd(cmd) HealBot_RegisterThis(this); end if (cmd=="x") then - initSpells=2; + InitSpells=2; NeedEquipUpdate=1 HealBot_RecalcSpells(); return; @@ -132,13 +86,7 @@ function HealBot_SlashCmd(cmd) end end -function HealBot_SendAddonMessage(prefix, text) - if GetNumRaidMembers() > 0 then - SendAddonMessage(prefix, text, "RAID") - elseif GetNumPartyMembers() > 0 then - SendAddonMessage(prefix, text, "PARTY") - end -end +-- HealBot_SendAddonMessage moved to HealBot_Controller_Comms.lua function HealBot_TargetName() if UnitIsEnemy("target","player") then return nil end @@ -213,1082 +161,26 @@ function HealBot_UseItem(item) UseContainerItem(bag,slot); end -function HealBot_GetSpellName(id) - if (not id) then - return nil; - end - local spellName, subSpellName = GetSpellName(id,BOOKTYPE_SPELL); - if (not spellName) then - return nil; - end - if (not subSpellName or subSpellName=="") then - return spellName; - end - return spellName .. " (" .. subSpellName .. ")"; -end - -function HealBot_GetSpellId(spell) - local id,idd = 1,0; - while true do - local spellName, subSpellName = GetSpellName(id,BOOKTYPE_SPELL); - if (spellName) then - if (spell == spellName .. " (" .. subSpellName .. ")") or (spell == spellName .. "(" .. subSpellName .. ")") then - return id; - end - if (spell == spellName) then - idd=id; - end - else - do break end - end - id = id + 1; - end - if idd>0 then - return idd - else - return nil; - end -end - -function HealBot_CastSpellByName(spell) - if (HealBot_Spells[spell] and HealBot_Spells[spell].BagSlot) then - HealBot_UseItem(spell); - return; - end - local id; - if not HealBot_Spells[spell] then - id = HealBot_GetSpellId(spell); - elseif HealBot_Spells[spell].id then - id = HealBot_Spells[spell].id - else - id = HealBot_GetSpellId(spell); - end - if (not id) then - return; - end - CastSpell(id,BOOKTYPE_SPELL); -end - -HealBot_CastingSpell = nil; -HealBot_CastingTarget = nil; - -function HealBot_StartCasting(spell,target,ttype) - HealBot_CastSpellByName(spell); - HealBot_CastingSpell = spell; - HealBot_CastingTarget = target; - if ( SpellCanTargetUnit(target) ) then - SpellTargetUnit(target); - ttype="fired"; - elseif SpellIsTargeting() then - SpellTargetUnit(target); - SpellStopTargeting() - elseif ttype=="direct" then - if ( CheckInteractDistance(target, 4) ) then - ttype="fired"; - end - end - - if HealBot_Config.ChatMessages then - local tName = target - if target=="target" then tName = HealBot_TargetName() or "target" else tName = UnitName(target) end - if not tName then tName = target end - local baseSpell = spell - local parenIndex = string.find(spell, "%(") - if parenIndex then - baseSpell = string.sub(spell, 1, parenIndex - 1) - end - for i = 1, 5 do - local msgConf = HealBot_Config.ChatMessages[i] - if msgConf and msgConf.Spell == baseSpell and msgConf.Channel ~= "None" then - local msg = msgConf.Message or "" - msg = string.gsub(msg, "#Spell#", spell) - msg = string.gsub(msg, "#Target#", tName) - local chan = msgConf.Channel - if chan == "Say" then - SendChatMessage(msg, "SAY") - elseif chan == "Party" and GetNumPartyMembers() > 0 then - SendChatMessage(msg, "PARTY") - elseif chan == "Raid" and GetNumRaidMembers() > 0 then - SendChatMessage(msg, "RAID") - elseif chan == "Whisper" and UnitIsPlayer(target) then - SendChatMessage(msg, "WHISPER", nil, tName) - end - end - end - end - if ttype=="fired" and HealBot_Spells[spell] then --- if not HealBot_Spells[spell].CastTime then --- if HealBot_Spells[spell].id then --- HealBot_InitGetSpellData(spell, HealBot_Spells[spell].id, HealBot_UnitClass("player")) --- HealBot_AddDebug("Requested init spell data for "..spell) --- end --- if not HealBot_Spells[spell].CastTime then --- HealBot_Spells[spell].CastTime=0 --- HealBot_AddDebug("Unable to init spell data for "..spell) --- InitSpells=2; --- return --- end --- end - if HealBot_Spells[spell].CastTime > 1 then - HealValue=HealBot_Spells[spell].HealsDur; - HealBot_SendAddonMessage( HEALBOT_ADDON_ID, ">> "..UnitName(target).." <<=>> "..HealValue.." << " ); - end - end -end - -function HealBot_StopCasting() - if HealBot_CastingTarget then - if HealBot_HealsIn[UnitName(HealBot_CastingTarget)] then - if HealValue > 0 then - HealBot_SendAddonMessage( HEALBOT_ADDON_ID, ">> "..UnitName(HealBot_CastingTarget).." <<=>> "..0-HealValue.." << " ); - HealValue=0; - end - end - end - HealBot_CastingSpell = nil; - HealBot_CastingTarget = nil; - local bar = HealBot_Action_HealthBar(HealBot_Action_AbortButton); - local ar=HealBot_Config.babortcolr[HealBot_Config.Current_Skin] or 0.1; - local ag=HealBot_Config.babortcolg[HealBot_Config.Current_Skin] or 0.1; - local ab=HealBot_Config.babortcolb[HealBot_Config.Current_Skin] or 0.5; - local aa=HealBot_Config.babortcola[HealBot_Config.Current_Skin] or 1; - bar.txt = getglobal(bar:GetName().."_text"); bar:SetStatusBarColor(ar,ag,ab,0); - local sr=HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; - local sg=HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; - local sb=HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; - local sa=HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; - bar.txt:SetTextColor(sr,sg,sb,sa); -end - - -local HealBot_Health60 = { - ["DRUID"] = 3500, - ["MAGE"] = 2500, - ["HUNTER"] = 3500, - ["PALADIN"] = 4000, - ["PRIEST"] = 2500, - ["ROGUE"] = 3500, - ["SHAMAN"] = 3800, - ["WARLOCK"] = 3500, - ["WARRIOR"] = 5000, -} -function HealBot_UnitHealth(unit) - local Current,Desired = UnitHealth(unit),UnitHealthMax(unit); - if unit=='target' and Desired==100 then - local class,level = HealBot_UnitClass(unit),UnitLevel(unit); - if HealBot_Health60[class] and level>0 then - Desired = math.floor(HealBot_Health60[class]/60*level+0.5) - else - Desired = UnitHealthMax('player'); - end - Current = Desired/100*Current; - end - return Current,Desired; -end - -function HealBot_CheckCasting(unit) - if not HealBot_CastingSpell or HealBot_AlwaysHeal() then return nil end - if not HealBot_Spells[HealBot_CastingSpell] then return nil end - if not unit then unit = HealBot_CastingTarget end - if unit~=HealBot_CastingTarget then return nil end - - local bar = HealBot_Action_HealthBar(HealBot_Action_AbortButton); - local ar=HealBot_Config.babortcolr[HealBot_Config.Current_Skin] or 0.1; - local ag=HealBot_Config.babortcolg[HealBot_Config.Current_Skin] or 0.1; - local ab=HealBot_Config.babortcolb[HealBot_Config.Current_Skin] or 0.5; - local aa=HealBot_Config.babortcola[HealBot_Config.Current_Skin] or 1; - bar.txt = getglobal(bar:GetName().."_text"); - - if HealBot_IsCasting==false and HealBot_AbortButton==0 then - bar:SetStatusBarColor(ar,ag,ab,0); - local sr=HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; - local sg=HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; - local sb=HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; - local sa=HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; - bar.txt:SetTextColor(sr,sg,sb,sa); - return nil - end - - local Current,Desired = HealBot_UnitHealth(unit) - local Needed = Desired-Current; - Needed = Needed * (1 + (HealBot_Config.OverHeal*4)); - if Needed<0 then Needed = 0 end - if (Needed>HealBot_Spells[HealBot_CastingSpell].HealsDur) then - local sr=HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; - local sg=HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; - local sb=HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; - local sa=HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; - bar.txt:SetTextColor(sr,sg,sb,sa); - bar:SetStatusBarColor(ar,ag,ab,0); - return nil - elseif HealBot_AbortButton==1 and HealBot_IsCasting==true then - - bar:SetStatusBarColor(ar,ag,ab,aa); - local sr=HealBot_Config.btextenabledcolr[HealBot_Config.Current_Skin]; - local sg=HealBot_Config.btextenabledcolg[HealBot_Config.Current_Skin]; - local sb=HealBot_Config.btextenabledcolb[HealBot_Config.Current_Skin]; - local sa=HealBot_Config.btextenabledcola[HealBot_Config.Current_Skin]; - bar.txt = getglobal(bar:GetName().."_text"); - bar.txt:SetTextColor(sr,sg,sb,sa); - end -end - -function HealBot_CastSpellOnFriend(spell,target) - local old; - local ttype="other"; - if (not spell or not target or not UnitName(target)) then - return; - end - if (UnitCanAttack("player","target")) then - old = "enemy"; - else - old = UnitName("target"); - if UnitName("target")~=UnitName(target) then - TargetUnit(target); - else - ttype="direct"; - end - end - HealBot_StartCasting(spell,target,ttype); - if (old=="enemy") then - TargetLastEnemy(); - elseif (old) then - TargetByName(old); - else - ClearTarget(); - end -end +-- Spell parsing and casting functions moved to HealBot_Controller_Spells.lua function HealBot_UnitClass(unit) local playerClass, englishClass = UnitClass(unit); return englishClass; end --- TBD: use the event UNIT_AURA to keep track instead of querying each time - -function HealBot_UnitAffected(unit,effect) - if not effect then return nil; end - local i = 1 - while true do - local buff = UnitBuff(unit,i) - if not buff then - do break end - end - if buff==effect then - return buff - end - i = i + 1 - end - i = 1 - while true do - local debuff = UnitDebuff(unit,i) - if not debuff then - do break end - end - if debuff==effect then - return debuff - end - i = i + 1 - end - return nil; -end +---- HealBot_UnitAffected moved to HealBot_Controller_Aura.lua -- safer to use GameTooltip:SetUnitBuff and read the lines in the tooltip ... -- maybe make an additional GameTooltip frame if possible ? -function HealBot_SetItemDefaults(spell) - if not HealBot_Spells[spell].Target then - HealBot_Spells[spell].Target = {"player","party","pet"}; - end - if not HealBot_Spells[spell].Price then - HealBot_Spells[spell].Price = 0; - end - if not HealBot_Spells[spell].CastTime then - HealBot_Spells[spell].CastTime = 0; - end - if not HealBot_Spells[spell].Mana then - HealBot_Spells[spell].Mana = 0; - end - if not HealBot_Spells[spell].Channel then - HealBot_Spells[spell].Channel = HealBot_Spells[spell].CastTime; - end - if not HealBot_Spells[spell].Duration then - HealBot_Spells[spell].Duration = HealBot_Spells[spell].Channel; - end - if not HealBot_Spells[spell].HealsMin then - HealBot_Spells[spell].HealsMin = 0; - end - if not HealBot_Spells[spell].HealsMax then - HealBot_Spells[spell].HealsMax = 0; - end - HealBot_Spells[spell].RealHealing=0; - HealBot_Spells[spell].HealsCast = (HealBot_Spells[spell].HealsMin+HealBot_Spells[spell].HealsMax)/2; - if not HealBot_Spells[spell].HealsExt then - HealBot_Spells[spell].HealsExt = 0; - end -end - -function HealBot_SetSpellDefaults(spell) - HealBot_Spells[spell].HealsDur = floor((HealBot_Spells[spell].HealsCast+HealBot_Spells[spell].HealsExt) + HealBot_Spells[spell].RealHealing); -end - -function HealBot_AddHeal(spell) - HealBot_SetSpellDefaults(spell); - table.foreachi(HealBot_Spells[spell].Target,function (i,val) - table.insert(HealBot_Heals[val],spell); - end); - HealBot_Spells[spell].BagSlot = HealBot_GetBagSlot(spell); -end - - -function HealBot_FindHealSpells() - local id = 1; - if InitSpells>0 then NeedEquipUpdate=1; return; end - - HealBot_Heals = { player = {}, pet = {}, party = {} }; - - table.foreach(HealBot_CurrentSpells, function (index,spell) - if (HealBot_Spells[spell]) then - if CalcEquipBonus then - local healingbonus_penalty=1; - if HealBot_Spells[spell].Level < 20 then - healingbonus_penalty=(1-((20-HealBot_Spells[spell].Level)*0.0375)); - end - local temp_Spell_cast=3.5; - if HealBot_Spells[spell].CastTime == 0 then - temp_Spell_cast=3.5; - end - if not HealBot_Spells[spell].CastTime then - -- HealBot_SetOldDefaults(spell); --- HealBot_Report_Error( "================================" ); --- HealBot_Report_Error( "ERROR: HealBot_Spells[spell].CastTime == nil" ); --- HealBot_Report_Error( "ERROR: spell = "..spell ); - HealBot_Spells[spell].CastTime = 1.5; - end - if HealBot_Spells[spell].CastTime >= 1.5 and HealBot_Spells[spell].CastTime < 3.5 then - temp_Spell_cast=HealBot_Spells[spell].CastTime; - end - RealHealing = ((HealBot_GetBonus() * healingbonus_penalty) * (temp_Spell_cast/3.5)); - local playerClass, englishClass = UnitClass("player"); - local SpiBonus = 0; - if (englishClass=="PRIEST") then - SpiBonus = ((HealBot_SpiBonus(spell) * healingbonus_penalty) * (temp_Spell_cast/3.5)) - RealHealing = RealHealing + SpiBonus; - end - RealHealing = floor(RealHealing); - HealBot_Spells[spell].RealHealing = RealHealing; - end - HealBot_AddHeal(spell); - end - end); - - local items = {}; - for bag=0,NUM_BAG_FRAMES do - for slot=1,GetContainerNumSlots(bag) do - local item = HealBot_GetItemName(bag,slot); - if HealBot_Spells[item] and not items[item] then - HealBot_SetItemDefaults(item); - HealBot_AddHeal(item); - items[item] = 1; - end - end - end - table.foreach(HealBot_Heals, function (key,val) - if (table.getn(val)==0) then - HealBot_Heals[key] = nil; - end - end); - HealBot_Heals.target = HealBot_Heals.party; - for i=1,4 do - HealBot_Heals["party"..i] = HealBot_Heals.party; - HealBot_Heals["partypet"..i] = HealBot_Heals.party; - end - for i=1,40 do - HealBot_Heals["raid"..i] = HealBot_Heals.party; - HealBot_Heals["raidpet"..i] = HealBot_Heals.party; - end - - table.foreach(HealBot_Heals, function (key,val) - table.foreachi(val, function (i,val) - end); - end); - if CalcEquipBonus then - HealBot_AddDebug("...Done Equip Bonus:"..RealHealing); - end - CalcEquipBonus=false; -end - - -function HealBot_GetShapeshiftForm() - local forms = GetNumShapeshiftForms(); - if forms then - local i; - for i=1,forms do - local icon,name,active = GetShapeshiftFormInfo(i); - if active and not string.find(icon,"HumanoidForm") then return i; end - end - end - return nil; -end - -function HealBot_CanCastSpell(spell,unit) - local this = HealBot_Spells[spell]; - if this.Mana>UnitMana("player") then return false end; - if this.BagSlot then - local bag,slot = HealBot_UnpackBagSlot(this.BagSlot); - local start, duration, enable = GetContainerItemCooldown(bag,slot); - if (start > 0 and duration > 0 and enable > 0) then - return false; - end - end - return true; -end - - -function HealBot_GetHealSpell(unit,pattern) - if (not UnitName(unit)) then return nil end; - if UnitOnTaxi("player") then return nil end; - if HealBot_Config.ProtectPvP==1 and UnitIsPVP(unit) and not UnitIsPVP("player") then return nil end - if HealBot_UnitClass("player")=="DRUID" then - if HealBot_GetShapeshiftForm() then return nil end; - end - local spell = HealBot_GetSpellName(HealBot_GetSpellId(pattern)) - local range=40; - if HealBot_Spells[spell] then - if not HealBot_CanCastSpell(spell,unit) then return nil end; - range=HealBot_Spells[spell].range; - end - if HealBot_Range_Check(unit, range)==0 then return nil end; - return spell; -end - -function HealBot_HealUnit(unit,pattern) - HealBot_CastSpellOnFriend(HealBot_GetHealSpell(unit,pattern),unit); -end - -function HealBot_RecalcHeals(unit) - HealBot_Action_Refresh(unit); -end - -function HealBot_RecalcParty() - HealBot_Action_PartyChanged(); - HealBot_Action_RefreshButtons(); -end - -function HealBot_RecalcSpells() - HealBot_FindHealSpells(); - HealBot_RecalcParty(); -end +-- Spell parsing, default setup, finding, casting, and recalculating functions moved to HealBot_Controller_Spells.lua -------------------------------------------------------------------------------------------------- -- OnFoo functions -------------------------------------------------------------------------------------------------- -function HealBot_OnLoad(this) - this:RegisterEvent("VARIABLES_LOADED"); - - SLASH_HEALBOT1 = "/healbot"; - SLASH_HEALBOT2 = "/hb"; - SlashCmdList["HEALBOT"] = function(msg) - HealBot_SlashCmd(msg); - end - HealBot_AddError(HEALBOT_ADDON .. HEALBOT_LOADED); - - -- Register MVC Observers - HealBot_Model:RegisterObserver("UNIT_HEALTH_CHANGED", function(unitID) - HealBot_View_DirtyUnits[unitID] = true - end) - HealBot_Model:RegisterObserver("UNIT_POWER_CHANGED", function(unitID) - HealBot_View_DirtyUnits[unitID] = true - end) - HealBot_Model:RegisterObserver("UNIT_AURA_CHANGED", function(unitID) - HealBot_View_DirtyUnits[unitID] = true - end) - HealBot_Model:RegisterObserver("ROSTER_CHANGED", function() - Delay_RecalcParty = 1 - end) -end +-- Event loop handlers and event dispatcher moved to HealBot_Controller_Events.lua -function HealBot_RegisterThis(this) - -end - -HealBot_View_DirtyUnits = {} -local HealBot_Timer1,HealsIn_Timer = 0,0; -function HealBot_OnUpdate(this,arg1) - -- Process Dirty Queue for MVC View - if next(HealBot_View_DirtyUnits) ~= nil then - for unitID in pairs(HealBot_View_DirtyUnits) do - HealBot_Action_RefreshButtons(unitID) - HealBot_View_DirtyUnits[unitID] = nil - end - end - - HealBot_Timer1 = HealBot_Timer1+arg1; - if HealBot_Timer1>=2.5 then - if not HealBot_IsFighting then - HealsIn_Timer=HealsIn_Timer+1; - if HealsIn_Timer>=10 then - HealBot_HealsIn={}; - HealBot_Healers={}; - HealsIn_Timer=0; - end - - if HealBot_EquipChangeTimer > 0 then - HealBot_EquipChangeTimer = HealBot_EquipChangeTimer - arg1 - if HealBot_EquipChangeTimer <= 0 then - HealBot_EquipChangeTimer = 0 - HealBot_BonusScanner:ScanEquipment() - CalcEquipBonus=true; - HealBot_RecalcSpells(); - end - end - - if InitSpells>1 then - InitSpells=InitSpells+1; - if InitSpells>2 then - local cnt=HealBot_InitSpells(); - InitSpells=0; - HealBot_RecalcSpells(); - end - end - if Delay_RecalcParty>0 then - Delay_RecalcParty=Delay_RecalcParty+1 - if Delay_RecalcParty>1 then - Delay_RecalcParty=0; - HealBot_RecalcParty(); - end - end - else - HealsIn_Timer=0; - end - HealBot_Timer1 = 0; - HealBot_SpamCnt = 0; - end -end - --------------------------------------------------------------------------------- --- MVC Controller Layer --------------------------------------------------------------------------------- - -local HealBot_EventHandlers = { - ["UNIT_HEALTH"] = function(this, arg1) - if HealBot_Model:UpdateUnitHealth(arg1) then - HealBot_Model:NotifyObservers("UNIT_HEALTH_CHANGED", arg1) - end - HealBot_OnEvent_UnitHealth(this, arg1) - end, - ["UNIT_MANA"] = function(this, arg1) - if HealBot_Model:UpdateUnitPower(arg1) then - HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) - end - if (arg1=="player") then HealBot_RecalcHeals(); end - HealBot_Action_RefreshButtons(arg1); - end, - ["UNIT_RAGE"] = function(this, arg1) - if HealBot_Model:UpdateUnitPower(arg1) then - HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) - end - if (arg1=="player") then HealBot_RecalcHeals(); end - HealBot_Action_RefreshButtons(arg1); - end, - ["UNIT_ENERGY"] = function(this, arg1) - if HealBot_Model:UpdateUnitPower(arg1) then - HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) - end - if (arg1=="player") then HealBot_RecalcHeals(); end - HealBot_Action_RefreshButtons(arg1); - end, - ["UNIT_DISPLAYPOWER"] = function(this, arg1) - if HealBot_Model:UpdateUnitPower(arg1) then - HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) - end - if (arg1=="player") then HealBot_RecalcHeals(); end - HealBot_Action_RefreshButtons(arg1); - end, - ["UNIT_AURA"] = function(this, arg1) - HealBot_Model:MarkAuraChanged(arg1) - HealBot_Model:NotifyObservers("UNIT_AURA_CHANGED", arg1) - HealBot_OnEvent_UnitAura(this, arg1) - end, - ["PLAYER_TARGET_CHANGED"] = function(this) - if HealBot_Model:UpdateUnitIdentity("target") then - HealBot_Model:NotifyObservers("ROSTER_CHANGED", "target") - end - HealBot_Model:UpdateUnitStatus("target") - HealBot_Model:UpdateUnitHealth("target") - HealBot_Model:UpdateUnitPower("target") - HealBot_OnEvent_PlayerTargetChanged(this) - end, - ["PARTY_MEMBERS_CHANGED"] = function(this) - for _, unit in ipairs(HealBot_Model.partyMembers) do - HealBot_Model:RefreshUnit(unit) - end - for _, unit in ipairs(HealBot_Model.raidMembers) do - HealBot_Model:RefreshUnit(unit) - end - HealBot_Model:NotifyObservers("ROSTER_CHANGED") - HealBot_OnEvent_PartyMembersChanged(this) - end, - ["PLAYER_ENTERING_WORLD"] = function(this) - HealBot_Model:RefreshUnit("player") - HealBot_Model:RefreshUnit("pet") - HealBot_OnEvent_PlayerEnteringWorld(this) - end, - ["VARIABLES_LOADED"] = function(this) - HealBot_OnEvent_VariablesLoaded(this) - end, - -- Legacy pass-throughs - ["CHAT_MSG_ADDON"] = function(this, arg1,arg2,arg3,arg4) HealBot_OnEvent_AddonMsg(this,arg1,arg2,arg3,arg4) end, - ["SPELLCAST_START"] = function(this, arg1,arg2) HealBot_OnEvent_SpellcastStart(this,arg1,arg2) end, - ["SPELLCAST_STOP"] = function(this) HealBot_OnEvent_SpellcastStop(this) end, - ["SPELLCAST_INTERRUPTED"] = function(this) HealBot_OnEvent_SpellcastStop(this) end, - ["SPELLCAST_FAILED"] = function(this) HealBot_OnEvent_SpellcastStop(this) end, - ["PLAYER_REGEN_DISABLED"] = function(this) HealBot_OnEvent_PlayerRegenDisabled(this) end, - ["PLAYER_REGEN_ENABLED"] = function(this) HealBot_OnEvent_PlayerRegenEnabled(this) end, - ["BAG_UPDATE_COOLDOWN"] = function(this, arg1) HealBot_OnEvent_BagUpdateCooldown(this,arg1) end, - ["BAG_UPDATE"] = function(this, arg1) HealBot_OnEvent_BagUpdate(this,arg1) end, - ["PARTY_MEMBER_DISABLE"] = function(this, arg1) HealBot_OnEvent_PartyMemberDisable(this,arg1) end, - ["PARTY_MEMBER_ENABLE"] = function(this, arg1) HealBot_OnEvent_PartyMemberEnable(this,arg1) end, - ["CHAT_MSG_SYSTEM"] = function(this, arg1) HealBot_OnEvent_SystemMsg(this,arg1) end, - ["ZONE_CHANGED_NEW_AREA"] = function(this) HealBot_OnEvent_ZoneChanged(this) end, - ["UPDATE_INVENTORY_ALERTS"] = function(this) HealBot_OnEvent_PlayerEquipmentChanged(this) end, - ["UNIT_INVENTORY_CHANGED"] = function(this, arg1) HealBot_OnEvent_PlayerEquipmentChanged2(this,arg1) end, - ["PET_BAR_SHOWGRID"] = function(this) HealBot_OnEvent_PartyMembersChanged(this) end, - ["PET_BAR_HIDEGRID"] = function(this) HealBot_OnEvent_PartyMembersChanged(this) end, - ["SPELLS_CHANGED"] = function(this, arg1) HealBot_OnEvent_SpellsChanged(this,arg1) end -} - -function HealBot_OnEvent(this, event, arg1,arg2,arg3,arg4) - local handler = HealBot_EventHandlers[event] - if handler then - handler(this, arg1, arg2, arg3, arg4) - else - HealBot_AddDebug("OnEvent (" .. event .. ")") - end -end - -function HealBot_OnEvent_VariablesLoaded(this) - - local class=HealBot_UnitClass("player") - - table.foreach(HealBot_ConfigDefaults, function (key,val) - if not HealBot_Config[key] then - HealBot_Config[key] = val; - end - if type(val) == "table" and type(HealBot_Config[key]) == "table" then - for k, v in pairs(val) do - if HealBot_Config[key][k] == nil then - HealBot_Config[key][k] = v - end - end - end - end); - - local foundModern = false - if HealBot_Config.Skins then - for _, skin in ipairs(HealBot_Config.Skins) do - if skin == "Modern Flat" then foundModern = true break end - end - if not foundModern then - table.insert(HealBot_Config.Skins, "Modern Flat") - end - end - - HealBot_InitData(); - - if class=="PRIEST" or class=="DRUID" or class=="PALADIN" or class=="SHAMAN" then - - HealBot_BonusScanner:ScanEquipment(); - - if HealBot_Config.ActionVisible==1 then HealBot_Action:Show() end - - this:RegisterEvent("ZONE_CHANGED_NEW_AREA"); - this:RegisterEvent("PLAYER_REGEN_DISABLED"); - this:RegisterEvent("PLAYER_REGEN_ENABLED"); - this:RegisterEvent("PLAYER_TARGET_CHANGED"); - this:RegisterEvent("PARTY_MEMBERS_CHANGED"); - this:RegisterEvent("PARTY_MEMBER_DISABLE"); - this:RegisterEvent("PARTY_MEMBER_ENABLE"); - this:RegisterEvent("PET_BAR_SHOWGRID"); - this:RegisterEvent("PET_BAR_HIDEGRID"); - this:RegisterEvent("UNIT_HEALTH"); - this:RegisterEvent("UNIT_MANA"); - this:RegisterEvent("UNIT_RAGE"); - this:RegisterEvent("UNIT_ENERGY"); - this:RegisterEvent("UNIT_DISPLAYPOWER"); - this:RegisterEvent("SPELLS_CHANGED"); - this:RegisterEvent("SPELLCAST_START"); - this:RegisterEvent("SPELLCAST_STOP"); - this:RegisterEvent("SPELLCAST_INTERRUPTED"); - this:RegisterEvent("SPELLCAST_FAILED"); - this:RegisterEvent("BAG_UPDATE"); - this:RegisterEvent("BAG_UPDATE_COOLDOWN"); - this:RegisterEvent("UNIT_AURA"); --- this:RegisterEvent("CHARACTER_POINTS_CHANGED"); - this:RegisterEvent("UPDATE_INVENTORY_ALERTS"); - this:RegisterEvent("UNIT_INVENTORY_CHANGED"); - this:RegisterEvent("CHAT_MSG_ADDON"); - this:RegisterEvent("CHAT_MSG_SYSTEM"); - this:RegisterEvent("PLAYER_ENTERING_WORLD"); - InitSpells=2; - end -end - -function HealBot_OnEvent_AddonMsg(this,addon_id,inc_msg,dist_target,sender_id) - if addon_id==HEALBOT_ADDON_ID then - local tmpTest, unitname, heal_val - tmpTest,tmpTest,unitname,heal_val = string.find(inc_msg, ">> (%a+) <<=>> (.%d+) <<" ); - if heal_val then - if not HealBot_HealsIn[unitname] then - HealBot_HealsIn[unitname]=0; - end - HealBot_Healers[sender_id] = ">> "..unitname.." <<=>> "..heal_val.." <<"; - HealBot_HealsIn[unitname] = HealBot_HealsIn[unitname] + tonumber(heal_val); - if tonumber(heal_val) > 0 then - HealBot_RecalcHeals(HealBot_FindUnitID(unitname)) - elseif HealBot_HealsIn[unitname] < 0 then - HealBot_HealsIn[unitname]=0; - end - end - elseif addon_id=="HealBot" then - local tmpTest, datatype, datamsg, sender - local PName=UnitName("player"); - tmpTest, tmpTest, datatype, sender, datamsg = string.find(inc_msg, ">> (%a+) <<=>> (%a+) <<=>> (.+)"); - if datatype=="RequestVersion" then - HealBot_SendAddonMessage( "HealBot", ">> SendVersion <<=>> "..sender.." <<=>> Version="..HEALBOT_VERSION ); - elseif datatype=="SendVersion" and PName==sender then - HealBot_AddChat(sender_id..": "..datamsg); - end - elseif addon_id=="CTRA" then - if ( strsub(inc_msg, 1, 3) == "RES" ) then - if ( inc_msg == "RESNO" ) then - HealBot_AddDebug(sender_id.." Stopped ressing"); - if HealBot_Ressing[unitname] then HealBot_Ressing[unitname] = nil; end - else - local unitname, tmpTest - tmpTest, tmpTest, unitname = string.find(inc_msg, "^RES (.+)$"); - if ( unitname ) then - HealBot_AddDebug(sender_id.." is ressing "..unitname); - HealBot_Ressing[unitname] = sender_id; - end - end - HealBot_RecalcHeals(HealBot_FindUnitID(unitname)); - end - end -end - - -function HealBot_OnEvent_UnitHealth(this,unit) - if (not HealBot_Heals[unit]) then return end - HealBot_CheckCasting(unit); - HealBot_RecalcHeals(unit); - if unit==HealBot_Action_TooltipUnit then - HealBot_Action_RefreshTooltip(HealBot_Action_TooltipUnit); - end -end - -function HealBot_OnEvent_UnitMana(this,unit) - if (unit~="player") then return end - HealBot_RecalcHeals(); -end - -function HealBot_OnEvent_ZoneChanged(this) --- HealBot_AddDebug("HB: ZoneChange"); - _scale = 0; - Delay_RecalcParty=1; -end - -HealBot_MissingBuffs = {} - -function HealBot_CheckShamanWeaponBuff(spellName) - local hasMainHandEnchant, _, _, hasOffHandEnchant = GetWeaponEnchantInfo() - - if hasMainHandEnchant or hasOffHandEnchant then - return true - end - - return false -end - - -function HealBot_CheckBuffs(unit) - if HealBot_Config.BuffWatch ~= 1 then - HealBot_MissingBuffs[unit] = nil - return - end - - local inCombat = UnitAffectingCombat("player") or UnitAffectingCombat(unit) - if inCombat and HealBot_Config.BuffWatchInCombat ~= 1 then - HealBot_MissingBuffs[unit] = nil - return - end - - local myClass = UnitClass("player") - if not HealBot_Buff_Spells[myClass] then return end - - if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then - HealBot_MissingBuffs[unit] = nil - return - end - - -- Group Buff Equivalents - local HealBot_Buff_Equivalents = { - [HEALBOT_POWER_WORD_FORTITUDE] = HEALBOT_PRAYER_OF_FORTITUDE, - [HEALBOT_DIVINE_SPIRIT] = HEALBOT_PRAYER_OF_SPIRIT, - [HEALBOT_SHADOW_PROTECTION] = HEALBOT_PRAYER_OF_SHADOW_PROTECTION, - [HEALBOT_ARCANE_INTELLECT] = HEALBOT_ARCANE_BRILLIANCE, - [HEALBOT_MARK_OF_THE_WILD] = HEALBOT_GIFT_OF_THE_WILD, - } - - -- Gather buffs on unit - local hasBuff = {} - local i = 1 - while true do - local buffTexture = UnitBuff(unit, i) - if not buffTexture then break end - HealBot_ScanTooltip:SetOwner(UIParent, "ANCHOR_NONE") - HealBot_ScanTooltip:ClearLines() - HealBot_ScanTooltip:SetUnitBuff(unit, i) - local buffName = HealBot_ScanTooltipTextLeft1:GetText() - if buffName then - hasBuff[buffName] = true - end - i = i + 1 - end - HealBot_ScanTooltip:Hide() - - HealBot_MissingBuffs[unit] = nil - - if HealBot_Config.BuffDropDowns and HealBot_Config.BuffDropDowns[myClass] then - for j = 1, 8 do - local val = HealBot_Config.BuffDropDowns[myClass][j] - if val and val > 0 then - local isSelfOnly = (HealBot_Config.BuffWatchSelf and HealBot_Config.BuffWatchSelf[j] == 1) - if not (isSelfOnly and unit ~= "player") then - local spellName = HealBot_Buff_Spells[myClass][val] - - local hasIt = hasBuff[spellName] - if not hasIt and HealBot_Buff_Equivalents[spellName] then - hasIt = hasBuff[HealBot_Buff_Equivalents[spellName]] - end - - if not hasIt then - if myClass == "SHAMAN" and unit == "player" and string.find(spellName, " Weapon") then - hasIt = HealBot_CheckShamanWeaponBuff(spellName) - end - end - - if not hasIt then - HealBot_MissingBuffs[unit] = spellName - break - end - end - end - end - end -end - -function HealBot_OnEvent_UnitAura(this,unit) - local DebuffType; - - if HealBot_Heals[unit] and unit~="target" then - HealBot_UnitIcons[unit] = {} - local iconCount = 0 - - local HealBot_TrackedHoTs = { - ["Interface\\Icons\\Spell_Holy_Renew"] = true, - ["Interface\\Icons\\Spell_Nature_Rejuvenation"] = true, - ["Interface\\Icons\\Spell_Nature_ResistNature"] = true, - ["Interface\\Icons\\Spell_Holy_PowerWordShield"] = true, - ["Interface\\Icons\\Spell_Holy_SealOfProtection"] = true, - ["Interface\\Icons\\Spell_Holy_Excorcism"] = true, - } - - local i = 1; - while true do - local debuff, tmp, debuff_type = UnitDebuff(unit,i, 1) - if debuff then - if iconCount < 5 then - iconCount = iconCount + 1 - HealBot_UnitIcons[unit][iconCount] = debuff - end - if HealBot_CDCInc[UnitClass(unit)]==1 and HealBot_DebuffWatch[debuff_type]=="YES" then - HealBot_UnitDebuff[unit]=debuff_type - DebuffType=debuff_type; - if HealBot_DebuffPriority[debuff_type] then - do break end - end - end - i = i + 1; - else - if i==1 then HealBot_UnitDebuff[unit] = nil; end - do break end - end - end - - local b = 1 - while true do - local buff = UnitBuff(unit, b) - if not buff then break end - if HealBot_TrackedHoTs[buff] and iconCount < 5 then - iconCount = iconCount + 1 - HealBot_UnitIcons[unit][iconCount] = buff - end - b = b + 1 - end - - if HealBot_UnitDebuff[unit] then - if DebuffType and HealBot_Range_Check(unit, 27)==1 then - if HealBot_Config.ShowDebuffWarning==1 then - UIErrorsFrame:AddMessage(UnitName(unit).." suffers from "..DebuffType, - HealBot_Config.CDCBarColour[DebuffType].R, - HealBot_Config.CDCBarColour[DebuffType].G, - HealBot_Config.CDCBarColour[DebuffType].B, - 1, UIERRORS_HOLD_TIME); - end - if HealBot_Config.SoundDebuffWarning==1 then HealBot_PlaySound(HealBot_Config.SoundDebuffPlay); end - end - end - HealBot_CheckBuffs(unit); - HealBot_RecalcHeals(unit); - end -end - -function HealBot_OnEvent_PlayerRegenDisabled(this) - HealBot_RecalcParty(); - if (UnitIsDeadOrGhost("player")) or (UnitOnTaxi("player")) then - if HealBot_Config.AutoClose==1 and HealBot_Config.ActionVisible~=0 then HealBot_Action:Hide(); end; - else - HealBot_Action:Show(); - HealBot_IsFighting = true; - end --- HealBot_RecalcHeals(); -end - -function HealBot_OnEvent_PlayerRegenEnabled(this) - HealBot_IsFighting = false; - Delay_RecalcParty=1; -end - -function HealBot_OnEvent_PlayerTargetChanged(this) - HealBot_RecalcParty(); -end - -function HealBot_OnEvent_PartyMembersChanged(this) - Delay_RecalcParty=1; -end - -function HealBot_OnEvent_PartyMemberDisable(this,unit) - HealBot_RecalcHeals(); -end - -function HealBot_OnEvent_SystemMsg(this,msg) - if type(msg)=="string" then - local tmpTest, tmpTest, deserter = string.find(msg, HB_HASLEFTRAID); - if not deserter then - local tmpTest, tmpTest, deserter = string.find(msg, HB_HASLEFTPARTY); - end - if deserter then - if (HealBot_Healers[deserter]) then - local tmpTest, unitname, heal_val, heal_valn - tmpTest,tmpTest,unitname,heal_val = string.find(HealBot_Healers[deserter], ">> (%a+) <<=>> (.%d+) <<" ); - heal_valn=tonumber(heal_val) - HealBot_Healers[deserter]=nil; - HealBot_AddDebug("Healer "..deserter.." left the group - Last known activity was heal "..unitname.." for "..heal_val.." << trapped in event SystemMsg"); - if heal_valn>0 and HealBot_HealsIn[unitname] then - HealBot_HealsIn[unitname] = HealBot_HealsIn[unitname] - heal_valn; - if HealBot_HealsIn[unitname] < 0 then - HealBot_HealsIn[unitname]=0; - end - end - end - elseif msg==HB_YOULEAVETHEGROUP or msg==HB_YOULEAVETHERAID then - Delay_RecalcParty=1; - else - -- find other messges - end - end -end - -function HealBot_OnEvent_PartyMemberEnable(this,unit) - HealBot_RecalcHeals(); -end - -function HealBot_OnEvent_PlayerEquipmentChanged(this) - HealBot_EquipChangeTimer = 1; -end - -function HealBot_OnEvent_PlayerEquipmentChanged2(this,unit) - if unit=="player" then - HealBot_EquipChangeTimer = 1; - end -end - -function HealBot_OnEvent_SpellsChanged(this, arg1) - if arg1 then return; end - HealBot_AddDebug("HB: SpellsChanged"); - InitSpells=2; -end - -function HealBot_OnEvent_TalentsChanged(this, arg1) - HealBot_AddDebug("HB: TalentsChanged"); -end - -function HealBot_OnEvent_BagUpdate(this,bag) - if HealBot_EquipChangeTimer==0 then - HealBot_RecalcSpells(); - end -end - -function HealBot_OnEvent_BagUpdateCooldown(this,bag) - if not bag then - bag = "undef" - elseif HealBot_EquipChangeTimer==0 then - HealBot_RecalcSpells(); - end -end - -function HealBot_OnEvent_PlayerEnteringWorld(this) - HealBot_IsFighting = false; -end - -function HealBot_OnEvent_SpellcastStart(this,spell,duration) - HealBot_IsCasting = true; - HealBot_RecalcHeals(); - HealBot_CheckCasting(); - if spell==HEALBOT_RESURRECTION or spell==HEALBOT_ANCESTRALSPIRIT or spell==HEALBOT_REBIRTH or spell==HEALBOT_REDEMPTION then - if UnitName("Target") then - HealBot_SendAddonMessage( "CTRA", "RES "..UnitName("Target")); - --HealBot_AddDebug("I am ressing "..UnitName("Target")) - HealBot_IamRessing=true; - end - end -end - -function HealBot_OnEvent_SpellcastStop(this) - HealBot_IsCasting = false; - HealBot_StopCasting(); - HealBot_RecalcHeals(); - if HealBot_IamRessing then - HealBot_SendAddonMessage( "CTRA", "RESNO"); - --HealBot_AddDebug("I finished ressing") - HealBot_IamRessing=false; - end -end - -function HealBot_SpiBonus(spell) - local heals_modifer = 0; - local base, stat, posBuff, negBuff = UnitStat("player",5); - nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(2,14); -- Spiritual guidence - spiGuideBonus = stat * 0.05; - heals_modifer = heals_modifer + (currRank * spiGuideBonus); - return heals_modifer; -end - -function HealBot_GetBonus() - local HealBonus=HealBot_BonusScanner:GetBonus(); - return HealBonus; -end +-- SpiBonus and GetBonus functions moved to HealBot_Controller_Spells.lua function HealBot_FindUnitID(unitname) local text; @@ -1322,29 +214,7 @@ function HealBot_PlaySound(id) end end -function HealBot_InitSpells() - local id = 1 - local cnt = 0; - local class=HealBot_UnitClass("player") - HealBot_CurrentSpells = {}; - while true do - local spell = HealBot_GetSpellName(id); - if not spell then - do break end - end - if (HealBot_Spells[spell]) then - HealBot_Spells[spell].id = id; - HealBot_InitGetSpellData(spell, id, class); - table.insert(HealBot_CurrentSpells,spell); - cnt = cnt + 1; - end - id = id + 1; - end - if class=="PRIEST" or class=="DRUID" or class=="PALADIN" or class=="SHAMAN" then - HealBot_AddChat("Initiated HealBot_CurrentSpells with ".. cnt .." Spells"); - end - return cnt; -end +-- HealBot_InitSpells moved to HealBot_Controller_Spells.lua function HealBot_InitData() HealBot_Skins = HealBot_Config.Skins; @@ -1365,252 +235,11 @@ function HealBot_ToggleOptions() HealBot_TogglePanel(HealBot_Options) end -function HealBot_InitGetSpellData(spell, id, class) +-- HealBot_InitGetSpellData and HealBot_Generic_Patten moved to HealBot_Controller_Spells.lua - local i, _mana, _cast, _HealsMin, _HealsMax, _HealsExt, _duration, _range, _shield, _channel; - local tooltip = getglobal( "HealBot_ScanTooltip" ); - local tmpText, line, tmpTest +-- HealBot_Get_DebugChan moved to HealBot_Controller_Comms.lua - if ( not spell ) then - return; - end - - HealBot_ScanTooltip:SetOwner(HealBot_ScanTooltip, "ANCHOR_NONE") - HealBot_ScanTooltip:SetSpell( id, BOOKTYPE_SPELL ); - tmpText = getglobal("HealBot_ScanTooltipTextLeft2"); - if (tmpText:GetText()) then - line = tmpText:GetText(); - tmpTest,tmpTest,_mana = string.find(line, HB_TOOLTIP_MANA ); - else - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: HealBot_ScanTooltip is lost" ); - HealBot_Report_Error( "ERROR: If BonusScanner is used, try disabling BonusScanner" ); - end - - tmpText = getglobal("HealBot_ScanTooltipTextRight2"); - if (tmpText:GetText()) then - line = tmpText:GetText(); - tmpTest,tmpTest,_range = string.find(line, HB_TOOLTIP_RANGE ); - else - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: HealBot_ScanTooltip is lost" ); - HealBot_Report_Error( "ERROR: If BonusScanner is used, try disabling BonusScanner" ); - end - - tmpText = getglobal("HealBot_ScanTooltipTextLeft3"); - _cast = nil; - if (tmpText:GetText()) then - line = tmpText:GetText(); - if ( line == HB_TOOLTIP_INSTANT_CAST ) then - _cast = 0; - elseif line == HB_TOOLTIP_CHANNELED then - _cast = 0; - elseif ( tmpText ) then - tmpTest,tmpTest,_cast = string.find(line, HB_TOOLTIP_CAST_TIME ); - end - else - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: HealBot_ScanTooltip is lost" ); - HealBot_Report_Error( "ERROR: If BonusScanner is used, try disabling BonusScanner" ); - end - - tmpText = getglobal("HealBot_ScanTooltipTextLeft4"); - tmpTest = nil; - if (tmpText:GetText()) then - line = tmpText:GetText(); - if class == "PRIEST" then - if strsub(spell, 0, 14) == strsub(HEALBOT_POWER_WORD_SHIELD, 0, 14) then - tmpTest,tmpTest,_HealsMin,_shield = string.find(line, HB_SPELL_PATTERN_SHIELD ); - _HealsExt=0; - _HealsMax=_HealsMin; - elseif strsub(spell, 0, 4) == strsub(HEALBOT_RENEW, 0, 4) then - tmpTest,tmpTest,_HealsExt,tmpTest,_duration = string.find(line, HB_SPELL_PATTERN_RENEW ); - _HealsMin=0; - _HealsMax=0; - if ( _HealsExt == nil ) then - tmpTest,tmpTest,_HealsExt,_duration = string.find(line, HB_SPELL_PATTERN_RENEW1 ); - end - if ( _HealsExt == nil ) then - tmpTest,tmpTest,_duration,_HealsExt = string.find(line, HB_SPELL_PATTERN_RENEW2 ); - end - if ( _HealsExt == nil ) then - tmpTest,tmpTest,_duration,_HealsExt = string.find(line, HB_SPELL_PATTERN_RENEW3 ); - end - elseif strsub(spell, 0, 9) == strsub(HEALBOT_LESSER_HEAL, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line,HB_SPELL_PATTERN_LESSER_HEAL); - elseif strsub(spell, 0, 9) == strsub(HEALBOT_GREATER_HEAL, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_GREATER_HEAL ); - elseif strsub(spell, 0, 9) == strsub(HEALBOT_FLASH_HEAL, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_FLASH_HEAL ); - elseif strsub(spell, 0, 4) == strsub(HEALBOT_HEAL, 0, 4) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HEAL ); - end - elseif class=="DRUID" then - if strsub(spell, 0, 6) == strsub(HEALBOT_REGROWTH, 0, 6) then - tmpTest,tmpTest,_HealsMin,_HealsMax,_HealsExt = string.find(line, HB_SPELL_PATTERN_REGROWTH ); - if ( tmpTest == nil ) then - tmpTest,tmpTest,_HealsMin,_HealsMax,tmpTest,_HealsExt = string.find(line, HB_SPELL_PATTERN_REGROWTH1 ); - end - elseif strsub(spell, 0, 9) == strsub(HEALBOT_REJUVENATION, 0, 9) then - tmpTest,tmpTest,_HealsExt,_duration = string.find(line, HB_SPELL_PATTERN_REJUVENATION ); - _HealsMin=0; - _HealsMax=0; - if ( _HealsExt == nil ) then - tmpTest,tmpTest,_HealsExt,tmpTest,_duration = string.find(line, HB_SPELL_PATTERN_REJUVENATION1 ); - end - elseif strsub(spell, 0, 7) == strsub(HEALBOT_HEALING_TOUCH, 0, 7) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HEALING_TOUCH ); - end - elseif class=="PALADIN" then - if strsub(spell, 0, 9) == strsub(HEALBOT_HOLY_LIGHT, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HOLY_LIGHT ); - elseif strsub(spell, 0, 9) == strsub(HEALBOT_FLASH_OF_LIGHT, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_FLASH_OF_LIGHT ); - end - elseif class=="SHAMAN" then - if strsub(spell, 0, 9) == strsub(HEALBOT_HEALING_WAVE, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HEALING_WAVE ); - elseif strsub(spell, 0, 9) == strsub(HEALBOT_LESSER_HEALING_WAVE, 0, 9) then - tmpTest,_HealsMin,_HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_LESSER_HEALING_WAVE ); - end - end - else - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: HealBot_ScanTooltip is lost" ); - HealBot_Report_Error( "ERROR: If BonusScanner is used, try disabling BonusScanner" ); - end - - if ( _mana == nil ) then - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: _mana is NIL" ); - HealBot_Report_Error( "ERROR: Spell: "..spell ); - if HealBot_ScanTooltipTextLeft2:GetText() then - HealBot_Report_Error( "ERROR: Tooltip = >> "..HealBot_ScanTooltipTextLeft2:GetText().." <<" ); - end - HealBot_Report_Error( "ERROR: Patten = >> "..HB_TOOLTIP_MANA.." <<" ); - end - if ( _range == nil ) then - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: _range is NIL" ); - HealBot_Report_Error( "ERROR: Spell: "..spell ); - if HealBot_ScanTooltipTextRight2:GetText() then - HealBot_Report_Error( "ERROR: Tooltip = >> "..HealBot_ScanTooltipTextRight2:GetText().." <<" ); - end - HealBot_Report_Error( "ERROR: Patten = >> "..HB_TOOLTIP_RANGE.." <<" ); - end - if ( _cast == nil ) then - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: _cast is NIL" ); - HealBot_Report_Error( "ERROR: Spell: "..spell ); - if HealBot_ScanTooltipTextLeft3:GetText() then - HealBot_Report_Error( "ERROR: Tooltip = >> "..HealBot_ScanTooltipTextLeft3:GetText().." <<" ); - end - HealBot_Report_Error( "ERROR: Patten = >> "..HB_TOOLTIP_CAST_TIME.." <<" ); - end - if ( tmpTest == nil ) then - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: tmpTest == nil" ); - HealBot_Report_Error( "ERROR: spell = "..spell ); - if line then - HealBot_Report_Error( "ERROR: Tooltip = >> "..line.." <<" ); - end - end - - HealBot_Spells[spell].CastTime=tonumber(_cast); - HealBot_Spells[spell].Mana=tonumber(_mana); - HealBot_Spells[spell].Range=tonumber(_range); - HealBot_Spells[spell].HealsMin=tonumber(_HealsMin); - HealBot_Spells[spell].HealsMax=tonumber(_HealsMax); - if _HealsExt then - HealBot_Spells[spell].HealsExt=tonumber(_HealsExt); - end - if _duration then - HealBot_Spells[spell].Duration=tonumber(_duration); - end - if _shield then - HealBot_Spells[spell].Shield=tonumber(_shield); - end - if _channel then - HealBot_Spells[spell].Channel=tonumber(_channel); - end - - if not HealBot_Spells[spell].Target then - HealBot_Spells[spell].Target = {"player","party","pet"}; - end - if not HealBot_Spells[spell].Price then - HealBot_Spells[spell].Price = 0; - end - if not HealBot_Spells[spell].Channel then - HealBot_Spells[spell].Channel = HealBot_Spells[spell].CastTime; - end - if not HealBot_Spells[spell].Duration then - HealBot_Spells[spell].Duration = HealBot_Spells[spell].Channel; - end - if not HealBot_Spells[spell].RealHealing then - HealBot_Spells[spell].RealHealing=0; - end - HealBot_Spells[spell].HealsCast = (HealBot_Spells[spell].HealsMin+HealBot_Spells[spell].HealsMax)/2; - if not HealBot_Spells[spell].HealsExt then - HealBot_Spells[spell].HealsExt = 0; - end - -end - -function HealBot_Generic_Patten(matchStr,matchPattern) - local tmpTest,_HealsMin,_HealsMax,_HealsExt,_duration - tmpTest,tmpTest,_HealsMin,_HealsMax = string.find(matchStr, matchPattern ); - if ( tmpTest == nil ) then - HealBot_Report_Error( "================================" ); - HealBot_Report_Error( "ERROR: tmpTest == nil" ); - HealBot_Report_Error( "ERROR: pattern = "..matchPattern ); - HealBot_Report_Error( "ERROR: Tooltip = >> "..matchStr.." <<" ); - end - return tmpTest,_HealsMin,_HealsMax; -end - -function HealBot_Get_DebugChan() - local index = GetChannelName("HBmsg"); - if (index>0) then - return index; - else - return nil; - end -end - -function HealBot_Range_Check(unit, range) - - local return_val = 0; - if not range then - range=40; - end - if ( unit=="player" ) then - return_val = 1; - elseif ( UnitIsVisible(unit) == 1) then - local tx, ty = GetPlayerMapPosition(unit) - local dist - if tx > 0 or ty > 0 then - local px, py = GetPlayerMapPosition("player") - dist = sqrt((px - tx)^2 + (py - ty)^2) - if dist > _scale and (px > 0 or py > 0) then - if (CheckInteractDistance(unit, 4)) then - _scale = dist - end - end - if dist <=(_scale*range/27) then - return_val=1 - end - else - if (HealBot_Config.QualityRange == 1) or range <= 27 then - if ( CheckInteractDistance(unit, 4) ) then - return_val = 1; - end - else - return_val = 1; - end - end - end - return return_val; -end +-- HealBot_Range_Check moved to HealBot_Controller_Range.lua -------------------------------------------------------------------------------- -- Native Action Bar Hovercasting (Mouseover Hook) diff --git a/HealBotBlue.toc b/HealBotBlue.toc index a12fee4..1660ca4 100644 --- a/HealBotBlue.toc +++ b/HealBotBlue.toc @@ -1,17 +1,24 @@ -## Interface: 11200 -## Title: HealBotBlue -## Version: 1.3 -## Author: Bluewhale -## Notes: Adds panel with skinable bars for healing and decursive -## SavedVariables: HealBot_Config -..\..\FrameXML\Fonts.xml -..\..\FrameXML\OptionsFrameTemplates.xml -..\..\FrameXML\UIPanelTemplates.xml -..\..\FrameXML\UIDropDownMenu.xml -HealBot_Error.xml -HealBot_Model.lua -HealBot.xml -HealBot_Action.xml +## Interface: 11200 +## Title: HealBotBlue +## Version: 1.3 +## Author: Bluewhale +## Notes: Adds panel with skinable bars for healing and decursive +## SavedVariables: HealBot_Config +..\..\FrameXML\Fonts.xml +..\..\FrameXML\OptionsFrameTemplates.xml +..\..\FrameXML\UIPanelTemplates.xml +..\..\FrameXML\UIDropDownMenu.xml +HealBot_Error.xml +HealBot_Model.lua +HealBot_Controller_Range.lua +HealBot_Controller_Comms.lua +HealBot_Controller_Aura.lua +HealBot_Controller_Spells.lua +HealBot_Controller_Events.lua +HealBot.xml +HealBot_View_Layout.lua +HealBot_View_Tooltip.lua +HealBot_Action.xml HealBot_Options.xml HealBot_Options_General.xml HealBot_Options_Spells.xml @@ -20,4 +27,3 @@ HealBot_Options_CDC.xml HealBot_Options_Skins.xml HealBot_Options_Buffs.xml HealBot_Options_Chat.xml - diff --git a/HealBot_Action.lua b/HealBot_Action.lua index 7f618ea..94b8297 100644 --- a/HealBot_Action.lua +++ b/HealBot_Action.lua @@ -1,104 +1,4 @@ -function HealBot_Action_SetTexture(bar, btexture) - if not bar then return end - if btexture == 10 then - bar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8"); - else - bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar"); - end -end -local headerno=0; - -HealBot_Action_HealGroup = { - "player", - "pet", - "party1", - "party2", - "party3", - "party4", -}; - -HealBot_Action_HealTarget = { -}; - -HealBot_Action_HealButtons = { -}; - -HealBot_Action_UnitButtons = { -}; - -function HealBot_Action_AddDebug(msg) - HealBot_AddDebug("Action: " .. msg); -end - -function HealBot_HealthColor(unit,hlth,maxhlth) - if HealBot_UnitDebuff[unit] then - local debuff, tmp, debuff_type = UnitDebuff(unit,1, 1) - if not debuff then - HealBot_UnitDebuff[unit] = nil; - HealBot_UnitDebuff[unit.."_debuff_texture"]=nil - else - local dr = HealBot_Config.CDCBarColour[HealBot_UnitDebuff[unit]].R - local dg = HealBot_Config.CDCBarColour[HealBot_UnitDebuff[unit]].G - local db = HealBot_Config.CDCBarColour[HealBot_UnitDebuff[unit]].B - if HealBot_Config.btexture[HealBot_Config.Current_Skin] == 10 then - dr = dr * 4 - dg = dg * 4 - db = db * 4 - if dr > 1 then dr = 1 end - if dg > 1 then dg = 1 end - if db > 1 then db = 1 end - end - return dr, dg, db, HealBot_Config.Barcola[HealBot_Config.Current_Skin]; - end - end - local text = UnitName(unit); - if not HealBot_HealsIn[text] then - HealBot_HealsIn[text]=0; - end - - local pct = hlth+HealBot_HealsIn[text]; - if maxhlth and maxhlth > 0 then - if pctHealBot_Config.AlertLevel then - a=HealBot_Config.bardisa[HealBot_Config.Current_Skin]; - end - - local colorMode = HealBot_Config.bcolormode[HealBot_Config.Current_Skin] or 1 - if colorMode == 2 and HealBot_Model and HealBot_Model.units[unit] and HealBot_Model.units[unit].englishClass then - local engClass = HealBot_Model.units[unit].englishClass - if RAID_CLASS_COLORS and RAID_CLASS_COLORS[engClass] then - r = RAID_CLASS_COLORS[engClass].r - g = RAID_CLASS_COLORS[engClass].g - b = RAID_CLASS_COLORS[engClass].b - end - else - if pct>=0.98 then r = 0.0; end - if pct<0.98 and pct>=0.65 then r=2.94-(pct*3); end - if pct<=0.64 and pct>0.31 then g=(pct-0.31)*3; end - if pct<=0.31 then g = 0.0; end - end - return r,g,b,a; -end - -function HealBot_Action_HealthBar(button) - local name = button:GetName(); - return getglobal(name.."Bar"); -end - -function HealBot_Action_HealthBar2(button) - local name = button:GetName(); - return getglobal(name.."Bar2"); -end +-- Status bar layout functions and tables moved to HealBot_View_Layout.lua function HealBot_AlwaysHeal() return HealBot_Config.EnableHealthy==1 @@ -154,951 +54,9 @@ function HealBot_CanHeal(unit) return false end -function HealBot_Action_EnableButton(button) - local unit = button.unit; - local state = HealBot_Model.units[unit] - if not state then return end - - local hlth = state.health - local maxhlth = state.maxHealth - local name = state.name - if not name then name = UnitName(unit) end -- fallback +-- Status bar layout and grid rendering functions moved to HealBot_View_Layout.lua - local bar = HealBot_Action_HealthBar(button); - local bar2 = HealBot_Action_HealthBar2(button); - local bar3 = getglobal(button:GetName().."Bar3"); - local btexture=HealBot_Config.btexture[HealBot_Config.Current_Skin]; - local bheight=HealBot_Config.bheight[HealBot_Config.Current_Skin]; - local sr=HealBot_Config.btextenabledcolr[HealBot_Config.Current_Skin]; - local sg=HealBot_Config.btextenabledcolg[HealBot_Config.Current_Skin]; - local sb=HealBot_Config.btextenabledcolb[HealBot_Config.Current_Skin]; - local sa=HealBot_Config.btextenabledcola[HealBot_Config.Current_Skin]; - local r,g,b,a = HealBot_HealthColor(button.unit,hlth,maxhlth) - local btextheight=HealBot_Config.btextheight[HealBot_Config.Current_Skin] or 10; - if btextheight == 0 then btextheight = 10 end - local bwidth = HealBot_Config.bwidth[HealBot_Config.Current_Skin] - local textlen = floor(5+(((bwidth*1.8)/btextheight)-(btextheight/2))) - - bar:SetMinMaxValues(0,maxhlth); - bar:SetValue(hlth); - - local englishClass = state.englishClass - local isHealer = false - if englishClass == "PALADIN" or englishClass == "DRUID" or englishClass == "SHAMAN" or englishClass == "PRIEST" then - isHealer = true - end - local pt = state.powerType - - local showMana = false - if HealBot_Config.ShowManaBars == 1 and state.maxMana > 0 and pt == 0 then - if HealBot_Config.ManaBarsHealersOnly == 1 then - if isHealer then showMana = true end - else - showMana = true - end - end - - if showMana then - local pr, pg, pb = 0, 0, 1 - - if bar3 then - bar3:SetMinMaxValues(0, state.maxMana) - bar3:SetValue(state.mana) - bar3:SetStatusBarColor(pr, pg, pb, HealBot_Config.Barcola[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(bar3, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - bar3:Show() - bar3:SetHeight(bheight * 0.2) - end - - bar:SetHeight(bheight * 0.8) - bar2:SetHeight(bheight * 0.8) - else - if bar3 then - bar3:Hide() - end - bar:SetHeight(bheight) - bar2:SetHeight(bheight) - end - - if HealBot_HealsIn[name] then - bar2:SetMinMaxValues(0,maxhlth); - bar2:SetValue(hlth+HealBot_HealsIn[name]); - else - bar2:SetValue(0); - end - bar.txt = getglobal(bar:GetName().."_text"); - if (not HealBot_IsCasting and (HealBot_CanHeal(unit) or HealBot_MissingBuffs[unit])) then - button:Enable(); - if HealBot_UnitDebuff[unit] then - sr=HealBot_Config.btextcursecolr[HealBot_Config.Current_Skin]; - sg=HealBot_Config.btextcursecolg[HealBot_Config.Current_Skin]; - sb=HealBot_Config.btextcursecolb[HealBot_Config.Current_Skin]; - sa=HealBot_Config.btextcursecola[HealBot_Config.Current_Skin]; - elseif HealBot_MissingBuffs[unit] then - r=r*0.5; - g=g*0.5; - b=b*0.5; - sr=1.0; - sg=1.0; - sb=0.0; - end - bar:SetStatusBarColor(r,g,b,HealBot_Config.Barcola[HealBot_Config.Current_Skin]); - bar2:SetStatusBarColor(r,g,b,HealBot_Config.BarcolaInHeal[HealBot_Config.Current_Skin]); - else - button:Disable(); - sr=HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; - sg=HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; - sb=HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; - sa=HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; - bar:SetStatusBarColor(r,g,b,HealBot_Config.bardisa[HealBot_Config.Current_Skin]); - bar2:SetStatusBarColor(r,g,b,HealBot_Config.bardisa[HealBot_Config.Current_Skin]); - end - if string.len(name)>textlen then - name = string.sub(name,1,textlen-3) .. '...'; - end - bar.txt:SetText(name); - bar.txt:SetTextColor(sr,sg,sb,sa); - local fontName, fontHeight, fontFlags = bar.txt:GetFont() - local fontOutline = HealBot_Config.bfontoutline[HealBot_Config.Current_Skin] or 0 - if fontOutline == 1 then - bar.txt:SetFont(fontName, fontHeight, "OUTLINE") - else - bar.txt:SetFont(fontName, fontHeight, "") - end - local fontName, fontHeight, fontFlags = bar.txt:GetFont() - local fontOutline = HealBot_Config.bfontoutline[HealBot_Config.Current_Skin] or 0 - if fontOutline == 1 then - bar.txt:SetFont(fontName, fontHeight, "OUTLINE") - else - bar.txt:SetFont(fontName, fontHeight, "") - end - - for i = 1, 5 do - local icon = getglobal(button:GetName().."BarIcon"..i) - if icon then - if HealBot_UnitIcons and HealBot_UnitIcons[unit] and HealBot_UnitIcons[unit][i] then - icon:SetTexture(HealBot_UnitIcons[unit][i]) - icon:Show() - else - icon:Hide() - end - end - end -end - -function HealBot_Action_EnableButtons() - table.foreach(HealBot_Action_HealButtons, function (index,button) - HealBot_Action_EnableButton(button); - end); -end - -function HealBot_Action_RefreshButton(button) - if not button then return end - if type(button)~="table" then DEFAULT_CHAT_FRAME:AddMessage("***** "..type(button)) end - local unit = button.unit; - if HealBot_MayHeal(unit) then - HealBot_Action_EnableButton(button) - end -end - -function HealBot_Action_ResetSkin() - HealBot_Action_PartyChanged() - if HealBot_Options:IsVisible() then - HealBot_Action_SetTexture(HealBot_DiseaseColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_MagicColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_PoisonColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_CurseColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_EnTextColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_EnTextColorpickin, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_DisTextColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_Action_SetTexture(HealBot_DebTextColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) - HealBot_SetSkinColours() - end -end - -function HealBot_Action_RefreshButtons() - table.foreach(HealBot_Action_HealButtons, function (index,button) - HealBot_Action_RefreshButton(button); - end); -end - -function HealBot_Action_RefreshButtons(unit) - if unit and HealBot_Action_UnitButtons[unit] then - table.foreach(HealBot_Action_UnitButtons[unit], function (index,button) - HealBot_Action_RefreshButton(button); - end); - else - table.foreach(HealBot_Action_HealButtons, function (index,button) - HealBot_Action_RefreshButton(button); - end); - end -end - -function HealBot_Action_PositionButton(button,OsetX,OsetY,bwidth,bheight,checked,header) - local brspace=HealBot_Config.brspace[HealBot_Config.Current_Skin] or 3; - if header then - headerno=headerno+1; - local headerobj=getglobal("HealBot_Action_Header"..headerno); - local tmpY=OsetY - headerobj:SetText(header) - headerobj:Show(); - headerobj:ClearAllPoints(); - headerobj:SetHeight(bheight); - headerobj:SetWidth(bwidth); - headerobj:SetPoint("TOPLEFT","HealBot_Action","TOPLEFT",OsetX,-OsetY); - headerobj:Disable(); - OsetY = OsetY+headerobj:GetHeight()+brspace; - else - local unit = button.unit; - button:SetText(" "); - if (HealBot_MayHeal(unit)) then - button:Show(); - button:ClearAllPoints(); - button:SetHeight(bheight); - if checked then - button:SetWidth(bwidth-14); - button:SetPoint("TOPLEFT","HealBot_Action","TOPLEFT",OsetX+14,-OsetY); - else - button:SetWidth(bwidth); - button:SetPoint("TOPLEFT","HealBot_Action","TOPLEFT",OsetX,-OsetY); - end - OsetY = OsetY+button:GetHeight()+brspace; - else - button:Hide(); - end - end - return OsetY; -end - -function HealBot_Action_SetHeightWidth(width,height,bwidth) - if HealBot_ActionHeight then - HealBot_Action:SetHeight(HealBot_ActionHeight); - end - if HealBot_Config.GrowUpwards==1 then - local left,bottom = HealBot_Action:GetLeft(),HealBot_Action:GetBottom(); - if left and bottom then - if HealBot_Config.PanelAnchorX==-1 then HealBot_Config.PanelAnchorX=left; end - if HealBot_Config.PanelAnchorY==-1 then HealBot_Config.PanelAnchorY=bottom; end - HealBot_Action:ClearAllPoints(); - HealBot_Action:SetPoint("BOTTOMLEFT","UIParent","BOTTOMLEFT",HealBot_Config.PanelAnchorX,HealBot_Config.PanelAnchorY); - end - else - local left,top = HealBot_Action:GetLeft(),HealBot_Action:GetTop(); - if left and top then - HealBot_Action:ClearAllPoints(); - HealBot_Action:SetPoint("TOPLEFT","UIParent","BOTTOMLEFT",left,top); - end - end - HealBot_Action:SetHeight(height); - HealBot_ActionHeight = height; - local bpadding = (HealBot_Config.bpadding and HealBot_Config.bpadding[HealBot_Config.Current_Skin]) or 10 - HealBot_Action:SetWidth(width+bwidth+bpadding) -end - -function HealBot_Action_SetHealButton(index,unit) - if not index then - HealBot_Action_HealButtons = {}; - HealBot_Action_UnitButtons = {}; - return nil - end - local button = getglobal("HealBot_Action_HealUnit"..index); - button.unit = unit; - if unit then - table.insert(HealBot_Action_HealButtons,button); - if not HealBot_Action_UnitButtons[unit] then HealBot_Action_UnitButtons[unit] = {} end - table.insert(HealBot_Action_UnitButtons[unit],button); - else - button:Hide(); - end - return button; -end - -function HealBot_Action_PartyChanged() - -if not HealBot_IsFighting then - - local numBars = 0; - local numHeaders = 0; - local TempMaxH=0; - local HeaderPos = {}; - - for j=1,15 do - local headerobj=getglobal("HealBot_Action_Header"..j); - headerobj:SetText(" ") - headerobj:Hide(); - end - - local bwidth = HealBot_Config.bwidth[HealBot_Config.Current_Skin] or 85; - local sr=HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin] or 0.4; - local sg=HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin] or 0.4; - local sb=HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin] or 0.4; - local sa=HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin] or 0.6; - local bheight=HealBot_Config.bheight[HealBot_Config.Current_Skin] or 18; - local btexture=HealBot_Config.btexture[HealBot_Config.Current_Skin] or 5; - local bcspace=HealBot_Config.bcspace[HealBot_Config.Current_Skin] or 4; - local cols=HealBot_Config.numcols[HealBot_Config.Current_Skin] or 2; - local btextheight=HealBot_Config.btextheight[HealBot_Config.Current_Skin] or 10; - local abortsize=HealBot_Config.abortsize[HealBot_Config.Current_Skin] or 10; - local checked_start=0; - local checked_end=0; - headerno=0; - - for j=1,41 do - HealBot_Action_SetHealButton(j,nil); - end - for j=51,60 do - HealBot_Action_SetHealButton(j,nil); - end - HealBot_Action_SetHealButton(); - local i = 0; - local last = 0; - local GroupValid=numBars; - last = last+6 - if HealBot_Config.GroupHeals==1 then - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - HeaderPos[i+1] = HEALBOT_OPTIONS_GROUPHEALS - end - for _,unit in ipairs(HealBot_Action_HealGroup) do - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - i = i+1; - HealBot_Action_SetHealButton(i,unit); - numBars=numBars+1; - end - if i==last then break end - end - end - if numBars>GroupValid and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - numBars=numBars+1; - numHeaders=numHeaders+1; - end - - last = last+10 - local TankValid=numBars; - if HealBot_Config.TankHeals==1 then - if GetNumRaidMembers()>0 and CT_RA_MainTanks then - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - HeaderPos[i+1] = HEALBOT_OPTIONS_TANKHEALS - end - for j=1,10 do - if CT_RA_MainTanks[j] then - for k=1,GetNumRaidMembers() do - local unit = "raid"..k; - local PossibleMT=1; - if UnitInParty(unit) and HealBot_Config.GroupHeals==1 then - if not UnitIsUnit(unit, "player") then - PossibleMT=0; - end - end - if PossibleMT==1 then - if UnitName(unit)==CT_RA_MainTanks[j] then - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - i = i+1; - HealBot_Action_SetHealButton(i,unit); - numBars=numBars+1; - end - end - end - end - end - if i==last then break end - end - end - end - if numBars>TankValid and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - numBars=numBars+1; - numHeaders=numHeaders+1; - end - - last = last+10; - local h=50; - local TargetValid=numBars; - if HealBot_Config.TargetHeals==1 then - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - HeaderPos[i+1] = HEALBOT_OPTIONS_TARGETHEALS - end - for _,unit in ipairs(HealBot_Action_HealTarget) do - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - i = i+1; - h = h+1; - if checked_start==0 then checked_start=i; end - checked_end=i; - HealBot_Action_SetHealButton(h,unit); - local check = getglobal("HealBot_Action_HealUnit"..h.."Check"); - check.unit = unit; - check:SetChecked(1); - check:Show(); - numBars=numBars+1; - end - if i==last then break end - end - - last = last+1 - unit = HealBot_TargetName() - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal("target") then - i = i+1; - h = h+1; - if h<61 then - HealBot_Action_SetHealButton(h,"target"); - local check = getglobal("HealBot_Action_HealUnit"..h.."Check"); - - check:SetChecked(0); - check.unit = unit; - if check.unit then - if checked_start==0 then checked_start=i; end - checked_end=i; - check:Show(); - else - check:Hide(); - end - else - HealBot_Action_SetHealButton(i,"target"); - end - numBars=numBars+1; - end - end - if numBars>TargetValid and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - numBars=numBars+1; - numHeaders=numHeaders+1; - end - - last = last+40 - local ExtraValid=numBars; - if HealBot_Config.EmergencyHeals==1 then - local order = {}; - local units = {}; - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 and HealBot_Config.ExtraOrder==1 then - HeaderPos[i+1] = HEALBOT_OPTIONS_EMERGENCYHEALS - numBars=numBars+1; - numHeaders=numHeaders+1; - end - if HealBot_Config.EmergIncMonitor==1 then - if GetNumRaidMembers()>0 then - for j=1,40 do - local PossibleEmerg=1; - local unit = "raid"..j; - local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(j); - if not name then name="not known" end - if not class then class="not known" end - if not subgroup then subgroup="not known" end - - if UnitInParty(unit) and HealBot_Config.GroupHeals==1 then - PossibleEmerg=0; - end - if PossibleEmerg==1 then - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - if HealBot_Config.ExtraOrder==1 then - order[unit] = name; - elseif HealBot_Config.ExtraOrder==2 then - order[unit] = class; - elseif HealBot_Config.ExtraOrder==3 then - order[unit] = subgroup; - else - order[unit] = 0-UnitHealthMax(unit); - if UnitHealthMax(unit)>TempMaxH then TempMaxH=UnitHealthMax(unit); end - end - table.insert(units,unit); - numBars=numBars+1; - end - end - end - else - for _,unit in ipairs(HealBot_Action_HealGroup) do - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - if HealBot_Config.ExtraOrder==1 then - order[unit] = name; - elseif HealBot_Config.ExtraOrder==2 then - order[unit] = class; - elseif HealBot_Config.ExtraOrder==3 then - order[unit] = subgroup; - else - order[unit] = 0-UnitHealthMax(unit); - if UnitHealthMax(unit)>TempMaxH then TempMaxH=UnitHealthMax(unit); end - end - table.insert(units,unit); - numBars=numBars+1; - end - end - end - else - if GetNumRaidMembers()>0 then - for j=1,40 do - - local unit = "raid"..j; - local Class = UnitClass(unit); - local ProcessUnit = 0; - local PossibleEmerg=1; - local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(j); - if not name then name="not known" end - if not class then class="not known" end - if not subgroup then subgroup="not known" end - - if HealBot_EmergInc[Class]==1 then - ProcessUnit = 1; - end - if UnitInParty(unit) and HealBot_Config.GroupHeals==1 then - PossibleEmerg=0; - end - - if ProcessUnit==1 and PossibleEmerg==1 then - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - if HealBot_Config.ExtraOrder==1 then - order[unit] = name; - elseif HealBot_Config.ExtraOrder==2 then - order[unit] = class; - elseif HealBot_Config.ExtraOrder==3 then - order[unit] = subgroup; - else - order[unit] = 0-UnitHealthMax(unit); - if UnitHealthMax(unit)>TempMaxH then TempMaxH=UnitHealthMax(unit); end - end - table.insert(units,unit); - numBars=numBars+1; - end - end - end - else - for _,unit in ipairs(HealBot_Action_HealGroup) do - if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then - if HealBot_Config.ExtraOrder==1 then - order[unit] = name; - elseif HealBot_Config.ExtraOrder==2 then - order[unit] = class; - elseif HealBot_Config.ExtraOrder==3 then - order[unit] = subgroup; - else - order[unit] = 0-UnitHealthMax(unit); - if UnitHealthMax(unit)>TempMaxH then TempMaxH=UnitHealthMax(unit); end - end - table.insert(units,unit); - numBars=numBars+1; - end - end - end - end - table.sort(units,function (a,b) - if order[a]order[b] then return false end - return a0 then - for j=1,40 do - if not units[j] then break end - local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(strsub(units[j], 5)); - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 and HealBot_Config.ExtraOrder==2 and TempSort~=class then - TempSort=class - HeaderPos[i+1] = class - numBars=numBars+1; - numHeaders=numHeaders+1; - end - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 and HealBot_Config.ExtraOrder==3 and TempSort~=subgroup then - TempSort=subgroup - HeaderPos[i+1] = HEALBOT_OPTIONS_GROUPHEALS..subgroup - numBars=numBars+1; - numHeaders=numHeaders+1; - end - if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 and HealBot_Config.ExtraOrder==4 and TempMaxH>UnitHealthMax(units[j]) then - TempMaxH=TempMaxH-1000 - HeaderPos[i+1] = ">"..tostring(TempMaxH/1000).."k" - numBars=numBars+1; - numHeaders=numHeaders+1; - end - i = i+1; - HealBot_Action_SetHealButton(i,units[j]); - if i==last then break end - end - end - end - if numBars==ExtraValid+1 and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin]==1 then - HeaderPos[i+1] = nil; - numBars=numBars-1; - end - - local bpadding = (HealBot_Config.bpadding and HealBot_Config.bpadding[HealBot_Config.Current_Skin]) or 10 - OffsetY = bpadding; - OffsetX = bpadding; - MaxOffsetY=0; - - if cols>(numBars-numHeaders) then - cols=numBars-numHeaders; - end - if cols <= 0 then cols = 1 end - - local h=1; - local i=0; - local z=1; - - table.foreach(HealBot_Action_HealButtons, function (index,button) - i=i+1; - local checked=false; - local header; - - if HeaderPos[i] then - header=HeaderPos[i]; - OffsetY = HealBot_Action_PositionButton(nil,OffsetX,OffsetY,bwidth,bheight,checked,header); - if h==ceil((numBars)/cols) and z=i then checked=true; end - OffsetY = HealBot_Action_PositionButton(button,OffsetX,OffsetY,bwidth,bheight,checked,nil); - if h==ceil((numBars)/cols) and z width) then - width = txtL:GetWidth() + txtR:GetWidth() + 25 - end - end - HealBot_Tooltip:SetWidth(width) - HealBot_Tooltip:SetHeight(height) - HealBot_Tooltip:ClearAllPoints(); - if HealBot_Config.TooltipPos>1 then - if HealBot_Config.TooltipPos==2 then - HealBot_Tooltip:SetPoint("TOPRIGHT","HealBot_Action","TOPLEFT",0,0); - elseif HealBot_Config.TooltipPos==3 then - HealBot_Tooltip:SetPoint("TOPLEFT","HealBot_Action","RIGHT",0,0); - elseif HealBot_Config.TooltipPos==4 then - HealBot_Tooltip:SetPoint("BOTTOM","HealBot_Action","TOP",0,0); - else - HealBot_Tooltip:SetPoint("TOP","HealBot_Action","BOTTOM",0,0); - end - else - HealBot_Tooltip:SetPoint("BOTTOMRIGHT","WorldFrame","BOTTOMRIGHT",-105,105); - end - HealBot_Tooltip:Show(); -end - -function HealBot_Action_Tooltip_SpellInfo(spell,linenum) - local text - if HealBot_Spells[spell] then - if HealBot_Spells[spell].HealsDur>0 then - linenum=linenum+1 - HealBot_Action_Tooltip_SetLineLeft(HEALBOT_WORDS_CAST..": "..HealBot_Spells[spell].CastTime.." "..HEALBOT_WORDS_SEC..".",0.8,0.8,0.8,linenum) - HealBot_Action_Tooltip_SetLineRight("Mana: "..HealBot_Spells[spell].Mana,0.5,0.5,1,linenum) - if HealBot_Spells[spell].HealsMax>0 then - local Heals = HEALBOT_HEAL.." " - if HealBot_Spells[spell].Shield then - Heals = HEALBOT_TOOLTIP_SHIELD.." " - end - if HealBot_Spells[spell].HealsMin0 then - text=HEALBOT_HEAL.." "..HealBot_Spells[spell].HealsDur.." "..HEALBOT_WORDS_OVER.." "..HealBot_Spells[spell].Duration-HealBot_Spells[spell].CastTime.." sec." - linenum=linenum+1 - HealBot_Action_Tooltip_SetLineLeft(text,1,1,1,linenum) - end - if not HealBot_Spells[spell].Shield then - text=HEALBOT_TOOLTIP_ITEMBONUS.." +"..HealBot_GetBonus().." | "..HEALBOT_TOOLTIP_ACTUALBONUS.." +"..HealBot_Spells[spell].RealHealing.." " - linenum=linenum+1 - HealBot_Action_Tooltip_SetLineLeft(text,0.8,0.8,0.8,linenum) - end - end - end - return linenum -end - -function HealBot_Action_Tooltip_SpellSummary(spell) - local ret_val = " "; - if HealBot_Spells[spell] then - if HealBot_Spells[spell].HealsDur and HealBot_Spells[spell].HealsDur>0 then - if HealBot_Spells[spell].HealsMax and HealBot_Spells[spell].HealsMax>0 then - local Heals = " "..HEALBOT_HEAL.." "; - if HealBot_Spells[spell].Shield then - Heals = " "..HEALBOT_TOOLTIP_SHIELD.." "; - end - if (HealBot_Spells[spell].HealsMin or 0)<(HealBot_Spells[spell].HealsMax or 0) then - ret_val=ret_val..Heals..format("%d", (((HealBot_Spells[spell].HealsMin or 0)+(HealBot_Spells[spell].HealsMax or 0))/2) + (HealBot_Spells[spell].RealHealing or 0)); - else - ret_val=ret_val..Heals..format("%d", (HealBot_Spells[spell].HealsMax or 0) + (HealBot_Spells[spell].RealHealing or 0)); - end - end - if HealBot_Spells[spell].HealsExt and HealBot_Spells[spell].HealsExt>0 then - ret_val=ret_val.." HoT "..HealBot_Spells[spell].HealsDur; - end - if HealBot_Spells[spell].Mana then - ret_val=ret_val.." "..HEALBOT_WORDS_FOR.." "..HealBot_Spells[spell].Mana.." Mana"; - end - end - end - if string.len(ret_val)<5 then ret_val = " - "..spell; end - return ret_val -end - -function HealBot_Action_Tooltip_CheckForInstant(unit,spell,upd,linenum,check) - if HealBot_Spells[spell] then - if HealBot_Spells[spell].CastTime == 0 then - if HealBot_UnitAffected(unit,HealBot_Spells[spell].Buff) then return check,linenum end; - if HealBot_UnitAffected(unit,HealBot_Spells[spell].Debuff) then return check,linenum end; - if upd=="upd" then - linenum=linenum+1 - HealBot_Action_Tooltip_SetLineLeft(" "..spell,1,1,1,linenum) - end - else - return check,linenum; - end - else - return check,linenum - end - return check+1,linenum; -end - -function HealBot_Action_Tooltip_SetLineLeft(Text,R,G,B,linenum) - local txtL = getglobal("HealBot_TooltipTextL" .. linenum) - txtL:SetTextColor(R,G,B) - txtL:SetText(Text) - txtL:Show() -end - -function HealBot_Action_Tooltip_SetLineRight(Text,R,G,B,linenum) - local txtR = getglobal("HealBot_TooltipTextR" .. linenum) - txtR:SetTextColor(R,G,B) - txtR:SetText(Text) - txtR:Show() -end - -function HealBot_Action_Tooltip_ClearLines() - for j=1,30 do - local txtL = getglobal("HealBot_TooltipTextL" .. j) - local txtR = getglobal("HealBot_TooltipTextR" .. j) - txtL:SetText(" ") - txtR:SetText(" ") - txtL:Hide() - txtR:Hide() - end -end - -function HealBot_Action_ShowTooltip(this) - if HealBot_Config.ShowTooltip==0 then return end - if not this.unit then return end; - if not this:IsEnabled() then return end; - -- GameTooltip_SetDefaultAnchor(HealBot_Tooltip,this); - - - HealBot_Action_TooltipUnit = this.unit; - HealBot_Action_RefreshTooltip(this.unit); -end - -function HealBot_Action_HideTooltip(this) - if HealBot_Config.ShowTooltip==0 then return end - HealBot_Tooltip:Hide(); - HealBot_Action_TooltipUnit = nil; -end - -function HealBot_Action_Refresh(unit) - if (UnitIsDeadOrGhost("player")) or (UnitOnTaxi("player")) then - if HealBot_Config.AutoClose==1 and HealBot_Config.ActionVisible~=0 then - HealBot_Action:Hide(); - else - HealBot_Action_RefreshButtons(unit); - end - return; - end - HealBot_Action_RefreshButtons(unit); - if not HealBot_IsFighting then - if (HealBot_Action_MustHealSome()) then - HealBot_Action:Show(); - elseif HealBot_AbortButton==0 then - HealBot_Action:Show(); - elseif (not HealBot_Action_ShouldHealSome()) then - if HealBot_AbortButton==1 and HealBot_Config.AutoClose==1 and HealBot_Config.ActionVisible~=0 then - HealBot_Action:Hide(); - end - end - end -end +-- Refresh and reset functions moved to HealBot_View_Layout.lua function HealBot_Action_SpellPattern(button) local combos = HealBot_Config.KeyCombo[UnitClass("player")] diff --git a/HealBot_Controller_Aura.lua b/HealBot_Controller_Aura.lua new file mode 100644 index 0000000..0172e3c --- /dev/null +++ b/HealBot_Controller_Aura.lua @@ -0,0 +1,182 @@ +-- HealBot_Controller_Aura.lua +-- Handles tracking buffs/debuffs (auras) on group/raid units + +HealBot_MissingBuffs = {} + +function HealBot_UnitAffected(unit, effect) + if not effect then return nil; end + local i = 1 + while true do + local buff = UnitBuff(unit, i) + if not buff then + break + end + if buff == effect then + return buff + end + i = i + 1 + end + i = 1 + while true do + local debuff = UnitDebuff(unit, i) + if not debuff then + break + end + if debuff == effect then + return debuff + end + i = i + 1 + end + return nil; +end + +function HealBot_CheckShamanWeaponBuff(spellName) + local hasMainHandEnchant, _, _, hasOffHandEnchant = GetWeaponEnchantInfo() + if hasMainHandEnchant or hasOffHandEnchant then + return true + end + return false +end + +function HealBot_CheckBuffs(unit) + if HealBot_Config.BuffWatch ~= 1 then + HealBot_MissingBuffs[unit] = nil + return + end + + local inCombat = UnitAffectingCombat("player") or UnitAffectingCombat(unit) + if inCombat and HealBot_Config.BuffWatchInCombat ~= 1 then + HealBot_MissingBuffs[unit] = nil + return + end + + local myClass = UnitClass("player") + if not HealBot_Buff_Spells[myClass] then return end + + if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then + HealBot_MissingBuffs[unit] = nil + return + end + + -- Group Buff Equivalents + local HealBot_Buff_Equivalents = { + [HEALBOT_POWER_WORD_FORTITUDE] = HEALBOT_PRAYER_OF_FORTITUDE, + [HEALBOT_DIVINE_SPIRIT] = HEALBOT_PRAYER_OF_SPIRIT, + [HEALBOT_SHADOW_PROTECTION] = HEALBOT_PRAYER_OF_SHADOW_PROTECTION, + [HEALBOT_ARCANE_INTELLECT] = HEALBOT_ARCANE_BRILLIANCE, + [HEALBOT_MARK_OF_THE_WILD] = HEALBOT_GIFT_OF_THE_WILD, + } + + -- Gather buffs on unit + local hasBuff = {} + local i = 1 + while true do + local buffTexture = UnitBuff(unit, i) + if not buffTexture then break end + HealBot_ScanTooltip:SetOwner(UIParent, "ANCHOR_NONE") + HealBot_ScanTooltip:ClearLines() + HealBot_ScanTooltip:SetUnitBuff(unit, i) + local buffName = HealBot_ScanTooltipTextLeft1:GetText() + if buffName then + hasBuff[buffName] = true + end + i = i + 1 + end + HealBot_ScanTooltip:Hide() + + HealBot_MissingBuffs[unit] = nil + + if HealBot_Config.BuffDropDowns and HealBot_Config.BuffDropDowns[myClass] then + for j = 1, 8 do + local val = HealBot_Config.BuffDropDowns[myClass][j] + if val and val > 0 then + local isSelfOnly = (HealBot_Config.BuffWatchSelf and HealBot_Config.BuffWatchSelf[j] == 1) + if not (isSelfOnly and unit ~= "player") then + local spellName = HealBot_Buff_Spells[myClass][val] + + local hasIt = hasBuff[spellName] + if not hasIt and HealBot_Buff_Equivalents[spellName] then + hasIt = hasBuff[HealBot_Buff_Equivalents[spellName]] + end + + if not hasIt then + if myClass == "SHAMAN" and unit == "player" and string.find(spellName, " Weapon") then + hasIt = HealBot_CheckShamanWeaponBuff(spellName) + end + end + + if not hasIt then + HealBot_MissingBuffs[unit] = spellName + break + end + end + end + end + end +end + +function HealBot_OnEvent_UnitAura(this, unit) + local DebuffType; + + if HealBot_Heals[unit] and unit ~= "target" then + HealBot_UnitIcons[unit] = {} + local iconCount = 0 + + local HealBot_TrackedHoTs = { + ["Interface\\Icons\\Spell_Holy_Renew"] = true, + ["Interface\\Icons\\Spell_Nature_Rejuvenation"] = true, + ["Interface\\Icons\\Spell_Nature_ResistNature"] = true, + ["Interface\\Icons\\Spell_Holy_PowerWordShield"] = true, + ["Interface\\Icons\\Spell_Holy_SealOfProtection"] = true, + ["Interface\\Icons\\Spell_Holy_Excorcism"] = true, + } + + local i = 1; + while true do + local debuff, tmp, debuff_type = UnitDebuff(unit, i, 1) + if debuff then + if iconCount < 5 then + iconCount = iconCount + 1 + HealBot_UnitIcons[unit][iconCount] = debuff + end + if HealBot_CDCInc[UnitClass(unit)] == 1 and HealBot_DebuffWatch[debuff_type] == "YES" then + HealBot_UnitDebuff[unit] = debuff_type + DebuffType = debuff_type; + if HealBot_DebuffPriority[debuff_type] then + break + end + end + i = i + 1; + else + if i == 1 then HealBot_UnitDebuff[unit] = nil; end + break + end + end + + local b = 1 + while true do + local buff = UnitBuff(unit, b) + if not buff then break end + if HealBot_TrackedHoTs[buff] and iconCount < 5 then + iconCount = iconCount + 1 + HealBot_UnitIcons[unit][iconCount] = buff + end + b = b + 1 + end + + if HealBot_UnitDebuff[unit] then + if DebuffType and HealBot_Range_Check(unit, 27) == 1 then + if HealBot_Config.ShowDebuffWarning == 1 then + UIErrorsFrame:AddMessage(UnitName(unit) .. " suffers from " .. DebuffType, + HealBot_Config.CDCBarColour[DebuffType].R, + HealBot_Config.CDCBarColour[DebuffType].G, + HealBot_Config.CDCBarColour[DebuffType].B, + 1, UIERRORS_HOLD_TIME); + end + if HealBot_Config.SoundDebuffWarning == 1 then HealBot_PlaySound(HealBot_Config.SoundDebuffPlay); end + end + end + HealBot_CheckBuffs(unit); + HealBot_RecalcHeals(unit); + end +end diff --git a/HealBot_Controller_Comms.lua b/HealBot_Controller_Comms.lua new file mode 100644 index 0000000..ef8434a --- /dev/null +++ b/HealBot_Controller_Comms.lua @@ -0,0 +1,114 @@ +-- HealBot_Controller_Comms.lua +-- Handles network sync (addon messaging), errors, and chat debugging + +function HealBot_Get_DebugChan() + local index = GetChannelName("HBmsg"); + if (index > 0) then + return index; + else + return nil; + end +end + +function HealBot_AddChat(msg) + local chanid = HealBot_Get_DebugChan(); + if chanid and HealBot_SpamCnt < 3 then + HealBot_SpamCnt = HealBot_SpamCnt + 1; + local hour, minute = GetGameTime(); + if minute == 0 then + msg = "[" .. hour .. ":00] " .. msg; + elseif minute < 10 then + msg = "[" .. hour .. ":0" .. minute .. "] " .. msg; + else + msg = "[" .. hour .. ":" .. minute .. "] " .. msg; + end + SendChatMessage(msg, "CHANNEL", nil, chanid); + elseif ( DEFAULT_CHAT_FRAME ) then + DEFAULT_CHAT_FRAME:AddMessage(msg); + end +end + +function HealBot_AddDebug(msg) + local chanid = HealBot_Get_DebugChan(); + if chanid and HealBot_SpamCnt < 3 then + HealBot_SpamCnt = HealBot_SpamCnt + 1; + local hour, minute = GetGameTime(); + if minute == 0 then + msg = "[" .. hour .. ":00] DEBUG: " .. msg; + elseif minute < 10 then + msg = "[" .. hour .. ":0" .. minute .. "] DEBUG: " .. msg; + else + msg = "[" .. hour .. ":" .. minute .. "] DEBUG: " .. msg; + end + SendChatMessage(msg, "CHANNEL", nil, chanid); + end +end + +function HealBot_Report_Error(msg) + if HealBot_ErrorCnt < 28 then + HealBot_ErrorCnt = HealBot_ErrorCnt + 1; + ShowUIPanel(HealBot_Error); + HealBot_ErrorsIn(msg, HealBot_ErrorCnt); + end +end + +function HealBot_AddError(msg) + UIErrorsFrame:AddMessage(msg, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME); + HealBot_AddDebug(msg); +end + +function HealBot_SendAddonMessage(prefix, text) + if GetNumRaidMembers() > 0 then + SendAddonMessage(prefix, text, "RAID") + elseif GetNumPartyMembers() > 0 then + SendAddonMessage(prefix, text, "PARTY") + end +end + +function HealBot_OnEvent_AddonMsg(this, addon_id, inc_msg, dist_target, sender_id) + if addon_id == HEALBOT_ADDON_ID then + local tmpTest, unitname, heal_val + tmpTest, tmpTest, unitname, heal_val = string.find(inc_msg, ">> (%a+) <<=>> (.%d+) <<" ); + if heal_val then + if not HealBot_HealsIn[unitname] then + HealBot_HealsIn[unitname] = 0; + end + HealBot_Healers[sender_id] = ">> " .. unitname .. " <<=>> " .. heal_val .. " <<"; + HealBot_HealsIn[unitname] = HealBot_HealsIn[unitname] + tonumber(heal_val); + if tonumber(heal_val) > 0 then + HealBot_RecalcHeals(HealBot_FindUnitID(unitname)) + elseif HealBot_HealsIn[unitname] < 0 then + HealBot_HealsIn[unitname] = 0; + end + end + elseif addon_id == "HealBot" then + local tmpTest, datatype, datamsg, sender + local PName = UnitName("player"); + tmpTest, tmpTest, datatype, sender, datamsg = string.find(inc_msg, ">> (%a+) <<=>> (%a+) <<=>> (.+)"); + if datatype == "RequestVersion" then + HealBot_SendAddonMessage("HealBot", ">> SendVersion <<=>> " .. sender .. " <<=>> Version=" .. HEALBOT_VERSION); + elseif datatype == "SendVersion" and PName == sender then + HealBot_AddChat(sender_id .. ": " .. datamsg); + end + elseif addon_id == "CTRA" then + if ( string.sub(inc_msg, 1, 3) == "RES" ) then + if ( inc_msg == "RESNO" ) then + HealBot_AddDebug(sender_id .. " Stopped ressing"); + for unit, resser in pairs(HealBot_Ressing) do + if resser == sender_id then + HealBot_Ressing[unit] = nil; + HealBot_RecalcHeals(HealBot_FindUnitID(unit)); + end + end + else + local unitname, tmpTest + tmpTest, tmpTest, unitname = string.find(inc_msg, "^RES (.+)$"); + if ( unitname ) then + HealBot_AddDebug(sender_id .. " is ressing " .. unitname); + HealBot_Ressing[unitname] = sender_id; + HealBot_RecalcHeals(HealBot_FindUnitID(unitname)); + end + end + end + end +end diff --git a/HealBot_Controller_Events.lua b/HealBot_Controller_Events.lua new file mode 100644 index 0000000..7bd90f4 --- /dev/null +++ b/HealBot_Controller_Events.lua @@ -0,0 +1,383 @@ +-- HealBot_Controller_Events.lua +-- Manages WoW Frame events and periodic updates, routing to respective services + +HealBot_View_DirtyUnits = {} +local HealBot_Timer1, HealsIn_Timer = 0, 0; + +function HealBot_OnLoad(this) + this:RegisterEvent("VARIABLES_LOADED"); + + SLASH_HEALBOT1 = "/healbot"; + SLASH_HEALBOT2 = "/hb"; + SlashCmdList["HEALBOT"] = function(msg) + HealBot_SlashCmd(msg); + end + HealBot_AddError(HEALBOT_ADDON .. HEALBOT_LOADED); + + -- Register MVC Observers + HealBot_Model:RegisterObserver("UNIT_HEALTH_CHANGED", function(unitID) + HealBot_View_DirtyUnits[unitID] = true + end) + HealBot_Model:RegisterObserver("UNIT_POWER_CHANGED", function(unitID) + HealBot_View_DirtyUnits[unitID] = true + end) + HealBot_Model:RegisterObserver("UNIT_AURA_CHANGED", function(unitID) + HealBot_View_DirtyUnits[unitID] = true + end) + HealBot_Model:RegisterObserver("ROSTER_CHANGED", function() + Delay_RecalcParty = 1 + end) +end + +function HealBot_RegisterThis(this) + -- Deprecated / not used +end + +function HealBot_OnUpdate(this, arg1) + -- Process Dirty Queue for MVC View + if next(HealBot_View_DirtyUnits) ~= nil then + for unitID in pairs(HealBot_View_DirtyUnits) do + HealBot_Action_RefreshButtons(unitID) + HealBot_View_DirtyUnits[unitID] = nil + end + end + + HealBot_Timer1 = HealBot_Timer1 + arg1; + if HealBot_Timer1 >= 2.5 then + if not HealBot_IsFighting then + HealsIn_Timer = HealsIn_Timer + 1; + if HealsIn_Timer >= 10 then + HealBot_HealsIn = {}; + HealBot_Healers = {}; + HealsIn_Timer = 0; + end + + if HealBot_EquipChangeTimer > 0 then + HealBot_EquipChangeTimer = HealBot_EquipChangeTimer - arg1 + if HealBot_EquipChangeTimer <= 0 then + HealBot_EquipChangeTimer = 0 + HealBot_BonusScanner:ScanEquipment() + CalcEquipBonus = true; + HealBot_RecalcSpells(); + end + end + + if InitSpells > 1 then + InitSpells = InitSpells + 1; + if InitSpells > 2 then + local cnt = HealBot_InitSpells(); + InitSpells = 0; + HealBot_RecalcSpells(); + end + end + if Delay_RecalcParty > 0 then + Delay_RecalcParty = Delay_RecalcParty + 1 + if Delay_RecalcParty > 1 then + Delay_RecalcParty = 0; + HealBot_RecalcParty(); + end + end + else + HealsIn_Timer = 0; + end + HealBot_Timer1 = 0; + HealBot_SpamCnt = 0; + end +end + +local HealBot_EventHandlers = { + ["UNIT_HEALTH"] = function(this, arg1) + if HealBot_Model:UpdateUnitHealth(arg1) then + HealBot_Model:NotifyObservers("UNIT_HEALTH_CHANGED", arg1) + end + HealBot_OnEvent_UnitHealth(this, arg1) + end, + ["UNIT_MANA"] = function(this, arg1) + if HealBot_Model:UpdateUnitPower(arg1) then + HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) + end + if (arg1 == "player") then HealBot_RecalcHeals(); end + HealBot_Action_RefreshButtons(arg1); + end, + ["UNIT_RAGE"] = function(this, arg1) + if HealBot_Model:UpdateUnitPower(arg1) then + HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) + end + if (arg1 == "player") then HealBot_RecalcHeals(); end + HealBot_Action_RefreshButtons(arg1); + end, + ["UNIT_ENERGY"] = function(this, arg1) + if HealBot_Model:UpdateUnitPower(arg1) then + HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) + end + if (arg1 == "player") then HealBot_RecalcHeals(); end + HealBot_Action_RefreshButtons(arg1); + end, + ["UNIT_DISPLAYPOWER"] = function(this, arg1) + if HealBot_Model:UpdateUnitPower(arg1) then + HealBot_Model:NotifyObservers("UNIT_POWER_CHANGED", arg1) + end + if (arg1 == "player") then HealBot_RecalcHeals(); end + HealBot_Action_RefreshButtons(arg1); + end, + ["UNIT_AURA"] = function(this, arg1) + HealBot_Model:MarkAuraChanged(arg1) + HealBot_Model:NotifyObservers("UNIT_AURA_CHANGED", arg1) + HealBot_OnEvent_UnitAura(this, arg1) + end, + ["PLAYER_TARGET_CHANGED"] = function(this) + if HealBot_Model:UpdateUnitIdentity("target") then + HealBot_Model:NotifyObservers("ROSTER_CHANGED", "target") + end + HealBot_Model:UpdateUnitStatus("target") + HealBot_Model:UpdateUnitHealth("target") + HealBot_Model:UpdateUnitPower("target") + HealBot_OnEvent_PlayerTargetChanged(this) + end, + ["PARTY_MEMBERS_CHANGED"] = function(this) + for _, unit in ipairs(HealBot_Model.partyMembers) do + HealBot_Model:RefreshUnit(unit) + end + for _, unit in ipairs(HealBot_Model.raidMembers) do + HealBot_Model:RefreshUnit(unit) + end + HealBot_Model:NotifyObservers("ROSTER_CHANGED") + HealBot_OnEvent_PartyMembersChanged(this) + end, + ["PLAYER_ENTERING_WORLD"] = function(this) + HealBot_Model:RefreshUnit("player") + HealBot_Model:RefreshUnit("pet") + HealBot_OnEvent_PlayerEnteringWorld(this) + end, + ["VARIABLES_LOADED"] = function(this) + HealBot_OnEvent_VariablesLoaded(this) + end, + -- Legacy pass-throughs + ["CHAT_MSG_ADDON"] = function(this, arg1, arg2, arg3, arg4) HealBot_OnEvent_AddonMsg(this, arg1, arg2, arg3, arg4) end, + ["SPELLCAST_START"] = function(this, arg1, arg2) HealBot_OnEvent_SpellcastStart(this, arg1, arg2) end, + ["SPELLCAST_STOP"] = function(this) HealBot_OnEvent_SpellcastStop(this) end, + ["SPELLCAST_INTERRUPTED"] = function(this) HealBot_OnEvent_SpellcastStop(this) end, + ["SPELLCAST_FAILED"] = function(this) HealBot_OnEvent_SpellcastStop(this) end, + ["PLAYER_REGEN_DISABLED"] = function(this) HealBot_OnEvent_PlayerRegenDisabled(this) end, + ["PLAYER_REGEN_ENABLED"] = function(this) HealBot_OnEvent_PlayerRegenEnabled(this) end, + ["BAG_UPDATE_COOLDOWN"] = function(this, arg1) HealBot_OnEvent_BagUpdateCooldown(this, arg1) end, + ["BAG_UPDATE"] = function(this, arg1) HealBot_OnEvent_BagUpdate(this, arg1) end, + ["PARTY_MEMBER_DISABLE"] = function(this, arg1) HealBot_OnEvent_PartyMemberDisable(this, arg1) end, + ["PARTY_MEMBER_ENABLE"] = function(this, arg1) HealBot_OnEvent_PartyMemberEnable(this, arg1) end, + ["CHAT_MSG_SYSTEM"] = function(this, arg1) HealBot_OnEvent_SystemMsg(this, arg1) end, + ["ZONE_CHANGED_NEW_AREA"] = function(this) HealBot_OnEvent_ZoneChanged(this) end, + ["UPDATE_INVENTORY_ALERTS"] = function(this) HealBot_OnEvent_PlayerEquipmentChanged(this) end, + ["UNIT_INVENTORY_CHANGED"] = function(this, arg1) HealBot_OnEvent_PlayerEquipmentChanged2(this, arg1) end, + ["PET_BAR_SHOWGRID"] = function(this) HealBot_OnEvent_PartyMembersChanged(this) end, + ["PET_BAR_HIDEGRID"] = function(this) HealBot_OnEvent_PartyMembersChanged(this) end, + ["SPELLS_CHANGED"] = function(this, arg1) HealBot_OnEvent_SpellsChanged(this, arg1) end +} + +function HealBot_OnEvent(this, event, arg1, arg2, arg3, arg4) + local handler = HealBot_EventHandlers[event] + if handler then + handler(this, arg1, arg2, arg3, arg4) + else + HealBot_AddDebug("OnEvent (" .. event .. ")") + end +end + +function HealBot_OnEvent_VariablesLoaded(this) + local class = HealBot_UnitClass("player") + + table.foreach(HealBot_ConfigDefaults, function (key, val) + if not HealBot_Config[key] then + HealBot_Config[key] = val; + end + if type(val) == "table" and type(HealBot_Config[key]) == "table" then + for k, v in pairs(val) do + if HealBot_Config[key][k] == nil then + HealBot_Config[key][k] = v + end + end + end + end); + + local foundModern = false + if HealBot_Config.Skins then + for _, skin in ipairs(HealBot_Config.Skins) do + if skin == "Modern Flat" then foundModern = true break end + end + if not foundModern then + table.insert(HealBot_Config.Skins, "Modern Flat") + end + end + + HealBot_InitData(); + + if class == "PRIEST" or class == "DRUID" or class == "PALADIN" or class == "SHAMAN" then + HealBot_BonusScanner:ScanEquipment(); + + if HealBot_Config.ActionVisible == 1 then HealBot_Action:Show() end + + this:RegisterEvent("ZONE_CHANGED_NEW_AREA"); + this:RegisterEvent("PLAYER_REGEN_DISABLED"); + this:RegisterEvent("PLAYER_REGEN_ENABLED"); + this:RegisterEvent("PLAYER_TARGET_CHANGED"); + this:RegisterEvent("PARTY_MEMBERS_CHANGED"); + this:RegisterEvent("PARTY_MEMBER_DISABLE"); + this:RegisterEvent("PARTY_MEMBER_ENABLE"); + this:RegisterEvent("PET_BAR_SHOWGRID"); + this:RegisterEvent("PET_BAR_HIDEGRID"); + this:RegisterEvent("UNIT_HEALTH"); + this:RegisterEvent("UNIT_MANA"); + this:RegisterEvent("UNIT_RAGE"); + this:RegisterEvent("UNIT_ENERGY"); + this:RegisterEvent("UNIT_DISPLAYPOWER"); + this:RegisterEvent("SPELLS_CHANGED"); + this:RegisterEvent("SPELLCAST_START"); + this:RegisterEvent("SPELLCAST_STOP"); + this:RegisterEvent("SPELLCAST_INTERRUPTED"); + this:RegisterEvent("SPELLCAST_FAILED"); + this:RegisterEvent("BAG_UPDATE"); + this:RegisterEvent("BAG_UPDATE_COOLDOWN"); + this:RegisterEvent("UNIT_AURA"); + this:RegisterEvent("UPDATE_INVENTORY_ALERTS"); + this:RegisterEvent("UNIT_INVENTORY_CHANGED"); + this:RegisterEvent("CHAT_MSG_ADDON"); + this:RegisterEvent("CHAT_MSG_SYSTEM"); + this:RegisterEvent("PLAYER_ENTERING_WORLD"); + InitSpells = 2; + end +end + +function HealBot_OnEvent_UnitHealth(this, unit) + if (not HealBot_Heals[unit]) then return end + HealBot_CheckCasting(unit); + HealBot_RecalcHeals(unit); + if unit == HealBot_Action_TooltipUnit then + HealBot_Action_RefreshTooltip(HealBot_Action_TooltipUnit); + end +end + +function HealBot_OnEvent_UnitMana(this, unit) + if (unit ~= "player") then return end + HealBot_RecalcHeals(); +end + +function HealBot_OnEvent_ZoneChanged(this) + HealBot_ResetRangeScale(); + Delay_RecalcParty = 1; +end + +function HealBot_OnEvent_PlayerRegenDisabled(this) + HealBot_RecalcParty(); + if (UnitIsDeadOrGhost("player")) or (UnitOnTaxi("player")) then + if HealBot_Config.AutoClose == 1 and HealBot_Config.ActionVisible ~= 0 then HealBot_Action:Hide(); end; + else + HealBot_Action:Show(); + HealBot_IsFighting = true; + end +end + +function HealBot_OnEvent_PlayerRegenEnabled(this) + HealBot_IsFighting = false; + Delay_RecalcParty = 1; +end + +function HealBot_OnEvent_PlayerTargetChanged(this) + HealBot_RecalcParty(); +end + +function HealBot_OnEvent_PartyMembersChanged(this) + Delay_RecalcParty = 1; +end + +function HealBot_OnEvent_PartyMemberDisable(this, unit) + HealBot_RecalcHeals(); +end + +function HealBot_OnEvent_SystemMsg(this, msg) + if type(msg) == "string" then + local tmpTest, tmpTest, deserter = string.find(msg, HB_HASLEFTRAID); + if not deserter then + local tmpTest, tmpTest, deserter = string.find(msg, HB_HASLEFTPARTY); + end + if deserter then + if (HealBot_Healers[deserter]) then + local tmpTest, unitname, heal_val, heal_valn + tmpTest, tmpTest, unitname, heal_val = string.find(HealBot_Healers[deserter], ">> (%a+) <<=>> (.%d+) <<" ); + heal_valn = tonumber(heal_val) + HealBot_Healers[deserter] = nil; + HealBot_AddDebug("Healer " .. deserter .. " left the group - Last known activity was heal " .. unitname .. " for " .. heal_val .. " << trapped in event SystemMsg"); + if heal_valn > 0 and HealBot_HealsIn[unitname] then + HealBot_HealsIn[unitname] = HealBot_HealsIn[unitname] - heal_valn; + if HealBot_HealsIn[unitname] < 0 then + HealBot_HealsIn[unitname] = 0; + end + end + end + elseif msg == HB_YOULEAVETHEGROUP or msg == HB_YOULEAVETHERAID then + Delay_RecalcParty = 1; + end + end +end + +function HealBot_OnEvent_PartyMemberEnable(this, unit) + HealBot_RecalcHeals(); +end + +function HealBot_OnEvent_PlayerEquipmentChanged(this) + HealBot_EquipChangeTimer = 1; +end + +function HealBot_OnEvent_PlayerEquipmentChanged2(this, unit) + if unit == "player" then + HealBot_EquipChangeTimer = 1; + end +end + +function HealBot_OnEvent_SpellsChanged(this, arg1) + if arg1 then return; end + HealBot_AddDebug("HB: SpellsChanged"); + InitSpells = 2; +end + +function HealBot_OnEvent_TalentsChanged(this, arg1) + HealBot_AddDebug("HB: TalentsChanged"); +end + +function HealBot_OnEvent_BagUpdate(this, bag) + if HealBot_EquipChangeTimer == 0 then + HealBot_RecalcSpells(); + end +end + +function HealBot_OnEvent_BagUpdateCooldown(this, bag) + if not bag then + bag = "undef" + elseif HealBot_EquipChangeTimer == 0 then + HealBot_RecalcSpells(); + end +end + +function HealBot_OnEvent_PlayerEnteringWorld(this) + HealBot_IsFighting = false; +end + +function HealBot_OnEvent_SpellcastStart(this, spell, duration) + HealBot_IsCasting = true; + HealBot_RecalcHeals(); + HealBot_CheckCasting(); + if spell == HEALBOT_RESURRECTION or spell == HEALBOT_ANCESTRALSPIRIT or spell == HEALBOT_REBIRTH or spell == HEALBOT_REDEMPTION then + if UnitName("Target") then + HealBot_SendAddonMessage("CTRA", "RES " .. UnitName("Target")); + HealBot_IamRessing = true; + end + end +end + +function HealBot_OnEvent_SpellcastStop(this) + HealBot_IsCasting = false; + HealBot_StopCasting(); + HealBot_RecalcHeals(); + if HealBot_IamRessing then + HealBot_SendAddonMessage("CTRA", "RESNO"); + HealBot_IamRessing = false; + end +end diff --git a/HealBot_Controller_Range.lua b/HealBot_Controller_Range.lua new file mode 100644 index 0000000..e34a3e3 --- /dev/null +++ b/HealBot_Controller_Range.lua @@ -0,0 +1,42 @@ +-- HealBot_Controller_Range.lua +-- Handles periodic range checking for units + +local _scale = 0 + +function HealBot_ResetRangeScale() + _scale = 0 +end + +function HealBot_Range_Check(unit, range) + local return_val = 0; + if not range then + range = 40; + end + if ( unit == "player" ) then + return_val = 1; + elseif ( UnitIsVisible(unit) == 1 ) then + local tx, ty = GetPlayerMapPosition(unit) + local dist + if tx > 0 or ty > 0 then + local px, py = GetPlayerMapPosition("player") + dist = math.sqrt((px - tx)^2 + (py - ty)^2) + if dist > _scale and (px > 0 or py > 0) then + if ( CheckInteractDistance(unit, 4) ) then + _scale = dist + end + end + if dist <= (_scale * range / 27) then + return_val = 1 + end + else + if ( HealBot_Config.QualityRange == 1 ) or range <= 27 then + if ( CheckInteractDistance(unit, 4) ) then + return_val = 1; + end + else + return_val = 1; + end + end + end + return return_val; +end diff --git a/HealBot_Controller_Spells.lua b/HealBot_Controller_Spells.lua new file mode 100644 index 0000000..08116dc --- /dev/null +++ b/HealBot_Controller_Spells.lua @@ -0,0 +1,636 @@ +-- HealBot_Controller_Spells.lua +-- Centralized Spell management, calculation, and casting logic + +HealBot_CastingSpell = nil; +HealBot_CastingTarget = nil; + +local HealBot_Health60 = { + ["DRUID"] = 3500, + ["MAGE"] = 2500, + ["HUNTER"] = 3500, + ["PALADIN"] = 4000, + ["PRIEST"] = 2500, + ["ROGUE"] = 3500, + ["SHAMAN"] = 3800, + ["WARLOCK"] = 3500, + ["WARRIOR"] = 5000, +} + +function HealBot_GetSpellName(id) + if (not id) then + return nil; + end + local spellName, subSpellName = GetSpellName(id, BOOKTYPE_SPELL); + if (not spellName) then + return nil; + end + if (not subSpellName or subSpellName == "") then + return spellName; + end + return spellName .. " (" .. subSpellName .. ")"; +end + +function HealBot_GetSpellId(spell) + local id, idd = 1, 0; + while true do + local spellName, subSpellName = GetSpellName(id, BOOKTYPE_SPELL); + if (spellName) then + if (spell == spellName .. " (" .. subSpellName .. ")") or (spell == spellName .. "(" .. subSpellName .. ")") then + return id; + end + if (spell == spellName) then + idd = id; + end + else + break + end + id = id + 1; + end + if idd > 0 then + return idd + else + return nil; + end +end + +function HealBot_CastSpellByName(spell) + if (HealBot_Spells[spell] and HealBot_Spells[spell].BagSlot) then + HealBot_UseItem(spell); + return; + end + local id; + if not HealBot_Spells[spell] then + id = HealBot_GetSpellId(spell); + elseif HealBot_Spells[spell].id then + id = HealBot_Spells[spell].id + else + id = HealBot_GetSpellId(spell); + end + if (not id) then + return; + end + CastSpell(id, BOOKTYPE_SPELL); +end + +function HealBot_StartCasting(spell, target, ttype) + HealBot_CastSpellByName(spell); + HealBot_CastingSpell = spell; + HealBot_CastingTarget = target; + if ( SpellCanTargetUnit(target) ) then + SpellTargetUnit(target); + ttype = "fired"; + elseif SpellIsTargeting() then + SpellTargetUnit(target); + SpellStopTargeting() + elseif ttype == "direct" then + if ( CheckInteractDistance(target, 4) ) then + ttype = "fired"; + end + end + + if HealBot_Config.ChatMessages then + local tName = target + if target == "target" then tName = HealBot_TargetName() or "target" else tName = UnitName(target) end + if not tName then tName = target end + local baseSpell = spell + local parenIndex = string.find(spell, "%(") + if parenIndex then + baseSpell = string.sub(spell, 1, parenIndex - 1) + end + for i = 1, 5 do + local msgConf = HealBot_Config.ChatMessages[i] + if msgConf and msgConf.Spell == baseSpell and msgConf.Channel ~= "None" then + local msg = msgConf.Message or "" + msg = string.gsub(msg, "#Spell#", spell) + msg = string.gsub(msg, "#Target#", tName) + local chan = msgConf.Channel + if chan == "Say" then + SendChatMessage(msg, "SAY") + elseif chan == "Party" and GetNumPartyMembers() > 0 then + SendChatMessage(msg, "PARTY") + elseif chan == "Raid" and GetNumRaidMembers() > 0 then + SendChatMessage(msg, "RAID") + elseif chan == "Whisper" and UnitIsPlayer(target) then + SendChatMessage(msg, "WHISPER", nil, tName) + end + end + end + end + if ttype == "fired" and HealBot_Spells[spell] then + if HealBot_Spells[spell].CastTime > 1 then + HealValue = HealBot_Spells[spell].HealsDur; + HealBot_SendAddonMessage(HEALBOT_ADDON_ID, ">> " .. UnitName(target) .. " <<=>> " .. HealValue .. " << "); + end + end +end + +function HealBot_StopCasting() + if HealBot_CastingTarget then + if HealBot_HealsIn[UnitName(HealBot_CastingTarget)] then + if HealValue > 0 then + HealBot_SendAddonMessage(HEALBOT_ADDON_ID, ">> " .. UnitName(HealBot_CastingTarget) .. " <<=>> " .. 0 - HealValue .. " << "); + HealValue = 0; + end + end + end + HealBot_CastingSpell = nil; + HealBot_CastingTarget = nil; + local bar = HealBot_Action_HealthBar(HealBot_Action_AbortButton); + local ar = HealBot_Config.babortcolr[HealBot_Config.Current_Skin] or 0.1; + local ag = HealBot_Config.babortcolg[HealBot_Config.Current_Skin] or 0.1; + local ab = HealBot_Config.babortcolb[HealBot_Config.Current_Skin] or 0.5; + local aa = HealBot_Config.babortcola[HealBot_Config.Current_Skin] or 1; + bar.txt = getglobal(bar:GetName() .. "_text"); + bar:SetStatusBarColor(ar, ag, ab, 0); + local sr = HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; + local sg = HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; + local sb = HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; + local sa = HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; + bar.txt:SetTextColor(sr, sg, sb, sa); +end + +function HealBot_UnitHealth(unit) + local Current, Desired = UnitHealth(unit), UnitHealthMax(unit); + if unit == 'target' and Desired == 100 then + local class, level = HealBot_UnitClass(unit), UnitLevel(unit); + if HealBot_Health60[class] and level > 0 then + Desired = math.floor(HealBot_Health60[class] / 60 * level + 0.5) + else + Desired = UnitHealthMax('player'); + end + Current = Desired / 100 * Current; + end + return Current, Desired; +end + +function HealBot_CheckCasting(unit) + if not HealBot_CastingSpell or HealBot_AlwaysHeal() then return nil end + if not HealBot_Spells[HealBot_CastingSpell] then return nil end + if not unit then unit = HealBot_CastingTarget end + if unit ~= HealBot_CastingTarget then return nil end + + local bar = HealBot_Action_HealthBar(HealBot_Action_AbortButton); + local ar = HealBot_Config.babortcolr[HealBot_Config.Current_Skin] or 0.1; + local ag = HealBot_Config.babortcolg[HealBot_Config.Current_Skin] or 0.1; + local ab = HealBot_Config.babortcolb[HealBot_Config.Current_Skin] or 0.5; + local aa = HealBot_Config.babortcola[HealBot_Config.Current_Skin] or 1; + bar.txt = getglobal(bar:GetName() .. "_text"); + + if HealBot_IsCasting == false and HealBot_AbortButton == 0 then + bar:SetStatusBarColor(ar, ag, ab, 0); + local sr = HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; + local sg = HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; + local sb = HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; + local sa = HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; + bar.txt:SetTextColor(sr, sg, sb, sa); + return nil + end + + local Current, Desired = HealBot_UnitHealth(unit) + local Needed = Desired - Current; + Needed = Needed * (1 + (HealBot_Config.OverHeal * 4)); + if Needed < 0 then Needed = 0 end + if (Needed > HealBot_Spells[HealBot_CastingSpell].HealsDur) then + local sr = HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; + local sg = HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; + local sb = HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; + local sa = HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; + bar.txt:SetTextColor(sr, sg, sb, sa); + bar:SetStatusBarColor(ar, ag, ab, 0); + return nil + elseif HealBot_AbortButton == 1 and HealBot_IsCasting == true then + bar:SetStatusBarColor(ar, ag, ab, aa); + local sr = HealBot_Config.btextenabledcolr[HealBot_Config.Current_Skin]; + local sg = HealBot_Config.btextenabledcolg[HealBot_Config.Current_Skin]; + local sb = HealBot_Config.btextenabledcolb[HealBot_Config.Current_Skin]; + local sa = HealBot_Config.btextenabledcola[HealBot_Config.Current_Skin]; + bar.txt = getglobal(bar:GetName() .. "_text"); + bar.txt:SetTextColor(sr, sg, sb, sa); + end +end + +function HealBot_CastSpellOnFriend(spell, target) + local old; + local ttype = "other"; + if (not spell or not target or not UnitName(target)) then + return; + end + if (UnitCanAttack("player", "target")) then + old = "enemy"; + else + old = UnitName("target"); + if UnitName("target") ~= UnitName(target) then + TargetUnit(target); + else + ttype = "direct"; + end + end + HealBot_StartCasting(spell, target, ttype); + if (old == "enemy") then + TargetLastEnemy(); + elseif (old) then + TargetByName(old); + else + ClearTarget(); + end +end + +function HealBot_SetItemDefaults(spell) + if not HealBot_Spells[spell].Target then + HealBot_Spells[spell].Target = {"player", "party", "pet"}; + end + if not HealBot_Spells[spell].Price then + HealBot_Spells[spell].Price = 0; + end + if not HealBot_Spells[spell].CastTime then + HealBot_Spells[spell].CastTime = 0; + end + if not HealBot_Spells[spell].Mana then + HealBot_Spells[spell].Mana = 0; + end + if not HealBot_Spells[spell].Channel then + HealBot_Spells[spell].Channel = HealBot_Spells[spell].CastTime; + end + if not HealBot_Spells[spell].Duration then + HealBot_Spells[spell].Duration = HealBot_Spells[spell].Channel; + end + if not HealBot_Spells[spell].HealsMin then + HealBot_Spells[spell].HealsMin = 0; + end + if not HealBot_Spells[spell].HealsMax then + HealBot_Spells[spell].HealsMax = 0; + end + HealBot_Spells[spell].RealHealing = 0; + HealBot_Spells[spell].HealsCast = (HealBot_Spells[spell].HealsMin + HealBot_Spells[spell].HealsMax) / 2; + if not HealBot_Spells[spell].HealsExt then + HealBot_Spells[spell].HealsExt = 0; + end +end + +function HealBot_SetSpellDefaults(spell) + HealBot_Spells[spell].HealsDur = math.floor((HealBot_Spells[spell].HealsCast + HealBot_Spells[spell].HealsExt) + HealBot_Spells[spell].RealHealing); +end + +function HealBot_AddHeal(spell) + HealBot_SetSpellDefaults(spell); + table.foreachi(HealBot_Spells[spell].Target, function (i, val) + table.insert(HealBot_Heals[val], spell); + end); + HealBot_Spells[spell].BagSlot = HealBot_GetBagSlot(spell); +end + +function HealBot_FindHealSpells() + local id = 1; + if InitSpells > 0 then NeedEquipUpdate = 1; return; end + + HealBot_Heals = { player = {}, pet = {}, party = {} }; + + table.foreach(HealBot_CurrentSpells, function (index, spell) + if (HealBot_Spells[spell]) then + if CalcEquipBonus then + local healingbonus_penalty = 1; + if HealBot_Spells[spell].Level < 20 then + healingbonus_penalty = (1 - ((20 - HealBot_Spells[spell].Level) * 0.0375)); + end + local temp_Spell_cast = 3.5; + if HealBot_Spells[spell].CastTime == 0 then + temp_Spell_cast = 3.5; + end + if not HealBot_Spells[spell].CastTime then + HealBot_Spells[spell].CastTime = 1.5; + end + if HealBot_Spells[spell].CastTime >= 1.5 and HealBot_Spells[spell].CastTime < 3.5 then + temp_Spell_cast = HealBot_Spells[spell].CastTime; + end + RealHealing = ((HealBot_GetBonus() * healingbonus_penalty) * (temp_Spell_cast / 3.5)); + local playerClass, englishClass = UnitClass("player"); + local SpiBonus = 0; + if (englishClass == "PRIEST") then + SpiBonus = ((HealBot_SpiBonus(spell) * healingbonus_penalty) * (temp_Spell_cast / 3.5)) + RealHealing = RealHealing + SpiBonus; + end + RealHealing = math.floor(RealHealing); + HealBot_Spells[spell].RealHealing = RealHealing; + end + HealBot_AddHeal(spell); + end + end); + + local items = {}; + for bag = 0, NUM_BAG_FRAMES do + for slot = 1, GetContainerNumSlots(bag) do + local item = HealBot_GetItemName(bag, slot); + if HealBot_Spells[item] and not items[item] then + HealBot_SetItemDefaults(item); + HealBot_AddHeal(item); + items[item] = 1; + end + end + end + table.foreach(HealBot_Heals, function (key, val) + if (table.getn(val) == 0) then + HealBot_Heals[key] = nil; + end + end); + HealBot_Heals.target = HealBot_Heals.party; + for i = 1, 4 do + HealBot_Heals["party" .. i] = HealBot_Heals.party; + HealBot_Heals["partypet" .. i] = HealBot_Heals.party; + end + for i = 1, 40 do + HealBot_Heals["raid" .. i] = HealBot_Heals.party; + HealBot_Heals["raidpet" .. i] = HealBot_Heals.party; + end + + if CalcEquipBonus then + HealBot_AddDebug("...Done Equip Bonus:" .. RealHealing); + end + CalcEquipBonus = false; +end + +function HealBot_CanCastSpell(spell, unit) + local this = HealBot_Spells[spell]; + if this.Mana > UnitMana("player") then return false end; + if this.BagSlot then + local bag, slot = HealBot_UnpackBagSlot(this.BagSlot); + local start, duration, enable = GetContainerItemCooldown(bag, slot); + if (start > 0 and duration > 0 and enable > 0) then + return false; + end + end + return true; +end + +function HealBot_GetHealSpell(unit, pattern) + if (not UnitName(unit)) then return nil end; + if UnitOnTaxi("player") then return nil end; + if HealBot_Config.ProtectPvP == 1 and UnitIsPVP(unit) and not UnitIsPVP("player") then return nil end + if HealBot_UnitClass("player") == "DRUID" then + if HealBot_GetShapeshiftForm() then return nil end; + end + local spell = HealBot_GetSpellName(HealBot_GetSpellId(pattern)) + local range = 40; + if HealBot_Spells[spell] then + if not HealBot_CanCastSpell(spell, unit) then return nil end; + range = HealBot_Spells[spell].range; + end + if HealBot_Range_Check(unit, range) == 0 then return nil end; + return spell; +end + +function HealBot_HealUnit(unit, pattern) + HealBot_CastSpellOnFriend(HealBot_GetHealSpell(unit, pattern), unit); +end + +function HealBot_RecalcHeals(unit) + HealBot_Action_Refresh(unit); +end + +function HealBot_RecalcParty() + HealBot_Action_PartyChanged(); + HealBot_Action_RefreshButtons(); +end + +function HealBot_RecalcSpells() + HealBot_FindHealSpells(); + HealBot_RecalcParty(); +end + +function HealBot_SpiBonus(spell) + local heals_modifer = 0; + local base, stat, posBuff, negBuff = UnitStat("player", 5); + nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(2, 14); -- Spiritual guidance + spiGuideBonus = stat * 0.05; + heals_modifer = heals_modifer + (currRank * spiGuideBonus); + return heals_modifer; +end + +function HealBot_GetBonus() + local HealBonus = HealBot_BonusScanner:GetBonus(); + return HealBonus; +end + +function HealBot_InitSpells() + local id = 1 + local cnt = 0; + local class = HealBot_UnitClass("player") + HealBot_CurrentSpells = {}; + while true do + local spell = HealBot_GetSpellName(id); + if not spell then + break + end + if (HealBot_Spells[spell]) then + HealBot_Spells[spell].id = id; + HealBot_InitGetSpellData(spell, id, class); + table.insert(HealBot_CurrentSpells, spell); + cnt = cnt + 1; + end + id = id + 1; + end + if class == "PRIEST" or class == "DRUID" or class == "PALADIN" or class == "SHAMAN" then + HealBot_AddChat("Initiated HealBot_CurrentSpells with " .. cnt .. " Spells"); + end + return cnt; +end + +function HealBot_InitGetSpellData(spell, id, class) + local i, _mana, _cast, _HealsMin, _HealsMax, _HealsExt, _duration, _range, _shield, _channel; + local tooltip = getglobal("HealBot_ScanTooltip"); + local tmpText, line, tmpTest + + if (not spell) then + return; + end + + HealBot_ScanTooltip:SetOwner(HealBot_ScanTooltip, "ANCHOR_NONE") + HealBot_ScanTooltip:SetSpell(id, BOOKTYPE_SPELL); + tmpText = getglobal("HealBot_ScanTooltipTextLeft2"); + if (tmpText:GetText()) then + line = tmpText:GetText(); + tmpTest, tmpTest, _mana = string.find(line, HB_TOOLTIP_MANA); + else + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: HealBot_ScanTooltip is lost"); + HealBot_Report_Error("ERROR: If BonusScanner is used, try disabling BonusScanner"); + end + + tmpText = getglobal("HealBot_ScanTooltipTextRight2"); + if (tmpText:GetText()) then + line = tmpText:GetText(); + tmpTest, tmpTest, _range = string.find(line, HB_TOOLTIP_RANGE); + else + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: HealBot_ScanTooltip is lost"); + HealBot_Report_Error("ERROR: If BonusScanner is used, try disabling BonusScanner"); + end + + tmpText = getglobal("HealBot_ScanTooltipTextLeft3"); + _cast = nil; + if (tmpText:GetText()) then + line = tmpText:GetText(); + if (line == HB_TOOLTIP_INSTANT_CAST) then + _cast = 0; + elseif line == HB_TOOLTIP_CHANNELED then + _cast = 0; + elseif (tmpText) then + tmpTest, tmpTest, _cast = string.find(line, HB_TOOLTIP_CAST_TIME); + end + else + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: HealBot_ScanTooltip is lost"); + HealBot_Report_Error("ERROR: If BonusScanner is used, try disabling BonusScanner"); + end + + tmpText = getglobal("HealBot_ScanTooltipTextLeft4"); + tmpTest = nil; + if (tmpText:GetText()) then + line = tmpText:GetText(); + if class == "PRIEST" then + if string.sub(spell, 1, 14) == string.sub(HEALBOT_POWER_WORD_SHIELD, 1, 14) then + tmpTest, tmpTest, _HealsMin, _shield = string.find(line, HB_SPELL_PATTERN_SHIELD); + _HealsExt = 0; + _HealsMax = _HealsMin; + elseif string.sub(spell, 1, 4) == string.sub(HEALBOT_RENEW, 1, 4) then + tmpTest, tmpTest, _HealsExt, tmpTest, _duration = string.find(line, HB_SPELL_PATTERN_RENEW); + _HealsMin = 0; + _HealsMax = 0; + if (_HealsExt == nil) then + tmpTest, tmpTest, _HealsExt, _duration = string.find(line, HB_SPELL_PATTERN_RENEW1); + end + if (_HealsExt == nil) then + tmpTest, tmpTest, _duration, _HealsExt = string.find(line, HB_SPELL_PATTERN_RENEW2); + end + if (_HealsExt == nil) then + tmpTest, tmpTest, _duration, _HealsExt = string.find(line, HB_SPELL_PATTERN_RENEW3); + end + elseif string.sub(spell, 1, 9) == string.sub(HEALBOT_LESSER_HEAL, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_LESSER_HEAL); + elseif string.sub(spell, 1, 9) == string.sub(HEALBOT_GREATER_HEAL, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_GREATER_HEAL); + elseif string.sub(spell, 1, 9) == string.sub(HEALBOT_FLASH_HEAL, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_FLASH_HEAL); + elseif string.sub(spell, 1, 4) == string.sub(HEALBOT_HEAL, 1, 4) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HEAL); + end + elseif class == "DRUID" then + if string.sub(spell, 1, 6) == string.sub(HEALBOT_REGROWTH, 1, 6) then + tmpTest, tmpTest, _HealsMin, _HealsMax, _HealsExt = string.find(line, HB_SPELL_PATTERN_REGROWTH); + if (tmpTest == nil) then + tmpTest, tmpTest, _HealsMin, _HealsMax, tmpTest, _HealsExt = string.find(line, HB_SPELL_PATTERN_REGROWTH1); + end + elseif string.sub(spell, 1, 9) == string.sub(HEALBOT_REJUVENATION, 1, 9) then + tmpTest, tmpTest, _HealsExt, _duration = string.find(line, HB_SPELL_PATTERN_REJUVENATION); + _HealsMin = 0; + _HealsMax = 0; + if (_HealsExt == nil) then + tmpTest, tmpTest, _HealsExt, tmpTest, _duration = string.find(line, HB_SPELL_PATTERN_REJUVENATION1); + end + elseif string.sub(spell, 1, 7) == string.sub(HEALBOT_HEALING_TOUCH, 1, 7) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HEALING_TOUCH); + end + elseif class == "PALADIN" then + if string.sub(spell, 1, 9) == string.sub(HEALBOT_HOLY_LIGHT, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HOLY_LIGHT); + elseif string.sub(spell, 1, 9) == string.sub(HEALBOT_FLASH_OF_LIGHT, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_FLASH_OF_LIGHT); + end + elseif class == "SHAMAN" then + if string.sub(spell, 1, 9) == string.sub(HEALBOT_HEALING_WAVE, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_HEALING_WAVE); + elseif string.sub(spell, 1, 9) == string.sub(HEALBOT_LESSER_HEALING_WAVE, 1, 9) then + tmpTest, _HealsMin, _HealsMax = HealBot_Generic_Patten(line, HB_SPELL_PATTERN_LESSER_HEALING_WAVE); + end + end + else + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: HealBot_ScanTooltip is lost"); + HealBot_Report_Error("ERROR: If BonusScanner is used, try disabling BonusScanner"); + end + + if (_mana == nil) then + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: _mana is NIL"); + HealBot_Report_Error("ERROR: Spell: " .. spell); + if HealBot_ScanTooltipTextLeft2:GetText() then + HealBot_Report_Error("ERROR: Tooltip = >> " .. HealBot_ScanTooltipTextLeft2:GetText() .. " <<"); + end + HealBot_Report_Error("ERROR: Patten = >> " .. HB_TOOLTIP_MANA .. " <<"); + end + if (_range == nil) then + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: _range is NIL"); + HealBot_Report_Error("ERROR: Spell: " .. spell); + if HealBot_ScanTooltipTextRight2:GetText() then + HealBot_Report_Error("ERROR: Tooltip = >> " .. HealBot_ScanTooltipTextRight2:GetText() .. " <<"); + end + HealBot_Report_Error("ERROR: Patten = >> " .. HB_TOOLTIP_RANGE .. " <<"); + end + if (_cast == nil) then + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: _cast is NIL"); + HealBot_Report_Error("ERROR: Spell: " .. spell); + if HealBot_ScanTooltipTextLeft3:GetText() then + HealBot_Report_Error("ERROR: Tooltip = >> " .. HealBot_ScanTooltipTextLeft3:GetText() .. " <<"); + end + HealBot_Report_Error("ERROR: Patten = >> " .. HB_TOOLTIP_CAST_TIME .. " <<"); + end + if (tmpTest == nil) then + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: tmpTest == nil"); + HealBot_Report_Error("ERROR: spell = " .. spell); + if line then + HealBot_Report_Error("ERROR: Tooltip = >> " .. line .. " <<"); + end + end + + HealBot_Spells[spell].CastTime = tonumber(_cast); + HealBot_Spells[spell].Mana = tonumber(_mana); + HealBot_Spells[spell].Range = tonumber(_range); + HealBot_Spells[spell].HealsMin = tonumber(_HealsMin); + HealBot_Spells[spell].HealsMax = tonumber(_HealsMax); + if _HealsExt then + HealBot_Spells[spell].HealsExt = tonumber(_HealsExt); + end + if _duration then + HealBot_Spells[spell].Duration = tonumber(_duration); + end + if _shield then + HealBot_Spells[spell].Shield = tonumber(_shield); + end + if _channel then + HealBot_Spells[spell].Channel = tonumber(_channel); + end + + if not HealBot_Spells[spell].Target then + HealBot_Spells[spell].Target = {"player", "party", "pet"}; + end + if not HealBot_Spells[spell].Price then + HealBot_Spells[spell].Price = 0; + end + if not HealBot_Spells[spell].Channel then + HealBot_Spells[spell].Channel = HealBot_Spells[spell].CastTime; + end + if not HealBot_Spells[spell].Duration then + HealBot_Spells[spell].Duration = HealBot_Spells[spell].Channel; + end + if not HealBot_Spells[spell].RealHealing then + HealBot_Spells[spell].RealHealing = 0; + end + HealBot_Spells[spell].HealsCast = (HealBot_Spells[spell].HealsMin + HealBot_Spells[spell].HealsMax) / 2; + if not HealBot_Spells[spell].HealsExt then + HealBot_Spells[spell].HealsExt = 0; + end +end + +function HealBot_Generic_Patten(matchStr, matchPattern) + local tmpTest, _HealsMin, _HealsMax, _HealsExt, _duration + tmpTest, tmpTest, _HealsMin, _HealsMax = string.find(matchStr, matchPattern); + if (tmpTest == nil) then + HealBot_Report_Error("================================"); + HealBot_Report_Error("ERROR: tmpTest == nil"); + HealBot_Report_Error("ERROR: pattern = " .. matchPattern); + HealBot_Report_Error("ERROR: Tooltip = >> " .. matchStr .. " <<"); + end + return tmpTest, _HealsMin, _HealsMax; +end diff --git a/HealBot_Model.lua b/HealBot_Model.lua index 7a4c78b..38294e9 100644 --- a/HealBot_Model.lua +++ b/HealBot_Model.lua @@ -36,7 +36,7 @@ function HealBot_Model:NotifyObservers(event, unitID, ...) if self.observers[event] then local len = table.getn(self.observers[event]) for i = 1, len do - self.observers[event][i](unitID, unpack(arg)) + self.observers[event][i](unitID, ...) end end end diff --git a/HealBot_View_Layout.lua b/HealBot_View_Layout.lua new file mode 100644 index 0000000..b5d9079 --- /dev/null +++ b/HealBot_View_Layout.lua @@ -0,0 +1,772 @@ +-- HealBot_View_Layout.lua +-- Manages skins, status bar layouts, anchoring, and grid rendering for the HealBot panel + +local headerno = 0 + +HealBot_Action_HealGroup = { + "player", + "pet", + "party1", + "party2", + "party3", + "party4", +}; + +HealBot_Action_HealTarget = {}; +HealBot_Action_HealButtons = {}; +HealBot_Action_UnitButtons = {}; + +function HealBot_Action_SetTexture(bar, btexture) + if not bar then return end + if btexture == 10 then + bar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8"); + else + bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar"); + end +end + +function HealBot_HealthColor(unit, hlth, maxhlth) + if HealBot_UnitDebuff[unit] then + local debuff, tmp, debuff_type = UnitDebuff(unit, 1, 1) + if not debuff then + HealBot_UnitDebuff[unit] = nil; + HealBot_UnitDebuff[unit .. "_debuff_texture"] = nil + else + local dr = HealBot_Config.CDCBarColour[debuff_type].R + local dg = HealBot_Config.CDCBarColour[debuff_type].G + local db = HealBot_Config.CDCBarColour[debuff_type].B + if HealBot_Config.btexture[HealBot_Config.Current_Skin] == 10 then + dr = dr * 4 + dg = dg * 4 + db = db * 4 + if dr > 1 then dr = 1 end + if dg > 1 then dg = 1 end + if db > 1 then db = 1 end + end + return dr, dg, db, HealBot_Config.Barcola[HealBot_Config.Current_Skin]; + end + end + + local text = UnitName(unit); + if not HealBot_HealsIn[text] then + HealBot_HealsIn[text] = 0; + end + + local pct = hlth + HealBot_HealsIn[text]; + if maxhlth and maxhlth > 0 then + if pct < maxhlth then + pct = pct / maxhlth; + else + pct = 1; + end + else + pct = 0; + end + + local r, g, b = 1.0, 1.0, 0.0; + local a = HealBot_Config.Barcola[HealBot_Config.Current_Skin]; + if pct > HealBot_Config.AlertLevel then + a = HealBot_Config.bardisa[HealBot_Config.Current_Skin]; + end + + local colorMode = HealBot_Config.bcolormode[HealBot_Config.Current_Skin] or 1 + if colorMode == 2 and HealBot_Model and HealBot_Model.units[unit] and HealBot_Model.units[unit].englishClass then + local engClass = HealBot_Model.units[unit].englishClass + if RAID_CLASS_COLORS and RAID_CLASS_COLORS[engClass] then + r = RAID_CLASS_COLORS[engClass].r + g = RAID_CLASS_COLORS[engClass].g + b = RAID_CLASS_COLORS[engClass].b + end + else + if pct >= 0.98 then r = 0.0; end + if pct < 0.98 and pct >= 0.65 then r = 2.94 - (pct * 3); end + if pct <= 0.64 and pct > 0.31 then g = (pct - 0.31) * 3; end + if pct <= 0.31 then g = 0.0; end + end + return r, g, b, a; +end + +function HealBot_Action_HealthBar(button) + local name = button:GetName(); + return getglobal(name .. "Bar"); +end + +function HealBot_Action_HealthBar2(button) + local name = button:GetName(); + return getglobal(name .. "Bar2"); +end + +function HealBot_Action_EnableButton(button) + local unit = button.unit; + local state = HealBot_Model.units[unit] + if not state then return end + + local hlth = state.health + local maxhlth = state.maxHealth + local name = state.name + if not name then name = UnitName(unit) end -- fallback + + local bar = HealBot_Action_HealthBar(button); + local bar2 = HealBot_Action_HealthBar2(button); + local bar3 = getglobal(button:GetName() .. "Bar3"); + local btexture = HealBot_Config.btexture[HealBot_Config.Current_Skin]; + local bheight = HealBot_Config.bheight[HealBot_Config.Current_Skin]; + local sr = HealBot_Config.btextenabledcolr[HealBot_Config.Current_Skin]; + local sg = HealBot_Config.btextenabledcolg[HealBot_Config.Current_Skin]; + local sb = HealBot_Config.btextenabledcolb[HealBot_Config.Current_Skin]; + local sa = HealBot_Config.btextenabledcola[HealBot_Config.Current_Skin]; + local r, g, b, a = HealBot_HealthColor(button.unit, hlth, maxhlth) + local btextheight = HealBot_Config.btextheight[HealBot_Config.Current_Skin] or 10; + if btextheight == 0 then btextheight = 10 end + local bwidth = HealBot_Config.bwidth[HealBot_Config.Current_Skin] + local textlen = math.floor(5 + (((bwidth * 1.8) / btextheight) - (btextheight / 2))) + + bar:SetMinMaxValues(0, maxhlth); + bar:SetValue(hlth); + + local englishClass = state.englishClass + local isHealer = false + if englishClass == "PALADIN" or englishClass == "DRUID" or englishClass == "SHAMAN" or englishClass == "PRIEST" then + isHealer = true + end + local pt = state.powerType + + local showMana = false + if HealBot_Config.ShowManaBars == 1 and state.maxMana > 0 and pt == 0 then + if HealBot_Config.ManaBarsHealersOnly == 1 then + if isHealer then showMana = true end + else + showMana = true + end + end + + if showMana then + local pr, pg, pb = 0, 0, 1 + if bar3 then + bar3:SetMinMaxValues(0, state.maxMana) + bar3:SetValue(state.mana) + bar3:SetStatusBarColor(pr, pg, pb, HealBot_Config.Barcola[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(bar3, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + bar3:Show() + bar3:SetHeight(bheight * 0.2) + end + bar:SetHeight(bheight * 0.8) + bar2:SetHeight(bheight * 0.8) + else + if bar3 then + bar3:Hide() + end + bar:SetHeight(bheight) + bar2:SetHeight(bheight) + end + + if HealBot_HealsIn[name] then + bar2:SetMinMaxValues(0, maxhlth); + bar2:SetValue(hlth + HealBot_HealsIn[name]); + else + bar2:SetValue(0); + end + bar.txt = getglobal(bar:GetName() .. "_text"); + if (not HealBot_IsCasting and (HealBot_CanHeal(unit) or HealBot_MissingBuffs[unit])) then + button:Enable(); + if HealBot_UnitDebuff[unit] then + sr = HealBot_Config.btextcursecolr[HealBot_Config.Current_Skin]; + sg = HealBot_Config.btextcursecolg[HealBot_Config.Current_Skin]; + sb = HealBot_Config.btextcursecolb[HealBot_Config.Current_Skin]; + sa = HealBot_Config.btextcursecola[HealBot_Config.Current_Skin]; + elseif HealBot_MissingBuffs[unit] then + r = r * 0.5; + g = g * 0.5; + b = b * 0.5; + sr = 1.0; + sg = 1.0; + sb = 0.0; + end + bar:SetStatusBarColor(r, g, b, HealBot_Config.Barcola[HealBot_Config.Current_Skin]); + bar2:SetStatusBarColor(r, g, b, HealBot_Config.BarcolaInHeal[HealBot_Config.Current_Skin]); + else + button:Disable(); + sr = HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin]; + sg = HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin]; + sb = HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin]; + sa = HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin]; + bar:SetStatusBarColor(r, g, b, HealBot_Config.bardisa[HealBot_Config.Current_Skin]); + bar2:SetStatusBarColor(r, g, b, HealBot_Config.bardisa[HealBot_Config.Current_Skin]); + end + if string.len(name) > textlen then + name = string.sub(name, 1, textlen - 3) .. '...'; + end + bar.txt:SetText(name); + bar.txt:SetTextColor(sr, sg, sb, sa); + local fontName, fontHeight, fontFlags = bar.txt:GetFont() + local fontOutline = HealBot_Config.bfontoutline[HealBot_Config.Current_Skin] or 0 + if fontOutline == 1 then + bar.txt:SetFont(fontName, fontHeight, "OUTLINE") + else + bar.txt:SetFont(fontName, fontHeight, "") + end + + for i = 1, 5 do + local icon = getglobal(button:GetName() .. "BarIcon" .. i) + if icon then + if HealBot_UnitIcons and HealBot_UnitIcons[unit] and HealBot_UnitIcons[unit][i] then + icon:SetTexture(HealBot_UnitIcons[unit][i]) + icon:Show() + else + icon:Hide() + end + end + end +end + +function HealBot_Action_EnableButtons() + table.foreach(HealBot_Action_HealButtons, function (index, button) + HealBot_Action_EnableButton(button); + end); +end + +function HealBot_Action_RefreshButton(button) + if not button then return end + local unit = button.unit; + if HealBot_MayHeal(unit) then + HealBot_Action_EnableButton(button) + end +end + +function HealBot_Action_ResetSkin() + HealBot_Action_PartyChanged() + if HealBot_Options:IsVisible() then + HealBot_Action_SetTexture(HealBot_DiseaseColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_MagicColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_PoisonColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_CurseColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_EnTextColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_EnTextColorpickin, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_DisTextColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_Action_SetTexture(HealBot_DebTextColorpick, HealBot_Config.btexture[HealBot_Config.Current_Skin]) + HealBot_SetSkinColours() + end +end + +function HealBot_Action_RefreshButtons(unit) + if unit and HealBot_Action_UnitButtons[unit] then + table.foreach(HealBot_Action_UnitButtons[unit], function (index, button) + HealBot_Action_RefreshButton(button); + end); + else + table.foreach(HealBot_Action_HealButtons, function (index, button) + HealBot_Action_RefreshButton(button); + end); + end +end + +function HealBot_Action_PositionButton(button, OsetX, OsetY, bwidth, bheight, checked, header) + local brspace = HealBot_Config.brspace[HealBot_Config.Current_Skin] or 3; + if header then + headerno = headerno + 1; + local headerobj = getglobal("HealBot_Action_Header" .. headerno); + headerobj:SetText(header) + headerobj:Show(); + headerobj:ClearAllPoints(); + headerobj:SetHeight(bheight); + headerobj:SetWidth(bwidth); + headerobj:SetPoint("TOPLEFT", "HealBot_Action", "TOPLEFT", OsetX, -OsetY); + headerobj:Disable(); + OsetY = OsetY + headerobj:GetHeight() + brspace; + else + local unit = button.unit; + button:SetText(" "); + if (HealBot_MayHeal(unit)) then + button:Show(); + button:ClearAllPoints(); + button:SetHeight(bheight); + if checked then + button:SetWidth(bwidth - 14); + button:SetPoint("TOPLEFT", "HealBot_Action", "TOPLEFT", OsetX + 14, -OsetY); + else + button:SetWidth(bwidth); + button:SetPoint("TOPLEFT", "HealBot_Action", "TOPLEFT", OsetX, -OsetY); + end + OsetY = OsetY + button:GetHeight() + brspace; + else + button:Hide(); + end + end + return OsetY; +end + +function HealBot_Action_SetHeightWidth(width, height, bwidth) + if HealBot_ActionHeight then + HealBot_Action:SetHeight(HealBot_ActionHeight); + end + if HealBot_Config.GrowUpwards == 1 then + local left, bottom = HealBot_Action:GetLeft(), HealBot_Action:GetBottom(); + if left and bottom then + if HealBot_Config.PanelAnchorX == -1 then HealBot_Config.PanelAnchorX = left; end + if HealBot_Config.PanelAnchorY == -1 then HealBot_Config.PanelAnchorY = bottom; end + HealBot_Action:ClearAllPoints(); + HealBot_Action:SetPoint("BOTTOMLEFT", "UIParent", "BOTTOMLEFT", HealBot_Config.PanelAnchorX, HealBot_Config.PanelAnchorY); + end + else + local left, top = HealBot_Action:GetLeft(), HealBot_Action:GetTop(); + if left and top then + HealBot_Action:ClearAllPoints(); + HealBot_Action:SetPoint("TOPLEFT", "UIParent", "BOTTOMLEFT", left, top); + end + end + HealBot_Action:SetHeight(height); + HealBot_ActionHeight = height; + local bpadding = (HealBot_Config.bpadding and HealBot_Config.bpadding[HealBot_Config.Current_Skin]) or 10 + HealBot_Action:SetWidth(width + bwidth + bpadding) +end + +function HealBot_Action_SetHealButton(index, unit) + if not index then + HealBot_Action_HealButtons = {}; + HealBot_Action_UnitButtons = {}; + return nil + end + local button = getglobal("HealBot_Action_HealUnit" .. index); + button.unit = unit; + if unit then + table.insert(HealBot_Action_HealButtons, button); + if not HealBot_Action_UnitButtons[unit] then HealBot_Action_UnitButtons[unit] = {} end + table.insert(HealBot_Action_UnitButtons[unit], button); + else + button:Hide(); + end + return button; +end + +function HealBot_Action_PartyChanged() + if not HealBot_IsFighting then + local numBars = 0; + local numHeaders = 0; + local TempMaxH = 0; + local HeaderPos = {}; + + for j = 1, 15 do + local headerobj = getglobal("HealBot_Action_Header" .. j); + headerobj:SetText(" ") + headerobj:Hide(); + end + + local bwidth = HealBot_Config.bwidth[HealBot_Config.Current_Skin] or 85; + local sr = HealBot_Config.btextdisbledcolr[HealBot_Config.Current_Skin] or 0.4; + local sg = HealBot_Config.btextdisbledcolg[HealBot_Config.Current_Skin] or 0.4; + local sb = HealBot_Config.btextdisbledcolb[HealBot_Config.Current_Skin] or 0.4; + local sa = HealBot_Config.btextdisbledcola[HealBot_Config.Current_Skin] or 0.6; + local bheight = HealBot_Config.bheight[HealBot_Config.Current_Skin] or 18; + local btexture = HealBot_Config.btexture[HealBot_Config.Current_Skin] or 5; + local bcspace = HealBot_Config.bcspace[HealBot_Config.Current_Skin] or 4; + local cols = HealBot_Config.numcols[HealBot_Config.Current_Skin] or 2; + local btextheight = HealBot_Config.btextheight[HealBot_Config.Current_Skin] or 10; + local abortsize = HealBot_Config.abortsize[HealBot_Config.Current_Skin] or 10; + local checked_start = 0; + local checked_end = 0; + headerno = 0; + + for j = 1, 41 do + HealBot_Action_SetHealButton(j, nil); + end + for j = 51, 60 do + HealBot_Action_SetHealButton(j, nil); + end + HealBot_Action_SetHealButton(); + + local i = 0; + local last = 0; + local GroupValid = numBars; + last = last + 6 + if HealBot_Config.GroupHeals == 1 then + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + HeaderPos[i + 1] = HEALBOT_OPTIONS_GROUPHEALS + end + for _, unit in ipairs(HealBot_Action_HealGroup) do + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + i = i + 1; + HealBot_Action_SetHealButton(i, unit); + numBars = numBars + 1; + end + if i == last then break end + end + end + if numBars > GroupValid and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + + last = last + 10 + local TankValid = numBars; + if HealBot_Config.TankHeals == 1 then + if GetNumRaidMembers() > 0 and CT_RA_MainTanks then + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + HeaderPos[i + 1] = HEALBOT_OPTIONS_TANKHEALS + end + for j = 1, 10 do + if CT_RA_MainTanks[j] then + for k = 1, GetNumRaidMembers() do + local unit = "raid" .. k; + local PossibleMT = 1; + if UnitInParty(unit) and HealBot_Config.GroupHeals == 1 then + if not UnitIsUnit(unit, "player") then + PossibleMT = 0; + end + end + if PossibleMT == 1 then + if UnitName(unit) == CT_RA_MainTanks[j] then + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + i = i + 1; + HealBot_Action_SetHealButton(i, unit); + numBars = numBars + 1; + end + end + end + end + end + if i == last then break end + end + end + end + if numBars > TankValid and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + + last = last + 10; + local h = 50; + local TargetValid = numBars; + if HealBot_Config.TargetHeals == 1 then + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + HeaderPos[i + 1] = HEALBOT_OPTIONS_TARGETHEALS + end + for _, unit in ipairs(HealBot_Action_HealTarget) do + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + i = i + 1; + h = h + 1; + if checked_start == 0 then checked_start = i; end + checked_end = i; + HealBot_Action_SetHealButton(h, unit); + local check = getglobal("HealBot_Action_HealUnit" .. h .. "Check"); + check.unit = unit; + check:SetChecked(1); + check:Show(); + numBars = numBars + 1; + end + if i == last then break end + end + + last = last + 1 + local unit = HealBot_TargetName() + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal("target") then + i = i + 1; + h = h + 1; + if h < 61 then + HealBot_Action_SetHealButton(h, "target"); + local check = getglobal("HealBot_Action_HealUnit" .. h .. "Check"); + + check:SetChecked(0); + check.unit = unit; + if check.unit then + if checked_start == 0 then checked_start = i; end + checked_end = i; + check:Show(); + else + check:Hide(); + end + else + HealBot_Action_SetHealButton(i, "target"); + end + numBars = numBars + 1; + end + end + if numBars > TargetValid and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + + last = last + 40 + local ExtraValid = numBars; + if HealBot_Config.EmergencyHeals == 1 then + local order = {}; + local units = {}; + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 and HealBot_Config.ExtraOrder == 1 then + HeaderPos[i + 1] = HEALBOT_OPTIONS_EMERGENCYHEALS + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + if HealBot_Config.EmergIncMonitor == 1 then + if GetNumRaidMembers() > 0 then + for j = 1, 40 do + local PossibleEmerg = 1; + local unit = "raid" .. j; + local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(j); + if not name then name = "not known" end + if not class then class = "not known" end + if not subgroup then subgroup = "not known" end + + if UnitInParty(unit) and HealBot_Config.GroupHeals == 1 then + PossibleEmerg = 0; + end + if PossibleEmerg == 1 then + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + if HealBot_Config.ExtraOrder == 1 then + order[unit] = name; + elseif HealBot_Config.ExtraOrder == 2 then + order[unit] = class; + elseif HealBot_Config.ExtraOrder == 3 then + order[unit] = subgroup; + else + order[unit] = 0 - UnitHealthMax(unit); + if UnitHealthMax(unit) > TempMaxH then TempMaxH = UnitHealthMax(unit); end + end + table.insert(units, unit); + numBars = numBars + 1; + end + end + end + else + for _, unit in ipairs(HealBot_Action_HealGroup) do + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + if HealBot_Config.ExtraOrder == 1 then + order[unit] = name; + elseif HealBot_Config.ExtraOrder == 2 then + order[unit] = class; + elseif HealBot_Config.ExtraOrder == 3 then + order[unit] = subgroup; + else + order[unit] = 0 - UnitHealthMax(unit); + if UnitHealthMax(unit) > TempMaxH then TempMaxH = UnitHealthMax(unit); end + end + table.insert(units, unit); + numBars = numBars + 1; + end + end + end + else + if GetNumRaidMembers() > 0 then + for j = 1, 40 do + local unit = "raid" .. j; + local Class = UnitClass(unit); + local ProcessUnit = 0; + local PossibleEmerg = 1; + local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(j); + if not name then name = "not known" end + if not class then class = "not known" end + if not subgroup then subgroup = "not known" end + + if HealBot_EmergInc[Class] == 1 then + ProcessUnit = 1; + end + if UnitInParty(unit) and HealBot_Config.GroupHeals == 1 then + PossibleEmerg = 0; + end + + if ProcessUnit == 1 and PossibleEmerg == 1 then + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + if HealBot_Config.ExtraOrder == 1 then + order[unit] = name; + elseif HealBot_Config.ExtraOrder == 2 then + order[unit] = class; + elseif HealBot_Config.ExtraOrder == 3 then + order[unit] = subgroup; + else + order[unit] = 0 - UnitHealthMax(unit); + if UnitHealthMax(unit) > TempMaxH then TempMaxH = UnitHealthMax(unit); end + end + table.insert(units, unit); + numBars = numBars + 1; + end + end + end + else + for _, unit in ipairs(HealBot_Action_HealGroup) do + if not HealBot_Action_UnitButtons[unit] and HealBot_MayHeal(unit) then + if HealBot_Config.ExtraOrder == 1 then + order[unit] = name; + elseif HealBot_Config.ExtraOrder == 2 then + order[unit] = class; + elseif HealBot_Config.ExtraOrder == 3 then + order[unit] = subgroup; + else + order[unit] = 0 - UnitHealthMax(unit); + if UnitHealthMax(unit) > TempMaxH then TempMaxH = UnitHealthMax(unit); end + end + table.insert(units, unit); + numBars = numBars + 1; + end + end + end + end + table.sort(units, function (a, b) + if order[a] < order[b] then return true end + if order[a] > order[b] then return false end + return a < b + end) + local TempSort = "init" + TempMaxH = math.ceil(TempMaxH / 1000) * 1000; + + if GetNumRaidMembers() > 0 then + for j = 1, 40 do + if not units[j] then break end + local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(string.sub(units[j], 5)); + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 and HealBot_Config.ExtraOrder == 2 and TempSort ~= class then + TempSort = class + HeaderPos[i + 1] = class + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 and HealBot_Config.ExtraOrder == 3 and TempSort ~= subgroup then + TempSort = subgroup + HeaderPos[i + 1] = HEALBOT_OPTIONS_GROUPHEALS .. subgroup + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + if HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 and HealBot_Config.ExtraOrder == 4 and TempMaxH > UnitHealthMax(units[j]) then + TempMaxH = TempMaxH - 1000 + HeaderPos[i + 1] = ">" .. tostring(TempMaxH / 1000) .. "k" + numBars = numBars + 1; + numHeaders = numHeaders + 1; + end + i = i + 1; + HealBot_Action_SetHealButton(i, units[j]); + if i == last then break end + end + end + end + if numBars == ExtraValid + 1 and HealBot_Config.ShowHeader[HealBot_Config.Current_Skin] == 1 then + HeaderPos[i + 1] = nil; + numBars = numBars - 1; + end + + local bpadding = (HealBot_Config.bpadding and HealBot_Config.bpadding[HealBot_Config.Current_Skin]) or 10 + local OffsetY = bpadding; + local OffsetX = bpadding; + local MaxOffsetY = 0; + + if cols > (numBars - numHeaders) then + cols = numBars - numHeaders; + end + if cols <= 0 then cols = 1 end + + local h = 1; + local i = 0; + local z = 1; + + table.foreach(HealBot_Action_HealButtons, function (index, button) + i = i + 1; + local checked = false; + local header; + + if HeaderPos[i] then + header = HeaderPos[i]; + OffsetY = HealBot_Action_PositionButton(nil, OffsetX, OffsetY, bwidth, bheight, checked, header); + if h == math.ceil((numBars) / cols) and z < numBars then + h = 0; + if MaxOffsetY < OffsetY then MaxOffsetY = OffsetY; end + OffsetY = bpadding; + OffsetX = OffsetX + bwidth + bcspace; + end + h = h + 1; + z = z + 1; + end + + if checked_start <= i and checked_end >= i then checked = true; end + OffsetY = HealBot_Action_PositionButton(button, OffsetX, OffsetY, bwidth, bheight, checked, nil); + if h == math.ceil((numBars) / cols) and z < numBars then + h = 0; + if MaxOffsetY < OffsetY then MaxOffsetY = OffsetY; end + OffsetY = bpadding; + OffsetX = OffsetX + bwidth + bcspace; + end + z = z + 1; + h = h + 1; + local bar = HealBot_Action_HealthBar(button); + local bar2 = HealBot_Action_HealthBar2(button); + bar.txt = getglobal(bar:GetName() .. "_text"); + bar:SetHeight(bheight); + HealBot_Action_SetTexture(bar, btexture); + bar.txt:SetTextHeight(btextheight); + local barScale = bar:GetScale(); + bar:SetScale(barScale + 0.01); + bar:SetScale(barScale); + bar2:SetHeight(bheight); + HealBot_Action_SetTexture(bar2, btexture); + end); + + if MaxOffsetY < OffsetY then MaxOffsetY = OffsetY; end + + if HealBot_Config.HideOptions == 1 then + HealBot_Action_OptionsButton:Hide(); + else + HealBot_Action_OptionsButton:SetPoint("BOTTOM", "HealBot_Action", "BOTTOM", 0, bpadding); + HealBot_Action_OptionsButton:Show(); + MaxOffsetY = MaxOffsetY + 30; + end + + if HealBot_Config.HideAbort == 1 then + HealBot_Action_AbortButton:Hide(); + else + local bar = HealBot_Action_HealthBar(HealBot_Action_AbortButton); + local denom = 6 - (abortsize / 3) + if denom == 0 then denom = 1 end + local width = (bwidth - 12) + (OffsetX / denom); + + bar.txt = getglobal(bar:GetName() .. "_text"); + bar.txt:SetTextColor(sr, sg, sb, sa); + bar.txt:SetText(HEALBOT_ACTION_ABORT); + HealBot_Action_SetTexture(bar, btexture); + bar:SetMinMaxValues(0, 100); + bar:SetValue(100); + bar:ClearAllPoints(); + bar:SetHeight(bheight + abortsize); + bar:SetWidth(width); + bar:SetStatusBarColor(0.1, 0.1, 0.4, 0); + MaxOffsetY = MaxOffsetY + 30 + abortsize; + HealBot_Action_AbortButton:ClearAllPoints(); + HealBot_Action_AbortButton:SetWidth(width) + HealBot_Action_AbortButton:SetHeight(bheight + abortsize); + if HealBot_Config.HideOptions == 1 then + HealBot_Action_AbortButton:SetPoint("BOTTOM", "HealBot_Action", "BOTTOM", 0, bpadding); + bar:SetPoint("BOTTOM", "HealBot_Action", "BOTTOM", 0, bpadding); + else + HealBot_Action_AbortButton:SetPoint("BOTTOM", "HealBot_Action_OptionsButton", "TOP", 0, 10); + bar:SetPoint("BOTTOM", "HealBot_Action_OptionsButton", "TOP", 0, 10); + end + HealBot_Action_AbortButton:Show(); + end + + HealBot_Action_SetHeightWidth(OffsetX, MaxOffsetY + bpadding, bwidth); + end + HealBot_Action_RefreshButtons(); +end + +function HealBot_Action_Reset() + HealBot_Action:ClearAllPoints(); + HealBot_Action:SetPoint("TOP", "MinimapCluster", "BOTTOM", 7, 10); + HealBot_Action_HealTarget = {}; + HealBot_Action_PartyChanged(); +end + +function HealBot_Action_Refresh(unit) + if (UnitIsDeadOrGhost("player")) or (UnitOnTaxi("player")) then + if HealBot_Config.AutoClose == 1 and HealBot_Config.ActionVisible ~= 0 then + HealBot_Action:Hide(); + else + HealBot_Action_RefreshButtons(unit); + end + return; + end + HealBot_Action_RefreshButtons(unit); + if not HealBot_IsFighting then + if (HealBot_Action_MustHealSome()) then + HealBot_Action:Show(); + elseif HealBot_AbortButton == 0 then + HealBot_Action:Show(); + elseif (not HealBot_Action_ShouldHealSome()) then + if HealBot_AbortButton == 1 and HealBot_Config.AutoClose == 1 and HealBot_Config.ActionVisible ~= 0 then + HealBot_Action:Hide(); + end + end + end +end diff --git a/HealBot_View_Tooltip.lua b/HealBot_View_Tooltip.lua new file mode 100644 index 0000000..a270cc8 --- /dev/null +++ b/HealBot_View_Tooltip.lua @@ -0,0 +1,250 @@ +-- HealBot_View_Tooltip.lua +-- Handles rendering of unit and spell tooltips in the HealBot UI + +HealBot_Action_TooltipUnit = nil + +function HealBot_Action_RefreshTooltip(unit) + if HealBot_Config.ShowTooltip==0 then return end + if not unit then unit = HealBot_Action_TooltipUnit end + if not unit then return end; + + local hlth=UnitHealth(unit); + local maxhlth=UnitHealthMax(unit); + + local spellLeft = HealBot_GetHealSpell(unit,HealBot_Action_SpellPattern("Left")); + local spellMiddle = HealBot_GetHealSpell(unit,HealBot_Action_SpellPattern("Middle")); + local spellRight = HealBot_GetHealSpell(unit,HealBot_Action_SpellPattern("Right")); + local spellButton4 = HealBot_GetHealSpell(unit,HealBot_Action_SpellPattern("Button4")); + local spellButton5 = HealBot_GetHealSpell(unit,HealBot_Action_SpellPattern("Button5")); + local linenum = 1 + + HealBot_Action_Tooltip_ClearLines(); + + if HealBot_Config.Tooltip_ShowTarget==1 then + if UnitName(unit) then + HealBot_Action_Tooltip_SetLineLeft(UnitName(unit),0,1,0,linenum) + if hlth and maxhlth then + local r,g,b,a=HealBot_HealthColor(unit,hlth,maxhlth); + HealBot_Action_Tooltip_SetLineRight(hlth.."/"..maxhlth.." (-"..maxhlth-hlth..")",r,g,b,linenum) + end + end + end + + if HealBot_Config.Tooltip_ShowSpellDetail==1 then + if spellLeft then + linenum=linenum+2 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONLEFT.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellLeft,1,1,0,linenum) + linenum=HealBot_Action_Tooltip_SpellInfo(spellLeft,linenum); + end + if spellMiddle then + linenum=linenum+2 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONMIDDLE.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellMiddle,1,1,0,linenum) + linenum=HealBot_Action_Tooltip_SpellInfo(spellMiddle,linenum); + end + if spellRight then + linenum=linenum+2 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONRIGHT.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellRight,1,1,0,linenum) + linenum=HealBot_Action_Tooltip_SpellInfo(spellRight,linenum); + end + if spellButton4 then + linenum=linenum+2 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON4.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellButton4,1,1,0,linenum) + linenum=HealBot_Action_Tooltip_SpellInfo(spellButton4,linenum); + end + if spellButton5 then + linenum=linenum+2 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON5.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellButton5,1,1,0,linenum) + linenum=HealBot_Action_Tooltip_SpellInfo(spellButton5,linenum); + end + else + if spellLeft then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONLEFT..":",1,1,0,linenum) + HealBot_Action_Tooltip_SetLineRight(HealBot_Action_Tooltip_SpellSummary(spellLeft),1,1,1,linenum) + end + if spellMiddle then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONMIDDLE..":",1,1,0,linenum) + HealBot_Action_Tooltip_SetLineRight(HealBot_Action_Tooltip_SpellSummary(spellMiddle),1,1,1,linenum) + end + if spellRight then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONRIGHT..":",1,1,0,linenum) + HealBot_Action_Tooltip_SetLineRight(HealBot_Action_Tooltip_SpellSummary(spellRight),1,1,1,linenum) + end + if spellButton4 then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON4..":",1,1,0,linenum) + HealBot_Action_Tooltip_SetLineRight(HealBot_Action_Tooltip_SpellSummary(spellButton4),1,1,1,linenum) + end + if spellButton5 then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON5..":",1,1,0,linenum) + HealBot_Action_Tooltip_SetLineRight(HealBot_Action_Tooltip_SpellSummary(spellButton5),1,1,1,linenum) + end + end + if HealBot_Config.Tooltip_Recommend==1 then + local Instant_check=0; + if HealBot_Config.Tooltip_ShowSpellDetail==1 then linenum=linenum+1; end + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_TOOLTIP_RECOMMENDTEXT,0.8,0.8,0,linenum) + Instant_check=0; + Instant_check,linenum=HealBot_Action_Tooltip_CheckForInstant(unit,spellLeft,"upd",linenum,Instant_check); + Instant_check,linenum=HealBot_Action_Tooltip_CheckForInstant(unit,spellMiddle,"upd",linenum,Instant_check); + Instant_check,linenum=HealBot_Action_Tooltip_CheckForInstant(unit,spellRight,"upd",linenum,Instant_check); + Instant_check,linenum=HealBot_Action_Tooltip_CheckForInstant(unit,spellButton4,"upd",linenum,Instant_check); + Instant_check,linenum=HealBot_Action_Tooltip_CheckForInstant(unit,spellButton5,"upd",linenum,Instant_check); + if Instant_check==0 then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(" None",0.4,0.4,0.4,linenum) + end + end + + local height = 20 + local width = 0 + for i = 1, linenum do + local txtL = getglobal("HealBot_TooltipTextL" .. i) + local txtR = getglobal("HealBot_TooltipTextR" .. i) + height = height + txtL:GetHeight() + 2 + if (txtL:GetWidth() + txtR:GetWidth() + 25 > width) then + width = txtL:GetWidth() + txtR:GetWidth() + 25 + end + end + HealBot_Tooltip:SetWidth(width) + HealBot_Tooltip:SetHeight(height) + HealBot_Tooltip:ClearAllPoints(); + if HealBot_Config.TooltipPos>1 then + if HealBot_Config.TooltipPos==2 then + HealBot_Tooltip:SetPoint("TOPRIGHT","HealBot_Action","TOPLEFT",0,0); + elseif HealBot_Config.TooltipPos==3 then + HealBot_Tooltip:SetPoint("TOPLEFT","HealBot_Action","RIGHT",0,0); + elseif HealBot_Config.TooltipPos==4 then + HealBot_Tooltip:SetPoint("BOTTOM","HealBot_Action","TOP",0,0); + else + HealBot_Tooltip:SetPoint("TOP","HealBot_Action","BOTTOM",0,0); + end + else + HealBot_Tooltip:SetPoint("BOTTOMRIGHT","WorldFrame","BOTTOMRIGHT",-105,105); + end + HealBot_Tooltip:Show(); +end + +function HealBot_Action_Tooltip_SpellInfo(spell,linenum) + local text + if HealBot_Spells[spell] then + if HealBot_Spells[spell].HealsDur>0 then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_WORDS_CAST..": "..HealBot_Spells[spell].CastTime.." "..HEALBOT_WORDS_SEC..".",0.8,0.8,0.8,linenum) + HealBot_Action_Tooltip_SetLineRight("Mana: "..HealBot_Spells[spell].Mana,0.5,0.5,1,linenum) + if HealBot_Spells[spell].HealsMax>0 then + local Heals = HEALBOT_HEAL.." " + if HealBot_Spells[spell].Shield then + Heals = HEALBOT_TOOLTIP_SHIELD.." " + end + if HealBot_Spells[spell].HealsMin0 then + text=HEALBOT_HEAL.." "..HealBot_Spells[spell].HealsDur.." "..HEALBOT_WORDS_OVER.." "..HealBot_Spells[spell].Duration-HealBot_Spells[spell].CastTime.." sec." + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(text,1,1,1,linenum) + end + if not HealBot_Spells[spell].Shield then + text=HEALBOT_TOOLTIP_ITEMBONUS.." +"..HealBot_GetBonus().." | "..HEALBOT_TOOLTIP_ACTUALBONUS.." +"..HealBot_Spells[spell].RealHealing.." " + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(text,0.8,0.8,0.8,linenum) + end + end + end + return linenum +end + +function HealBot_Action_Tooltip_SpellSummary(spell) + local ret_val = " "; + if HealBot_Spells[spell] then + if HealBot_Spells[spell].HealsDur and HealBot_Spells[spell].HealsDur>0 then + if HealBot_Spells[spell].HealsMax and HealBot_Spells[spell].HealsMax>0 then + local Heals = " "..HEALBOT_HEAL.." "; + if HealBot_Spells[spell].Shield then + Heals = " "..HEALBOT_TOOLTIP_SHIELD.." "; + end + if (HealBot_Spells[spell].HealsMin or 0)<(HealBot_Spells[spell].HealsMax or 0) then + ret_val=ret_val..Heals..format("%d", (((HealBot_Spells[spell].HealsMin or 0)+(HealBot_Spells[spell].HealsMax or 0))/2) + (HealBot_Spells[spell].RealHealing or 0)); + else + ret_val=ret_val..Heals..format("%d", (HealBot_Spells[spell].HealsMax or 0) + (HealBot_Spells[spell].RealHealing or 0)); + end + end + if HealBot_Spells[spell].HealsExt and HealBot_Spells[spell].HealsExt>0 then + ret_val=ret_val.." HoT "..HealBot_Spells[spell].HealsDur; + end + if HealBot_Spells[spell].Mana then + ret_val=ret_val.." "..HEALBOT_WORDS_FOR.." "..HealBot_Spells[spell].Mana.." Mana"; + end + end + end + if string.len(ret_val)<5 then ret_val = " - "..spell; end + return ret_val +end + +function HealBot_Action_Tooltip_CheckForInstant(unit,spell,upd,linenum,check) + if HealBot_Spells[spell] then + if HealBot_Spells[spell].CastTime == 0 then + if HealBot_UnitAffected(unit,HealBot_Spells[spell].Buff) then return check,linenum end; + if HealBot_UnitAffected(unit,HealBot_Spells[spell].Debuff) then return check,linenum end; + if upd=="upd" then + linenum=linenum+1 + HealBot_Action_Tooltip_SetLineLeft(" "..spell,1,1,1,linenum) + end + else + return check,linenum; + end + else + return check,linenum + end + return check+1,linenum; +end + +function HealBot_Action_Tooltip_SetLineLeft(Text,R,G,B,linenum) + local txtL = getglobal("HealBot_TooltipTextL" .. linenum) + txtL:SetTextColor(R,G,B) + txtL:SetText(Text) + txtL:Show() +end + +function HealBot_Action_Tooltip_SetLineRight(Text,R,G,B,linenum) + local txtR = getglobal("HealBot_TooltipTextR" .. linenum) + txtR:SetTextColor(R,G,B) + txtR:SetText(Text) + txtR:Show() +end + +function HealBot_Action_Tooltip_ClearLines() + for j=1,30 do + local txtL = getglobal("HealBot_TooltipTextL" .. j) + local txtR = getglobal("HealBot_TooltipTextR" .. j) + txtL:SetText(" ") + txtR:SetText(" ") + txtL:Hide() + txtR:Hide() + end +end + +function HealBot_Action_ShowTooltip(this) + if HealBot_Config.ShowTooltip==0 then return end + if not this.unit then return end; + if not this:IsEnabled() then return end; + + HealBot_Action_TooltipUnit = this.unit; + HealBot_Action_RefreshTooltip(this.unit); +end + +function HealBot_Action_HideTooltip(this) + if HealBot_Config.ShowTooltip==0 then return end + HealBot_Tooltip:Hide(); + HealBot_Action_TooltipUnit = nil; +end diff --git a/README.md b/README.md index fd6acd3..2b28092 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ **HealBotBlue** By Bluewhale. -Original Vanilla HealBot, while being a staple healing addon in Wrath and later expansions, was in its infancy during Vanilla WoW. It was a memory and CPU hog that ate up resources, featured a clunky UI, and offered limited functions. To fix all of the above while still using it as a base framework, I refactored the entire codebase. The monolithic code structure was split to follow a more modern approach (MVC design pattern), making it more stable and easily editable. Additionally, the inefficient AURA scanning was replaced with lightweight (Observer Pattern) reactive programming. It now updates only what is necessary when a trigger fires, rather than performing clumsy, continuous scans of a 40-man raid. By doing so, I managed to throttle down CPU and memory usage significantly, placing it on par with modern addons. Additional functionalities are being added over time to provide a holistic, healer-centered raid frame. +Original HealBot, while being a staple healing addon in Wrath and later expansions, was in its infancy during Vanilla WoW. It was a memory and CPU hog that ate up resources, featured a clunky UI, and offered limited functions. To fix all of the above while still using it as a base framework, I refactored the entire codebase. The monolithic code structure was split to follow a more modern approach (MVC design pattern), making it more stable and easily editable. Additionally, the inefficient AURA scanning was replaced with lightweight (Observer Pattern) reactive programming. It now updates only what is necessary when a trigger fires, rather than performing clumsy, continuous scans of a 40-man raid. By doing so, I managed to throttle down CPU and memory usage significantly, placing it on par with modern addons. Additional functionalities are being added over time to provide a holistic, healer-centered raid frame. > **NOTE:** For HealBot to work correctly, the **Selfcast** feature in WoW options needs to be disabled. @@ -23,8 +23,12 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\ ### Change Log **v1.3** -* **Monolithic Code Modularization:** Split the massive, single-file options system (`HealBot_Options.xml`/`HealBot_Options.lua`) into 7 dedicated, content-named panel subfiles (`General`, `Spells`, `Healing`, `CDC`, `Skins`, `Buffs`, and `Chat`) to improve codebase modularity and stability. +* **Monolithic Code Modularization (Options):** Split the massive, single-file options system (`HealBot_Options.xml`/`HealBot_Options.lua`) into 7 dedicated, content-named panel subfiles (`General`, `Spells`, `Healing`, `CDC`, `Skins`, `Buffs`, and `Chat`) to improve codebase modularity and stability. +* **Core Code Decoupling (MVC & Observer Pattern):** Modularized `HealBot.lua` and `HealBot_Action.lua` by decoupling them into dedicated service files: `Range`, `Comms`, `Aura`, `Spells`, `Events` controllers, and `Layout`, `Tooltip` views. * **Scope Resolution Fix:** Promoted `HealBot_Options_ComboButtons_Button` to global scope in the base options file to resolve a `nil` comparison error when loading spell settings. +* **Global Scoping & Variable Promotion:** Promoted `CalcEquipBonus`, `HealBot_EquipChangeTimer`, `HealValue`, `InitSpells`, and `Delay_RecalcParty` to global scope in `HealBot.lua` to ensure cross-module visibility. +* **CTRA Resurrection Cancel Bug Fix:** Fixed a nil table indexing crash in the CTRA `RESNO` message handler in `HealBot_Controller_Comms.lua`. +* **Lua 5.1 Compatibility Update:** Forwarded observer parameters directly via vararg forwarding (`...`) in `HealBot_Model.lua` to avoid runtime errors with the deprecated `arg` table. * **Core Options Layout Correction:** Relocated the "Defaults" and "Close" buttons back into the parent options frame's container, ensuring they display consistently at the bottom of all configuration tabs. * **Skins Grid Alignment:** Fixed layout overlaps on the Skins panel by setting explicit width/height sizes (`123 x 17`) on the opacity sliders and reorganizing all 13 sliders into a clean, uniform 2-column grid. * **Startup Refresh Optimization:** Resolved initial loading lag where action bars would be missing by forcing an immediate UI refresh (`HealBot_RecalcSpells()`) once spell data loads.