From 0fead7d696066d4e3a396aa9b0ef80d12a10be1d Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Thu, 21 May 2026 04:25:44 -0500 Subject: [PATCH] shapeshift detection via GetShapeshiftFormID + UPDATE_SHAPESHIFT_FORM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit actionbar.lua: pageswitch now listens to UPDATE_SHAPESHIFT_FORM as the authoritative form-change signal. PLAYER_AURAS_CHANGED becomes a narrowly-scoped prowl-detection trigger that runs after form state is already known. The Nampower SPELL_GO_SELF hook drops its Cat Form (spellID 768) branch — UPDATE_SHAPESHIFT_FORM covers form changes without per-spellID watchlists. Prowl IDs stay hooked for zero-latency detection that PLAYER_AURAS_CHANGED can't match. autoshift.lua: the texture-list shapeshift detection becomes a form-ID- keyed map. GetShapeshiftFormID returns the active form directly so we don't have to texture-match against every possible form pattern. The moonkin_scan frame disappears entirely — its talent-conditional was a workaround for the agility buff sharing Moonkin's icon, which form ID makes irrelevant (form 31 is only reported when the player is genuinely in Moonkin Form). Loop also splits into two phases for clarity: mount priority pass, then form cancel. --- modules/actionbar.lua | 48 +++++++++++++------------- modules/autoshift.lua | 78 +++++++++++++++++-------------------------- 2 files changed, 56 insertions(+), 70 deletions(-) diff --git a/modules/actionbar.lua b/modules/actionbar.lua index 7d7cf538..0af1436b 100644 --- a/modules/actionbar.lua +++ b/modules/actionbar.lua @@ -949,11 +949,11 @@ pfUI:RegisterModule("actionbar", "vanilla", function () -- setup page switch frame local prowling = nil local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent) - pageswitch:RegisterEvent("PLAYER_AURAS_CHANGED") pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD") + pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM") + pageswitch:RegisterEvent("PLAYER_AURAS_CHANGED") pageswitch:RegisterEvent("PLAYER_LOGOUT") pageswitch:SetScript("OnEvent", function() - -- Handle shutdown to prevent crash 132 if event == "PLAYER_LOGOUT" then this:UnregisterAllEvents() this:SetScript("OnEvent", nil) @@ -963,34 +963,38 @@ pfUI:RegisterModule("actionbar", "vanilla", function () if class ~= "DRUID" then return end - -- On login/reload: full scan if event == "PLAYER_ENTERING_WORLD" then prowling = FullScan() return end - -- PLAYER_AURAS_CHANGED: smart scanning - if event == "PLAYER_AURAS_CHANGED" then - if prowlActive then - if IsStealthed() then - prowling = true - else - -- Prowl ended; recheck cat form (we might have shifted out entirely) - prowlActive = nil - prowling = nil - inCatForm = HasCatForm() - end - elseif not inCatForm then - -- Not in cat form, do a full scan (might have just shifted) - prowling = FullScan() + if event == "UPDATE_SHAPESHIFT_FORM" then + -- Authoritative form-change signal — recompute and short-circuit. + inCatForm = HasCatForm() + if not inCatForm then + prowlActive = nil + prowling = nil + end + return + end + + -- PLAYER_AURAS_CHANGED: form changes already went through + -- UPDATE_SHAPESHIFT_FORM above; this handler only watches prowl on/off. + if inCatForm then + if IsStealthed() then + prowlActive = true + prowling = true + else + prowlActive = nil + prowling = nil end - -- If inCatForm but not prowlActive, no scan needed (wait for SPELL_GO_SELF hook) end end) - -- Prowl/CatForm detection via Nampower SPELL_GO_SELF hook (replaces UNIT_CASTEVENT) - -- Prowl Spell IDs: 5215 (Rank 1), 6783 (Rank 2), 9913 (Rank 3) - -- Cat Form Spell ID: 768 + -- Eager prowl detection via Nampower SPELL_GO_SELF — zero-latency vs. + -- PLAYER_AURAS_CHANGED, which can land a frame or two behind the cast. + -- Form changes are no longer hooked here; UPDATE_SHAPESHIFT_FORM covers + -- those without needing a per-spellID watchlist. local PROWL_IDS = { [5215] = true, [6783] = true, [9913] = true } pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {} pfUI.libdebuff_spell_go_hooks["actionbar_prowl"] = function(spellId) @@ -999,8 +1003,6 @@ pfUI:RegisterModule("actionbar", "vanilla", function () inCatForm = true prowlActive = true prowling = true - elseif spellId == 768 then - inCatForm = true end end pageswitch:SetScript("OnUpdate", function() diff --git a/modules/autoshift.lua b/modules/autoshift.lua index 77e57929..f14ddc3c 100644 --- a/modules/autoshift.lua +++ b/modules/autoshift.lua @@ -24,31 +24,25 @@ pfUI:RegisterModule("autoshift", "vanilla", function () "根据您的骑行技能提高速度。", "根据骑术技能提高速度。", "又慢又稳......", } + -- Form ID -> icon-path fragment of that form's buff. GetShapeshiftFormID + -- tells us the active form directly; we still need to locate the buff in + -- the player's array to find the bid for CancelPlayerBuff. + -- + -- Replaces the old texture-list scan plus moonkin_scan frame: the agility + -- buff that shares moonkin's icon no longer causes false positives because + -- form ID 31 is only reported when the player is genuinely in Moonkin Form, + -- regardless of what other buffs happen to be active. pfUI.autoshift.shapeshifts = { - "ability_racial_bearform", "ability_druid_catform", "ability_druid_travelform", - "ability_druid_aquaticform", "spell_shadow_shadowform", "spell_nature_spiritwolf", + [1] = "ability_druid_catform", -- Cat Form + [3] = "ability_druid_travelform", -- Travel Form + [4] = "ability_druid_aquaticform", -- Aquatic Form + [5] = "ability_racial_bearform", -- Bear Form + [8] = "ability_racial_bearform", -- Dire Bear (shares texture with Bear) + [16] = "spell_nature_spiritwolf", -- Shaman Ghost Wolf + [28] = "spell_shadow_shadowform", -- Priest Shadowform + [31] = "spell_nature_forceofnature", -- Druid Moonkin } - -- an agility buff exists which has the same icon as the moonkin form - -- therefore only add the moonkin icon to the removable buffs if - -- moonkin is skilled and player is druid. Frame is required as talentpoints - -- are only accessible after certain events. - local moonkin_scan = CreateFrame("Frame") - moonkin_scan:RegisterEvent("PLAYER_ENTERING_WORLD") - moonkin_scan:RegisterEvent("UNIT_NAME_UPDATE") - moonkin_scan:SetScript("OnEvent", function() - local _, class = UnitClass("player") - if class == "DRUID" then - local _,_,_,_,moonkin = GetTalentInfo(1,16) - if moonkin == 1 then - table.insert(pfUI.autoshift.shapeshifts, "spell_nature_forceofnature") - moonkin_scan:UnregisterAllEvents() - end - else - moonkin_scan:UnregisterAllEvents() - end - end) - pfUI.autoshift.errors = { SPELL_FAILED_NOT_MOUNTED, ERR_ATTACK_MOUNTED, ERR_TAXIPLAYERALREADYMOUNTED, SPELL_FAILED_NOT_SHAPESHIFT, SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED, SPELL_NOT_SHAPESHIFTED, SPELL_NOT_SHAPESHIFTED_NOSPACE, ERR_CANT_INTERACT_SHAPESHIFTED, ERR_NOT_WHILE_SHAPESHIFTED, @@ -71,19 +65,17 @@ pfUI:RegisterModule("autoshift", "vanilla", function () return end - -- delay shapeshift cancel - local CancelLater = nil - -- scan through buffs and cancel shapeshift/mount for id, errorstring in pairs(pfUI.autoshift.errors) do if arg1 == errorstring then - -- dont's cancel form when clicking on npcs while in combat + -- don't cancel form when clicking on npcs while in combat if arg1 == ERR_CANT_INTERACT_SHAPESHIFTED and UnitAffectingCombat("player") then return end - for i=0,31,1 do - -- detect mounts based on tooltip text + -- Phase 1: mounts take priority (mount/shapeshift can't coexist in + -- vanilla, but the original error list also covers mount-only states). + for i = 0, 31 do pfUI.autoshift.scanner:SetPlayerBuff(i) for _, str in pairs(pfUI.autoshift.mounts) do if pfUI.autoshift.scanner:Find(str) then @@ -91,27 +83,19 @@ pfUI:RegisterModule("autoshift", "vanilla", function () return end end - - -- detect shapeshift based on texture - local buff = GetPlayerBuffTexture(i) - if buff then - for id, bufftype in pairs(pfUI.autoshift.shapeshifts) do - if string.find(string.lower(buff), bufftype, 1) then - if string.find(string.lower(buff), "spell_shadow_shadowform", 1) then - -- only cancel shadow form if no other buff was hindering casting - CancelLater = i - else - CancelPlayerBuff(i) - return - end - end - end - end end - -- if nothing else was found, cancel shadowform - if CancelLater then - CancelPlayerBuff(CancelLater) + -- Phase 2: cancel the active shapeshift if any. GetShapeshiftFormID + -- gives us the form directly; we iterate to find its buff bid. + local formTexture = pfUI.autoshift.shapeshifts[GetShapeshiftFormID()] + if formTexture then + for i = 0, 31 do + local buff = GetPlayerBuffTexture(i) + if buff and string.find(string.lower(buff), formTexture, 1) then + CancelPlayerBuff(i) + return + end + end end end end