docs(spec): 更新英雄专属武器系统设计文档

将原英雄个人装备系统设计文档重命名为英雄专属武器系统设计文档,更新了背景目标、数据结构、锻造重铸规则、局内集成、存档结构、UI交互等内容,将装备相关逻辑替换为专属武器逻辑,并完成现有光环卡改名适配。
This commit is contained in:
panFD
2026-08-21 23:41:21 +08:00
parent 5c731b61a7
commit 3d2bba0cac

View File

@@ -1,33 +1,39 @@
# 英雄个人装备系统设计 # 英雄专属武器系统设计
- 日期2026-08-21 - 日期2026-08-21
- 状态已评审brainstorming 阶段用户逐节确认) - 状态已评审brainstorming 阶段用户逐节确认)
- 涉及文件:`HeroEquipSet.ts`(新增)、`heroSet.ts``HeroAttrs.ts``HeroAttrsComp.ts``SingletonModuleComp.ts``GameEvent.ts``EquipSet.ts`(改名 TalentSet - 涉及文件:`HeroWeaponSet.ts`(新增)、`heroSet.ts``HeroAttrs.ts``HeroAttrsComp.ts``SingletonModuleComp.ts``GameEvent.ts``GameSet.ts``EquipSet.ts`(改名 TalentSet
## 1. 背景与目标 ## 1. 背景与目标
现有"装备"`EquipSet.ts`8701~8820 段)是**全体驻场光环卡**:购买后全队加属性,经 `EquipBoxComp → FieldSkillHelper` 全局聚合。 现有"装备"`EquipSet.ts`8701~8820 段)是**全体驻场光环卡**:购买后全队加属性,经 `EquipBoxComp → FieldSkillHelper` 全局聚合。
本设计新增**英雄个人装备**:每个英雄可装备**一件**装备,词条只作用于该英雄本身;与现有光环卡**并存**,光环卡在表述上改名为**战队天赋**。 本设计新增**英雄专属武器**:每个英雄天生绑定一件专属武器,不可卸下、不可更换;词条只作用于该英雄本身;与现有光环卡**并存**,光环卡在表述上改名为**战队天赋**。
核心特性: 核心特性:
- 装备在**玩家获得时**(开箱子 / 抽卡)随机生成词条并固定(暗黑式:从词条池随机抽 N 条、数值在区间内随机)。
- **词条数量与数值高低决定装备品质**。
- 装备是**局外资产**,随云存档持久化;局内只有战斗,不产生、不修改装备,开局快照生效。
## 2. 数据结构HeroEquipSet.ts新增 - 武器通过**锻造**成长:每次锻造 = 等级 +1并从词条池加权随机获得 1 条词条(暗黑式:数值在区间内随机)。
- 抽中**已有词条则数值累加**,新词条则新增一条。
- 等级上限 **16 级****特定里程碑等级必得该英雄专属词条**(数值随机,合并到同 attr
- 0 级白板武器自带 1 条**初始专属词条**。
- **品质按等级阶梯跃升**,与词条数脱钩。
- **重铸**看广告将武器归零0 级 0 随机词条),已投入材料全部返还,初始专属词条重新 roll。
- 武器是**局外资产**,随云存档持久化;局内只有战斗,不产生、不修改武器,开局快照生效。
位置:`assets/script/game/common/config/HeroEquipSet.ts`,与 `EquipSet.ts`(将改名 `TalentSet.ts`)平级。 ## 2. 数据结构HeroWeaponSet.ts新增
位置:`assets/script/game/common/config/HeroWeaponSet.ts`,与 `EquipSet.ts`(将改名 `TalentSet.ts`)平级。
```typescript ```typescript
import { Attrs } from "./HeroAttrs"; import { Attrs } from "./HeroAttrs";
/** 装备品质(由词条数推导1=普通 2=稀有 3=史诗 4=传说 */ /** 武器品质(按等级阶梯推导,不存储 */
export enum EquipQuality { export enum WeaponQuality {
Normal = 1, Fine = 1, // 优秀绿0~3 级
Rare = 2, Superior = 2, // 精良4~7 级
Epic = 3, Epic = 3, // 史诗8~11 级
Legend = 4, Legend = 4, // 传说12~15 级
Unique = 5, // 唯一16 级满级
} }
/** 词条池条目attr 指向 Attrs 枚举roll 时 value 在 [min, max] 内随机 */ /** 词条池条目attr 指向 Attrs 枚举roll 时 value 在 [min, max] 内随机 */
@@ -38,125 +44,129 @@ export interface AffixPoolEntry {
weight: number; // 抽取权重 weight: number; // 抽取权重
} }
/** 装备底座:只定义随机规则,不含固定词条(段位 9001-9099 */ /** 专属词条里程碑:到达 level 必得value 在 [min, max] 内随机后合并到同 attr */
export interface HeroEquipBase { export interface ExclusiveAffix {
base_id: number; level: number; // 0=初始自带1~16=锻造到达解锁
name: string; attr: Attrs;
icon: string; min: number;
affix_count: [number, number]; // 词条数区间,如 [1,2] max: number;
affix_pool: AffixPoolEntry[];
} }
/** 装备实例roll 生成后固定;不冗余存 name/icon查询走底座表 */ /** 武器底座key=英雄模板 uuid段位 9001-9099只定义规则不含实例 */
export interface HeroEquipInstance { export interface HeroWeaponBase {
inst_id: number; // 实例唯一 idequip_next_id 自增) hero_uuid: number;
base_id: number;
affixes: { attr: Attrs; value: number }[]; // quality = affixes.length不单独存储
}
/** 装备箱子配置(段位 8901-89992 档) */
export interface EquipBoxConfig {
uuid: number;
name: string; name: string;
icon: string; icon: string;
pool: { base_id: number; weight: number }[]; // 底座加权池 affix_pool: AffixPoolEntry[]; // 随机词条池attr 白名单见第 4.4 节)
exclusive: ExclusiveAffix[]; // 专属词条里程碑,按 level 升序,必含 1 条 level=0
forge_cost: { item_id: number; count: number }[]; // 单次锻造材料(各级同价)
} }
/** 武器状态:挂在英雄存档上,局外持久化;同 attr 词条已合并 */
export interface HeroWeaponState {
level: number; // = 已锻造次数0=未锻造白板,上限 16
affixes: { attr: Attrs; value: number }[]; // 随机词条+专属词条,同 attr 已累加合并
invested: { item_id: number; count: number }[]; // 累计已投入材料(重铸全量返还用)
}
```
### 品质阶梯
| 等级 | 品质 | 颜色 |
|------|------|------|
| 0~3 | 优秀Fine | 绿 |
| 4~7 | 精良Superior | 蓝 |
| 8~11 | 史诗Epic | 紫 |
| 12~15 | 传说Legend | 橙 |
| 16 | 唯一Unique | 红 |
```typescript
/** 按武器等级推导品质(不存储,实时推导) */
export function getWeaponQuality(level: number): WeaponQuality
``` ```
### 词条口径(与现有百分比口径一致) ### 词条口径(与现有百分比口径一致)
- **数值型**(仅 `ap` / `hp_max` / `defense`Flat 数值roll 取**整数**。 - **数值型**(仅 `ap` / `hp_max` / `defense`Flat 数值roll 取**整数**。
- **概率型**其余全部百分点roll 取 **1 位小数** - **概率型**其余全部百分点roll 取 **1 位小数**
- 词条池仅允许出现第 4.4 节白名单中的 attr配置时校验。 - 词条池与专属词条仅允许出现第 4.4 节白名单中的 attr配置加载时校验。
### 品质规则 ## 3. 锻造与重铸
- `quality = 词条数`1~4 对应普通~传说),初版从简;数值高低不参与品质,后续可扩展档位评分。 ### 3.1 核心纯函数HeroWeaponSet.ts
## 3. Roll 生成与获取途径
### 3.1 核心纯函数HeroEquipSet.ts
```typescript ```typescript
/** /**
* 获得装备时调用:按底座规则 roll 出一件词条固定的装备实例 * 锻造一次:等级+1 → 从 affix_pool 加权抽 1 条词条合并(已有 attr 数值累加,新 attr 新增)
* @param base_id 装备底座 id * → 若新等级命中 exclusive 里程碑roll 专属词条数值并合并到同 attr
* @param instId 实例 id由 SingletonModuleComp 分配) * @param base 武器底座
* @param state 当前武器状态level < 16
* @returns 新状态base 非法或已满级返回 null 并 mLogger.warn
*/ */
export function rollHeroEquip(base_id: number, instId: number): HeroEquipInstance | null export function forgeWeapon(base: HeroWeaponBase, state: HeroWeaponState): HeroWeaponState | null
```
Roll 流程:
1. `affix_count: [min, max]` 内均匀随机词条数 N。
2.`affix_pool` 按 weight 加权**不放回**抽 N 条不同 attr同装备不重复属性
3. 每条词条在 `[min, max]` 内随机(数值型取整,概率型 1 位小数)。
4. 品质 = N。
```typescript
/** /**
* 按底座池加权抽取并 roll 出 count 件装备(箱子/抽卡统一入口 * 初始化武器0 级白板 + roll level=0 的初始专属词条(获得英雄/懒初始化时调用
* @param pool 底座加权池
* @param count 抽取件数
* @param allocInstId 实例 id 分配器(每次调用返回新 id
* @param guaranteeMinQuality N 连保底品质0=不保底)
*/ */
export function rollEquipPool( export function initWeapon(base: HeroWeaponBase): HeroWeaponState
pool: { base_id: number; weight: number }[],
count: number, /**
allocInstId: () => number, * 重铸等级与随机词条清零invested 全量返还初始专属词条level=0重新 roll
guaranteeMinQuality: EquipQuality = 0, * @returns 新状态 + 应返还材料列表(= 原 invested 全量)
): HeroEquipInstance[] */
export function reforgeWeapon(base: HeroWeaponBase, state: HeroWeaponState): {
state: HeroWeaponState;
refund: { item_id: number; count: number }[];
}
``` ```
保底逻辑:当 `count >= GACHA_COUNT` 且全部 < `guaranteeMinQuality` 时,最后一件重 roll 至 ≥ 保底品质。 锻造流程:
1. `level+1`
2.`affix_pool` 按 weight 加权抽 1 条 attr`[min, max]` 内随机(数值型取整,概率型 1 位小数)。
3. 该 attr 已存在于 `affixes` → 数值累加;否则新增一条。
4.`exclusive` 中存在 `level == 新等级` 的条目 → 每条在 `[min, max]` 内随机并累加到同 attr不存在则新增
5. `invested` 累加本次 `forge_cost`
### 3.2 可调参数GameSet.ts ### 3.2 可调参数GameSet.ts
```typescript ```typescript
GACHA_COUNT = 10, // N 连连抽数(后期可调 20/30 MAX_WEAPON_LEVEL = 16, // 武器锻造等级上限
GACHA_MAX_DRAW = 10, // 单次最多一起抽的张数
GACHA_GUARANTEE_QUALITY = 2, // N 连保底品质(稀有)
``` ```
### 3.3 双获取通道 ### 3.3 材料与货币
| 途径 | 流程 | 产出 | - 锻造材料为通用道具(如"精铁"),走现有道具库存;`forge_cost` 各级同价(递增为后续扩展,见 YAGNI
|------|------|------| - 重铸不消耗材料,改为**看激励视频广告**:广告播放完成回调成功后才执行 `reforgeWeapon``invested` 中材料全量返还道具库存。
| **装备箱子**2 档:普通箱/高级箱) | 商店购买/奖励 → `equip_boxes` 库存 → 背包界面点击开箱 | 固定 1 件,走 `rollEquipPool(box.pool, 1)` |
| **抽卡**(消耗装备抽卡券) | 抽卡界面选 1 抽 / N 连(默认 10≤ GACHA_MAX_DRAW→ 消耗 `equip_tickets` | N 件,走 `rollEquipPool(gachaPool, N, 保底)` |
- 高级箱与普通箱的差异仅为 `pool` 中底座权重(高词条数底座权重更高)。
- 单局结算奖励可发放:箱子(入 `equip_boxes`)和/或装备抽卡券(入 `equip_tickets`)。
## 4. 局内集成HeroAttrsComp ## 4. 局内集成HeroAttrsComp
**原则:局内快照,只读生效。** 开战创建英雄实体时快照装备词条,局内不参与装备增删改。 **原则:局内快照,只读生效。** 开战创建英雄实体时快照武器词条,局内不参与武器增删改。
### 4.1 快照字段 ### 4.1 快照字段
```typescript ```typescript
// HeroAttrsComp 新增 // HeroAttrsComp 新增
/** 装备词条加成缓存(局外穿戴装备在开局时快照;数值型=flat概率型=百分点) */ /** 武器词条加成缓存(局外武器在开局时快照;数值型=flat概率型=百分点) */
equip_mods: Partial<Record<Attrs, number>> = {}; equip_mods: Partial<Record<Attrs, number>> = {};
``` ```
- 不存 `HeroEquipInstance`,只存"每条属性加了多少"的聚合结果(同 attr 多词条快照时已累加)。 - 不存 `HeroWeaponState`,只存"每条属性加了多少"的聚合结果(`affixes` 已同 attr 合并,直接遍历即可)。
- `reset()` 中清空。 - `reset()` 中清空。
- 局内 UI 若要显示装备名,从 `hero_equips` 反查,不影响战斗数据。 - 局内 UI 若要显示武器名,从 `hero_weapons` 反查,不影响战斗数据。
### 4.2 快照时机 ### 4.2 快照时机
英雄实体初始化(读 `HeroInfo` 填充属性的同一入口处)追加: 英雄实体初始化(读 `HeroInfo` 填充属性的同一入口处)追加:
```typescript ```typescript
const instId = smc.data.hero_equips[hero_uuid]; const weapon = smc.getHeroWeapon(hero_uuid);
const inst = instId ? smc.data.equips[instId] : null; attrsComp.equip_mods = aggregateWeaponAffixes(weapon); // HeroWeaponSet.ts 纯函数UI 属性预览可复用
attrsComp.equip_mods = aggregateAffixes(inst); // HeroEquipSet.ts 纯函数UI 属性预览可复用
``` ```
### 4.3 结算接入(最小侵入,每个 getFinal 加一项) ### 4.3 结算接入(最小侵入,每个 getFinal 加一项)
**数值型**装备 flat **先进基础值,再吃驻场光环百分比**(乘算放大,装备更值钱): **数值型**武器 flat **先进基础值,再吃驻场光环百分比**(乘算放大,武器更值钱):
```typescript ```typescript
public getFinalAp(): number { public getFinalAp(): number {
@@ -167,9 +177,9 @@ public getFinalAp(): number {
} }
``` ```
顺序:装备 flat → 驻场光环 % → 计时 buff。同理 `getFinalHpMax``getFinalDefense` 顺序:武器 flat → 驻场光环 % → 计时 buff。同理 `getFinalHpMax``getFinalDefense`
**概率型**:百分点直接累加(`base + equip + timed` **概率型**:百分点直接累加(`base + weapon + timed`
```typescript ```typescript
public getFinalCritical(): number { public getFinalCritical(): number {
@@ -197,66 +207,44 @@ public getFinalCritical(): number {
```typescript ```typescript
data: any = { data: any = {
// ...现有字段 // ...现有字段
/** 玩家装备库key=inst_id局外资产随云存档持久化 */ /** 英雄武器状态key=英雄模板 uuid局外资产随云存档持久化,懒初始化 */
equips: {} as Record<number, HeroEquipInstance>, hero_weapons: {} as Record<number, HeroWeaponState>,
/** 实例 id 自增计数器(云端同步,防 id 冲突) */
equip_next_id: 1,
/** 英雄穿戴表key=英雄模板 uuidvalue=inst_id0=未穿戴) */
hero_equips: {} as Record<number, number>,
/** 装备箱子库存key=箱子 uuidvalue=数量 */
equip_boxes: {} as Record<number, number>,
/** 装备抽卡券数量 */
equip_tickets: 0,
} }
``` ```
`GameDate` 接口同步补充上述可选字段。 `GameDate` 接口同步补充上述可选字段。
**数据量评估**:每件装备实例 JSON 约 60~80 字节(只存 `inst_id/base_id/affixes`,不冗余存 name/icon500 件40KB远低于微信云存储 1MB 单记录上限。`quality` 不存储(= `affixes.length`)。 **数据量评估**:每件武器 JSON 约 40~60 字节(只存 `level/affixes/invested`50 英雄3KB远低于微信云存储 1MB 单记录上限。品质不存储(= `getWeaponQuality(level)` 实时推导)。
### 数据操作方法(封装在 SingletonModuleCompUI 不直改数据) ### 数据操作方法(封装在 SingletonModuleCompUI 不直改数据)
```typescript ```typescript
addEquipInstances(insts: HeroEquipInstance[]): void // 入库 + markDataDirty + Equip_Update getHeroWeapon(heroUuid: number): HeroWeaponState // 懒初始化:无记录时按底座 initWeapon 生成 0 级白板
wearEquip(heroUuid: number, instId: number): boolean // 穿戴/替换,旧装自动回库 forgeHeroWeapon(heroUuid: number): boolean // 校验满级/材料 → 扣材料 → forgeWeapon → 累加 invested → markDataDirty + HeroWeapon_Update
unwearEquip(heroUuid: number): void reforgeHeroWeapon(heroUuid: number): boolean // 广告成功回调内调用 → 返还 invested 全量 → reforgeWeapon → markDataDirty + HeroWeapon_Update
consumeEquipBox(boxUuid: number): HeroEquipInstance | null // 开箱扣库存→roll→入库→返回
drawEquips(count: number): HeroEquipInstance[] | null // 抽卡扣券→roll(保底)→入库
addEquipTickets(n: number): void
addEquipBox(boxUuid: number, n: number): void
``` ```
每个方法内部 `gameDataSync.markDataDirty()` + `oops.message.dispatchEvent` 对应事件 每个方法内部 `gameDataSync.markDataDirty()` + `oops.message.dispatchEvent(GameEvent.HeroWeapon_Update)`
### 新增事件GameEvent.ts ### 新增事件GameEvent.ts
```typescript ```typescript
static Equip_Update = "Equip_Update"; // 装备库/穿戴表变更(背包/英雄界面刷新) static HeroWeapon_Update = "HeroWeapon_Update"; // 武器锻造/重铸变更(英雄界面刷新)
static EquipBox_Update = "EquipBox_Update"; // 箱子数量变更
static EquipTicket_Update = "EquipTicket_Update"; // 抽卡券数量变更
``` ```
监听方必须在 `onDestroy`/`onDisable` 注销oops 规范)。 监听方必须在 `onDestroy`/`onDisable` 注销oops 规范)。
## 6. UI 与交互 ## 6. UI 与交互
### 6.1 英雄界面(唯一穿戴入口) ### 6.1 英雄界面 · 武器页签(唯一操作入口)
- 英雄详情面板新增**装备槽**(属性区附近):空槽显示 `+`,已穿戴显示装备图标+品质边框 - 英雄详情面板新增**武器页签**:武器图标 + 品质边框(按等级变色)+ 等级x/16+ 当前词条列表attr 名 + 数值)
- **穿戴/换装**:点击装备槽 → 弹出装备选择层,仅列**未被任何英雄穿戴**的装备(按品质排序)→ 点击即穿戴/替换(旧装回库) - 【锻造】按钮显示本次材料消耗材料不足或满级16 级)置灰;点击 → 扣材料锻造 → 弹出结果浮层,高亮本次新增/累加的词条变化与品质跃升提示
- **卸下**:已穿戴时选择层提供【卸下】按钮 - 【重铸】按钮:弹确认框(说明"观看广告重置武器,全部锻造材料返还")→ 调激励视频广告 → 播放完成回调成功后执行重铸并展示返还材料
- 已被其他英雄穿戴的装备在选择层中**不显示**。
### 6.2 装备背包界面(资产总览 + 开箱) ### 6.2 不做独立武器背包
- 入口:主界面"装备"按钮(复用 `tip_done.equip` 引导标记) 武器与英雄强绑定,无装备网格列表、无穿戴选择层;武器信息只在英雄界面展示
- 布局:装备网格列表(按品质:传说→史诗→稀有→普通;已穿戴的标记穿戴英雄头像角标)+ 点击看词条详情;顶部**箱子区**(普通箱/高级箱图标+数量),点击开箱 → 展示战利品 → 入库。
### 6.3 抽卡界面
- 入口:商店界面新增"装备抽卡"页签。
- 【抽 1 次】= 1 张券;【抽 N 次】= N 张券N 显示当前 `GACHA_COUNT`);券不足按钮置灰。
- 结果浮层:网格展示 N 件装备(图标+品质色),点击看词条;保底装备高亮"保底"标记。
## 7. 现有光环卡改名"战队天赋" ## 7. 现有光环卡改名"战队天赋"
@@ -266,16 +254,17 @@ static EquipTicket_Update = "EquipTicket_Update"; // 抽卡券数量变更
## 8. 错误处理与边界 ## 8. 错误处理与边界
- `rollHeroEquip` 对未知 `base_id` 返回 `null``mLogger.warn``rollEquipPool` 空池返回空数组 - `forgeWeapon` 对未知 `hero_uuid` 底座或已满级16 级)返回 `null``mLogger.warn``forgeHeroWeapon` 材料不足返回 false不扣材料
- 穿戴时 `instId` 存在(云端数据异常)→ `wearEquip` 返回 false不崩溃 - 存档中 `hero_weapons[hero_uuid]` 存在但底座表无对应配置(云端数据异常)→ 快照时按 0 级白板处理(`equip_mods = {}`
- 英雄被穿戴的装备在库中不存在 → 快照时按无装备处理(`equip_mods = {}` - 词条池 / 专属词条 attr 不在白名单 → 配置加载时 `mLogger.warn` 并跳过该词条
- 词条池 attr 不在白名单 → 配置加载时 `mLogger.warn` 并跳过该词条 - 旧存档无 `hero_weapons` 字段 → `Object.assign` 合并后由 `getHeroWeapon` 懒初始化兜底(参照 `getHeroGrowth` 的懒初始化模式)
- 旧存档无 `equips` 等新字段 → `Object.assign` 合并后由访问方兜底初始化(参照 `getHeroGrowth` 的懒初始化模式) - 重铸广告播放失败/中途关闭 → 不执行重铸,数据不变
- `invested``level × forge_cost` 不一致(异常数据)→ 重铸以 `invested` 记录为准。
## 9. 不做的事YAGNI ## 9. 不做的事YAGNI
- 跨局账号级装备强化/分解/合成系统 - 武器卸下/更换/交易;多武器槽位
- 装备部位(武器/盔甲等多槽位)——当前每英雄仅 1 槽 - 词条锁定(洗单词条保留其他词条)
- 数值档位评分影响品质(初版品质=词条数)。 - 锻造材料随等级递增(初版各级同价)。
- 局内掉落/换装 - 词条重 roll洗练——重铸全洗已覆盖该需求
- 词条重 roll洗练 - 局内掉落/换武器