聊天重做前缓存
This commit is contained in:
611
Chat.lua
611
Chat.lua
@@ -18,6 +18,7 @@ local DEFAULTS = {
|
||||
topPadding = 30,
|
||||
bottomPadding = 8,
|
||||
bgAlpha = 0.45,
|
||||
hoverTransparent = true,
|
||||
activeTab = 1,
|
||||
editBoxPosition = "bottom",
|
||||
editBoxX = 0,
|
||||
@@ -739,27 +740,87 @@ local function GetTranslateFilterKeyForEvent(event)
|
||||
return TRANSLATE_EVENT_FILTERS[event]
|
||||
end
|
||||
|
||||
local function ParseHardcoreDeathMessage(text)
|
||||
if type(text) ~= "string" or text == "" then return nil end
|
||||
if not string.find(text, "硬核") and not string.find(text, "死亡") then
|
||||
local lower = string.lower(text)
|
||||
if not string.find(lower, "hc news") and not string.find(lower, "has fallen")
|
||||
and not string.find(lower, "died") and not string.find(lower, "slain") then
|
||||
return nil
|
||||
-- ============================================================
|
||||
-- HC 公会成员缓存:用于"仅通报工会成员"过滤
|
||||
-- ============================================================
|
||||
local HCGuildMemberCache = {}
|
||||
|
||||
local function RefreshHCGuildCache()
|
||||
HCGuildMemberCache = {}
|
||||
if not (IsInGuild and IsInGuild()) then return end
|
||||
if not GetNumGuildMembers then return end
|
||||
local total = GetNumGuildMembers()
|
||||
for i = 1, total do
|
||||
local name = GetGuildRosterInfo(i)
|
||||
if type(name) == "string" and name ~= "" then
|
||||
HCGuildMemberCache[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function IsHCGuildMember(name)
|
||||
return type(name) == "string" and HCGuildMemberCache[name] == true
|
||||
end
|
||||
|
||||
-- 从 HC 系统消息中提取主角色名(死亡/升级均支持)
|
||||
local function ParseHCCharacterName(text)
|
||||
if type(text) ~= "string" then return nil end
|
||||
-- 中文死亡格式: "硬核角色 NAME(等级 N)"
|
||||
local _, _, n1 = string.find(text, "硬核角色%s+(.-)(等级")
|
||||
if n1 and n1 ~= "" then return n1 end
|
||||
-- 中文升级格式: "NAME 在硬核模式中已达到"
|
||||
local _, _, n2 = string.find(text, "^(.-)%s+在硬核模式")
|
||||
if n2 and n2 ~= "" then return n2 end
|
||||
-- 英文死亡格式: "Hardcore character NAME (Level N)"
|
||||
local _, _, n3 = string.find(text, "[Hh]ardcore character%s+(.-)%s+%(Level")
|
||||
if n3 and n3 ~= "" then return n3 end
|
||||
-- 英文升级格式: "NAME has reached level"
|
||||
local _, _, n4 = string.find(text, "^(.-)%s+has reached level")
|
||||
if n4 and n4 ~= "" then return n4 end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- 检测HC等级里程碑消息(如"达到20级");返回等级数字,否则返回nil
|
||||
local function ParseHardcoreLevelMessage(text)
|
||||
if type(text) ~= "string" or text == "" then return nil end
|
||||
local lower = string.lower(text)
|
||||
-- 英文: "has reached level X" / "reached level X"
|
||||
if string.find(lower, "reached level") then
|
||||
local _, _, lvl = string.find(lower, "reached level%s+(%d+)")
|
||||
return tonumber(lvl) or 1
|
||||
end
|
||||
-- 中文: "达到 X 级" / "已达到X级"
|
||||
if string.find(text, "达到") then
|
||||
local _, _, lvl = string.find(text, "达到%s*(%d+)%s*级")
|
||||
if lvl then return tonumber(lvl) end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- 检测HC死亡通报消息;排除等级里程碑;返回等级数字,否则返回nil
|
||||
local function ParseHardcoreDeathMessage(text)
|
||||
if type(text) ~= "string" or text == "" then return nil end
|
||||
-- 先排除等级里程碑消息,避免误判(里程碑消息也含"死亡"字样)
|
||||
if ParseHardcoreLevelMessage(text) then return nil end
|
||||
-- 初步过滤:必须含有死亡/击杀相关关键词
|
||||
local lower = string.lower(text)
|
||||
local hasHC = string.find(text, "硬核") or string.find(lower, "hardcore") or string.find(lower, "hc")
|
||||
local hasDead = string.find(text, "死亡") or string.find(text, "击杀")
|
||||
or string.find(lower, "has fallen") or string.find(lower, "slain")
|
||||
or string.find(lower, "died") or string.find(lower, "hc news")
|
||||
if not hasDead then return nil end
|
||||
local clean = string.gsub(text, "|c%x%x%x%x%x%x%x%x", "")
|
||||
clean = string.gsub(clean, "|r", "")
|
||||
local _, _, lvlStr = string.find(clean, "Level%s+(%d+)")
|
||||
-- 英文格式: Level: 17 / Level 17
|
||||
local _, _, lvlStr = string.find(clean, "Level%s*:%s*(%d+)")
|
||||
if lvlStr then return tonumber(lvlStr) end
|
||||
local _, _, lvlStr2 = string.find(clean, "(%d+)%s*级")
|
||||
local _, _, lvlStr2 = string.find(clean, "Level%s+(%d+)")
|
||||
if lvlStr2 then return tonumber(lvlStr2) end
|
||||
local _, _, lvlStr3 = string.find(clean, "Level:%s+(%d+)")
|
||||
-- 中文格式: 等级 1 / 等级1 / (等级 1)
|
||||
local _, _, lvlStr3 = string.find(clean, "等级%s*(%d+)")
|
||||
if lvlStr3 then return tonumber(lvlStr3) end
|
||||
local lower = string.lower(clean)
|
||||
if string.find(lower, "hc news") or (string.find(clean, "硬核") and (string.find(clean, "死亡") or string.find(lower, "has fallen"))) then
|
||||
return 1
|
||||
end
|
||||
-- 兜底:有死亡关键词即返回1
|
||||
if hasDead then return 1 end
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -774,6 +835,68 @@ local function CleanTextForTranslation(text)
|
||||
return clean
|
||||
end
|
||||
|
||||
-- HC 死亡消息的句式精确解析:提取怪物名和地点名,翻译后原位替换,玩家名不动
|
||||
-- 中文格式: "...硬核角色 [玩家](等级 N)被 [怪物](等级 N)击杀。这发生在 [地点]。..."
|
||||
-- 英文格式: "...character [PLAYER] (Level N) has been slain by [MONSTER] (Level N)...in [ZONE]."
|
||||
local function TranslateHCDeathParts(text, onDone)
|
||||
local api = _G.STranslateAPI
|
||||
local canTranslate = api and api.IsReady and api.IsReady() and api.ForceToChinese
|
||||
|
||||
-- 提取怪物名(中文句式)
|
||||
local _, _, monster = string.find(text, ")被%s*(.-)(等级")
|
||||
-- 提取地点名(中文句式)
|
||||
local _, _, zone = string.find(text, "这发生在%s*(.-)。")
|
||||
|
||||
-- 英文句式兜底
|
||||
if not monster then
|
||||
local _, _, m = string.find(text, "slain by%s+(.-)%s+%(Level")
|
||||
if m then monster = m end
|
||||
end
|
||||
if not zone then
|
||||
local _, _, z = string.find(text, "This happened in%s+(.-)[%.%!]")
|
||||
if z then zone = z end
|
||||
end
|
||||
|
||||
-- 收集需要翻译的词(含英文字母才有翻译意义)
|
||||
local targets = {}
|
||||
if monster and string.find(monster, "[A-Za-z]") then
|
||||
table.insert(targets, monster)
|
||||
end
|
||||
if zone and string.find(zone, "[A-Za-z]") and zone ~= monster then
|
||||
table.insert(targets, zone)
|
||||
end
|
||||
|
||||
if not canTranslate or table.getn(targets) == 0 then
|
||||
onDone(text)
|
||||
return
|
||||
end
|
||||
|
||||
-- 并行翻译,全部完成后替换回原文
|
||||
local out = text
|
||||
local total = table.getn(targets)
|
||||
local doneCount = 0
|
||||
for i = 1, total do
|
||||
local orig = targets[i]
|
||||
api.ForceToChinese(orig, function(translated, err, meta)
|
||||
if translated and translated ~= "" and translated ~= orig then
|
||||
local esc = string.gsub(orig, "([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
|
||||
local safeRepl = string.gsub(translated, "%%", "%%%%")
|
||||
out = string.gsub(out, esc, safeRepl)
|
||||
end
|
||||
doneCount = doneCount + 1
|
||||
if doneCount == total then
|
||||
onDone(out)
|
||||
end
|
||||
end, "Nanami-UI-HC")
|
||||
end
|
||||
end
|
||||
|
||||
-- 等级里程碑消息:玩家名无需翻译(是玩家自选名),整条消息已由服务器本地化,直接转发
|
||||
local function TranslateHCLevelParts(text, onDone)
|
||||
-- 仅当存在非玩家名的英文才翻译;里程碑消息唯一英文就是玩家名,故直接原文转发
|
||||
onDone(text)
|
||||
end
|
||||
|
||||
local function ForceHide(object)
|
||||
if not object then return end
|
||||
object:Hide()
|
||||
@@ -789,6 +912,66 @@ local function ForceInvisible(object)
|
||||
if object.EnableMouse then object:EnableMouse(false) end
|
||||
end
|
||||
|
||||
local function StripChatFrameArtwork(chatFrame)
|
||||
if not chatFrame then return end
|
||||
|
||||
local frameID = chatFrame.GetID and chatFrame:GetID()
|
||||
if frameID then
|
||||
if SetChatWindowColor then
|
||||
pcall(function() SetChatWindowColor(frameID, 0, 0, 0) end)
|
||||
end
|
||||
if SetChatWindowAlpha then
|
||||
pcall(function() SetChatWindowAlpha(frameID, 0) end)
|
||||
end
|
||||
end
|
||||
|
||||
if FCF_SetWindowAlpha then
|
||||
pcall(function() FCF_SetWindowAlpha(chatFrame, 0) end)
|
||||
end
|
||||
|
||||
if chatFrame.SetBackdropColor then
|
||||
chatFrame:SetBackdropColor(0, 0, 0, 0)
|
||||
end
|
||||
if chatFrame.SetBackdropBorderColor then
|
||||
chatFrame:SetBackdropBorderColor(0, 0, 0, 0)
|
||||
end
|
||||
if chatFrame.SetBackdrop then
|
||||
pcall(function() chatFrame:SetBackdrop(nil) end)
|
||||
end
|
||||
|
||||
local frameName = chatFrame.GetName and chatFrame:GetName()
|
||||
if type(frameName) == "string" and frameName ~= "" then
|
||||
local legacyTextures = {
|
||||
frameName .. "Background",
|
||||
frameName .. "BackgroundLeft",
|
||||
frameName .. "BackgroundMiddle",
|
||||
frameName .. "BackgroundRight",
|
||||
frameName .. "BottomButton",
|
||||
frameName .. "ButtonFrame",
|
||||
frameName .. "ButtonFrameBackground",
|
||||
}
|
||||
for _, texName in ipairs(legacyTextures) do
|
||||
local tex = _G[texName]
|
||||
if tex then
|
||||
if tex.SetTexture then tex:SetTexture(nil) end
|
||||
if tex.SetVertexColor then tex:SetVertexColor(0, 0, 0, 0) end
|
||||
if tex.SetAlpha then tex:SetAlpha(0) end
|
||||
tex:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local regions = { chatFrame:GetRegions() }
|
||||
for _, region in ipairs(regions) do
|
||||
if region and region.GetObjectType and region:GetObjectType() == "Texture" then
|
||||
if region.SetTexture then region:SetTexture(nil) end
|
||||
if region.SetVertexColor then region:SetVertexColor(0, 0, 0, 0) end
|
||||
if region.SetAlpha then region:SetAlpha(0) end
|
||||
region:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateFont(parent, size, justify)
|
||||
if SFrames and SFrames.CreateFontString then
|
||||
return SFrames:CreateFontString(parent, size, justify)
|
||||
@@ -1547,6 +1730,9 @@ local function EnsureDB()
|
||||
if type(db.editBoxY) ~= "number" then db.editBoxY = tonumber(db.editBoxY) or DEFAULTS.editBoxY end
|
||||
if db.translateEnabled == nil then db.translateEnabled = true end
|
||||
if db.chatMonitorEnabled == nil then db.chatMonitorEnabled = true end
|
||||
if db.hcDeathToGuild == nil then db.hcDeathToGuild = true end
|
||||
if db.hcLevelToGuild == nil then db.hcLevelToGuild = true end
|
||||
if db.hcGuildMemberOnly == nil then db.hcGuildMemberOnly = false end
|
||||
if type(db.layoutVersion) ~= "number" then db.layoutVersion = 1 end
|
||||
if db.layoutVersion < 2 then
|
||||
db.topPadding = DEFAULTS.topPadding
|
||||
@@ -1895,6 +2081,7 @@ function SFrames.Chat:GetConfig()
|
||||
topPadding = math.floor(Clamp(db.topPadding, 24, 64) + 0.5),
|
||||
bottomPadding = math.floor(Clamp(db.bottomPadding, 4, 18) + 0.5),
|
||||
bgAlpha = Clamp(db.bgAlpha, 0, 1),
|
||||
hoverTransparent = (db.hoverTransparent ~= false),
|
||||
editBoxPosition = editBoxPosition,
|
||||
editBoxX = tonumber(db.editBoxX) or DEFAULTS.editBoxX,
|
||||
editBoxY = tonumber(db.editBoxY) or DEFAULTS.editBoxY,
|
||||
@@ -3321,6 +3508,19 @@ function SFrames.Chat:RefreshConfigFrame()
|
||||
end
|
||||
end
|
||||
|
||||
if self.cfgMonitorSection then
|
||||
self.cfgMonitorSection:SetAlpha(1)
|
||||
end
|
||||
if self.cfgMonitorCb then
|
||||
self.cfgMonitorCb:Enable()
|
||||
end
|
||||
if self.cfgMonitorDesc then
|
||||
self.cfgMonitorDesc:SetTextColor(0.7, 0.7, 0.74)
|
||||
end
|
||||
if self.cfgMonitorReloadHint then
|
||||
self.cfgMonitorReloadHint:SetTextColor(0.9, 0.75, 0.5)
|
||||
end
|
||||
|
||||
if self.configControls then
|
||||
for i = 1, table.getn(self.configControls) do
|
||||
local ctrl = self.configControls[i]
|
||||
@@ -3485,12 +3685,12 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
transDesc:SetPoint("TOPLEFT", engineSection, "TOPLEFT", 38, -50)
|
||||
transDesc:SetWidth(520)
|
||||
transDesc:SetJustifyH("LEFT")
|
||||
transDesc:SetText("关闭后将完全停止调用 STranslateAPI 翻译接口,所有标签的自动翻译均不生效。")
|
||||
transDesc:SetText("关闭后将完全停止调用 STranslateAPI 翻译接口,所有标签的自动翻译均不生效。聊天监控可独立启用。")
|
||||
transDesc:SetTextColor(0.7, 0.7, 0.74)
|
||||
|
||||
local monitorSection = CreateCfgSection(generalPage, "聊天消息监控", 0, -136, 584, 160, fontPath)
|
||||
|
||||
AddControl(CreateCfgCheck(monitorSection, "启用聊天消息监控与收集", 16, -30,
|
||||
local monitorCb = CreateCfgCheck(monitorSection, "启用聊天消息监控与收集", 16, -30,
|
||||
function() return EnsureDB().chatMonitorEnabled ~= false end,
|
||||
function(checked)
|
||||
EnsureDB().chatMonitorEnabled = (checked == true)
|
||||
@@ -3498,15 +3698,19 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
function()
|
||||
SFrames.Chat:RefreshConfigFrame()
|
||||
end
|
||||
))
|
||||
)
|
||||
AddControl(monitorCb)
|
||||
self.cfgMonitorCb = monitorCb
|
||||
self.cfgMonitorSection = monitorSection
|
||||
|
||||
local monDesc = monitorSection:CreateFontString(nil, "OVERLAY")
|
||||
monDesc:SetFont(fontPath, 10, "OUTLINE")
|
||||
monDesc:SetPoint("TOPLEFT", monitorSection, "TOPLEFT", 38, -50)
|
||||
monDesc:SetWidth(520)
|
||||
monDesc:SetJustifyH("LEFT")
|
||||
monDesc:SetText("启用后将拦截聊天消息,提供消息历史缓存、右键复制 [+] 标记、频道翻译触发等功能。\n关闭后消息将原样通过,不做任何处理(翻译、复制等功能不可用)。")
|
||||
monDesc:SetText("启用后将拦截聊天消息,提供消息历史缓存、右键复制 [+] 标记、职业染色等功能。\n可独立于 AI 翻译开关使用。关闭后消息将原样通过,[+] 复制等功能不可用。")
|
||||
monDesc:SetTextColor(0.7, 0.7, 0.74)
|
||||
self.cfgMonitorDesc = monDesc
|
||||
|
||||
local reloadHint = monitorSection:CreateFontString(nil, "OVERLAY")
|
||||
reloadHint:SetFont(fontPath, 10, "OUTLINE")
|
||||
@@ -3515,11 +3719,12 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
reloadHint:SetJustifyH("LEFT")
|
||||
reloadHint:SetText("提示:更改监控开关后建议 /reload 以确保完全生效。")
|
||||
reloadHint:SetTextColor(0.9, 0.75, 0.5)
|
||||
self.cfgMonitorReloadHint = reloadHint
|
||||
end
|
||||
|
||||
local windowPage = CreatePage("window")
|
||||
do
|
||||
local appearance = CreateCfgSection(windowPage, "窗口外观", 0, 0, 584, 274, fontPath)
|
||||
local appearance = CreateCfgSection(windowPage, "窗口外观", 0, 0, 584, 304, fontPath)
|
||||
AddControl(CreateCfgSlider(appearance, "宽度", 16, -46, 260, 320, 900, 1,
|
||||
function() return EnsureDB().width end,
|
||||
function(v) EnsureDB().width = v end,
|
||||
@@ -3580,13 +3785,18 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
function(checked) EnsureDB().showPlayerLevel = (checked == true) end,
|
||||
function() SFrames.Chat:RefreshConfigFrame() end
|
||||
))
|
||||
AddControl(CreateCfgCheck(appearance, "悬停显示背景", 16, -248,
|
||||
function() return EnsureDB().hoverTransparent ~= false end,
|
||||
function(checked) EnsureDB().hoverTransparent = (checked == true) end,
|
||||
function() SFrames.Chat:ApplyConfig(); SFrames.Chat:RefreshConfigFrame() end
|
||||
))
|
||||
|
||||
self.cfgWindowSummaryText = appearance:CreateFontString(nil, "OVERLAY")
|
||||
self.cfgWindowSummaryText:SetFont(fontPath, 10, "OUTLINE")
|
||||
self.cfgWindowSummaryText:SetPoint("BOTTOMLEFT", appearance, "BOTTOMLEFT", 16, 10)
|
||||
self.cfgWindowSummaryText:SetTextColor(0.74, 0.74, 0.8)
|
||||
|
||||
local inputSection = CreateCfgSection(windowPage, "输入框", 0, -290, 584, 114, fontPath)
|
||||
local inputSection = CreateCfgSection(windowPage, "输入框", 0, -320, 584, 114, fontPath)
|
||||
self.cfgInputModeText = inputSection:CreateFontString(nil, "OVERLAY")
|
||||
self.cfgInputModeText:SetFont(fontPath, 11, "OUTLINE")
|
||||
self.cfgInputModeText:SetPoint("TOPLEFT", inputSection, "TOPLEFT", 16, -30)
|
||||
@@ -3618,7 +3828,7 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
inputTip:SetText("建议优先使用顶部或底部模式;自由拖动适合特殊布局。")
|
||||
inputTip:SetTextColor(0.74, 0.74, 0.8)
|
||||
|
||||
local actionSection = CreateCfgSection(windowPage, "窗口操作", 0, -398, 584, 96, fontPath)
|
||||
local actionSection = CreateCfgSection(windowPage, "窗口操作", 0, -428, 584, 96, fontPath)
|
||||
CreateCfgButton(actionSection, "重置位置", 16, -32, 108, 24, function()
|
||||
SFrames.Chat:ResetPosition()
|
||||
SFrames.Chat:RefreshConfigFrame()
|
||||
@@ -4050,7 +4260,7 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
|
||||
local hcPage = CreatePage("hc")
|
||||
do
|
||||
local hcControls = CreateCfgSection(hcPage, "硬核生存服务器专属", 0, 0, 584, 182, fontPath)
|
||||
local hcControls = CreateCfgSection(hcPage, "硬核生存服务器专属", 0, 0, 584, 382, fontPath)
|
||||
|
||||
local hcStatusText = hcControls:CreateFontString(nil, "OVERLAY")
|
||||
hcStatusText:SetFont(fontPath, 10, "OUTLINE")
|
||||
@@ -4095,7 +4305,7 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
deathTip:SetPoint("TOPLEFT", hcControls, "TOPLEFT", 16, -112)
|
||||
deathTip:SetWidth(540)
|
||||
deathTip:SetJustifyH("LEFT")
|
||||
deathTip:SetText("关闭那些“某某在XX级死亡”的系统提示。")
|
||||
deathTip:SetText("关闭那些[某某在XX级死亡]的系统提示。")
|
||||
deathTip:SetTextColor(0.8, 0.7, 0.7)
|
||||
|
||||
AddControl(CreateCfgSlider(hcControls, "最低死亡通报等级", 340, -82, 210, 0, 60, 1,
|
||||
@@ -4104,6 +4314,48 @@ function SFrames.Chat:EnsureConfigFrame()
|
||||
function(v) return (v == 0) and "所有击杀" or (tostring(v) .. " 级及以上") end,
|
||||
function() SFrames.Chat:RefreshConfigFrame() end
|
||||
))
|
||||
|
||||
AddControl(CreateCfgCheck(hcControls, "翻译死亡通报并转发到公会频道", 16, -138,
|
||||
function() return EnsureDB().hcDeathToGuild ~= false end,
|
||||
function(checked) EnsureDB().hcDeathToGuild = (checked == true) end,
|
||||
function() SFrames.Chat:RefreshConfigFrame() end
|
||||
))
|
||||
|
||||
local guildTip = hcControls:CreateFontString(nil, "OVERLAY")
|
||||
guildTip:SetFont(fontPath, 10, "OUTLINE")
|
||||
guildTip:SetPoint("TOPLEFT", hcControls, "TOPLEFT", 16, -164)
|
||||
guildTip:SetWidth(540)
|
||||
guildTip:SetJustifyH("LEFT")
|
||||
guildTip:SetText("开启 AI 翻译时,将死亡黄字系统消息翻译后自动发送到公会频道。需 AI 翻译已启用。")
|
||||
guildTip:SetTextColor(0.8, 0.7, 0.7)
|
||||
|
||||
AddControl(CreateCfgCheck(hcControls, "翻译等级里程碑并转发到公会频道", 16, -204,
|
||||
function() return EnsureDB().hcLevelToGuild ~= false end,
|
||||
function(checked) EnsureDB().hcLevelToGuild = (checked == true) end,
|
||||
function() SFrames.Chat:RefreshConfigFrame() end
|
||||
))
|
||||
|
||||
local levelGuildTip = hcControls:CreateFontString(nil, "OVERLAY")
|
||||
levelGuildTip:SetFont(fontPath, 10, "OUTLINE")
|
||||
levelGuildTip:SetPoint("TOPLEFT", hcControls, "TOPLEFT", 16, -228)
|
||||
levelGuildTip:SetWidth(540)
|
||||
levelGuildTip:SetJustifyH("LEFT")
|
||||
levelGuildTip:SetText("开启 AI 翻译时,将[达到X级]里程碑系统消息翻译后转发到公会频道。与死亡通报独立控制。")
|
||||
levelGuildTip:SetTextColor(0.8, 0.7, 0.7)
|
||||
|
||||
AddControl(CreateCfgCheck(hcControls, "仅通报工会成员的死亡/升级消息", 16, -270,
|
||||
function() return EnsureDB().hcGuildMemberOnly == true end,
|
||||
function(checked) EnsureDB().hcGuildMemberOnly = (checked == true) end,
|
||||
function() SFrames.Chat:RefreshConfigFrame() end
|
||||
))
|
||||
|
||||
local guildMemberTip = hcControls:CreateFontString(nil, "OVERLAY")
|
||||
guildMemberTip:SetFont(fontPath, 10, "OUTLINE")
|
||||
guildMemberTip:SetPoint("TOPLEFT", hcControls, "TOPLEFT", 16, -296)
|
||||
guildMemberTip:SetWidth(540)
|
||||
guildMemberTip:SetJustifyH("LEFT")
|
||||
guildMemberTip:SetText("勾选后,仅当死亡/升级角色为本工会成员时才转发到公会频道。默认关闭(通报所有人)。")
|
||||
guildMemberTip:SetTextColor(0.8, 0.7, 0.7)
|
||||
end
|
||||
|
||||
local close = CreateCfgButton(panel, "保存", 430, -588, 150, 28, function()
|
||||
@@ -4399,6 +4651,7 @@ function SFrames.Chat:CreateContainer()
|
||||
})
|
||||
chatShadow:SetBackdropColor(0, 0, 0, 0.55)
|
||||
chatShadow:SetBackdropBorderColor(0, 0, 0, 0.4)
|
||||
f.chatShadow = chatShadow
|
||||
|
||||
local topGlow = f:CreateTexture(nil, "BACKGROUND")
|
||||
topGlow:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
@@ -4674,6 +4927,9 @@ function SFrames.Chat:CreateContainer()
|
||||
scrollTrack:SetPoint("BOTTOM", scrollDownBtn, "TOP", 0, 2)
|
||||
scrollTrack:SetWidth(4)
|
||||
scrollTrack:SetVertexColor(0.18, 0.19, 0.22, 0.9)
|
||||
f.scrollUpBtn = scrollUpBtn
|
||||
f.scrollDownBtn = scrollDownBtn
|
||||
f.scrollTrack = scrollTrack
|
||||
|
||||
local resize = CreateFrame("Button", nil, f)
|
||||
resize:SetWidth(16)
|
||||
@@ -4709,6 +4965,67 @@ function SFrames.Chat:CreateContainer()
|
||||
f.resizeHandle = resize
|
||||
self.frame = f
|
||||
|
||||
-- ── Hover-transparent: fade background/chrome when mouse is not over chat ──
|
||||
f.sfHoverAlpha = 0
|
||||
f.sfHoverTarget = 0
|
||||
local FADE_SPEED = 4.0 -- alpha per second
|
||||
-- Apply initial transparent state immediately after first config apply
|
||||
f.sfHoverInitPending = true
|
||||
|
||||
local function IsMouseOverChat()
|
||||
if MouseIsOver(f) then return true end
|
||||
if SFrames.Chat.editBackdrop and SFrames.Chat.editBackdrop:IsShown() and MouseIsOver(SFrames.Chat.editBackdrop) then return true end
|
||||
if SFrames.Chat.configFrame and SFrames.Chat.configFrame:IsShown() then return true end
|
||||
local editBox = ChatFrameEditBox or ChatFrame1EditBox
|
||||
if editBox and editBox:IsShown() then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
local hoverFrame = CreateFrame("Frame", nil, f)
|
||||
hoverFrame:SetScript("OnUpdate", function()
|
||||
if not (SFrames and SFrames.Chat and SFrames.Chat.frame) then return end
|
||||
local cfg = SFrames.Chat:GetConfig()
|
||||
-- On first tick, snap to correct state (no animation)
|
||||
if f.sfHoverInitPending then
|
||||
f.sfHoverInitPending = nil
|
||||
if cfg.hoverTransparent then
|
||||
local over = IsMouseOverChat() and 1 or 0
|
||||
f.sfHoverAlpha = over
|
||||
f.sfHoverTarget = over
|
||||
SFrames.Chat:ApplyHoverAlpha(over)
|
||||
else
|
||||
f.sfHoverAlpha = 1
|
||||
f.sfHoverTarget = 1
|
||||
end
|
||||
return
|
||||
end
|
||||
if not cfg.hoverTransparent then
|
||||
if f.sfHoverAlpha ~= 1 then
|
||||
f.sfHoverAlpha = 1
|
||||
SFrames.Chat:ApplyHoverAlpha(1)
|
||||
end
|
||||
return
|
||||
end
|
||||
local target = IsMouseOverChat() and 1 or 0
|
||||
f.sfHoverTarget = target
|
||||
local cur = f.sfHoverAlpha or 1
|
||||
if math.abs(cur - target) < 0.01 then
|
||||
if cur ~= target then
|
||||
f.sfHoverAlpha = target
|
||||
SFrames.Chat:ApplyHoverAlpha(target)
|
||||
end
|
||||
return
|
||||
end
|
||||
local dt = arg1 or 0.016
|
||||
if target > cur then
|
||||
cur = math.min(cur + FADE_SPEED * dt, target)
|
||||
else
|
||||
cur = math.max(cur - FADE_SPEED * dt, target)
|
||||
end
|
||||
f.sfHoverAlpha = cur
|
||||
SFrames.Chat:ApplyHoverAlpha(cur)
|
||||
end)
|
||||
|
||||
if not self.hiddenConfigButton then
|
||||
local hiddenConfigButton = CreateFrame("Button", "SFramesChatHiddenConfigButton", UIParent, "UIPanelButtonTemplate")
|
||||
hiddenConfigButton:SetWidth(74)
|
||||
@@ -4749,9 +5066,45 @@ function SFrames.Chat:CreateContainer()
|
||||
f:SetWidth(Clamp(db.width, 320, 900))
|
||||
f:SetHeight(Clamp(db.height, 120, 460))
|
||||
|
||||
-- Background alpha: always show at configured bgAlpha
|
||||
-- Background alpha: respect hoverTransparent on init
|
||||
local bgA = Clamp(db.bgAlpha or DEFAULTS.bgAlpha, 0, 1)
|
||||
f:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], bgA)
|
||||
if db.hoverTransparent ~= false then
|
||||
f:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], 0)
|
||||
f:SetBackdropBorderColor(CFG_THEME.panelBorder[1], CFG_THEME.panelBorder[2], CFG_THEME.panelBorder[3], 0)
|
||||
if f.chatShadow then
|
||||
f.chatShadow:SetBackdropColor(0, 0, 0, 0)
|
||||
f.chatShadow:SetBackdropBorderColor(0, 0, 0, 0)
|
||||
end
|
||||
if f.innerShade then
|
||||
f.innerShade:SetVertexColor(0, 0, 0, 0)
|
||||
f.innerShade:Hide()
|
||||
end
|
||||
if f.watermark then
|
||||
f.watermark:SetVertexColor(1, 0.78, 0.9, 0)
|
||||
f.watermark:Hide()
|
||||
end
|
||||
if f.title and f.title.SetAlpha then f.title:SetAlpha(0)
|
||||
elseif f.title and f.title.SetTextColor then f.title:SetTextColor(1, 0.82, 0.93, 0) end
|
||||
if f.title then f.title:Hide() end
|
||||
if f.titleBtn then
|
||||
f.titleBtn:EnableMouse(false)
|
||||
f.titleBtn:Hide()
|
||||
end
|
||||
if f.leftCat then
|
||||
f.leftCat:SetVertexColor(1, 0.82, 0.9, 0)
|
||||
f.leftCat:Hide()
|
||||
end
|
||||
if f.tabBar then f.tabBar:SetAlpha(0) f.tabBar:Hide() end
|
||||
if f.configButton then f.configButton:SetAlpha(0) f.configButton:EnableMouse(false) f.configButton:Hide() end
|
||||
if f.whisperButton then f.whisperButton:SetAlpha(0) f.whisperButton:EnableMouse(false) f.whisperButton:Hide() end
|
||||
if f.scrollUpBtn then f.scrollUpBtn:SetAlpha(0) f.scrollUpBtn:EnableMouse(false) f.scrollUpBtn:Hide() end
|
||||
if f.scrollDownBtn then f.scrollDownBtn:SetAlpha(0) f.scrollDownBtn:EnableMouse(false) f.scrollDownBtn:Hide() end
|
||||
if f.scrollTrack then f.scrollTrack:SetVertexColor(0.18, 0.19, 0.22, 0) f.scrollTrack:Hide() end
|
||||
if f.resizeHandle then f.resizeHandle:SetAlpha(0) f.resizeHandle:EnableMouse(false) f.resizeHandle:Hide() end
|
||||
if f.hint then f.hint:Hide() end
|
||||
else
|
||||
f:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], bgA)
|
||||
end
|
||||
end
|
||||
|
||||
function SFrames.Chat:HideDefaultChrome()
|
||||
@@ -5225,8 +5578,15 @@ function SFrames.Chat:ApplyChatFrameBaseStyle(chatFrame, isCombat)
|
||||
end
|
||||
if chatFrame.SetHyperlinksEnabled then chatFrame:SetHyperlinksEnabled(1) end
|
||||
if chatFrame.SetIndentedWordWrap then chatFrame:SetIndentedWordWrap(false) end
|
||||
if chatFrame.SetShadowOffset then chatFrame:SetShadowOffset(1, -1) end
|
||||
if chatFrame.SetShadowColor then chatFrame:SetShadowColor(0, 0, 0, 0.92) end
|
||||
if chatFrame.SetShadowOffset then chatFrame:SetShadowOffset(0, 0) end
|
||||
if chatFrame.SetShadowColor then chatFrame:SetShadowColor(0, 0, 0, 0) end
|
||||
StripChatFrameArtwork(chatFrame)
|
||||
if not chatFrame.sfArtworkHooked and chatFrame.HookScript then
|
||||
chatFrame.sfArtworkHooked = true
|
||||
chatFrame:HookScript("OnShow", function()
|
||||
StripChatFrameArtwork(chatFrame)
|
||||
end)
|
||||
end
|
||||
|
||||
self:EnforceChatWindowLock(chatFrame)
|
||||
if not chatFrame.sfDragLockHooked and chatFrame.HookScript then
|
||||
@@ -6324,6 +6684,122 @@ function SFrames.Chat:StyleEditBox()
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply hover-transparent alpha to background, shadow, and chrome elements.
|
||||
-- alpha=0 means fully transparent (mouse away), alpha=1 means fully visible (mouse over).
|
||||
function SFrames.Chat:ApplyHoverAlpha(alpha)
|
||||
if not self.frame then return end
|
||||
local f = self.frame
|
||||
local cfg = self:GetConfig()
|
||||
local bgA = Clamp(cfg.bgAlpha or DEFAULTS.bgAlpha, 0, 1)
|
||||
local chromeVisible = alpha > 0.01
|
||||
|
||||
-- Main backdrop
|
||||
f:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], bgA * alpha)
|
||||
local showBorder = (cfg.showBorder ~= false)
|
||||
local borderR, borderG, borderB = self:GetBorderColorRGB()
|
||||
if showBorder then
|
||||
f:SetBackdropBorderColor(borderR, borderG, borderB, 0.95 * alpha)
|
||||
else
|
||||
f:SetBackdropBorderColor(borderR, borderG, borderB, 0)
|
||||
end
|
||||
|
||||
-- Shadow
|
||||
if f.chatShadow then
|
||||
f.chatShadow:SetBackdropColor(0, 0, 0, 0.55 * alpha)
|
||||
f.chatShadow:SetBackdropBorderColor(0, 0, 0, 0.4 * alpha)
|
||||
end
|
||||
|
||||
-- Inner shade
|
||||
if f.innerShade then
|
||||
f.innerShade:SetVertexColor(0, 0, 0, 0.2 * alpha)
|
||||
if chromeVisible then f.innerShade:Show() else f.innerShade:Hide() end
|
||||
end
|
||||
|
||||
-- Watermark
|
||||
if f.watermark then
|
||||
f.watermark:SetVertexColor(1, 0.78, 0.9, 0.08 * alpha)
|
||||
if chromeVisible then f.watermark:Show() else f.watermark:Hide() end
|
||||
end
|
||||
|
||||
-- Title, tab bar, config button, whisper button, scroll buttons, resize handle
|
||||
local chromeAlpha = alpha
|
||||
if f.title and f.title.SetAlpha then
|
||||
f.title:SetAlpha(chromeAlpha)
|
||||
if chromeVisible then f.title:Show() else f.title:Hide() end
|
||||
elseif f.title and f.title.SetTextColor then
|
||||
f.title:SetTextColor(1, 0.82, 0.93, chromeAlpha)
|
||||
if chromeVisible then f.title:Show() else f.title:Hide() end
|
||||
end
|
||||
if f.titleBtn then
|
||||
f.titleBtn:EnableMouse(chromeVisible)
|
||||
if chromeVisible then f.titleBtn:Show() else f.titleBtn:Hide() end
|
||||
end
|
||||
if f.leftCat then
|
||||
f.leftCat:SetVertexColor(1, 0.82, 0.9, 0.8 * chromeAlpha)
|
||||
if chromeVisible then f.leftCat:Show() else f.leftCat:Hide() end
|
||||
end
|
||||
if f.tabBar then
|
||||
f.tabBar:SetAlpha(chromeAlpha)
|
||||
if chromeVisible then f.tabBar:Show() else f.tabBar:Hide() end
|
||||
end
|
||||
if f.configButton then
|
||||
f.configButton:SetAlpha(chromeAlpha)
|
||||
f.configButton:EnableMouse(chromeVisible)
|
||||
if chromeVisible then f.configButton:Show() else f.configButton:Hide() end
|
||||
end
|
||||
if f.whisperButton then
|
||||
f.whisperButton:SetAlpha(chromeAlpha)
|
||||
f.whisperButton:EnableMouse(chromeVisible)
|
||||
if chromeVisible then f.whisperButton:Show() else f.whisperButton:Hide() end
|
||||
end
|
||||
if f.scrollUpBtn then
|
||||
f.scrollUpBtn:SetAlpha(chromeAlpha)
|
||||
f.scrollUpBtn:EnableMouse(chromeVisible)
|
||||
if chromeVisible then f.scrollUpBtn:Show() else f.scrollUpBtn:Hide() end
|
||||
end
|
||||
if f.scrollDownBtn then
|
||||
f.scrollDownBtn:SetAlpha(chromeAlpha)
|
||||
f.scrollDownBtn:EnableMouse(chromeVisible)
|
||||
if chromeVisible then f.scrollDownBtn:Show() else f.scrollDownBtn:Hide() end
|
||||
end
|
||||
if f.scrollTrack then
|
||||
f.scrollTrack:SetVertexColor(0.18, 0.19, 0.22, 0.9 * chromeAlpha)
|
||||
if chromeVisible then f.scrollTrack:Show() else f.scrollTrack:Hide() end
|
||||
end
|
||||
if f.resizeHandle then
|
||||
f.resizeHandle:SetAlpha(chromeAlpha)
|
||||
f.resizeHandle:EnableMouse(chromeVisible)
|
||||
if chromeVisible then f.resizeHandle:Show() else f.resizeHandle:Hide() end
|
||||
end
|
||||
if f.hint then
|
||||
if chromeVisible then f.hint:Show() else f.hint:Hide() end
|
||||
end
|
||||
|
||||
-- Edit backdrop (only backdrop colors, not child alpha — preserve editbox text visibility)
|
||||
if self.editBackdrop then
|
||||
local editBox = ChatFrameEditBox or ChatFrame1EditBox
|
||||
local editBackdropVisible = chromeVisible and editBox and editBox:IsShown()
|
||||
if self.editBackdrop.SetBackdropColor then
|
||||
self.editBackdrop:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], 0.96 * chromeAlpha)
|
||||
end
|
||||
if self.editBackdrop.SetBackdropBorderColor then
|
||||
self.editBackdrop:SetBackdropBorderColor(CFG_THEME.panelBorder[1], CFG_THEME.panelBorder[2], CFG_THEME.panelBorder[3], 0.98 * chromeAlpha)
|
||||
end
|
||||
if self.editBackdrop.catIcon then
|
||||
self.editBackdrop.catIcon:SetVertexColor(1, 0.84, 0.94, 0.9 * chromeAlpha)
|
||||
end
|
||||
if self.editBackdrop.topLine then
|
||||
self.editBackdrop.topLine:SetVertexColor(1, 0.76, 0.9, 0.85 * chromeAlpha)
|
||||
end
|
||||
self.editBackdrop:EnableMouse(editBackdropVisible)
|
||||
if editBackdropVisible then
|
||||
self.editBackdrop:Show()
|
||||
else
|
||||
self.editBackdrop:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SFrames.Chat:ApplyFrameBorderStyle()
|
||||
if not self.frame then return end
|
||||
|
||||
@@ -6413,7 +6889,12 @@ function SFrames.Chat:ApplyConfig()
|
||||
end
|
||||
|
||||
local bgA = Clamp(cfg.bgAlpha or DEFAULTS.bgAlpha, 0, 1)
|
||||
self.frame:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], bgA)
|
||||
local hAlpha = (self.frame.sfHoverAlpha ~= nil) and self.frame.sfHoverAlpha or 1
|
||||
if cfg.hoverTransparent then
|
||||
self.frame:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], bgA * hAlpha)
|
||||
else
|
||||
self.frame:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], bgA)
|
||||
end
|
||||
|
||||
self:AttachChatFrame()
|
||||
self:HideDefaultChrome()
|
||||
@@ -6427,6 +6908,11 @@ function SFrames.Chat:ApplyConfig()
|
||||
self:StartStabilizer()
|
||||
self:SetUnlocked(SFrames and SFrames.isUnlocked)
|
||||
self:RefreshConfigFrame()
|
||||
|
||||
-- Re-apply hover alpha so ApplyConfig doesn't leave chrome visible
|
||||
if cfg.hoverTransparent and self.frame.sfHoverAlpha ~= nil then
|
||||
self:ApplyHoverAlpha(self.frame.sfHoverAlpha)
|
||||
end
|
||||
end
|
||||
|
||||
function SFrames.Chat:Initialize()
|
||||
@@ -6524,6 +7010,61 @@ function SFrames.Chat:Initialize()
|
||||
end)
|
||||
end
|
||||
|
||||
-- HC死亡系统消息:AI开启时翻译并转发到公会
|
||||
if not SFrames.Chat._hcDeathGuildHooked then
|
||||
SFrames.Chat._hcDeathGuildHooked = true
|
||||
local hcDeathEvFrame = CreateFrame("Frame", "SFramesChatHCDeathGuildEvents", UIParent)
|
||||
hcDeathEvFrame:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
hcDeathEvFrame:SetScript("OnEvent", function()
|
||||
if not (SFrames and SFrames.Chat) then return end
|
||||
local db = EnsureDB()
|
||||
-- AI翻译必须开启
|
||||
if db.translateEnabled == false then return end
|
||||
-- 必须在公会中才能发送
|
||||
if not (IsInGuild and IsInGuild()) then return end
|
||||
local messageText = arg1
|
||||
if type(messageText) ~= "string" or messageText == "" then return end
|
||||
|
||||
local isDeath = ParseHardcoreDeathMessage(messageText) ~= nil
|
||||
local isLevel = (not isDeath) and (ParseHardcoreLevelMessage(messageText) ~= nil)
|
||||
if not isDeath and not isLevel then return end
|
||||
|
||||
-- 仅通报工会成员:提取角色名并检查缓存
|
||||
if db.hcGuildMemberOnly then
|
||||
local charName = ParseHCCharacterName(messageText)
|
||||
if not charName or not IsHCGuildMember(charName) then return end
|
||||
end
|
||||
|
||||
-- 死亡通报:检查 hcDeathToGuild 及等级过滤
|
||||
if isDeath then
|
||||
if db.hcDeathToGuild == false then return end
|
||||
if db.hcDeathDisable then return end
|
||||
local deathLvl = ParseHardcoreDeathMessage(messageText)
|
||||
if db.hcDeathLevelMin and deathLvl and deathLvl > 0 and deathLvl < db.hcDeathLevelMin then return end
|
||||
end
|
||||
|
||||
-- 等级里程碑:检查 hcLevelToGuild
|
||||
if isLevel then
|
||||
if db.hcLevelToGuild == false then return end
|
||||
end
|
||||
|
||||
local cleanText = CleanTextForTranslation(messageText)
|
||||
if cleanText == "" then return end
|
||||
local function doSend(finalText)
|
||||
pcall(function() SendChatMessage("[HC] " .. finalText, "GUILD") end)
|
||||
end
|
||||
if isDeath then
|
||||
TranslateHCDeathParts(cleanText, doSend)
|
||||
else
|
||||
TranslateHCLevelParts(cleanText, doSend)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
SFrames:RegisterEvent("GUILD_ROSTER_UPDATE", function()
|
||||
RefreshHCGuildCache()
|
||||
end)
|
||||
|
||||
SFrames:RegisterEvent("PLAYER_ENTERING_WORLD", function()
|
||||
if SFrames and SFrames.Chat then
|
||||
SFrames.Chat:ApplyConfig()
|
||||
@@ -6532,7 +7073,7 @@ function SFrames.Chat:Initialize()
|
||||
end
|
||||
LoadPersistentClassCache()
|
||||
if IsInGuild and IsInGuild() and GuildRoster then
|
||||
GuildRoster()
|
||||
GuildRoster() -- 触发 GUILD_ROSTER_UPDATE → RefreshHCGuildCache
|
||||
end
|
||||
SFrames:RefreshClassColorCache()
|
||||
|
||||
@@ -6714,6 +7255,17 @@ function SFrames.Chat:Initialize()
|
||||
|
||||
do
|
||||
local db = EnsureDB()
|
||||
|
||||
-- HC系统消息(死亡/里程碑黄字)特殊处理:
|
||||
-- AI翻译未开启时直接透传,不加 [+] 注释;
|
||||
-- AI开启时正常记录,翻译转发由独立的 CHAT_MSG_SYSTEM 事件处理器完成。
|
||||
if ParseHardcoreDeathMessage(text) or ParseHardcoreLevelMessage(text) then
|
||||
if db.translateEnabled == false then
|
||||
origAddMessage(self, text, r, g, b, alpha, holdTime)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local chanName = GetChannelNameFromChatLine(text)
|
||||
|
||||
if chanName and IsIgnoredChannelByDefault(chanName) then
|
||||
@@ -7137,4 +7689,3 @@ end
|
||||
end
|
||||
SFrames:RegisterEvent("PLAYER_REGEN_DISABLED", ChatCombatReanchor)
|
||||
SFrames:RegisterEvent("PLAYER_REGEN_ENABLED", ChatCombatReanchor)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user