完成焦点等开发
This commit is contained in:
250
Tweaks.lua
250
Tweaks.lua
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user