# Nanami-Plates 仇恨系统对接需求文档 ## 概述 Nanami-DPS 已完成仇恨监控系统的全面重做,采用自适应双轨混合架构(TWThreat API 服务端直读 + 本地战斗日志推演)。本文档定义 Nanami-Plates 姓名板插件需要对接的数据接口、渲染要求和交互规范。 --- ## 一、数据接口 ### 1.1 全局引用 ```lua local TE = NanamiDPS.ThreatEngine -- 仇恨引擎实例 local TC = NanamiDPS.ThreatCoefficients -- 系数常量 ``` ### 1.2 核心 API 函数 | 函数签名 | 返回值 | 说明 | |----------|--------|------| | `TE:GetActiveTargetKey()` | `string` 或 `nil` | 获取当前激活目标的唯一键(优先API目标键,回退到本地目标) | | `TE:GetCurrentTargetKey()` | `string` 或 `nil` | 获取玩家当前 target 的唯一键 | | `TE.GetTargetKey(unitID)` | `string` 或 `nil` | 根据 unitID 计算目标唯一键(格式: `G:GUID` / `I:RaidIcon` / `V:Name:MaxHP`) | | `TE:GetThreatList(targetKey)` | `table` | 返回按仇恨降序排列的列表,每项包含 `{name, threat, tps, perc, isTanking, isMelee, relativePercent}` | | `TE:GetOTStatus(targetKey)` | `table` | 返回当前玩家的 OT 分析: `{safe, pct, threshold, otPoint, buffer, myThreat, tankThreat, tankName, isMelee}` | | `TE:IsAPIActiveForTarget(targetKey)` | `boolean` | 判断该目标是否使用服务端 API 数据(精确模式) | | `TE.IsEliteOrBoss(unitID)` | `boolean` | 判断目标是否为精英/Boss | | `TE.IsInGroup()` | `boolean` | 判断玩家是否在小队/团队中 | ### 1.3 状态字段(只读) | 字段 | 类型 | 说明 | |------|------|------| | `TE.inCombat` | `boolean` | 当前是否在战斗中 | | `TE.apiActive` | `boolean` | API 直读引擎是否激活 | | `TE.playerName` | `string` | 当前玩家名称 | | `TE.playerClass` | `string` | 当前玩家职业 token(大写) | | `TE.targets` | `table` | 三维数据表,结构见下文 | ### 1.4 三维数据结构 ``` TE.targets[targetKey] = { players = { [playerName] = { threat = number, -- 绝对仇恨值 tps = number, -- 每秒仇恨制造率 isTanking = boolean, -- 是否持有仇恨 isMelee = boolean, -- 是否在近战范围 perc = number, -- 相对百分比 (0-100) history = table, -- TPS 计算用历史数据 lastThreatTime = number, -- 最后一次仇恨更新时间 }, ... }, tankName = string, -- 当前坦克名称 tankThreat = number, -- 坦克仇恨值 lastUpdate = number, -- 最后更新时间戳 source = string, -- "api" | "api_tm" | "local" } ``` ### 1.5 回调注册 ```lua -- 仇恨数据更新时触发(API 报文到达或本地计算完成) NanamiDPS:RegisterCallback("threat_update", "NanamiPlates", function() -- 在此刷新姓名板仇恨显示 end) ``` ### 1.6 OT 阈值常量 ```lua TC.OT_MELEE_THRESHOLD = 1.10 -- 近战 110% TC.OT_RANGED_THRESHOLD = 1.30 -- 远程 130% ``` --- ## 二、姓名板渲染要求 ### 2.1 仇恨百分比指示器 **位置**: 锚定在每个敌方姓名板的名称文字正上方或正下方 **格式**: `XX%` 数字文本,字号 10-12 **更新频率**: 跟随 `threat_update` 回调,约 0.5 秒一次 ### 2.2 颜色渐变规则 根据玩家在当前目标上的仇恨百分比(相对于 OT 阈值)分级着色: | 百分比区间 | 颜色 | 含义 | |-----------|------|------| | 0% - 49% | 绿色 `(0.2, 1.0, 0.2)` | 安全 | | 50% - 79% | 黄色 `(1.0, 1.0, 0.0)` | 警戒 | | 80% - 100%+ | 红色 `(1.0, 0.2, 0.0)` | 危险,即将 OT | ### 2.3 仇恨条背景色 为已持有仇恨(`isTanking = true`)的目标姓名板生命条添加边框高亮: - 坦克视角: 正在坦克的怪物边框为**绿色** - DPS/治疗视角: 自己正在被攻击的怪物边框为**红色** ### 2.4 数据来源标识(可选) 在仇恨百分比旁边显示小图标区分数据来源: - API 数据: 小圆点 (绿色) — 表示服务端精确数据 - 本地推算: 小圆点 (灰色) — 表示基于战斗日志估算 --- ## 三、Tank Mode 姓名板增强 当玩家为坦克角色(防御姿态/巨熊形态)时: ### 3.1 多目标仇恨概览 在每个姓名板上显示**仇恨排名第二**的玩家名称和百分比,格式: ``` [怪物名称] ██████████████ 100% ← 仇恨条(自己) 紧随: Mage_A 85% ← 第二名信息 ``` ### 3.2 松动目标高亮 如果某个怪物上的第二名仇恨超过坦克仇恨的 80%(接近 110% OT 线),该姓名板整体加红色闪烁边框,提示坦克需要立即补仇恨技能。 ### 3.3 快速目标切换(SuperWoW 依赖) 如果检测到 SuperWoW/SuperAPI 可用: - 姓名板上的仇恨区域可点击 - 点击后调用 `TargetUnit` 或 `SetTarget` 切换到该怪物 - 方便坦克无需 TAB 键即可快速切换目标 --- ## 四、Healer Mode 姓名板增强 当玩家为治疗角色时: ### 4.1 主坦锁定 允许设置一个"主坦克焦点"(通过 `/nanami mt [name]` 或右键团队框架) ### 4.2 未稳固目标标记 在姓名板上对**主坦克仇恨不是最高**的怪物添加特殊标记(橙色感叹号),提示治疗者该怪物仇恨不稳定,可能随时转向治疗者。 --- ## 五、性能要求 | 项目 | 要求 | |------|------| | 姓名板遍历频率 | 最大 0.5 秒一次(跟随 `threat_update` 回调) | | 内存开销 | 单个姓名板附加元素不超过 3 个 FontString + 1 个 Texture | | 不可见姓名板 | 必须隐藏对应的仇恨指示器,避免孤立 UI 元素 | | 战斗外 | 所有仇恨指示器隐藏,不做任何查询 | | 数据清理 | 离开战斗后清除所有姓名板附加元素 | --- ## 六、对接代码示例 ```lua -- Nanami-Plates 中的对接示例 local TE = NanamiDPS and NanamiDPS.ThreatEngine local TC = NanamiDPS and NanamiDPS.ThreatCoefficients -- 获取某个 nameplate 对应怪物的玩家仇恨百分比 local function GetMyThreatPct(unitID) if not TE or not TE.inCombat then return nil end local targetKey = TE.GetTargetKey(unitID) if not targetKey then return nil end local td = TE.targets[targetKey] if not td then return nil end local pd = td.players[TE.playerName] if not pd then return nil end return pd.perc, pd.isTanking, pd.isMelee end -- 获取该怪物仇恨第二名 local function GetSecondThreat(unitID) if not TE or not TE.inCombat then return nil end local targetKey = TE.GetTargetKey(unitID) if not targetKey then return nil end local list = TE:GetThreatList(targetKey) if list and list[2] then return list[2].name, list[2].perc, list[2].threat end return nil end -- 注册回调 if NanamiDPS then NanamiDPS:RegisterCallback("threat_update", "NanamiPlates_Threat", function() -- 遍历所有可见姓名板,刷新仇恨数据 NanamiPlates:RefreshAllThreatIndicators() end) end ``` --- ## 七、版本兼容性 - **必须依赖**: Nanami-DPS >= 1.0.0(含 ThreatEngine 模块) - **可选依赖**: SuperWoW / SuperAPI(用于姓名板高级交互) - **WoW 客户端**: 1.12.x (Turtle WoW) - **Lua 环境**: 不支持现代 Lua 特性(使用 `table.getn` 代替 `#`,`pairs` 代替 `next`)