完成焦点等开发

This commit is contained in:
rucky
2026-03-31 18:03:23 +08:00
parent c7dd0f4848
commit 6e18269bfd
34 changed files with 6803 additions and 542 deletions

View File

@@ -7,18 +7,21 @@
-- 5. Dark UI - darken the entire interface
-- 6. WorldMap Window - turn fullscreen map into a movable/scalable window
-- 7. Hunter Aspect Guard - cancel Cheetah/Pack when taking damage in combat (avoid OOC false positives)
-- 8. Mouseover Cast - cast on mouseover unit without changing target
--------------------------------------------------------------------------------
SFrames.Tweaks = SFrames.Tweaks or {}
local Tweaks = SFrames.Tweaks
SFrames.castdb = SFrames.castdb or {}
SFrames.guidToName = SFrames.guidToName or {}
SFrames.castByName = SFrames.castByName or {} -- [unitName] = { spell, start, casttime, icon, channel }
local function GetTweaksCfg()
if not SFramesDB or type(SFramesDB.Tweaks) ~= "table" then
return { autoStance = true, superWoW = true, turtleCompat = true,
cooldownNumbers = true, darkUI = false, worldMapWindow = false,
hunterAspectGuard = true }
hunterAspectGuard = true, mouseoverCast = false }
end
return SFramesDB.Tweaks
end
@@ -104,6 +107,49 @@ local function InitSuperWoW()
castdb[guid].icon = icon
castdb[guid].channel = (arg3 == "CHANNEL") or false
-- Build GUID -> name mapping and castByName
-- Try UnitName(guid) directly (SuperWoW allows GUID as unitID)
local castName
local ok_n, gname = pcall(UnitName, guid)
if ok_n and gname and gname ~= "" and gname ~= UNKNOWN then
castName = gname
SFrames.guidToName[guid] = gname
end
-- Fallback: scan known unitIDs with UnitGUID
if not castName and UnitGUID then
local scanUnits = {"target","targettarget","party1","party2","party3","party4",
"party1target","party2target","party3target","party4target"}
local rn = GetNumRaidMembers and GetNumRaidMembers() or 0
for ri = 1, rn do
table.insert(scanUnits, "raid"..ri)
table.insert(scanUnits, "raid"..ri.."target")
end
for _, u in ipairs(scanUnits) do
local ok1, exists = pcall(UnitExists, u)
if ok1 and exists then
local ok2, ug = pcall(UnitGUID, u)
if ok2 and ug and ug == guid then
local ok3, uname = pcall(UnitName, u)
if ok3 and uname then
castName = uname
SFrames.guidToName[guid] = uname
end
break
end
end
end
end
-- Write castByName for name-based consumers (Focus castbar)
if castName then
SFrames.castByName[castName] = {
cast = spell,
start = GetTime(),
casttime = arg5,
icon = icon,
channel = (arg3 == "CHANNEL") or false,
}
end
SFrames.superwow_active = true
elseif arg3 == "FAIL" then
local guid = arg1
@@ -115,6 +161,15 @@ local function InitSuperWoW()
castdb[guid].icon = nil
castdb[guid].channel = nil
end
-- Clear castByName
local failName = SFrames.guidToName[guid]
if not failName then
local ok_n, gname = pcall(UnitName, guid)
if ok_n and gname and gname ~= "" and gname ~= UNKNOWN then failName = gname end
end
if failName and SFrames.castByName[failName] then
SFrames.castByName[failName] = nil
end
end
end)
end
@@ -1074,6 +1129,185 @@ local function InitHunterAspectGuard()
end)
end
--------------------------------------------------------------------------------
-- Combat Background Notify
-- Flash the Windows taskbar icon when entering combat (UnitXP SP3 required).
--------------------------------------------------------------------------------
local function InitCombatNotify()
if not (type(UnitXP) == "function" and pcall(UnitXP, "nop", "nop")) then return end
local f = CreateFrame("Frame", "NanamiCombatNotify")
f:RegisterEvent("PLAYER_REGEN_DISABLED")
f:SetScript("OnEvent", function()
pcall(UnitXP, "notify", "taskbarIcon")
end)
end
--------------------------------------------------------------------------------
-- Mouseover Cast
-- When enabled, action bar presses and CastSpellByName calls will target the
-- unit under the mouse cursor without changing current target.
--
-- Strategy:
-- UseAction hook: temporarily TargetUnit(moUnit) before the real UseAction,
-- then restore previous target afterwards. This preserves the hardware
-- event callstack so the client doesn't reject the action.
-- CastSpellByName hook (SuperWoW): pass moUnit as 2nd arg directly.
-- CastSpellByName hook (no SuperWoW): same target-swap trick.
--------------------------------------------------------------------------------
local mouseoverCastEnabled = false
local origUseAction = nil
local origCastSpellByName = nil
local inMouseoverAction = false -- re-entrancy guard
local function GetMouseoverUnit()
local focus = GetMouseFocus and GetMouseFocus()
if focus then
if focus.unit and UnitExists(focus.unit) then
return focus.unit
end
local parent = focus:GetParent()
if parent and parent.unit and UnitExists(parent.unit) then
return parent.unit
end
end
if UnitExists("mouseover") then
return "mouseover"
end
return nil
end
local function MouseoverUseAction(action, cursor, onSelf)
-- Don't interfere: picking up action, or re-entrant call
if cursor == 1 or inMouseoverAction then
return origUseAction(action, cursor, onSelf)
end
local moUnit = GetMouseoverUnit()
if not moUnit then
return origUseAction(action, cursor, onSelf)
end
-- Skip if mouseover IS current target (no swap needed)
if UnitIsUnit and UnitExists("target") and UnitIsUnit(moUnit, "target") then
return origUseAction(action, cursor, onSelf)
end
-- Remember current target state
local hadTarget = UnitExists("target")
local prevTargetName = hadTarget and UnitName("target") or nil
-- Temporarily target the mouseover unit
inMouseoverAction = true
TargetUnit(moUnit)
-- Execute the real UseAction on the now-targeted mouseover unit
origUseAction(action, cursor, onSelf)
-- Handle ground-targeted spells (Blizzard, Flamestrike, etc.)
if SpellIsTargeting and SpellIsTargeting() then
SpellTargetUnit(moUnit)
end
if SpellIsTargeting and SpellIsTargeting() then
SpellStopTargeting()
end
-- Restore previous target
if hadTarget and prevTargetName then
-- Target back the previous unit
TargetLastTarget()
-- Verify restoration worked
if not UnitExists("target") or UnitName("target") ~= prevTargetName then
-- TargetLastTarget failed, try by name
TargetByName(prevTargetName, true)
end
else
-- Had no target before, clear
ClearTarget()
end
inMouseoverAction = false
end
local function MouseoverCastSpellByName(spell, arg2)
-- Already has explicit target arg, pass through
if arg2 then
return origCastSpellByName(spell, arg2)
end
-- Re-entrancy guard (e.g. called from within MouseoverUseAction's chain)
if inMouseoverAction then
return origCastSpellByName(spell)
end
local moUnit = GetMouseoverUnit()
if not moUnit then
return origCastSpellByName(spell)
end
-- SuperWoW: direct unit parameter, no target swap needed
if SUPERWOW_VERSION then
origCastSpellByName(spell, moUnit)
if SpellIsTargeting and SpellIsTargeting() then
SpellTargetUnit(moUnit)
end
if SpellIsTargeting and SpellIsTargeting() then
SpellStopTargeting()
end
return
end
-- No SuperWoW: target-swap
local hadTarget = UnitExists("target")
local prevTargetName = hadTarget and UnitName("target") or nil
if not (hadTarget and UnitIsUnit and UnitIsUnit(moUnit, "target")) then
TargetUnit(moUnit)
end
origCastSpellByName(spell)
if SpellIsTargeting and SpellIsTargeting() then
SpellTargetUnit("target")
end
if SpellIsTargeting and SpellIsTargeting() then
SpellStopTargeting()
end
if hadTarget and prevTargetName then
TargetLastTarget()
if not UnitExists("target") or UnitName("target") ~= prevTargetName then
TargetByName(prevTargetName, true)
end
elseif not hadTarget then
ClearTarget()
end
end
local function InitMouseoverCast()
if UseAction then
origUseAction = UseAction
UseAction = MouseoverUseAction
end
if CastSpellByName then
origCastSpellByName = CastSpellByName
CastSpellByName = MouseoverCastSpellByName
end
mouseoverCastEnabled = true
DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami] 鼠标指向施法已启用" ..
(SUPERWOW_VERSION and "SuperWoW 模式)" or "(目标切换模式)") .. "|r")
end
function Tweaks:SetMouseoverCast(enabled)
if enabled and not mouseoverCastEnabled then
InitMouseoverCast()
elseif not enabled and mouseoverCastEnabled then
if origUseAction then UseAction = origUseAction end
if origCastSpellByName then CastSpellByName = origCastSpellByName end
mouseoverCastEnabled = false
DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami] 鼠标指向施法已关闭|r")
end
end
--------------------------------------------------------------------------------
-- Module API
--------------------------------------------------------------------------------
@@ -1131,6 +1365,20 @@ function Tweaks:Initialize()
end
end
if cfg.combatNotify ~= false then
local ok, err = pcall(InitCombatNotify)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444Nanami-UI: CombatNotify init failed: " .. tostring(err) .. "|r")
end
end
if cfg.mouseoverCast then
local ok, err = pcall(InitMouseoverCast)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage("|cffff4444Nanami-UI: MouseoverCast init failed: " .. tostring(err) .. "|r")
end
end
if cfg.darkUI then
local ok, err = pcall(InitDarkUI)
if not ok then