将原英雄个人装备系统设计文档重命名为英雄专属武器系统设计文档,更新了背景目标、数据结构、锻造重铸规则、局内集成、存档结构、UI交互等内容,将装备相关逻辑替换为专属武器逻辑,并完成现有光环卡改名适配。
271 lines
12 KiB
Markdown
271 lines
12 KiB
Markdown
# 英雄专属武器系统设计
|
||
|
||
- 日期:2026-08-21
|
||
- 状态:已评审(brainstorming 阶段用户逐节确认)
|
||
- 涉及文件:`HeroWeaponSet.ts`(新增)、`heroSet.ts`、`HeroAttrs.ts`、`HeroAttrsComp.ts`、`SingletonModuleComp.ts`、`GameEvent.ts`、`GameSet.ts`、`EquipSet.ts`(改名 TalentSet)
|
||
|
||
## 1. 背景与目标
|
||
|
||
现有"装备"(`EquipSet.ts`,8701~8820 段)是**全体驻场光环卡**:购买后全队加属性,经 `EquipBoxComp → FieldSkillHelper` 全局聚合。
|
||
|
||
本设计新增**英雄专属武器**:每个英雄天生绑定一件专属武器,不可卸下、不可更换;词条只作用于该英雄本身;与现有光环卡**并存**,光环卡在表述上改名为**战队天赋**。
|
||
|
||
核心特性:
|
||
|
||
- 武器通过**锻造**成长:每次锻造 = 等级 +1,并从词条池加权随机获得 1 条词条(暗黑式:数值在区间内随机)。
|
||
- 抽中**已有词条则数值累加**,新词条则新增一条。
|
||
- 等级上限 **16 级**;**特定里程碑等级必得该英雄专属词条**(数值随机,合并到同 attr)。
|
||
- 0 级白板武器自带 1 条**初始专属词条**。
|
||
- **品质按等级阶梯跃升**,与词条数脱钩。
|
||
- **重铸**:看广告将武器归零(0 级 0 随机词条),已投入材料全部返还,初始专属词条重新 roll。
|
||
- 武器是**局外资产**,随云存档持久化;局内只有战斗,不产生、不修改武器,开局快照生效。
|
||
|
||
## 2. 数据结构(HeroWeaponSet.ts,新增)
|
||
|
||
位置:`assets/script/game/common/config/HeroWeaponSet.ts`,与 `EquipSet.ts`(将改名 `TalentSet.ts`)平级。
|
||
|
||
```typescript
|
||
import { Attrs } from "./HeroAttrs";
|
||
|
||
/** 武器品质(按等级阶梯推导,不存储) */
|
||
export enum WeaponQuality {
|
||
Fine = 1, // 优秀(绿)0~3 级
|
||
Superior = 2, // 精良(蓝)4~7 级
|
||
Epic = 3, // 史诗(紫)8~11 级
|
||
Legend = 4, // 传说(橙)12~15 级
|
||
Unique = 5, // 唯一(红)16 级满级
|
||
}
|
||
|
||
/** 词条池条目:attr 指向 Attrs 枚举,roll 时 value 在 [min, max] 内随机 */
|
||
export interface AffixPoolEntry {
|
||
attr: Attrs;
|
||
min: number; // 数值型=整数下限;概率型=百分点下限
|
||
max: number;
|
||
weight: number; // 抽取权重
|
||
}
|
||
|
||
/** 专属词条里程碑:到达 level 必得,value 在 [min, max] 内随机后合并到同 attr */
|
||
export interface ExclusiveAffix {
|
||
level: number; // 0=初始自带;1~16=锻造到达解锁
|
||
attr: Attrs;
|
||
min: number;
|
||
max: number;
|
||
}
|
||
|
||
/** 武器底座:key=英雄模板 uuid(段位 9001-9099),只定义规则不含实例 */
|
||
export interface HeroWeaponBase {
|
||
hero_uuid: number;
|
||
name: string;
|
||
icon: string;
|
||
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 取**整数**。
|
||
- **概率型**(其余全部):百分点,roll 取 **1 位小数**。
|
||
- 词条池与专属词条仅允许出现第 4.4 节白名单中的 attr,配置加载时校验。
|
||
|
||
## 3. 锻造与重铸
|
||
|
||
### 3.1 核心纯函数(HeroWeaponSet.ts)
|
||
|
||
```typescript
|
||
/**
|
||
* 锻造一次:等级+1 → 从 affix_pool 加权抽 1 条词条合并(已有 attr 数值累加,新 attr 新增)
|
||
* → 若新等级命中 exclusive 里程碑,roll 专属词条数值并合并到同 attr
|
||
* @param base 武器底座
|
||
* @param state 当前武器状态(level < 16)
|
||
* @returns 新状态;base 非法或已满级返回 null 并 mLogger.warn
|
||
*/
|
||
export function forgeWeapon(base: HeroWeaponBase, state: HeroWeaponState): HeroWeaponState | null
|
||
|
||
/**
|
||
* 初始化武器:0 级白板 + roll level=0 的初始专属词条(获得英雄/懒初始化时调用)
|
||
*/
|
||
export function initWeapon(base: HeroWeaponBase): HeroWeaponState
|
||
|
||
/**
|
||
* 重铸:等级与随机词条清零,invested 全量返还,初始专属词条(level=0)重新 roll
|
||
* @returns 新状态 + 应返还材料列表(= 原 invested 全量)
|
||
*/
|
||
export function reforgeWeapon(base: HeroWeaponBase, state: HeroWeaponState): {
|
||
state: HeroWeaponState;
|
||
refund: { item_id: number; count: number }[];
|
||
}
|
||
```
|
||
|
||
锻造流程:
|
||
|
||
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)
|
||
|
||
```typescript
|
||
MAX_WEAPON_LEVEL = 16, // 武器锻造等级上限
|
||
```
|
||
|
||
### 3.3 材料与货币
|
||
|
||
- 锻造材料为通用道具(如"精铁"),走现有道具库存;`forge_cost` 各级同价(递增为后续扩展,见 YAGNI)。
|
||
- 重铸不消耗材料,改为**看激励视频广告**:广告播放完成回调成功后才执行 `reforgeWeapon`;`invested` 中材料全量返还道具库存。
|
||
|
||
## 4. 局内集成(HeroAttrsComp)
|
||
|
||
**原则:局内快照,只读生效。** 开战创建英雄实体时快照武器词条,局内不参与武器增删改。
|
||
|
||
### 4.1 快照字段
|
||
|
||
```typescript
|
||
// HeroAttrsComp 新增
|
||
/** 武器词条加成缓存(局外武器在开局时快照;数值型=flat,概率型=百分点) */
|
||
equip_mods: Partial<Record<Attrs, number>> = {};
|
||
```
|
||
|
||
- 不存 `HeroWeaponState`,只存"每条属性加了多少"的聚合结果(`affixes` 已同 attr 合并,直接遍历即可)。
|
||
- `reset()` 中清空。
|
||
- 局内 UI 若要显示武器名,从 `hero_weapons` 反查,不影响战斗数据。
|
||
|
||
### 4.2 快照时机
|
||
|
||
英雄实体初始化(读 `HeroInfo` 填充属性的同一入口处)追加:
|
||
|
||
```typescript
|
||
const weapon = smc.getHeroWeapon(hero_uuid);
|
||
attrsComp.equip_mods = aggregateWeaponAffixes(weapon); // HeroWeaponSet.ts 纯函数,UI 属性预览可复用
|
||
```
|
||
|
||
### 4.3 结算接入(最小侵入,每个 getFinal 加一项)
|
||
|
||
**数值型**:武器 flat **先进基础值,再吃驻场光环百分比**(乘算放大,武器更值钱):
|
||
|
||
```typescript
|
||
public getFinalAp(): number {
|
||
const equipFlat = this.equip_mods[Attrs.ap] ?? 0;
|
||
const runtimeAp = this.getRuntimeAp(this.ap + equipFlat);
|
||
const mods = this.aggregateTimedMods(Attrs.ap);
|
||
return (runtimeAp + mods.flatSum) * (1 + mods.pctSum / 100);
|
||
}
|
||
```
|
||
|
||
顺序:武器 flat → 驻场光环 % → 计时 buff。同理 `getFinalHpMax`、`getFinalDefense`。
|
||
|
||
**概率型**:百分点直接累加(`base + weapon + timed`):
|
||
|
||
```typescript
|
||
public getFinalCritical(): number {
|
||
const base = this.getRuntimeCritical() + (this.equip_mods[Attrs.critical] ?? 0);
|
||
const mods = this.aggregateTimedMods(Attrs.critical);
|
||
return base + mods.flatSum + mods.pctSum;
|
||
}
|
||
```
|
||
|
||
### 4.4 接入清单(词条池 attr 白名单)
|
||
|
||
| getFinal 方法 | 词条类型 |
|
||
|---|---|
|
||
| getFinalAp / getFinalHpMax / getFinalDefense | 数值型 |
|
||
| getFinalCritical / getFinalCritDamage | 概率型 |
|
||
| getFinalFreezeChance / getFinalStunChance / getFinalParalyzeChance | 概率型 |
|
||
| getFinalPunctureChance / getFinalPunctureDmgBonus / getFinalWindFury | 概率型 |
|
||
| getFinalCriticalRes / getFinalFreezeRes / getFinalStunRes | 概率型 |
|
||
| getFinalKnockbackChance / getFinalKnockbackDistance / getFinalKnockbackRes | 概率型 |
|
||
| getFinalVulnerableRes / getFinalParalyzePower | 概率型 |
|
||
| getEffectiveSkillCd(speed 词条 → 攻速) | 概率型 |
|
||
|
||
## 5. 局外数据持久化(SingletonModuleComp.data)
|
||
|
||
```typescript
|
||
data: any = {
|
||
// ...现有字段
|
||
/** 英雄武器状态:key=英雄模板 uuid;局外资产随云存档持久化,懒初始化 */
|
||
hero_weapons: {} as Record<number, HeroWeaponState>,
|
||
}
|
||
```
|
||
|
||
`GameDate` 接口同步补充上述可选字段。
|
||
|
||
**数据量评估**:每件武器 JSON 约 40~60 字节(只存 `level/affixes/invested`);50 英雄 ≈ 3KB,远低于微信云存储 1MB 单记录上限。品质不存储(= `getWeaponQuality(level)` 实时推导)。
|
||
|
||
### 数据操作方法(封装在 SingletonModuleComp,UI 不直改数据)
|
||
|
||
```typescript
|
||
getHeroWeapon(heroUuid: number): HeroWeaponState // 懒初始化:无记录时按底座 initWeapon 生成 0 级白板
|
||
forgeHeroWeapon(heroUuid: number): boolean // 校验满级/材料 → 扣材料 → forgeWeapon → 累加 invested → markDataDirty + HeroWeapon_Update
|
||
reforgeHeroWeapon(heroUuid: number): boolean // 广告成功回调内调用 → 返还 invested 全量 → reforgeWeapon → markDataDirty + HeroWeapon_Update
|
||
```
|
||
|
||
每个方法内部 `gameDataSync.markDataDirty()` + `oops.message.dispatchEvent(GameEvent.HeroWeapon_Update)`。
|
||
|
||
### 新增事件(GameEvent.ts)
|
||
|
||
```typescript
|
||
static HeroWeapon_Update = "HeroWeapon_Update"; // 武器锻造/重铸变更(英雄界面刷新)
|
||
```
|
||
|
||
监听方必须在 `onDestroy`/`onDisable` 注销(oops 规范)。
|
||
|
||
## 6. UI 与交互
|
||
|
||
### 6.1 英雄界面 · 武器页签(唯一操作入口)
|
||
|
||
- 英雄详情面板新增**武器页签**:武器图标 + 品质边框(按等级变色)+ 等级(x/16)+ 当前词条列表(attr 名 + 数值)。
|
||
- 【锻造】按钮:显示本次材料消耗;材料不足或满级(16 级)置灰;点击 → 扣材料锻造 → 弹出结果浮层,高亮本次新增/累加的词条变化与品质跃升提示。
|
||
- 【重铸】按钮:弹确认框(说明"观看广告重置武器,全部锻造材料返还")→ 调激励视频广告 → 播放完成回调成功后执行重铸并展示返还材料。
|
||
|
||
### 6.2 不做独立武器背包
|
||
|
||
武器与英雄强绑定,无装备网格列表、无穿戴选择层;武器信息只在英雄界面展示。
|
||
|
||
## 7. 现有光环卡改名"战队天赋"
|
||
|
||
- `EquipSet.ts` → `TalentSet.ts`(文件改名);`EquipPoolList` → `TalentPoolList`;`drawEquipCards` → `drawTalentCards`;`findEquipByUuid` → `findTalentByUuid`。
|
||
- UI 文案"装备"→"战队天赋";`tip_done.equip` 字段名不变(避免存档兼容问题),语义指向天赋面板。
|
||
- 局内组件 `EquipBoxComp` / `MissEquipComp` / `EquipListComp` 等**逻辑不变**,仅引用路径与显示文案调整。
|
||
|
||
## 8. 错误处理与边界
|
||
|
||
- `forgeWeapon` 对未知 `hero_uuid` 底座或已满级(16 级)返回 `null` 并 `mLogger.warn`;`forgeHeroWeapon` 材料不足返回 false,不扣材料。
|
||
- 存档中 `hero_weapons[hero_uuid]` 存在但底座表无对应配置(云端数据异常)→ 快照时按 0 级白板处理(`equip_mods = {}`)。
|
||
- 词条池 / 专属词条 attr 不在白名单 → 配置加载时 `mLogger.warn` 并跳过该词条。
|
||
- 旧存档无 `hero_weapons` 字段 → `Object.assign` 合并后由 `getHeroWeapon` 懒初始化兜底(参照 `getHeroGrowth` 的懒初始化模式)。
|
||
- 重铸广告播放失败/中途关闭 → 不执行重铸,数据不变。
|
||
- `invested` 与 `level × forge_cost` 不一致(异常数据)→ 重铸以 `invested` 记录为准。
|
||
|
||
## 9. 不做的事(YAGNI)
|
||
|
||
- 武器卸下/更换/交易;多武器槽位。
|
||
- 词条锁定(洗单词条保留其他词条)。
|
||
- 锻造材料随等级递增(初版各级同价)。
|
||
- 词条重 roll(洗练)——重铸全洗已覆盖该需求。
|
||
- 局内掉落/换武器。
|