diff --git a/HealBotBlue.toc b/HealBotBlue.toc index 03a6a82..6309ac0 100644 --- a/HealBotBlue.toc +++ b/HealBotBlue.toc @@ -1,6 +1,6 @@ ## Interface: 11200 ## Title: HealBotBlue -## Version: 1.4.1 +## Version: 1.5 ## Author: Bluewhale ## Notes: Adds panel with skinable bars for healing and decursive ## SavedVariables: HealBot_Config diff --git a/HealBot_Action.lua b/HealBot_Action.lua index 052a0c6..f7130b9 100644 --- a/HealBot_Action.lua +++ b/HealBot_Action.lua @@ -117,11 +117,107 @@ function HealBot_Action_HealUnit_OnLeave(this) HealBot_Action_HideTooltip(this); end +-------------------------------------------------------------------------------------------------- +-- Puppeteer Utility Ports for Macro and Item Execution +-------------------------------------------------------------------------------------------------- + +function HealBot_SplitString(str, delimiter) + local result = {} + local start_pos = 1 + while true do + local end_pos = string.find(str, delimiter, start_pos, true) + if not end_pos then + table.insert(result, string.sub(str, start_pos)) + break + end + table.insert(result, string.sub(str, start_pos, end_pos - 1)) + start_pos = end_pos + string.len(delimiter) + end + return result +end + +function HealBot_RunMacroText(body) + local commands = HealBot_SplitString(body, "\n") + for i = 1, table.getn(commands) do + ChatFrameEditBox:SetText(commands[i]) + ChatEdit_SendText(ChatFrameEditBox) + end +end + +function HealBot_RunMacro(name) + if GetMacroIndexByName(name) == 0 then return end + local _, _, body = GetMacroInfo(GetMacroIndexByName(name)) + HealBot_RunMacroText(body) +end + +function HealBot_GetBagSlotInfo(bag, slot) + local link = GetContainerItemLink(bag, slot) + if not link then return end + local _, _, name = string.find(link, "%[(.*)%]") + local _, count = GetContainerItemInfo(bag, slot) + return name, count +end + +function HealBot_FindBagSlot(itemName) + local bestBag, bestSlot, lowestStackSize + for bag = 0, NUM_BAG_FRAMES do + for slot = 1, GetContainerNumSlots(bag) do + local name, count = HealBot_GetBagSlotInfo(bag, slot) + if itemName == name then + if not lowestStackSize or lowestStackSize > count then + bestBag = bag + bestSlot = slot + lowestStackSize = count + end + end + end + end + return bestBag, bestSlot +end + +function HealBot_UseItem(itemName) + local bag, slot = HealBot_FindBagSlot(itemName) + if not bag then return false end + UseContainerItem(bag, slot) + return true +end + function HealBot_Action_HealUnit_OnClick(this,button) local decode_button = HealBot_Decode_Button(button); local pattern = HealBot_Action_SpellPattern(decode_button); - -- Resurrection override on dead target + if not pattern then return end + + -- Buff casting override (Spells only) + if HealBot_Config.BuffWatch == 1 then + local inCombat = UnitAffectingCombat("player") + if (not inCombat) or (HealBot_Config.BuffWatchInCombat == 1) then + if HealBot_MissingBuffs[this.unit] then + local missingBuff = HealBot_MissingBuffs[this.unit] + if decode_button == "Left" or decode_button == "Right" then + HealBot_CastSpellOnFriend(missingBuff, this.unit) + return + end + end + end + end + + -- Priority 1: Inline Scripts (starts with /) + if string.sub(pattern, 1, 1) == "/" then + local oldTarget = nil + if UnitExists("target") then oldTarget = UnitName("target") end + TargetUnit(this.unit) + HealBot_RunMacroText(pattern) + if oldTarget then + TargetByName(oldTarget) + else + ClearTarget() + end + return + end + + -- Priority 2: Spells + -- Resurrection override on dead target first if UnitIsDeadOrGhost(this.unit) then local rezSpell = HealBot_GetRezSpellForClass(); if rezSpell then @@ -129,24 +225,41 @@ function HealBot_Action_HealUnit_OnClick(this,button) return end end - - -- Buff casting override - if HealBot_Config.BuffWatch == 1 then - local inCombat = UnitAffectingCombat("player") - if (not inCombat) or (HealBot_Config.BuffWatchInCombat == 1) then - local myClass = UnitClass("player") - if HealBot_MissingBuffs[this.unit] then - local missingBuff = HealBot_MissingBuffs[this.unit] - if decode_button == "Left" or decode_button == "Right" then - HealBot_CastSpellOnFriend(missingBuff, this.unit) - return - end - end + + if HealBot_Spells[pattern] or HealBot_GetSpellId(pattern) then + HealBot_HealUnit(this.unit, pattern); + return + end + + -- Priority 3: Named Macros + if GetMacroIndexByName(pattern) ~= 0 then + local oldTarget = nil + if UnitExists("target") then oldTarget = UnitName("target") end + TargetUnit(this.unit) + HealBot_RunMacro(pattern) + if oldTarget then + TargetByName(oldTarget) + else + ClearTarget() end - end + return + end + + -- Priority 4: Items + local bag, slot = HealBot_FindBagSlot(pattern) + if bag then + local oldTarget = nil + if UnitExists("target") then oldTarget = UnitName("target") end + TargetUnit(this.unit) + UseContainerItem(bag, slot) + if SpellIsTargeting() then SpellTargetUnit(this.unit) end + if oldTarget then TargetByName(oldTarget) else ClearTarget() end + return + end - HealBot_HealUnit(this.unit,pattern); - end + -- Fallback attempt + HealBot_HealUnit(this.unit, pattern); +end function HealBot_Action_HealUnitCheck_OnClick(this) if not this.unit then return end diff --git a/HealBot_Controller_Spells.lua b/HealBot_Controller_Spells.lua index b766d4e..97f9dd9 100644 --- a/HealBot_Controller_Spells.lua +++ b/HealBot_Controller_Spells.lua @@ -475,19 +475,43 @@ end function HealBot_GetHealSpell(unit, pattern) if (not UnitName(unit)) then return nil end; + if not pattern 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_ActiveShapeshiftStance and HealBot_Config.AutoUnshift ~= 1 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; + + -- Handle Inline Scripts + if string.sub(pattern, 1, 1) == "/" then + return pattern; + end + + -- Handle Spells + if HealBot_Spells[pattern] or HealBot_GetSpellId(pattern) then + local spell = HealBot_GetSpellName(HealBot_GetSpellId(pattern)) or pattern + local range = 40; + if HealBot_Spells[spell] then + if not HealBot_CanCastSpell(spell, unit) then return nil end; + range = HealBot_Spells[spell].range or 40; + end + if HealBot_Range_Check(unit, range) == 0 then return nil end; + return spell; + end + + -- Handle Named Macros + if GetMacroIndexByName(pattern) ~= 0 then + return pattern; + end + + -- Handle Items + local bag, slot = HealBot_FindBagSlot(pattern) + if bag then + -- optionally check item range, but skipping for now as it's complex in vanilla + return pattern; + end + + return nil; end function HealBot_HealUnit(unit, pattern) diff --git a/HealBot_Data.lua b/HealBot_Data.lua index d41f83d..6915aa6 100644 --- a/HealBot_Data.lua +++ b/HealBot_Data.lua @@ -559,3 +559,23 @@ HealBot_SpamCnt=0; HealBot_Action_TooltipUnit=nil; HealBot_Ressing = {}; HealBot_IamRessing = false; + +-- Table Recycling Pool (Puppeteer-inspired) +local tablePool = {} +function HealBot_GetTable() + local t = table.remove(tablePool) + if not t then + t = {} + end + return t +end + +function HealBot_ReleaseTable(t) + if type(t) == "table" then + for k in pairs(t) do + t[k] = nil + end + table.insert(tablePool, t) + end +end + diff --git a/HealBot_View_Tooltip.lua b/HealBot_View_Tooltip.lua index a270cc8..7c1dd11 100644 --- a/HealBot_View_Tooltip.lua +++ b/HealBot_View_Tooltip.lua @@ -3,6 +3,16 @@ HealBot_Action_TooltipUnit = nil +function HealBot_Tooltip_GetSpellColor(spell) + local r, g, b = 1, 1, 0 -- default yellow + if spell and HealBot_Spells and HealBot_Spells[spell] and HealBot_Spells[spell].Mana then + if UnitMana("player") < HealBot_Spells[spell].Mana then + r, g, b = 1, 0, 0 -- red if not enough mana + end + end + return r, g, b +end + function HealBot_Action_RefreshTooltip(unit) if HealBot_Config.ShowTooltip==0 then return end if not unit then unit = HealBot_Action_TooltipUnit end @@ -33,53 +43,63 @@ function HealBot_Action_RefreshTooltip(unit) 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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellLeft) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONLEFT.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellLeft,sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellMiddle) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONMIDDLE.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellMiddle,sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellRight) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONRIGHT.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellRight,sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellButton4) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON4.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellButton4,sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellButton5) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON5.." "..HEALBOT_OPTIONS_COMBOBUTTON..": "..spellButton5,sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellLeft) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONLEFT..":",sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellMiddle) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONMIDDLE..":",sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellRight) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTONRIGHT..":",sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellButton4) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON4..":",sr,sg,sb,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) + local sr, sg, sb = HealBot_Tooltip_GetSpellColor(spellButton5) + HealBot_Action_Tooltip_SetLineLeft(HEALBOT_OPTIONS_BUTTON5..":",sr,sg,sb,linenum) HealBot_Action_Tooltip_SetLineRight(HealBot_Action_Tooltip_SpellSummary(spellButton5),1,1,1,linenum) end end diff --git a/README.md b/README.md index 4b4049a..f62a2e5 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,21 @@ Default installation path: `C:\Program Files\World of Warcraft\Interface\AddOns\ * **Non-Mana Resource Tracking:** Track Rage, Energy, and Focus for non-mana classes. * **Highly Customizable Skins:** Fully configure dimensions (width, height), row spacing, column layouts, custom textures, opacity, class-colored frames, and outline of fonts. +### To be implemented +- **GUID-based frame mapping:** Migrate internal state to use lightweight GUID tracking to resolve bugs when targets switch or duplicate names appear (e.g., enemy mobs, warlock pets). +- **Mod Integration Optimization:** Add conditional SuperWoW/UnitXP SP3 integration to reduce GC spikes and improve frame rendering accuracy. + ### Known issues * **New spell rank bug** you may experience lua error spaming your chat frame after learning new healing spells. /reload will clear the issue. To be fixed next release ### Change Log +**v1.5** +* **Macro, Item, and Script Bindings:** Support for binding named macros, inventory items, and inline scripts (e.g. `/target`) directly to mouse clicks in the Spells tab. All of these now natively support implicit `@mouseover` targeting on the unit frame you click, without losing your current target. +* **Modifier-Aware Tooltips & Resource Costs:** The tooltip dynamically updates to show exactly what action is bound when holding down Shift, Ctrl, or Alt over a frame. Spells also show their required mana cost, turning red if you do not have enough mana to cast them. +* **Memory Recycling Pool:** Added a local table recycling pool in the data layer to greatly reduce Lua garbage collection (GC) spikes, improving overall client frame rates during intense combat. + **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.