diff --git a/docs/intermediate/data-state-overview.md b/docs/intermediate/data-state-overview.md new file mode 100644 index 0000000..cff0874 --- /dev/null +++ b/docs/intermediate/data-state-overview.md @@ -0,0 +1,64 @@ +# 数据端总览 + +数据端的一切都从主对象 `state` 开始。本篇列出作者最常需要的对象与接口,并说明如何从 `state` 拿到它们,作为中级教程其余篇章的查表入口。 + +## state:一切入口 + +`state` 是数据端的主对象(类型为 `ICoreState`),地图、勇士、怪物、战斗、Flag 等全部挂在它下面。在数据端代码里,它通常由框架传入——例如道具效果拿到 `(item, state)`、插件拿到 `state`;你也可以从任一数据端对象向外取得它。 + +```ts +// 从 state 出发访问勇士、地图与 Flag +const hp = state.hero.attribute.getFinalAttribute('hp'); +const floorId = state.hero.location.floorId; +const cleared = state.flags.getFieldValue('cleared'); +``` + +## 常用对象与获取方式 + +下表把最常用的对象与接口列在一起: + +| 对象 | 获取方式 | 用途 | 分层 | +| --- | --- | --- | --- | +| `hero` | `state.hero` | 勇士状态(属性、道具、装备、位置) | L1 | +| `maps` | `state.maps` | 楼层集合与楼层激活 | L1 | +| `flags` | `state.flags` | 全局可存档变量 | L1 | +| `enemyManager` | `state.enemyManager` | 怪物创建与查询 | L1 | +| `enemyContext` | `state.enemyContext` | 怪物属性构建与光环 | L2 | +| `combat` | `state.combat` | 战斗流程 | L2 | +| `eventSystem` | `state.eventSystem` | 地图事件执行 | L2 | +| `pathfinding` | `state.pathfinding` | 勇士寻路 | L2 | +| `tileStore` | `state.tileStore` | 图块定义存储 | L0 | +| `itemStore` | `state.itemStore` | 道具定义存储 | L0 | +| `mapStore` | `state.mapStore` | 地图定义存储 | L0 | +| `eventStore` | `state.eventStore` | 游戏事件存储 | L0 | +| `faceManager` | `state.faceManager` | 朝向管理 | L0 | +| `roleFace` | `state.roleFace` | 朝向绑定 | L0 | +| `replaySystem` | `state.replaySystem` | 录像系统 | L0 | +| `loader` | `state.loader` | 数据端加载对象 | L3 | +| `loadManager` | `state.loadManager` | 加载管理器 | L3 | +| `getDataPlugin(id)` | `state.getDataPlugin('xxx')` | 取得已加装的插件 | L3 | +| `emitTip(...)` | `state.emitTip('提示')` | 通知渲染端提示 | L0 | + +```ts +const plugin = state.getDataPlugin('main'); +state.emitTip('获得黄钥匙'); +``` + +## 分层标注(L0–L3) + +上表的「分层」列标注了对象所属的模块层级:L0 公共层、L1 数据层、L2 系统层、L3 顶层。分层的意义在于区分「哪些内容会进存档」「哪些内容负责产生动作」,但**不影响你如何使用它们**——所有对象都能从 `state` 直接访问,调用方式完全一致。 + +```ts +// 无论哪个分层,访问方式都相同 +state.flags.setFieldValue('door-open', true); // L1 +const map = state.maps.getActiveMap('floor-1'); // L1 +state.emitTip('获得黄钥匙'); // L0 +``` + +## 从这里继续 + +- 勇士的数值与加成:见[勇士属性](./hero-attribute.md); +- 勇士的道具与装备:见[勇士道具](./hero-items.md); +- 地图的层次结构:见[地图结构](./map-structure.md); +- 全局可存档变量:见[Flag 变量](./flags.md); +- 在关键动作上挂接处理:见[钩子](./hooks.md)。 diff --git a/docs/intermediate/hero-attribute.md b/docs/intermediate/hero-attribute.md new file mode 100644 index 0000000..e031faa --- /dev/null +++ b/docs/intermediate/hero-attribute.md @@ -0,0 +1,95 @@ +# 勇士属性 + +勇士属性决定战斗数值的最终结果。本篇讲解如何读取基础与最终属性、如何修改基础属性,以及如何用修饰器施加临时或永久的加成。访问勇士属性的入口见[数据端总览](./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 +import { ValueModifier, PercentageModifier } from '@user/data-base'; + +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); +``` + +`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'); +``` + +## 示例 + +```ts +// 攻击 +15 永久:直接改基础属性 +state.hero.getModifiableAttribute().add('atk', 15); + +// 防御 -20% 临时:挂一个百分比修饰器,战斗结束后再删除 +const weaken = new PercentageModifier(-0.2); +state.hero.getModifiableAttribute().addModifier('def', weaken); +``` + +想在属性变化时收到通知,可以用属性钩子族的 `onUpdateAttribute`,见[钩子](./hooks.md)。 diff --git a/docs/intermediate/hero-items.md b/docs/intermediate/hero-items.md new file mode 100644 index 0000000..77809c5 --- /dev/null +++ b/docs/intermediate/hero-items.md @@ -0,0 +1,65 @@ +# 勇士道具 + +勇士道具(`state.hero.items`)负责管理勇士拥有的除装备外的所有道具,并提供增加、查询、使用等接口。装备虽然也是道具,但归独立的装备系统,见[勇士装备](./hero-equipment.md)。 + +## 四种类别 + +道具的类别由 `ItemCategory` 定义,决定它被拾取或使用时的行为: + +| 类别 | 含义 | +| --- | --- | +| `Constant` | 永久道具,使用后不消耗 | +| `Consumable` | 一次性道具,使用后消耗一个 | +| `Pick` | 即捡即用道具,拾取时立刻生效 | +| `Equipment` | 装备类道具,走装备系统 | + +`Unknown` 表示尚未归类。`Pick` 道具不会进入背包:`addItem` 到 `Pick` 道具时会直接触发它的效果。 + +## 常用接口 + +- `addItem(item, count?)`:增加道具数量,`count` 可以填负数以扣除道具,默认为 1;对 `Pick` 道具会立刻执行其效果。 +- `getItemState(item)`:获取指定道具的状态,不存在返回 `null`。 +- `iterateItems()`:迭代勇士拥有的所有道具。 +- `useItem(item)`:使用道具,**仅对 `Constant` 与 `Consumable` 生效**,`Consumable` 使用后数量减一。 +- `itemCount(item)`:获取指定道具的持有数量。 + +`item` 参数可以传道具的图块数字或字符串 id。 + +```ts +// 发一把黄钥匙(+1) +state.hero.items.addItem('yellow-key'); + +// 扣减道具(数量传负数) +state.hero.items.addItem('yellow-key', -2); + +// 使用道具 +state.hero.items.useItem('yellow-key'); + +// 查询数量 +const count = state.hero.items.itemCount('yellow-key'); +``` + +## 迭代背包 + +```ts +for (const [num, item] of state.hero.items.iterateItems()) { + // num 为道具图块数字,item 为道具状态 + state.emitTip(`${item.id}: ${item.count}`); +} +``` + +## 道具效果属内容层 + +道具的**使用效果本身**写在内容层的 `src/content/item.ts`,通过 `IItemEffect` 描述:`useEffect` 在 `Pick` 拾取或 `Constant`/`Consumable` 使用时触发,`canUse` 决定能否使用,`useEvent` 关联使用事件。道具效果可以调用 `state`(数据端主对象)与道具数据,因而能修改勇士属性、设置 Flag、发送提示等。 + +```ts +// src/content/item.ts(内容层) +export const item200: ItemEffect = { + useEffect(item, state) { + state.hero.getModifiableAttribute().add('atk', 5); // [!code ++] + state.emitTip('攻击力提升!'); // [!code ++] + } +}; +``` + +想了解从 `state` 能取到哪些对象,见[数据端总览](./data-state-overview.md)。