diff --git a/HealBot.lua b/HealBot.lua index 62360c5..28020e0 100644 --- a/HealBot.lua +++ b/HealBot.lua @@ -284,12 +284,16 @@ do spellName = HealBot_ScanTooltipTextLeft1 and HealBot_ScanTooltipTextLeft1:GetText() end + HealBot_CastFailed = false orig(slot, checkCursor, onSelf) if autoSelfCast then SetCVar("autoSelfCast", autoSelfCast) end - if spellName and HealBot_AnnounceCast then + if spellName and not HealBot_CastFailed then + HealBot_CastingSpell = spellName + HealBot_CastingTarget = mouseover + HealBot_Process_HealValue(spellName, mouseover) HealBot_AnnounceCast(spellName, mouseover) end end diff --git a/HealBotBlue.toc b/HealBotBlue.toc index 4d8e139..03a6a82 100644 --- a/HealBotBlue.toc +++ b/HealBotBlue.toc @@ -1,6 +1,6 @@ ## Interface: 11200 ## Title: HealBotBlue -## Version: 1.4.0 +## Version: 1.4.1 ## Author: Bluewhale ## Notes: Adds panel with skinable bars for healing and decursive ## SavedVariables: HealBot_Config diff --git a/HealBot_Controller_Comms.lua b/HealBot_Controller_Comms.lua index ef8434a..32fd9eb 100644 --- a/HealBot_Controller_Comms.lua +++ b/HealBot_Controller_Comms.lua @@ -70,6 +70,7 @@ function HealBot_OnEvent_AddonMsg(this, addon_id, inc_msg, dist_target, sender_i local tmpTest, unitname, heal_val tmpTest, tmpTest, unitname, heal_val = string.find(inc_msg, ">> (%a+) <<=>> (.%d+) <<" ); if heal_val then + if sender_id == UnitName("player") then return end if not HealBot_HealsIn[unitname] then HealBot_HealsIn[unitname] = 0; end diff --git a/HealBot_Controller_Events.lua b/HealBot_Controller_Events.lua index f294ee2..122b32f 100644 --- a/HealBot_Controller_Events.lua +++ b/HealBot_Controller_Events.lua @@ -3,10 +3,10 @@ HealBot_View_DirtyUnits = {} local HealBot_Timer1, HealsIn_Timer = 0, 0; +HealBot_LastModState = "" function HealBot_OnLoad(this) this:RegisterEvent("VARIABLES_LOADED"); - this:RegisterEvent("MODIFIER_STATE_CHANGED"); SLASH_HEALBOT1 = "/healbot"; SLASH_HEALBOT2 = "/hb"; @@ -42,6 +42,20 @@ function HealBot_RegisterThis(this) end function HealBot_OnUpdate(this, arg1) + if HealBot_Action_TooltipUnit and HealBot_Tooltip:IsVisible() then + local currentModState = "" + if IsShiftKeyDown() then currentModState = currentModState .. "S" end + if IsControlKeyDown() then currentModState = currentModState .. "C" end + if IsAltKeyDown() then currentModState = currentModState .. "A" end + + if HealBot_LastModState ~= currentModState then + HealBot_LastModState = currentModState + HealBot_Action_RefreshTooltip(HealBot_Action_TooltipUnit) + end + else + HealBot_LastModState = "" + end + if HealBot_TargetRestorePending then HealBot_TargetRestoreTimer = HealBot_TargetRestoreTimer + arg1; if HealBot_TargetRestoreTimer >= 0.1 then @@ -179,17 +193,12 @@ local HealBot_EventHandlers = { ["VARIABLES_LOADED"] = function(this) HealBot_OnEvent_VariablesLoaded(this) end, - ["MODIFIER_STATE_CHANGED"] = function(this, arg1, arg2) - if HealBot_Action_TooltipUnit and HealBot_Tooltip:IsVisible() then - HealBot_Action_RefreshTooltip(HealBot_Action_TooltipUnit) - end - 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, + ["SPELLCAST_STOP"] = function(this) HealBot_OnEvent_SpellcastStop(this, "SPELLCAST_STOP") end, + ["SPELLCAST_INTERRUPTED"] = function(this) HealBot_OnEvent_SpellcastStop(this, "SPELLCAST_INTERRUPTED") end, + ["SPELLCAST_FAILED"] = function(this) HealBot_OnEvent_SpellcastStop(this, "SPELLCAST_FAILED") 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, @@ -464,8 +473,11 @@ function HealBot_OnEvent_SpellcastStart(this, spell, duration) end end -function HealBot_OnEvent_SpellcastStop(this) +function HealBot_OnEvent_SpellcastStop(this, eventName) HealBot_IsCasting = false; + if eventName == "SPELLCAST_FAILED" then + HealBot_CastFailed = true; + end HealBot_StopCasting(); HealBot_RecalcHeals(); if HealBot_IamRessing then diff --git a/HealBot_Controller_Spells.lua b/HealBot_Controller_Spells.lua index 1677da2..b766d4e 100644 --- a/HealBot_Controller_Spells.lua +++ b/HealBot_Controller_Spells.lua @@ -127,10 +127,45 @@ function HealBot_AnnounceCast(spell, target) end end +function HealBot_Process_HealValue(spell, target) + if HealBot_Spells[spell] and HealBot_Spells[spell].CastTime > 1 then + HealBot_HealValue = HealBot_Spells[spell].HealsDur; + + -- Apply dynamic Preservation bonus + if HEALBOT_REGROWTH and string.find(spell, HEALBOT_REGROWTH) then + local presRank = HealBot_GetTalentRank("Preservation") + if presRank > 0 then + local hasRejuv = false + for i=1,32 do + local buff = UnitBuff(target, i) + if not buff then break end + if string.find(buff, "Spell_Nature_Rejuvenation") then + hasRejuv = true + break + end + end + if hasRejuv then + local extHeal = HealBot_Spells[spell].HealsExt or 0 + local bonus = extHeal * (presRank * 0.10) + HealBot_HealValue = HealBot_HealValue + math.floor(bonus) + end + end + end + local uname = UnitName(target) + if uname then + HealBot_SendAddonMessage(HEALBOT_ADDON_ID, ">> " .. uname .. " <<=>> " .. HealBot_HealValue .. " << "); + if not HealBot_HealsIn[uname] then + HealBot_HealsIn[uname] = 0; + end + HealBot_HealsIn[uname] = HealBot_HealsIn[uname] + HealBot_HealValue; + HealBot_RecalcHeals(HealBot_FindUnitID(uname)); + end + end +end + function HealBot_StartCasting(spell, target, ttype) + HealBot_CastFailed = false; HealBot_CastSpellByName(spell); - HealBot_CastingSpell = spell; - HealBot_CastingTarget = target; if ( SpellCanTargetUnit(target) ) then SpellTargetUnit(target); ttype = "fired"; @@ -138,28 +173,36 @@ function HealBot_StartCasting(spell, target, ttype) SpellTargetUnit(target); SpellStopTargeting() elseif ttype == "direct" then - if ( CheckInteractDistance(target, 4) ) then + if ( UnitIsUnit(target, "player") or CheckInteractDistance(target, 4) ) then ttype = "fired"; end end - HealBot_AnnounceCast(spell, target) - if ttype == "fired" and HealBot_Spells[spell] then - if HealBot_Spells[spell].CastTime > 1 then - HealBot_HealValue = HealBot_Spells[spell].HealsDur; - HealBot_SendAddonMessage(HEALBOT_ADDON_ID, ">> " .. UnitName(target) .. " <<=>> " .. HealBot_HealValue .. " << "); + if not HealBot_CastFailed then + HealBot_CastingSpell = spell; + HealBot_CastingTarget = target; + HealBot_Process_HealValue(spell, target); + HealBot_AnnounceCast(spell, target); end end end function HealBot_StopCasting() if HealBot_CastingTarget then - if HealBot_HealsIn[UnitName(HealBot_CastingTarget)] then - if HealBot_HealValue > 0 then - HealBot_SendAddonMessage(HEALBOT_ADDON_ID, ">> " .. UnitName(HealBot_CastingTarget) .. " <<=>> " .. 0 - HealBot_HealValue .. " << "); - HealBot_HealValue = 0; + if HealBot_HealValue > 0 then + local uname = UnitName(HealBot_CastingTarget) + if uname then + HealBot_SendAddonMessage(HEALBOT_ADDON_ID, ">> " .. uname .. " <<=>> " .. 0 - HealBot_HealValue .. " << "); + if HealBot_HealsIn[uname] then + HealBot_HealsIn[uname] = HealBot_HealsIn[uname] - HealBot_HealValue; + if HealBot_HealsIn[uname] < 0 then + HealBot_HealsIn[uname] = 0; + end + HealBot_RecalcHeals(HealBot_FindUnitID(uname)); + end end + HealBot_HealValue = 0; end end HealBot_CastingSpell = nil; @@ -298,7 +341,46 @@ function HealBot_SetItemDefaults(spell) end function HealBot_SetSpellDefaults(spell) - HealBot_Spells[spell].HealsDur = math.floor((HealBot_Spells[spell].HealsCast + HealBot_Spells[spell].HealsExt) + HealBot_Spells[spell].RealHealing); + local baseHeal = HealBot_Spells[spell].HealsCast or 0 + local extHeal = HealBot_Spells[spell].HealsExt or 0 + local realHeal = HealBot_Spells[spell].RealHealing or 0 + + local _, class = UnitClass("player") + if class == "DRUID" then + local giftRank = HealBot_GetTalentRank("Gift of Nature") + local genRank = HealBot_GetTalentRank("Genesis") + + local mult = 1 + (giftRank * 0.02) + baseHeal = baseHeal * mult + extHeal = extHeal * mult + realHeal = realHeal * mult + + if genRank > 0 then + extHeal = extHeal * (1 + (genRank * 0.05)) + end + elseif class == "PRIEST" then + local spiritRank = HealBot_GetTalentRank("Spiritual Healing") + local mult = 1 + (spiritRank * 0.06) + baseHeal = baseHeal * mult + extHeal = extHeal * mult + realHeal = realHeal * mult + + if HEALBOT_RENEW and string.find(spell, HEALBOT_RENEW) then + local renewRank = HealBot_GetTalentRank("Improved Renew") + extHeal = extHeal * (1 + (renewRank * 0.05)) + end + elseif class == "PALADIN" then + local hlRank = HealBot_GetTalentRank("Healing Light") + if (HEALBOT_HOLY_LIGHT and string.find(spell, HEALBOT_HOLY_LIGHT)) or + (HEALBOT_FLASH_OF_LIGHT and string.find(spell, HEALBOT_FLASH_OF_LIGHT)) or + (HEALBOT_HOLY_SHOCK and string.find(spell, HEALBOT_HOLY_SHOCK)) then + local mult = 1 + (hlRank * 0.04) + baseHeal = baseHeal * mult + realHeal = realHeal * mult + end + end + + HealBot_Spells[spell].HealsDur = math.floor(baseHeal + extHeal + realHeal); end function HealBot_AddHeal(spell) @@ -433,17 +515,38 @@ function HealBot_RecalcSpells() HealBot_RecalcParty(); end +function HealBot_GetTalentRank(talentName) + for t = 1, GetNumTalentTabs() do + for i = 1, GetNumTalents(t) do + local nameTalent, icon, tier, column, currRank, maxRank = GetTalentInfo(t, i); + if nameTalent == talentName then + return currRank; + end + end + end + return 0; +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); + local currRank = HealBot_GetTalentRank("Spiritual Guidance") + if currRank > 0 then + local spiGuideBonus = stat * 0.05; + heals_modifer = heals_modifer + (currRank * spiGuideBonus); + end return heals_modifer; end function HealBot_GetBonus() - local HealBonus = HealBot_BonusScanner:GetBonus(); + local HealBonus = HealBot_BonusScanner:GetBonus() or 0; + if UnitClass("player") == "PALADIN" then + local ironRank = HealBot_GetTalentRank("Ironclad") + if ironRank > 0 then + local base, effectiveArmor = UnitArmor("player") + HealBonus = HealBonus + (effectiveArmor * (ironRank * 0.01)) + end + end return HealBonus; end diff --git a/HealBot_Localization.en.lua b/HealBot_Localization.en.lua index e979c13..c63c21b 100644 --- a/HealBot_Localization.en.lua +++ b/HealBot_Localization.en.lua @@ -1,4 +1,4 @@ -HEALBOT_VERSION = "1.4"; +HEALBOT_VERSION = "1.4.1"; ------------- -- ENGLISH -- diff --git a/README.md b/README.md index e5f6c42..4b4049a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,13 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\ ### Change Log +**v1.4.1** +* **Talent-based Healing Calculations:** Implemented dynamic spell healing calculations for Druid, Priest, and Paladin classes based on talents. +* **Modifier Key Polling:** Replaced MODIFIER_STATE_CHANGED event with polling in HealBot_OnUpdate to track modifier keys reliably. +* **Incoming Heals Fix:** Fixed a bug where incoming heals failed to broadcast or show on the UI due to synchronous GCD fails and `CheckInteractDistance` limitations in the 1.12 API. +* **Hovercast Incoming Heals:** Added full support for processing and displaying incoming heals when using action bar hovercasting. +* **Zero-latency Self-Heals:** Incoming heals for the local player now process instantly with zero latency and no network reliance, ensuring they function even when playing solo. + **v1.4** * **Extra Frame support for pets and familiars added:** Togglable from healing tab - Extra frames option selected & pets selected from dropdown. * **Customizable Health Text:** Added a new dropdown in the Healing Options tab that allows users to choose how health text is displayed on unit frames (`Name Only`, `Health Percentage`, `Real Health`, or `Health Deficit`). Includes a smart truncation algorithm to preserve critical health numbers even if the unit name is too long.