From 40eb89b475ce8e58394750dd4b4f8ac05e2f7f91 Mon Sep 17 00:00:00 2001 From: Glyalith Date: Fri, 7 Aug 2026 22:49:14 -0600 Subject: [PATCH] Fix hero-talent spell icons reverting to their base spell on zone change ns._cdIconHeal skips its repaint when its per-button texture memo equals the live texture. The memo was write-behind: only _cdIconHeal itself ever stamped it, on the reasoning that other painters always paint the current texture, so a stale memo could cost a redundant repaint but never a wrong skip. That holds only while "current" is the texture the memo already holds. A spell override can resolve late -- zoning into or out of an instance on a hero talent briefly reports Black Arrow as Kill Shot, Death Charge as Death's Advance -- so ForceButtonRefresh painted the BASE texture while the memo still held the override's. The SPELL_UPDATE_ICON heal that follows, whose whole purpose is repainting an override change, then compared equal and skipped, leaving the base icon up until something else repainted the slot (pressing the button sometimes did). Stamp the memo in ForceButtonRefresh with the texture it paints, so the memo always matches what is on the icon. The assisted-combat painters are deliberately left unstamped: they paint the SUGGESTED spell's texture rather than the action's, and stamping that would make the heal clobber the One Button Assist icon. --- .../EllesmereUIActionBars.lua | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/EllesmereUIActionBars/EllesmereUIActionBars.lua b/EllesmereUIActionBars/EllesmereUIActionBars.lua index c05f6e88..7738ce0e 100644 --- a/EllesmereUIActionBars/EllesmereUIActionBars.lua +++ b/EllesmereUIActionBars/EllesmereUIActionBars.lua @@ -3306,7 +3306,20 @@ function EAB_VTABLE.ForceButtonRefresh(btn, action) if not action then return end local icon = btn.icon or btn.Icon if icon then - icon:SetTexture(GetActionTexture(action)) + -- Stamp the icon texture-delta memo with what we actually paint. + -- ns._cdIconHeal skips its repaint when the memo equals the live + -- texture, so the memo is only safe while it matches what is ON the + -- icon. This path paints whatever GetActionTexture reports RIGHT NOW, + -- and a spell override can resolve late (zone in/out on a hero talent: + -- Black Arrow briefly reports as Kill Shot). Painting the base texture + -- here while the memo still held the override's left the two + -- disagreeing, and the SPELL_UPDATE_ICON heal that follows -- the one + -- whose whole job is repainting an override change -- then compared + -- equal and skipped, stranding the base icon until something else + -- repainted the slot. + local tex = GetActionTexture(action) + icon:SetTexture(tex) + EFD(btn).lastIconTex = tex -- The mixin's Update() HIDES the icon region for empty slots and only -- re-Shows it in its filled branch. A slot that was empty before this -- content change still has a hidden icon region, so painting the new @@ -4065,11 +4078,17 @@ do -- Shared per-button icon heal (texture-delta memo). The texture -- fileID is the override's visible fingerprint (never secret, per -- the API docs), so only buttons whose texture actually changed pay - -- the mixin path. The memo is write-behind everywhere else on - -- purpose: paths that paint the icon without stamping it - -- (ForceButtonRefresh, the infrequent walk's UpdateAction) always - -- paint the CURRENT texture, so a stale memo can only cause one - -- redundant repaint here -- never a wrong skip. Used by the full + -- the mixin path. INVARIANT: the memo must equal what is ON the icon, + -- so every path that paints the ACTION's texture stamps it too + -- (ForceButtonRefresh). This was previously write-behind, reasoning + -- that unstamped painters "always paint the CURRENT texture, so a + -- stale memo can only cause one redundant repaint -- never a wrong + -- skip"; that holds only while CURRENT means the texture the memo + -- already holds, and an override resolving late (zone in/out on a + -- hero talent) breaks it in exactly the direction that does cause a + -- wrong skip. The assist painters are the deliberate exception -- + -- they paint the SUGGESTED spell's texture, not the action's, and + -- stamping that would make this heal clobber them. Used by the full -- walk below and by the payload-targeted SPELL_UPDATE_ICON fast -- path in the dispatcher prologue; ns-hosted (200-local cap). ns._cdIconHeal = function(btn)