mirror of
https://github.com/waynebian01/Shigure.git
synced 2026-09-03 07:15:24 +08:00
添加首领单位支持,更新相关状态和施法逻辑,移除不必要的 EXBoss 文件,调整配置和文档
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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 },
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user