From 66ff1ae7115948fd6dddbaf45171612623a21cba Mon Sep 17 00:00:00 2001 From: DuvelCorp Date: Thu, 10 Sep 2026 16:38:14 +0200 Subject: [PATCH] Animations: wire remaining effects + 2-column options panel - Shared FX toolkit in fx-celebrate.lua: mth_shake tween type, MTH_FX_Flash glow helper, MTH_CelebrateRunaway red shaking banner, gold title reset. - pet.runaway, pet.happiness, pet.loyaltyup, pet.feed - combat.ammoswap, combat.rangedswap, combat.mmprocs - ui.zbar, ui.stable, ui.minimapping, ui.optionsclose - book.model, book.search - Options > Animations panel now uses a balanced two-column layout. - CHANGELOG Animations entry expanded (no version bump). --- CHANGELOG.md | 2 +- api/core-feed-tracking.lua | 7 ++ api/core-scan-stablemaster.lua | 3 + api/fx-celebrate.lua | 144 ++++++++++++++++++++++++++++++++ api/hunterbook-model-viewer.lua | 14 ++++ api/hunterbook-tab-stable.lua | 11 +++ api/hunterbook.lua | 33 +++++++- api/map-markers.lua | 19 +++++ api/options-animations.lua | 26 ++++-- api/options-shell.lua | 20 ++++- modules/expammo/engine.lua | 9 ++ modules/smartammo/engine.lua | 15 ++++ modules/zhunter/zBar.lua | 23 +++++ modules/zhunter/zButtonPet.lua | 28 +++++++ 14 files changed, 343 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 949e830..6c2cce7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ Resurrecting on Octowow. ### Added -- **Animations**: MetaHunt now plays optional animations that make the interface feel alive. A firework-and-banner celebration bursts on screen when your pet learns a new ability or a new rank, the Great Book slides up and fades in when you open it and gently fades its list when you switch tabs, the options window slides in when opened and its tabs fade as you switch them, the minimap button pulses when clicked, collapsible zButton bars fade their buttons in as they expand, and world-map markers fade in when you locate a beast, vendor or master. Everything is optional and off-switchable: a new **Animations** tab (Options → Animations) lets you toggle each effect on its own — or disable them all at once. Powered by the PizzaSauce animation library. +- **Animations**: MetaHunt now plays optional animations that make the interface feel alive. A firework-and-banner celebration bursts on screen when your pet learns a new ability or a new rank, and a red shaking banner warns you when a pet runs away for good. Your pet button flashes when its happiness changes, it gains a loyalty level, or you feed it; the ammo button flashes when Smart Ammo swaps your bullets or auto-equips after a weapon swap; and the MM Widget pulses on a Lock & Load proc. The Great Book slides up and fades in when you open it, gently fades its list when you switch tabs or run a search, fades beast models into view, and fades out when closed; stable slots flash when a pet is deposited, withdrawn or swapped. The options window slides in when opened, fades its tabs as you switch them, and fades out when closed. The minimap button pulses when clicked, minimap markers ping as beasts come into range, collapsible zButton bars fade their buttons in as they expand, the zBar fades in when enabled, and world-map markers fade in when you locate a beast, vendor or master. Everything is optional and off-switchable: a new **Animations** tab (Options → Animations) lets you toggle each effect on its own — or disable them all at once. Powered by the PizzaSauce animation library. - **zTrack — Find Fish**: Added new spell Find Fish to the zTrack list of trackings. diff --git a/api/core-feed-tracking.lua b/api/core-feed-tracking.lua index 565831e..95871df 100644 --- a/api/core-feed-tracking.lua +++ b/api/core-feed-tracking.lua @@ -1385,6 +1385,13 @@ function MTH_FEED_FinalizeAttempt(attemptId, payload) if petKey ~= "" then MTH_FEED_Runtime.sessionFeedsByPetId[petKey] = (tonumber(MTH_FEED_Runtime.sessionFeedsByPetId[petKey]) or 0) + 1 end + if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" + and MTH_FX_IsEnabled("pet.feed") then + local target = getglobal("zButtonPet") + if target then + MTH_FX_Flash(target, 0.35, 1.00, 0.35, 0.5) + end + end else petRow.totals.rejected = petRow.totals.rejected + 1 if reason == "no-buff" then diff --git a/api/core-scan-stablemaster.lua b/api/core-scan-stablemaster.lua index 45b4bf1..efd50aa 100644 --- a/api/core-scan-stablemaster.lua +++ b/api/core-scan-stablemaster.lua @@ -2791,6 +2791,9 @@ function MTH_PETS_RecordPetRunaway(source, rawMessage) if MTH:IsMessageEnabled("petRanAway", true) then MTH:Print("YOUR PET " .. tostring(displayName) .. " HAS RUNAWAY FOREVER :-( Try to feed it better next time.") end + if type(MTH_CelebrateRunaway) == "function" then + MTH_CelebrateRunaway(displayName) + end end return true diff --git a/api/fx-celebrate.lua b/api/fx-celebrate.lua index ee6af0f..3649c85 100644 --- a/api/fx-celebrate.lua +++ b/api/fx-celebrate.lua @@ -33,6 +33,7 @@ MTH_FX_DBG = { -- Sections shown, in order, in the options panel. `titleKey` localizes `title`. MTH_FX_ANIM_SECTIONS = { { id = "pet", title = "Pet", titleKey = "ANIM_SECTION_PET" }, + { id = "combat", title = "Combat", titleKey = "ANIM_SECTION_COMBAT" }, { id = "book", title = "Book", titleKey = "ANIM_SECTION_BOOK" }, { id = "ui", title = "Interface", titleKey = "ANIM_SECTION_UI" }, } @@ -46,6 +47,34 @@ MTH_FX_ANIM_DEFS = { label = "Celebrate new pet abilities", labelKey = "ANIM_PET_CELEBRATE", tooltip = "Play a firework and a banner in the middle of the screen when your pet learns a new ability or a new rank.", tooltipKey = "ANIM_PET_CELEBRATE_TIP" }, + { key = "pet.runaway", section = "pet", default = true, + label = "Warn when a pet runs away", labelKey = "ANIM_PET_RUNAWAY", + tooltip = "Flash a red, shaking banner in the middle of the screen when one of your pets runs away for good.", + tooltipKey = "ANIM_PET_RUNAWAY_TIP" }, + { key = "pet.happiness", section = "pet", default = true, + label = "Flash on happiness change", labelKey = "ANIM_PET_HAPPINESS", + tooltip = "Briefly flash the pet happiness area green when your pet cheers up, or red when it gets unhappy.", + tooltipKey = "ANIM_PET_HAPPINESS_TIP" }, + { key = "pet.loyaltyup", section = "pet", default = true, + label = "Glow on loyalty gain", labelKey = "ANIM_PET_LOYALTYUP", + tooltip = "Give a short golden glow when your pet gains a loyalty level.", + tooltipKey = "ANIM_PET_LOYALTYUP_TIP" }, + { key = "pet.feed", section = "pet", default = true, + label = "Flash when feeding your pet", labelKey = "ANIM_PET_FEED", + tooltip = "Give a short green flash when Feed-O-Matic feeds your pet.", + tooltipKey = "ANIM_PET_FEED_TIP" }, + { key = "combat.ammoswap", section = "combat", default = true, + label = "Flash on Smart Ammo swap", labelKey = "ANIM_COMBAT_AMMOSWAP", + tooltip = "Briefly highlight the zAmmo button when Smart Ammo swaps your ammo.", + tooltipKey = "ANIM_COMBAT_AMMOSWAP_TIP" }, + { key = "combat.rangedswap", section = "combat", default = true, + label = "Flash on ranged weapon swap", labelKey = "ANIM_COMBAT_RANGEDSWAP", + tooltip = "Briefly highlight the zRanged button when you hot-swap a ranged weapon.", + tooltipKey = "ANIM_COMBAT_RANGEDSWAP_TIP" }, + { key = "combat.mmprocs", section = "combat", default = true, + label = "Pulse MM Widget procs", labelKey = "ANIM_COMBAT_MMPROCS", + tooltip = "Pulse an MM Widget cell when a Lock and Load or Experimental ammo proc lights up.", + tooltipKey = "ANIM_COMBAT_MMPROCS_TIP" }, { key = "book.open", section = "book", default = true, label = "Fade in the Hunter's Book", labelKey = "ANIM_BOOK_OPEN", tooltip = "Fade the Hunter's Book window in when you open it.", @@ -54,6 +83,14 @@ MTH_FX_ANIM_DEFS = { label = "Fade Hunter's Book tab changes", labelKey = "ANIM_BOOK_TABS", tooltip = "Fade the results list back in when you switch tabs inside the Hunter's Book.", tooltipKey = "ANIM_BOOK_TABS_TIP" }, + { key = "book.model", section = "book", default = true, + label = "Fade in beast models", labelKey = "ANIM_BOOK_MODEL", + tooltip = "Fade the beast model viewer in when it loads a new model.", + tooltipKey = "ANIM_BOOK_MODEL_TIP" }, + { key = "book.search", section = "book", default = true, + label = "Fade search results", labelKey = "ANIM_BOOK_SEARCH", + tooltip = "Fade the results list when your search filter changes.", + tooltipKey = "ANIM_BOOK_SEARCH_TIP" }, { key = "ui.options", section = "ui", default = true, label = "Slide the options window open", labelKey = "ANIM_UI_OPTIONS", tooltip = "Slide and fade the MetaHunt options window in when you open it.", @@ -62,14 +99,30 @@ MTH_FX_ANIM_DEFS = { label = "Fade options tab changes", labelKey = "ANIM_UI_OPTIONTABS", tooltip = "Fade the settings panel when you switch tabs inside the options window.", tooltipKey = "ANIM_UI_OPTIONTABS_TIP" }, + { key = "ui.optionsclose", section = "ui", default = true, + label = "Fade windows closed", labelKey = "ANIM_UI_OPTIONSCLOSE", + tooltip = "Fade the options and Hunter's Book windows out when you close them.", + tooltipKey = "ANIM_UI_OPTIONSCLOSE_TIP" }, { key = "ui.minimap", section = "ui", default = true, label = "Pulse the minimap button on click", labelKey = "ANIM_UI_MINIMAP", tooltip = "Give the minimap button a quick pulse when you click it.", tooltipKey = "ANIM_UI_MINIMAP_TIP" }, + { key = "ui.minimapping", section = "ui", default = true, + label = "Ping the minimap on locate", labelKey = "ANIM_UI_MINIMAPPING", + tooltip = "Send a short ping ripple over the minimap when you locate a tracked NPC.", + tooltipKey = "ANIM_UI_MINIMAPPING_TIP" }, { key = "ui.zbuttons", section = "ui", default = true, label = "Fade zButton bars open", labelKey = "ANIM_UI_ZBUTTONS", tooltip = "Fade the child buttons in when a collapsible zButton bar (pet, tracking, aspects...) expands.", tooltipKey = "ANIM_UI_ZBUTTONS_TIP" }, + { key = "ui.zbar", section = "ui", default = true, + label = "Fade the zBar in", labelKey = "ANIM_UI_ZBAR", + tooltip = "Fade the unified zBar buttons in when the bar becomes active.", + tooltipKey = "ANIM_UI_ZBAR_TIP" }, + { key = "ui.stable", section = "ui", default = true, + label = "Flash stable slots on change", labelKey = "ANIM_UI_STABLE", + tooltip = "Flash a stable slot in the Hunter's Book when a pet is deposited or withdrawn.", + tooltipKey = "ANIM_UI_STABLE_TIP" }, { key = "ui.mappins", section = "ui", default = true, label = "Fade map markers in", labelKey = "ANIM_UI_MAPPINS", tooltip = "Fade the world map markers in when you locate a beast, vendor or master.", @@ -166,6 +219,63 @@ else end MTH_FX_DBG.step = "3-after-registertype" +-- Register a decaying "shake" tween type: oscillates the frame around its own +-- current anchor point, with the amplitude fading to zero over the tween so it +-- always lands back exactly where it started. Used for negative events. +if Sauce and type(Sauce.RegisterType) == "function" and not Sauce.MTH_HasShake then + local ok, err = pcall(function() + Sauce.MTH_HasShake = true + Sauce:RegisterType("mth_shake", { + init = function(target) + local p, rel, rp, x, y = target:GetPoint(1) + return { p or "CENTER", rel, rp or p or "CENTER", x or 0, y or 0 } + end, + apply = function(target, from, to, t, tween) + local mag = tween.magnitude or 8 + local freq = tween.freq or 34 + local decay = 1 - t + local offset = math.sin(t * freq) * mag * decay + target:ClearAllPoints() + target:SetPoint(from[1], from[2], from[3], (from[4] or 0) + offset, from[5] or 0) + end, + }) + end) + MTH_FX_DBG.shakeOk = ok + MTH_FX_DBG.shakeErr = err +end + +-- Reusable flash: overlays a short additive colour glow on any frame/button and +-- fades it out. Safe on any frame that supports CreateTexture; silent no-op +-- otherwise. The overlay texture is cached on the frame for reuse. +function MTH_FX_Flash(frame, r, g, b, duration) + if not Sauce or not frame then + return + end + if type(frame.CreateTexture) ~= "function" then + return + end + local tex = frame.mthFxFlash + if not tex then + tex = frame:CreateTexture(nil, "OVERLAY") + tex:SetTexture("Interface\\Buttons\\CheckButtonHilight") + tex:SetBlendMode("ADD") + tex:SetAllPoints(frame) + frame.mthFxFlash = tex + end + tex:SetVertexColor(r or 1, g or 1, b or 1) + tex:SetAlpha(0) + tex:Show() + local dur = duration or 0.45 + Sauce:Stop(tex) + Sauce:Sequence({ + Sauce:FadeTo(tex, 0.85, dur * 0.35), + Sauce:FadeTo(tex, 0, dur * 0.65, "inQuad", { + onFinish = function() tex:Hide() end, + }), + }) +end + + local MTH_FX_NUM_SPARKS = 22 local MTH_FX_Container local MTH_FX_MsgFrame @@ -240,6 +350,7 @@ function MTH_Celebrate(title, subtitle) return end + MTH_FX_TitleFS:SetTextColor(1, 0.82, 0) MTH_FX_TitleFS:SetText(title or "") MTH_FX_SubFS:SetText(subtitle or "") @@ -305,6 +416,39 @@ function MTH_CelebratePetAbility(label) MTH_Celebrate(MTH_FX_L("ANIM_PET_LEARNED_TITLE", "New Pet Ability!"), label) end +-- Dramatic negative banner: a red, shaking message shown when a pet runs away +-- for good. Gated on pet.runaway. Reuses the shared message frame. +function MTH_CelebrateRunaway(petName) + if not MTH_FX_IsEnabled("pet.runaway") then + return + end + if not Sauce then + return + end + if not MTH_FX_EnsureFrames() then + return + end + + MTH_FX_TitleFS:SetTextColor(1, 0.15, 0.15) + MTH_FX_TitleFS:SetText(MTH_FX_L("ANIM_PET_RUNAWAY_TITLE", "Your pet ran away!")) + MTH_FX_SubFS:SetText(petName or "") + + Sauce:Stop(MTH_FX_MsgFrame) + MTH_FX_MsgFrame:SetAlpha(0) + MTH_FX_MsgFrame:Show() + Sauce:Sequence({ + Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18), + Sauce:Tween(MTH_FX_MsgFrame, { + type = "mth_shake", + duration = 0.55, + easing = "linear", + magnitude = 11, + freq = 42, + }), + Sauce:FadeOut(MTH_FX_MsgFrame, 0.7, "inQuad", { delay = 1.6 }), + }) +end + -- DEBUG: mark that the whole file finished loading and record which globals it -- successfully defined. TEMP diagnostic. MTH_FX_DBG.step = "4-complete" diff --git a/api/hunterbook-model-viewer.lua b/api/hunterbook-model-viewer.lua index ef0ace4..644f6d9 100644 --- a/api/hunterbook-model-viewer.lua +++ b/api/hunterbook-model-viewer.lua @@ -6,6 +6,18 @@ -- Uses MTH_DS_BeastSkins[familyName] generated by .tools/generators/gen_beast_skins.py -- Skin rendering: PlayerModel:SetUnit(CreatureDisplayInfo.ID) +local MTH_MV_Sauce = PizzaSauce + +-- Fade a model frame in when a new skin is displayed, unless disabled. +local function MTH_MV_AnimateModel(model) + if not MTH_MV_Sauce or not model then return end + if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("book.model") then return end + if type(model.SetAlpha) ~= "function" then return end + if not model:IsShown() then return end + model:SetAlpha(0) + MTH_MV_Sauce:FadeTo(model, 1, 0.30, "outQuad") +end + local MTH_MV = { inited = false, family = nil, @@ -51,6 +63,8 @@ local function MTH_MV_DisplayCurrent() MTH_MV_ApplyModel(MTH_MV.previewModel, skin.id) MTH_MV_ApplyModel(MTH_MV.popupModel, skin.id) + MTH_MV_AnimateModel(MTH_MV.previewModel) + MTH_MV_AnimateModel(MTH_MV.popupModel) local indexStr = tostring(MTH_MV.index) .. " / " .. tostring(n) if MTH_MV.popupSkinLabel then diff --git a/api/hunterbook-tab-stable.lua b/api/hunterbook-tab-stable.lua index 7ba3618..2a9284e 100644 --- a/api/hunterbook-tab-stable.lua +++ b/api/hunterbook-tab-stable.lua @@ -1245,6 +1245,17 @@ function MTH_BOOKTAB_RenderStableCards() local row = slotEntry and slotEntry.row or nil MTH_BOOK_DebugTrace("Render card#" .. tostring(i) .. " petId=" .. tostring(petId) .. " hasRow=" .. tostring(type(row) == "table")) + -- Animations: flash the slot when its occupant changes (deposit / + -- withdraw / swap), but never on the first render of this card. + if card._stableInit and card._lastStablePetId ~= petId then + if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" + and MTH_FX_IsEnabled("ui.stable") then + MTH_FX_Flash(card, 1.00, 0.82, 0.20, 0.55) + end + end + card._lastStablePetId = petId + card._stableInit = true + card:ClearAllPoints() card:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 8, y) card:SetPoint("TOPRIGHT", ui.frame, "TOPRIGHT", -8, y) diff --git a/api/hunterbook.lua b/api/hunterbook.lua index f531778..7b713fd 100644 --- a/api/hunterbook.lua +++ b/api/hunterbook.lua @@ -29,6 +29,34 @@ local function MTH_BOOK_AnimateTabSwitch() Sauce:FadeIn(list, 0.25) end +-- Fade the book window out before hiding it, unless disabled (shares the +-- "fade windows closed" toggle with the options window). +local function MTH_BOOK_FadeOutWindow(frame) + if not frame then return end + if not Sauce + or (type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.optionsclose")) then + if type(frame.SetAlpha) == "function" then frame:SetAlpha(1) end + frame:Hide() + return + end + Sauce:FadeOut(frame, 0.22, "inQuad", { + onFinish = function() + frame:Hide() + if type(frame.SetAlpha) == "function" then frame:SetAlpha(1) end + end, + }) +end + +-- Fade the results list back in after a search/filter is applied, unless disabled. +local function MTH_BOOK_AnimateSearchResults() + if not Sauce then return end + if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("book.search") then return end + local list = getglobal("MTH_BOOK_ListBackdrop") + if not list then return end + if type(list.SetAlpha) == "function" then list:SetAlpha(0) end + Sauce:FadeIn(list, 0.22) +end + MTH_HUNTERBOOK_LOADED = true MTH_HUNTERBOOK_TABS = MTH_HUNTERBOOK_TABS or {} @@ -4719,6 +4747,7 @@ local function MTH_BOOK_ApplyInputs() MTH_BOOK_STATE.selectedPetRankEntry = nil MTH_BOOK_STATE.petRankRows = {} MTH_BOOK_RefreshFilter() + MTH_BOOK_AnimateSearchResults() end local function MTH_BOOK_SetQuickFilter(index) @@ -5301,7 +5330,7 @@ local function MTH_BOOK_WireUI(frame) closeButton:SetWidth(22) closeButton:SetHeight(22) closeButton:SetText("X") - closeButton:SetScript("OnClick", function() this:GetParent():Hide() end) + closeButton:SetScript("OnClick", function() MTH_BOOK_FadeOutWindow(this:GetParent()) end) end if applyButton then applyButton:SetScript("OnClick", MTH_BOOK_ApplyInputs) end @@ -5752,7 +5781,7 @@ function MTH_ToggleHunterBook() local frame = MTH_BOOK_EnsureWindow() if not frame then return end if frame:IsShown() then - frame:Hide() + MTH_BOOK_FadeOutWindow(frame) else frame:Show() MTH_BOOK_FadeInWindow(frame) diff --git a/api/map-markers.lua b/api/map-markers.lua index 9d51619..778a5cd 100644 --- a/api/map-markers.lua +++ b/api/map-markers.lua @@ -38,6 +38,15 @@ local function MTH_Map_AnimatePin(pin) MTH_MAP_Sauce:FadeIn(pin, 0.3) end +-- Ping a minimap pin that just came into range. Only called for genuinely new +-- nodes (see UpdateMinimap), never every tick, to avoid strobing. +local function MTH_Map_PingMinimapPin(pin) + if not MTH_MAP_Sauce or not pin then return end + if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.minimapping") then return end + if type(pin.SetAlpha) == "function" then pin:SetAlpha(0) end + MTH_MAP_Sauce:FadeIn(pin, 0.35) +end + local MTH_MAP_MINIMAP_ZOOM = { [0] = { [0] = 300, [1] = 240, [2] = 180, [3] = 120, [4] = 80, [5] = 50 }, [1] = { [0] = 466 + 2/3, [1] = 400, [2] = 333 + 1/3, [3] = 266 + 2/6, [4] = 200, [5] = 133 + 1/3 }, @@ -948,6 +957,7 @@ function MTH_Map:HideMinimapPins() for i = 1, table.getn(self.minimapPins) do if self.minimapPins[i] then self.minimapPins[i]:Hide() end end + self._miniShownSet = nil end function MTH_Map:HideAllPins() @@ -1161,6 +1171,8 @@ function MTH_Map:UpdateMinimap() local xDraw = Minimap:GetWidth() / xScale / 100 local yDraw = Minimap:GetHeight() / yScale / 100 local shown = 0 + local prevShown = self._miniShownSet or {} + local nowShown = {} for i = 1, table.getn(nodes) do local node = nodes[i] @@ -1182,9 +1194,16 @@ function MTH_Map:UpdateMinimap() pin:ClearAllPoints() pin:SetPoint("CENTER", Minimap, "CENTER", xPos, -yPos) pin:Show() + + local nodeKey = tostring(node.name or "") .. "@" .. tostring(node.x) .. "," .. tostring(node.y) + nowShown[nodeKey] = true + if not prevShown[nodeKey] then + MTH_Map_PingMinimapPin(pin) + end end end end + self._miniShownSet = nowShown for i = shown + 1, table.getn(self.minimapPins) do if self.minimapPins[i] then self.minimapPins[i]:Hide() end diff --git a/api/options-animations.lua b/api/options-animations.lua index f36fd77..9513d7a 100644 --- a/api/options-animations.lua +++ b/api/options-animations.lua @@ -120,25 +120,37 @@ function MTH_SetupAnimationsOptions() end) end - -- Per-animation checkboxes, grouped by section. - local y = -104 + -- Per-animation checkboxes, grouped by section, laid out in two columns. + -- Each whole section is placed into whichever column is currently shorter + -- (largest remaining Y), so the two columns stay balanced as defs grow. local rowStep = 26 local sectionGap = 12 + local columns = { + { headerX = 15, cbX = 28, y = -104 }, + { headerX = 300, cbX = 313, y = -104 }, + } local s = 1 while MTH_FX_ANIM_SECTIONS and s <= table.getn(MTH_FX_ANIM_SECTIONS) do local sec = MTH_FX_ANIM_SECTIONS[s] + + -- Pick the shorter column (the one whose cursor is nearest the top). + local col = columns[1] + if columns[2].y > columns[1].y then + col = columns[2] + end + local header = container:CreateFontString(nil, "ARTWORK", "GameFontNormal") - header:SetPoint("TOPLEFT", container, "TOPLEFT", 15, y) + header:SetPoint("TOPLEFT", container, "TOPLEFT", col.headerX, col.y) header:SetText(MTH_ANIM_L(sec.titleKey, sec.title)) header:SetTextColor(1.00, 0.82, 0.00) - y = y - 22 + col.y = col.y - 22 local d = 1 while MTH_FX_ANIM_DEFS and d <= table.getn(MTH_FX_ANIM_DEFS) do local def = MTH_FX_ANIM_DEFS[d] if def.section == sec.id then local cbName = "MetaHuntOptionsAnim_" .. string.gsub(def.key, "%.", "_") - local cb = MTH_CreateCheckbox(container, cbName, MTH_ANIM_L(def.labelKey, def.label), y, 28) + local cb = MTH_CreateCheckbox(container, cbName, MTH_ANIM_L(def.labelKey, def.label), col.y, col.cbX) if cb then cb.mthKey = def.key cb:SetChecked(MTH_FX_GetFlag(def.key)) @@ -155,12 +167,12 @@ function MTH_SetupAnimationsOptions() cb:SetScript("OnLeave", function() GameTooltip:Hide() end) end table.insert(MTH_ANIM_CTRL, cb) - y = y - rowStep + col.y = col.y - rowStep end end d = d + 1 end - y = y - sectionGap + col.y = col.y - sectionGap s = s + 1 end diff --git a/api/options-shell.lua b/api/options-shell.lua index 1b27bbb..ee2d9e8 100644 --- a/api/options-shell.lua +++ b/api/options-shell.lua @@ -47,6 +47,24 @@ local function MTH_OPT_AnimateTabContent(tabKey) end end +-- Fade the options window out before hiding it, unless disabled. +local function MTH_OPT_AnimateClose() + local frame = MetaHuntOptions + if not frame then return end + if not MTH_OPT_Sauce + or (type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.optionsclose")) then + frame:SetAlpha(1) + frame:Hide() + return + end + MTH_OPT_Sauce:FadeOut(frame, 0.22, "inQuad", { + onFinish = function() + frame:Hide() + frame:SetAlpha(1) + end, + }) +end + local function MTH_RegisterEscClose(frameName) if not (frameName and UISpecialFrames) then return @@ -103,7 +121,7 @@ local function MTH_EnsureOptionsFrame() closeBtn:SetWidth(MTH_OPTIONS_CONST.CLOSE_BTN_SIZE) closeBtn:SetHeight(MTH_OPTIONS_CONST.CLOSE_BTN_SIZE) closeBtn:SetText(MTH_OPT_L("COMMON_CLOSE_SHORT", "X")) - closeBtn:SetScript("OnClick", function() MetaHuntOptions:Hide() end) + closeBtn:SetScript("OnClick", function() MTH_OPT_AnimateClose() end) end MTH_BuildOptionsTree() diff --git a/modules/expammo/engine.lua b/modules/expammo/engine.lua index 777cf60..1ea62ee 100644 --- a/modules/expammo/engine.lua +++ b/modules/expammo/engine.lua @@ -403,6 +403,7 @@ local lnl = { expiresAt = 0, DURATION = 10, -- Lock and Load real duration (tooltip: "Lasts 10 sec or until Aimed Shot is cast") } +local MTH_EA_LnLWasActive = false -- for detecting the proc rising edge (animations) -- ── Confirmed icon paths (captured in-game 2026-03-21) ──────── -- Action-button spells (spellbook scan) @@ -700,10 +701,18 @@ MTH_EA_UpdateUI = function() cellTop.icon:SetVertexColor(1.00, 0.82, 0.00, 1) cellTop:SetBackdropBorderColor(1.00, 0.82, 0.00, 1) cellTopCD:SetText("|cffffffff" .. secs .. "|r") + if not MTH_EA_LnLWasActive then + if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" + and MTH_FX_IsEnabled("combat.mmprocs") then + MTH_FX_Flash(cellTop, 1.00, 0.82, 0.20, 0.55) + end + MTH_EA_LnLWasActive = true + end else cellTop.icon:SetVertexColor(0.28, 0.28, 0.28, 0.40) cellTop:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.35) cellTopCD:SetText("") + MTH_EA_LnLWasActive = false end end diff --git a/modules/smartammo/engine.lua b/modules/smartammo/engine.lua index 6d6f14f..bdb506a 100644 --- a/modules/smartammo/engine.lua +++ b/modules/smartammo/engine.lua @@ -221,6 +221,14 @@ local function MTHSmartAmmo_ShowFallbackWarning(previousAmmo, newAmmo) elseif UIErrorsFrame and UIErrorsFrame.AddMessage then UIErrorsFrame:AddMessage(warning, 1.0, 0.15, 0.15, 1.0) end + + if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" + and MTH_FX_IsEnabled("combat.ammoswap") then + local target = getglobal("zButtonAmmo") + if target then + MTH_FX_Flash(target, 1.00, 0.75, 0.20, 0.5) + end + end end local MTHSmartAmmo_FallbackCheckFrame = nil @@ -371,6 +379,13 @@ local function MTHSmartAmmo_HandleWeaponSwapAutoEquip(reason) if MTHSmartAmmo_EquipAmmoFromSlot(bestAmmo.bag, bestAmmo.slot) then MTH_SA_LastKnownEquippedName = bestAmmo.name MTHSmartAmmo_RefreshAmmoButtonDisplay() + if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" + and MTH_FX_IsEnabled("combat.rangedswap") then + local target = getglobal("zButtonAmmo") + if target then + MTH_FX_Flash(target, 0.30, 0.70, 1.00, 0.5) + end + end end end diff --git a/modules/zhunter/zBar.lua b/modules/zhunter/zBar.lua index 2579d7a..c39f5fe 100644 --- a/modules/zhunter/zBar.lua +++ b/modules/zhunter/zBar.lua @@ -5,6 +5,8 @@ local ZBAR_FRAME_NAME = "MTH_ZBar_Anchor" +local ZBar_Sauce = PizzaSauce + local ZBAR_ALL_BUTTONS = { "zButtonAspect", "zButtonAmmo", @@ -282,6 +284,26 @@ function MTH_ZBar_Release() end end +-- Animations: gentle staggered fade-in of the bar's buttons when it activates. +function MTH_ZBar_AnimateActivate() + if not ZBar_Sauce then return end + if type(MTH_FX_IsEnabled) ~= "function" or not MTH_FX_IsEnabled("ui.zbar") then + return + end + local s = MTH_ZBar_GetSaved() + local order = s.order or {} + for i = 1, table.getn(order) do + local buttonName = order[i] + if type(buttonName) == "string" then + local btn = getglobal(buttonName) + if btn and btn:IsShown() then + btn:SetAlpha(0) + ZBar_Sauce:FadeTo(btn, 1, 0.30, "outQuad", { delay = (i - 1) * 0.05 }) + end + end + end +end + -- Called when the user toggles the Enable checkbox in the options. function MTH_ZBar_SetEnabled(enabled) local s = MTH_ZBar_GetSaved() @@ -289,6 +311,7 @@ function MTH_ZBar_SetEnabled(enabled) if enabled then MTH_ZBar_EnsureAnchor() MTH_ZBar_ApplyLayout() + MTH_ZBar_AnimateActivate() else MTH_ZBar_Release() end diff --git a/modules/zhunter/zButtonPet.lua b/modules/zhunter/zButtonPet.lua index e70f24a..88d6a37 100644 --- a/modules/zhunter/zButtonPet.lua +++ b/modules/zhunter/zButtonPet.lua @@ -312,6 +312,8 @@ local function zButtonPet_GetCorePetInfo() return nil end +local zButtonPet_LastLoyalty = nil + local function zButtonPet_RefreshSavedPetState(saved, corePetInfo) local hasLivePet = false if type(UnitExists) == "function" then @@ -343,8 +345,34 @@ local function zButtonPet_RefreshSavedPetState(saved, corePetInfo) if happiness == nil and type(GetPetHappiness) == "function" then happiness = GetPetHappiness() end + local prevHappiness = saved["pet"]["happiness"] saved["pet"]["happiness"] = happiness + -- Animations: flash the pet button when happiness or loyalty changes. + if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" then + if MTH_FX_IsEnabled("pet.happiness") + and prevHappiness ~= nil and happiness ~= nil and happiness ~= prevHappiness then + local target = getglobal("zButtonPet") + if target then + if happiness > prevHappiness then + MTH_FX_Flash(target, 0.30, 1.00, 0.35, 0.5) + else + MTH_FX_Flash(target, 1.00, 0.30, 0.30, 0.5) + end + end + end + local loyalty = corePetInfo and tonumber(corePetInfo.loyaltyLevel) or nil + if MTH_FX_IsEnabled("pet.loyaltyup") and loyalty ~= nil then + if zButtonPet_LastLoyalty ~= nil and loyalty > zButtonPet_LastLoyalty then + local target = getglobal("zButtonPet") + if target then + MTH_FX_Flash(target, 1.00, 0.82, 0.20, 0.6) + end + end + zButtonPet_LastLoyalty = loyalty + end + end + if isDead then saved["pet"]["status"] = 1 elseif currentHealth and currentHealthMax and currentHealthMax > 0 then