diff --git a/Assets/IconManifest.json b/Assets/IconManifest.json index 5d852658..f3a1c643 100644 --- a/Assets/IconManifest.json +++ b/Assets/IconManifest.json @@ -61,4 +61,4 @@ { "kind": "spec", "classId": 13, "specId": 2, "wowSpecId": 1468, "fileDataId": 4511812, "class": "Evoker", "name": "Preservation", "nameZhCn": "恩护", "slug": "classicon_evoker_preservation", "target": "Spec/evoker-preservation.jpg" }, { "kind": "spec", "classId": 13, "specId": 3, "wowSpecId": 1473, "fileDataId": 5198700, "class": "Evoker", "name": "Augmentation", "nameZhCn": "增辉", "slug": "classicon_evoker_augmentation", "target": "Spec/evoker-augmentation.jpg" } ] -} +}w diff --git a/Fuyutsui/Fuyutsui.toc b/Fuyutsui/Fuyutsui.toc index 0a680194..afcc1b60 100644 --- a/Fuyutsui/Fuyutsui.toc +++ b/Fuyutsui/Fuyutsui.toc @@ -4,7 +4,7 @@ ## Version: 1.2.1.10 ## Author: Wayne Bian ## IconTexture: Interface\AddOns\Fuyutsui\core\Spell_Misc_EmotionHappy.blp -## OptionalDeps: LibRangeCheck-3.0, ExwindTools, EXBoss +## OptionalDeps: LibRangeCheck-3.0 ## SavedVariables: FuyutsuiADB ## Category-enUS: Combat @@ -31,7 +31,6 @@ core/config.lua core/curves.lua core/block.lua core/stateblocks.lua -core/exboss.lua core/commands.lua core/quickbutton.lua core/macro.lua diff --git a/Fuyutsui/core/core.lua b/Fuyutsui/core/core.lua index 77a9bb98..5b77a30d 100644 --- a/Fuyutsui/core/core.lua +++ b/Fuyutsui/core/core.lua @@ -165,6 +165,10 @@ Fuyutsui.state = { Fuyutsui.blocks = {} Fuyutsui.target = {} Fuyutsui.focus = {} +Fuyutsui.boss = {} +for index = 1, 5 do + Fuyutsui.boss["boss" .. index] = {} +end Fuyutsui.nameplate = {} Fuyutsui.group = {} Fuyutsui.groupList = {} diff --git a/Fuyutsui/core/curves.lua b/Fuyutsui/core/curves.lua index 6805fc73..b1943fc9 100644 --- a/Fuyutsui/core/curves.lua +++ b/Fuyutsui/core/curves.lua @@ -44,4 +44,4 @@ end Fuyutsui.powerCurves = powerCurves Fuyutsui.curve100 = Fuyutsui:CreateColorCurveScaling(100) Fuyutsui.curve255 = Fuyutsui:CreateColorCurve(255, 255) -Fuyutsui.castCurve = Fuyutsui:CreateColorCurve(2.55, 255) +Fuyutsui.castCurve = Fuyutsui:CreateColorCurve(25.5, 255) diff --git a/Fuyutsui/core/events.lua b/Fuyutsui/core/events.lua index 5cb92685..8f481a70 100644 --- a/Fuyutsui/core/events.lua +++ b/Fuyutsui/core/events.lua @@ -240,6 +240,9 @@ function Fuyutsui:UNIT_HEALTH(_, unit) if unit == "focus" then self:UpdateFocusHealth() end + if unit and unit:match("^boss[1-5]$") then + self:UpdateUnitHealthBlock(unit) + end if self.group[unit] then self:UpdateUnitDeath(unit, "health") end @@ -249,6 +252,9 @@ function Fuyutsui:UNIT_MAXHEALTH(_, unit) if unit == "player" then self:UpdatePlayerHealth() end + if unit and unit:match("^boss[1-5]$") then + self:UpdateUnitHealthBlock(unit) + end if self.group[unit] then self:UpdateUnitDeath(unit, "health") end @@ -440,6 +446,9 @@ function Fuyutsui:OnUpdate(elapsed) self:UpdatePlayerCastBlocks() self:UpdateUnitCastingOrChannelingInfo("target") self:UpdateUnitCastingOrChannelingInfo("focus") + for index = 1, 5 do + self:UpdateUnitCastingOrChannelingInfo("boss" .. index) + end self:UpdateGroupInRangeAndHealth() self.timeElapsed = self.timeElapsed + elapsed @@ -449,6 +458,11 @@ function Fuyutsui:OnUpdate(elapsed) self:UpdateRune() self:UpdateTargetRangeBlock() self:UpdateFocusRangeBlock() + for index = 1, 5 do + local unit = "boss" .. index + self:UpdateUnitCanAttack(unit) + self:UpdateUnitRangeBlock(unit) + end self:UpdateEnemyCount() self:UpdateItemCooldown() self.timeElapsed = 0 diff --git a/Fuyutsui/core/exboss.lua b/Fuyutsui/core/exboss.lua deleted file mode 100644 index a52c1e95..00000000 --- a/Fuyutsui/core/exboss.lua +++ /dev/null @@ -1,342 +0,0 @@ --- Fuyutsui -> EXBoss 数据桥接。 --- EXBoss 不存在时,本文件不注册任何 EX 事件,也不创建技能像素。 - -local Fuyutsui = _G.Fuyutsui -if not Fuyutsui then return end - -local START_EVENT = "EXBOSS_BOSS_OBSERVED_CAST_START" -local STOP_EVENT = "EXBOSS_BOSS_OBSERVED_CAST_STOP" -local OWNER = "Fuyutsui.ExBoss" -local MAX_BLOCK_INDEX = 510 - --- [spellID] = 像素的逻辑名称。 --- 名称相同的 spellID 共用一个顶部像素。 -local spells = { - [381516] = "打断施法", -} - -local ExBoss = Fuyutsui.ExBoss or {} -Fuyutsui.ExBoss = ExBoss -ExBoss.spells = spells -ExBoss.spellPixels = ExBoss.spellPixels or {} -ExBoss.pixelGroups = ExBoss.pixelGroups or {} -ExBoss.bound = false -ExBoss.nativeBound = false -ExBoss.layoutBlocks = nil - -local function IsAddonLoaded(name) - if C_AddOns and type(C_AddOns.IsAddOnLoaded) == "function" then - return C_AddOns.IsAddOnLoaded(name) == true - end - return type(IsAddOnLoaded) == "function" and IsAddOnLoaded(name) == true -end - -local function NormalizeSpellID(value) - if type(issecretvalue) == "function" and issecretvalue(value) then - return nil - end - local id = tonumber(value) - return id and id > 0 and id or nil -end - -local function GetMaxBlockIndex(value, currentMax) - if type(value) ~= "table" then - return currentMax - end - - for key, child in pairs(value) do - if key == "index" and type(child) == "number" then - currentMax = math.max(currentMax, math.floor(child)) - elseif type(child) == "table" then - currentMax = GetMaxBlockIndex(child, currentMax) - end - end - - return currentMax -end - -local function GetSortedSpellIDs() - local ids = {} - for spellID in pairs(spells) do - ids[#ids + 1] = spellID - end - table.sort(ids) - return ids -end - -local function ClearSpellState(entry) - if type(entry) ~= "table" then return end - entry.active = false - entry.runtimeID = nil - entry.unit = nil - entry.castKind = nil - entry.castBarID = nil - entry.totalDuration = 0 - entry.startedAt = 0 -end - -local function EnsureSpellPixels() - local blocks = Fuyutsui.blocks - if type(blocks) ~= "table" or type(blocks.state) ~= "table" or next(blocks.state) == nil then - return false - end - if ExBoss.layoutBlocks == blocks then - return true - end - - local nextIndex = GetMaxBlockIndex(blocks, 0) + 1 - local newPixels = {} - local newGroups = {} - - for _, spellID in ipairs(GetSortedSpellIDs()) do - local name = spells[spellID] - local group = newGroups[name] - if not group then - if nextIndex > MAX_BLOCK_INDEX then - print("|cffff0000[Fuyutsui] EXBoss 技能像素空间不足,无法创建 " .. tostring(name) .. "|r") - break - end - - group = { - name = name, - index = nextIndex, - spellIDs = {}, - } - newGroups[name] = group - nextIndex = nextIndex + 1 - end - - local entry = ExBoss.spellPixels[spellID] or {} - entry.spellID = spellID - entry.name = name - entry.group = group - entry.index = group.index - group.spellIDs[#group.spellIDs + 1] = spellID - ClearSpellState(entry) - newPixels[spellID] = entry - end - - ExBoss.spellPixels = newPixels - ExBoss.pixelGroups = newGroups - ExBoss.layoutBlocks = blocks - return true -end - -local function GetCurrentDuration(entry) - if type(entry) ~= "table" or not entry.active or not entry.unit then - return nil - end - - if entry.castKind == "channel" then - return UnitChannelDuration(entry.unit) - end - return UnitCastingDuration(entry.unit) -end - -local function GetActiveGroupEntry(group) - if type(group) ~= "table" then return nil end - - local activeEntry - for _, spellID in ipairs(group.spellIDs or {}) do - local entry = ExBoss.spellPixels[spellID] - if entry and entry.active then - if not activeEntry or (tonumber(entry.startedAt) or 0) > (tonumber(activeEntry.startedAt) or 0) then - activeEntry = entry - end - end - end - return activeEntry -end - -local function UpdatePixelGroup(group) - if type(group) ~= "table" or not group.index then return end - - local value = 0 - local duration = GetCurrentDuration(GetActiveGroupEntry(group)) - if duration then - local color = duration:EvaluateRemainingDuration(Fuyutsui.castCurve) - local _, _, blue = color:GetRGB() - value = blue or 0 - end - - Fuyutsui:CreateTexture(group.index, value) -end - -local function ReadNativeSpellID(unit, castKind, eventSpellID) - local spellID = NormalizeSpellID(eventSpellID) - if spellID then return spellID end - - if castKind == "channel" then - local ok, _, _, _, _, _, _, _, apiSpellID = pcall(UnitChannelInfo, unit) - return ok and NormalizeSpellID(apiSpellID) or nil - end - - local ok, _, _, _, _, _, _, _, _, apiSpellID = pcall(UnitCastingInfo, unit) - return ok and NormalizeSpellID(apiSpellID) or nil -end - -local function OnBossCastStart(_, payload) - if type(payload) ~= "table" then return end - - local spellID = NormalizeSpellID(payload.spellID) - local name = spellID and spells[spellID] - if not name then return end - local entry = ExBoss.spellPixels[spellID] - local runtimeID = tonumber(payload.runtimeID) - local castBarID = tonumber(payload.castBarID) - local now = GetTime() - local duplicateStart = entry and entry.active - and ((runtimeID ~= nil and entry.runtimeID == runtimeID) - or (runtimeID == nil and castBarID ~= nil and entry.castBarID == castBarID) - or (entry.unit == payload.unit and math.abs(now - (tonumber(entry.startedAt) or 0)) <= 0.5)) - - if not duplicateStart then - print("开始施放指定技能") - end - - if not EnsureSpellPixels() then return end - entry = ExBoss.spellPixels[spellID] - if not entry then return end - - entry.active = true - entry.runtimeID = runtimeID - entry.unit = type(payload.unit) == "string" and payload.unit or nil - entry.castKind = tostring(payload.castKind or "cast") - entry.castBarID = castBarID - entry.totalDuration = tonumber(payload.totalDuration) or 0 - entry.startedAt = tonumber(payload.startedAt) or now - UpdatePixelGroup(entry.group) -end - -local function OnBossCastStop(_, payload) - if type(payload) ~= "table" then return end - - local spellID = NormalizeSpellID(payload.spellID) - local runtimeID = tonumber(payload.runtimeID) - - if spellID and ExBoss.spellPixels[spellID] then - local entry = ExBoss.spellPixels[spellID] - if runtimeID == nil or entry.runtimeID == nil or entry.runtimeID == runtimeID then - ClearSpellState(entry) - UpdatePixelGroup(entry.group) - end - return - end - - for _, entry in pairs(ExBoss.spellPixels) do - if runtimeID ~= nil and entry.runtimeID == runtimeID then - ClearSpellState(entry) - UpdatePixelGroup(entry.group) - end - end -end - -local function OnNativeBossCastStart(_, event, ...) - local unit = select(1, ...) - if type(unit) ~= "string" or not unit:match("^boss[1-5]$") then return end - - local eventSpellID = select(3, ...) - local castBarID = select(select("#", ...), ...) - local castKind = event == "UNIT_SPELLCAST_CHANNEL_START" and "channel" or "cast" - local spellID = ReadNativeSpellID(unit, castKind, eventSpellID) - if not spellID or not spells[spellID] then return end - - OnBossCastStart(nil, { - spellID = spellID, - unit = unit, - castKind = castKind, - castBarID = castBarID, - startedAt = GetTime(), - }) -end - -local function OnNativeBossCastStop(_, event, ...) - local unit = select(1, ...) - if type(unit) ~= "string" or not unit:match("^boss[1-5]$") then return end - - local eventSpellID = select(3, ...) - local castBarID = select(select("#", ...), ...) - local castKind = event == "UNIT_SPELLCAST_CHANNEL_STOP" and "channel" or "cast" - local spellID = NormalizeSpellID(eventSpellID) - - for _, entry in pairs(ExBoss.spellPixels) do - local sameUnit = entry.active and entry.unit == unit - local sameKind = entry.castKind == castKind - local sameCastBar = castBarID == nil or entry.castBarID == nil or entry.castBarID == castBarID - local sameSpell = spellID == nil or entry.spellID == spellID - if sameUnit and sameKind and sameCastBar and sameSpell then - local group = entry.group - ClearSpellState(entry) - UpdatePixelGroup(group) - end - end -end - -local nativeFrame = CreateFrame("Frame") -local function BindNativeBossEvents() - if ExBoss.nativeBound then return end - - nativeFrame:RegisterEvent("UNIT_SPELLCAST_START") - nativeFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START") - nativeFrame:RegisterEvent("UNIT_SPELLCAST_STOP") - nativeFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP") - nativeFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED") - nativeFrame:RegisterEvent("UNIT_SPELLCAST_FAILED") - nativeFrame:RegisterEvent("UNIT_SPELLCAST_FAILED_QUIET") - nativeFrame:SetScript("OnEvent", function(frame, event, ...) - if event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then - OnNativeBossCastStart(frame, event, ...) - else - OnNativeBossCastStop(frame, event, ...) - end - end) - ExBoss.nativeBound = true -end - -local function BindExBossEvents() - if ExBoss.bound then return true end - if not IsAddonLoaded("EXBoss") then return false end - - local ExwindTools = _G.ExwindTools - if not ExwindTools or type(ExwindTools.RegisterEvent) ~= "function" then - return false - end - - ExwindTools:RegisterEvent(START_EVENT, OWNER .. ".Start", OnBossCastStart) - ExwindTools:RegisterEvent(STOP_EVENT, OWNER .. ".Stop", OnBossCastStop) - BindNativeBossEvents() - ExBoss.bound = true - return true -end - -local updateFrame = CreateFrame("Frame") -updateFrame:SetScript("OnUpdate", function() - if not ExBoss.bound then return end - if not EnsureSpellPixels() then return end - - for _, group in pairs(ExBoss.pixelGroups) do - UpdatePixelGroup(group) - end -end) - -local driver = CreateFrame("Frame") -driver:RegisterEvent("ADDON_LOADED") -driver:RegisterEvent("PLAYER_LOGIN") -driver:RegisterEvent("PLAYER_ENTERING_WORLD") -driver:RegisterEvent("PLAYER_TALENT_UPDATE") -driver:SetScript("OnEvent", function(_, event, addonName) - if event == "ADDON_LOADED" then - if addonName == "ExwindTools" or addonName == "EXBoss" then - BindExBossEvents() - end - return - end - - if event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_TALENT_UPDATE" then - BindExBossEvents() - EnsureSpellPixels() - end -end) - --- EXBoss 可能比 Fuyutsui 更早加载。 -BindExBossEvents() diff --git a/Fuyutsui/core/spells.lua b/Fuyutsui/core/spells.lua index 388a5dcb..fcd579d0 100644 --- a/Fuyutsui/core/spells.lua +++ b/Fuyutsui/core/spells.lua @@ -43,15 +43,15 @@ Fuyutsui.DebugPrintNewSpellEntry = DebugPrintNewSpellEntry Fuyutsui.DebugPrintSpellBlockLine = DebugPrintSpellBlockLine local overrideSpells = { - [432459] = 1289728, -- 神圣壁垒 - [432472] = 1289728, -- 圣洁武器 - [444995] = 455630, -- 涌动图腾 - [1242173] = 228260, -- 虚空齐射 - [1241413] = 31884, -- 愤怒之锤 - [24275] = 31884, -- 愤怒之锤 - [1241288] = 31884, -- 愤怒之锤 - [1277026] = 31884, -- 愤怒之锤 - [1279408] = 31884, -- 愤怒之锤 + [432459] = 1289728, -- 神圣壁垒 + [432472] = 1289728, -- 圣洁武器 + [444995] = 455630, -- 涌动图腾 + [1242173] = 228260, -- 虚空齐射 + [1241413] = 31884, -- 愤怒之锤 + [24275] = 31884, -- 愤怒之锤 + [1241288] = 31884, -- 愤怒之锤 + [1277026] = 31884, -- 愤怒之锤 + [1279408] = 31884, -- 愤怒之锤 } function Fuyutsui:IsSpellKnown(spellID) @@ -114,7 +114,7 @@ local dispelAbilities = { } local offensiveDispelAbilities = { - [1] = { 528 }, + [1] = { 528, 370, 378773 }, [9] = { 2908 }, } diff --git a/Fuyutsui/core/stateblocks.lua b/Fuyutsui/core/stateblocks.lua index 4c29742e..7c1b7358 100644 --- a/Fuyutsui/core/stateblocks.lua +++ b/Fuyutsui/core/stateblocks.lua @@ -5,6 +5,7 @@ local EvaluateColorFromBoolean = C_CurveUtil.EvaluateColorFromBoolean local state = Fuyutsui.state local target = Fuyutsui.target local focus = Fuyutsui.focus +local boss = Fuyutsui.boss local ColorValue0 = CreateColor(0, 0, 0, 1) local ColorValue1 = CreateColor(0, 0, 1 / 255, 1) @@ -40,7 +41,7 @@ local function GetItemCooldownPixel(self, countKey, itemID) return 1 end ---- mode: "cast" | "channel" +--- mode: "cast" | "castElapsed" | "channel" function Fuyutsui:GetUnitCastPixel(unit, mode) local castCurve = self.castCurve if mode == "channel" then @@ -56,7 +57,12 @@ function Fuyutsui:GetUnitCastPixel(unit, mode) local cast = UnitCastingDuration(unit) if not cast then return 0 end - local castingDurationColor = cast:EvaluateRemainingDuration(castCurve) + local castingDurationColor + if mode == "castElapsed" then + castingDurationColor = cast:EvaluateElapsedDuration(castCurve) + else + castingDurationColor = cast:EvaluateRemainingDuration(castCurve) + end ---@diagnostic disable-next-line: param-type-mismatch local _, _, b = castingDurationColor:GetRGB() return b @@ -83,7 +89,7 @@ function Fuyutsui:GetUnitInterruptiblePixel(unit, mode) return interruptible end --- blocks.state 键:下列分类用名称本身;目标/焦点用 分类..名称 +-- blocks.state 键:下列分类用名称本身;单位分类用 分类..名称 local bareKeyCategories = { ["状态"] = true, ["能量"] = true, @@ -241,7 +247,8 @@ local stateBlockGetters = { if not target.maxRange then return nil end return target.maxRange / 255 end, - ["施法"] = function(self) return self:GetUnitCastPixel("target", "cast") end, + ["施法(倒计时)"] = function(self) return self:GetUnitCastPixel("target", "cast") end, + ["施法(正计时)"] = function(self) return self:GetUnitCastPixel("target", "castElapsed") end, ["施法可打断"] = function(self) return self:GetUnitInterruptiblePixel("target", "cast") end, ["引导"] = function(self) return self:GetUnitCastPixel("target", "channel") end, ["引导可打断"] = function(self) return self:GetUnitInterruptiblePixel("target", "channel") end, @@ -254,13 +261,35 @@ local stateBlockGetters = { if not focus.maxRange then return nil end return focus.maxRange / 255 end, - ["施法"] = function(self) return self:GetUnitCastPixel("focus", "cast") end, + ["施法(倒计时)"] = function(self) return self:GetUnitCastPixel("focus", "cast") end, + ["施法(正计时)"] = function(self) return self:GetUnitCastPixel("focus", "castElapsed") end, ["施法可打断"] = function(self) return self:GetUnitInterruptiblePixel("focus", "cast") end, ["引导"] = function(self) return self:GetUnitCastPixel("focus", "channel") end, ["引导可打断"] = function(self) return self:GetUnitInterruptiblePixel("focus", "channel") end, }, } +for index = 1, 5 do + local unit = "boss" .. index + local category = "首领" .. index + local cache = boss[unit] + + stateBlockGetters[category] = { + ["类型"] = function() return cache.type or 0 end, + ["驱散类型"] = function() return 0 end, + ["生命值"] = function() return cache.healthPercent or 0 end, + ["距离"] = function() + if not cache.maxRange then return nil end + return cache.maxRange / 255 + end, + ["施法(倒计时)"] = function(self) return self:GetUnitCastPixel(unit, "cast") end, + ["施法(正计时)"] = function(self) return self:GetUnitCastPixel(unit, "castElapsed") end, + ["施法可打断"] = function(self) return self:GetUnitInterruptiblePixel(unit, "cast") end, + ["引导"] = function(self) return self:GetUnitCastPixel(unit, "channel") end, + ["引导可打断"] = function(self) return self:GetUnitInterruptiblePixel(unit, "channel") end, + } +end + for powerType, powerName in pairs(Fuyutsui.powerNameMap) do if powerName ~= "符文" then local pt = powerType @@ -281,6 +310,10 @@ function Fuyutsui:UpdateStateBlock(category, name) local cat = stateBlockGetters[category] if not cat then return end local getter = cat[name] + -- 兼容已有职业配置中的旧名称;新配置统一使用“施法(倒计时)” + if not getter and name == "施法" and category ~= "状态" then + getter = cat["施法(倒计时)"] + end if not getter then return end local key = bareKeyCategories[category] and name or (category .. name) local b = self.blocks diff --git a/Fuyutsui/core/target.lua b/Fuyutsui/core/target.lua index 8a457b02..aa6fff01 100644 --- a/Fuyutsui/core/target.lua +++ b/Fuyutsui/core/target.lua @@ -5,6 +5,7 @@ local rc = LibStub("LibRangeCheck-3.0") local state = Fuyutsui.state local target = Fuyutsui.target local focus = Fuyutsui.focus +local boss = Fuyutsui.boss local nameplate = Fuyutsui.nameplate function Fuyutsui:GetUnitRange(unit) @@ -25,6 +26,7 @@ local unitZHMap = { local function GetUnitCache(unit) if unit == "target" then return target end if unit == "focus" then return focus end + if boss and boss[unit] then return boss[unit] end end local function isSameUnit(unit1, unit2) @@ -110,7 +112,6 @@ function Fuyutsui:UpdateUnitRangeBlock(unit) end function Fuyutsui:UpdateUnitCastingOrChannelingInfo(unit) - if not UnitExists(unit) then return end local obj = unitZHMap[unit] if not obj then return end diff --git a/Fuyutsui/main.lua b/Fuyutsui/main.lua index 3af09b33..f50a0d44 100644 --- a/Fuyutsui/main.lua +++ b/Fuyutsui/main.lua @@ -25,6 +25,10 @@ function Fuyutsui:UpdatePlayerBlocks() self:UpdateFocusRangeBlock() self:UpdateTargetHealth() self:UpdateFocusHealth() + for index = 1, 5 do + self:UpdateUnitFullInfo("boss" .. index) + self:UpdateUnitRangeBlock("boss" .. index) + end self:UpdateEnemyCount() self:UpdateGroup() self:GetItemCount() @@ -51,10 +55,13 @@ function Fuyutsui:LoadPlayerBlocks(specIndex) local index = 1 - -- states 支持分类表:状态/能量/物品/配置开关/目标/焦点 - -- blocks.state 键:除目标/焦点外用名称本身;目标/焦点用 分类..名称(如 目标生命值) + -- states 支持分类表:状态/能量/物品/配置开关/目标/焦点/首领1-5 + -- blocks.state 键:基础分类用名称本身;单位分类用 分类..名称(如 目标生命值) if type(t.states) == "table" then - local stateCategoryOrder = { "状态", "能量", "物品", "配置开关", "目标", "焦点" } + local stateCategoryOrder = { + "状态", "能量", "物品", "配置开关", "目标", "焦点", + "首领1", "首领2", "首领3", "首领4", "首领5", + } local bareKeyCategories = { ["状态"] = true, ["能量"] = true, @@ -63,6 +70,8 @@ function Fuyutsui:LoadPlayerBlocks(specIndex) } local nested = t.states["状态"] or t.states["能量"] or t.states["物品"] or t.states["配置开关"] or t.states["目标"] or t.states["焦点"] + or t.states["首领1"] or t.states["首领2"] or t.states["首领3"] + or t.states["首领4"] or t.states["首领5"] if nested then for _, category in ipairs(stateCategoryOrder) do local list = t.states[category] diff --git a/Infrastructure/ClassStateCatalog.cs b/Infrastructure/ClassStateCatalog.cs index 2c75acfe..9d8e0288 100644 --- a/Infrastructure/ClassStateCatalog.cs +++ b/Infrastructure/ClassStateCatalog.cs @@ -12,6 +12,11 @@ internal static class ClassStateCatalog public const string CategoryResource = "能量"; public const string CategoryTarget = "目标"; public const string CategoryFocus = "焦点"; + public const string CategoryBoss1 = "首领1"; + public const string CategoryBoss2 = "首领2"; + public const string CategoryBoss3 = "首领3"; + public const string CategoryBoss4 = "首领4"; + public const string CategoryBoss5 = "首领5"; public static readonly string[] TopCategories = [ @@ -20,7 +25,12 @@ internal static class ClassStateCatalog CategoryItem, CategoryConfig, CategoryTarget, - CategoryFocus + CategoryFocus, + CategoryBoss1, + CategoryBoss2, + CategoryBoss3, + CategoryBoss4, + CategoryBoss5 ]; private static readonly (string Category, string[] Names)[] Categories = @@ -50,11 +60,31 @@ internal static class ClassStateCatalog ]), (CategoryTarget, [ - "类型", "驱散类型", "生命值", "距离", "施法", "施法可打断", "引导", "引导可打断" + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" ]), (CategoryFocus, [ - "类型", "驱散类型", "生命值", "距离", "施法", "施法可打断", "引导", "引导可打断" + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" + ]), + (CategoryBoss1, + [ + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" + ]), + (CategoryBoss2, + [ + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" + ]), + (CategoryBoss3, + [ + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" + ]), + (CategoryBoss4, + [ + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" + ]), + (CategoryBoss5, + [ + "类型", "驱散类型", "生命值", "距离", "施法", "施法(正计时)", "施法可打断", "引导", "引导可打断" ]) ]; @@ -94,6 +124,12 @@ internal static class ClassStateCatalog return true; } + // 兼容旧职业配置:单位分类中的“施法”已更名为“施法(倒计时)”。 + if (name == "施法" && category != CategoryState && TopCategories.Contains(category)) + { + return true; + } + // Lua 中已有但目录尚未收录的状态仍放在“状态”中,保证可见、可编辑。 return string.Equals(category, CategoryState, StringComparison.Ordinal) && FindCategory(name) is null; diff --git a/UI/StatusForm.cs b/UI/StatusForm.cs index 8524156a..33d2497c 100644 --- a/UI/StatusForm.cs +++ b/UI/StatusForm.cs @@ -798,11 +798,11 @@ public sealed class StatusForm : Form 92), 1, 1); fields.Controls.Add(CreateAboutFieldCard( "目标", - ["类型", "生命值", "距离", "施法", "施法可打断", "引导", "引导可打断"], + ["类型", "生命值", "距离", "施法(倒计时)", "施法(正计时)", "施法可打断", "引导", "引导可打断"], 104), 0, 2); fields.Controls.Add(CreateAboutFieldCard( "焦点", - ["类型", "生命值", "距离", "施法", "施法可打断", "引导", "引导可打断"], + ["类型", "生命值", "距离", "施法(倒计时)", "施法(正计时)", "施法可打断", "引导", "引导可打断"], 104), 1, 2); panel.Controls.Add(fields, 0, 2);