template/docs/intermediate/hero-attribute.md
unanmed cfa602f25c docs(10-11): 按 D-48 重做实操(概念+基础接口)
- 概念/基础接口 12 篇的实操部分改为六步照抄式(目标/改哪里/复制什么/改什么/怎么验证/注意)

- 片段一律去掉 import,统一附复制说明并指向 code-basics 的复制约定

- code-basics 删除 dev.md 的 import 置顶约定句,新增「照抄教程里的代码时,怎么处理 import」小节

- 仅改 docs/intermediate/*.md 与本篇审阅记录
2026-10-19 15:15:45 +08:00

116 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 勇士属性
勇士属性决定战斗数值的最终结果。本篇讲解如何读取基础与最终属性、如何修改基础属性,以及如何用修饰器施加临时或永久的加成。访问勇士属性的入口见[数据端总览](./data-state-overview.md)。
## 基础属性与最终属性
勇士属性(`state.hero.attribute`)区分**基础值**与**最终值**:
- `getBaseAttribute(name)`:基础属性,即未经任何 Buff 或装备加成的原始值;
- `getFinalAttribute(name)`:最终属性,即基础值经所有修饰器加成后的结果。
```ts
const base = state.hero.attribute.getBaseAttribute('atk');
const finalAtk = state.hero.attribute.getFinalAttribute('atk');
```
属性名与勇士属性接口 `IHeroAttr` 的字段一致,常用的有 `hp`、`hpmax`、`atk`、`def`、`mdef`、`money`、`exp` 等。
## 修改基础属性
要修改勇士的基础属性,先取到可修改对象 `getModifiableAttribute()`,再调用:
- `set(name, value)`:直接设置;
- `add(name, value)`:增减;
- `mul(name, value)`:乘以某个数;
- `div(name, value)`:除以某个数。
```ts
const attr = state.hero.getModifiableAttribute();
attr.add('atk', 10); // 攻击 +10
attr.set('hp', 1000); // 生命值设为 1000
attr.mul('money', 2); // 金币翻倍
```
只读访问(只想读取、不加修改)可用 `state.hero.attribute`;需要修改时统一走 `getModifiableAttribute()`。
## 修饰器:数值与百分比加成
直接改基础属性的效果是永久的,它会直接改变勇士数值;而**修饰器**把加成从基础值里独立出来,既可以用于永久加成,也可以用于临时加成——是否临时,取决于你什么时候把它移除。属性包内置了两种:
- `ValueModifier`(type `@system/value`):数值加减,最终值 = 原值 + 修饰值;
- `PercentageModifier`(type `@system/percentage`):按**基础值**计算百分比,最终值 = 原值 + 基础值 × 修饰值。
用 `addModifier(name, modifier, save?)` 挂上修饰器,用 `deleteModifier(name, modifier)` 移除:
```ts
const attr = state.hero.getModifiableAttribute();
// 攻击永久 +5:加一个数值修饰器
attr.addModifier('atk', new ValueModifier(5));
// 防御临时 -30%:按基础防御算的百分比修饰器
const defDown = new PercentageModifier(-0.3);
attr.addModifier('def', defDown);
// 需要移除时,传回同一个修饰器对象
attr.deleteModifier('def', defDown);
```
> 复制说明:片段已省略 `import`(`ValueModifier`、`PercentageModifier` 来自 `@user/data-base`);文件顶部通常已有,不要再粘,报缺少时再补(详见 [代码基础](./code-basics.md) 的「照抄教程里的代码时,怎么处理 import」)。
想要一段**临时**加成时,就先 `addModifier(...)` 挂上修饰器,在不再需要时用 `deleteModifier(...)` 把它移除,加成随即消失;反之,只要一直不移除,它就是永久加成。
`PercentageModifier` 的百分比以**基础值**为基数,而非当前最终值,因此多个百分比修饰器不会彼此叠乘。装备产生的加成走的也是同一套修饰器机制,见[勇士装备](./hero-equipment.md)。
## save 参数与读档一致性
`addModifier(name, modifier, save?)` 的 `save` 参数默认为 `true`,表示该修饰器会进入存档、读档后自动恢复;传 `false` 则表示**不进入存档**。
:::warning
当修饰器不进入存档时,读档后它不会自动恢复。此时应由负责分配该修饰器的**外部系统**在读档后重新分配,否则会出现「存档前有加成、读档后消失」的不一致。仅在加成完全由外部系统掌控时才使用 `save: false`。
:::
## 只读与试算
- `getAttribute()`:取只读属性对象,只能读、不能改;
- `getIsolatedAttribute()`:取一份**独立的**属性副本,修改它不会影响勇士本身,适合在不改动实际数值的前提下试算某个效果。
```ts
// 试算:把攻击 +999 后最终攻击会是多少
const trial = state.hero.getIsolatedAttribute();
trial.add('atk', 999);
const preview = trial.getFinalAttribute('atk');
```
## 示例:永久加攻与临时减防
**目标**:让勇士攻击永久 +15;再给一个「防御 -20%」的临时减益,战斗结束后撤掉。
**改哪里**:希望效果生效的地方——例如某个事件函数、钩子或道具效果里。
**复制什么**:
```ts
// 攻击永久 +15:直接改基础属性
state.hero.getModifiableAttribute().add('atk', 15);
// 防御临时 -20%:挂一个百分比修饰器
const weaken = new PercentageModifier(-0.2);
state.hero.getModifiableAttribute().addModifier('def', weaken);
```
> 复制说明:片段已省略 `import`(`PercentageModifier` 来自 `@user/data-base`);文件顶部通常已有,不要再粘,报缺少时再补(详见 [代码基础](./code-basics.md) 的「照抄教程里的代码时,怎么处理 import」)。
**改什么**:把 `'atk'`、`'def'` 换成你要改的属性名,把 `15`、`-0.2` 换成你的数值(`-0.2` 即 -20%)。
**怎么验证**:加攻后读 `state.hero.attribute.getFinalAttribute('atk')`,应比原来高 15;挂上减益后读 `getFinalAttribute('def')`,应比基础值低 20%。
**注意**:`add('atk', 15)` 改的是**基础属性**,是永久的;临时减益要自己留住 `weaken` 这个修饰器对象,在不再需要时用 `deleteModifier('def', weaken)` 撤销,否则它会一直生效。
想在属性变化时收到通知,可以用属性钩子族的 `onUpdateAttribute`,见[钩子](./hooks.md)。
> 修饰器的底层原理(修饰器如何参与属性求值、加减乘除类修饰器的组合规则等)会在**高级教程**中讲解(预留标记:`高级教程 · 修饰器原理`);待高级教程就位后,此处会替换为链接。
<!-- TODO(advanced): 修饰器原理 -->