mirror of
https://github.com/motajs/template.git
synced 2026-10-19 21:30:30 +08:00
docs(10-08): 重写 combat-overview、enemy-specials(引导式)
- combat-overview 用平实语言解释计算后怪物、为何重建、为何分四步 - enemy-specials 改为引导式开篇,保留两类定义、实现位置表与编辑器约定
This commit is contained in:
parent
a24457cfef
commit
b1b7c8e0e7
@ -1,29 +1,57 @@
|
||||
# 战斗总览
|
||||
|
||||
战斗在数据端完成:怪物先被计算成「计算后怪物」,再由伤害计算器根据怪物与勇士的属性算出伤害与回合数,最后经过战前脚本、战后脚本与状态钩子结算。本篇串起这条主线,各环节的细节见对应文档。
|
||||
在游戏里点一下怪物,勇士走过去、挥几刀,屏幕上出现伤害和回合数。对玩家来说这只是一瞬间,但在数据端,它背后有一条固定的流水线:先准备一份属性准确的怪物,再把双方的属性换算成伤害,最后跑一遍战前与战后的规则。
|
||||
|
||||
本篇沿着这条流水线走一遍,让你知道每一步在做什么、该去哪篇文档看细节。
|
||||
|
||||
## 先准备一份「计算后怪物」
|
||||
|
||||
怪物数据里保存的是一份**模板**:基础属性(`hp`、`atk`、`def` 等),加上若干特殊属性(先攻、连击、光环……)。它描述的是「这只怪物本来是什么样」。
|
||||
|
||||
可战斗不能直接拿模板来算。光环会改周围怪物的属性,查询会根据周围情况调整自己,最终效果还会在收尾时再改一次。如果直接拿模板去算伤害,这些影响就全丢了。
|
||||
|
||||
所以进入战斗前,怪物上下文会把每只怪物**重建**成一份属性已经算准的副本,这就是「计算后怪物」。重建的入口是:
|
||||
|
||||
```ts
|
||||
state.enemyContext.buildup();
|
||||
```
|
||||
|
||||
`buildup()` 会按固定顺序处理每只怪物,把所有特殊属性的作用真正写进属性里。之后伤害计算器读到的就是最终结果,而不是模板上的原始数字。
|
||||
|
||||
### 为什么要重建
|
||||
|
||||
一句话:**为了拿到准确的怪物属性**。
|
||||
|
||||
模板上的 `atk` 只是怪物「自己写的」攻击力。实际战斗中,它可能被旁边的光环加成,也可能因为查询看到周围有多少同类而提高,还可能被最终效果改写。只有把这些作用全部算进去,伤害计算才可能准确。
|
||||
|
||||
重建还有一个好处:每次重建都从模板的原始值出发,所以反复 `buildup()` 不会让加成越叠越多。光环、查询与最终效果改的始终是那份计算后副本,模板本身保持不变。
|
||||
|
||||
### 为什么分四步
|
||||
|
||||
重建不是一次算完的,而是分成四个阶段。原因很简单:**不同阶段要处理的事情不一样**。有的阶段要先决定「怪物拥有哪些特殊属性」,有的阶段才能去改基础属性,还有的阶段需要看周围的怪物。
|
||||
|
||||
1. **光环与特殊查询**:先把特殊属性转换成光环视图,按优先级执行会修改特殊属性的效果,确定每只怪物最终拥有哪些特殊属性。
|
||||
2. **普通光环**:执行修改基础属性的光环效果。
|
||||
3. **常规查询**:允许读取上下文(周围怪物、位置等),并修改自身基础属性。
|
||||
4. **最终效果**:不再查询上下文,只对自身基础属性做收尾调整。
|
||||
|
||||
可以把它想成装配一件装备:先确认要装哪些零件(第 1 步),再装主体(第 2 步),接着根据使用环境微调(第 3 步),最后做一次收尾检查(第 4 步)。顺序不能随便换,因为后面的步骤要建立在前面步骤的结果上。
|
||||
|
||||
前三步都可能受优先级影响;光环与查询共用同一套优先级,见[怪物光环](./enemy-aura.md)与[怪物查询](./enemy-query.md)。最终效果见[最终属性效果](./enemy-final.md)。
|
||||
|
||||
## 伤害大致怎么算
|
||||
|
||||
一次战斗的伤害可以写成 `f(enemy, hero)`:`enemy` 是计算后的怪物对象,`hero` 是勇士属性对象。计算器读取双方的 `hp`、`atk`、`def`、`mdef` 等属性,返回:
|
||||
属性准备完之后,伤害计算器把它们换算成两个数字。一次战斗的伤害可以写成 `f(enemy, hero)`:`enemy` 是计算后的怪物对象,`hero` 是勇士属性对象。计算器读取双方的 `hp`、`atk`、`def`、`mdef` 等属性,返回:
|
||||
|
||||
- `damage`:本次战斗勇士会损失的生命值;
|
||||
- `turn`:战斗持续多少回合。
|
||||
|
||||
数据端不逐帧模拟战斗,只根据公式给出这两个值。公式的实现与替换见[伤害计算](./enemy-calculator.md)。
|
||||
|
||||
## 计算后怪物:buildup 的四个阶段
|
||||
|
||||
怪物在数据里保存的是基础属性与特殊属性。进入战斗前,怪物上下文会把每个怪物重建为「计算后怪物」(`enemyContext.buildup()`),分四步:
|
||||
|
||||
1. **光环与特殊查询**:把特殊属性转换成光环视图,按优先级执行会修改特殊属性的效果,确定每个怪物最终拥有哪些特殊属性;
|
||||
2. **普通光环**:执行修改基础属性的光环效果;
|
||||
3. **常规查询**:允许读取上下文(周围怪物、位置等),并修改自身基础属性;
|
||||
4. **最终效果**:不再查询上下文,只对自身基础属性做收尾调整。
|
||||
|
||||
前三步都可能受优先级影响;光环与查询共用同一套优先级,见[怪物光环](./enemy-aura.md)与[怪物查询](./enemy-query.md)。最终效果见[最终属性效果](./enemy-final.md)。
|
||||
|
||||
## 战斗流程
|
||||
|
||||
把上面几步连起来,就是一次完整的战斗:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[怪物基础数据] --> B[光环 / 查询 / 最终]
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
# 特殊属性
|
||||
|
||||
特殊属性是挂在怪物身上的机制:一个数字 `code` 加一份可选参数 `value`。怪物的基础属性决定「有多少血、多少攻防」,特殊属性决定「它还会做什么」,例如先攻、连击、坚固、光环、中毒。访问怪物特殊属性的入口见[战斗总览](./combat-overview.md)。
|
||||
一只怪物的基础属性决定它「有多少血、多少攻防」。但魔塔里的怪物往往还会做别的事:先手攻击、一回合打两下、给周围的怪物加成……这些「还会做什么」就记在特殊属性里。
|
||||
|
||||
每个特殊属性就是一个数字 `code` 加一份可选参数 `value`。`code` 说明这是哪一种机制,`value` 保存它的参数;没有参数的属性,`value` 为空。先攻、连击、坚固、光环、中毒都是这样挂到怪物身上的。
|
||||
|
||||
访问怪物特殊属性的入口见[战斗总览](./combat-overview.md)。
|
||||
|
||||
## 在数据里的样子
|
||||
|
||||
@ -28,12 +32,14 @@ if (enemy.hasSpecial(3)) {
|
||||
|
||||
## 两类定义
|
||||
|
||||
写一条特殊属性时,先问自己一个问题:这个机制需要参数吗?答案不同,使用的工厂函数也不同。
|
||||
|
||||
内置特殊属性在 `src/data/enemy/special.ts` 的 `createSpecials(state)` 中定义,使用两个工厂函数:
|
||||
|
||||
- `defineNonePropertySpecial(code, config)`:无参数,`value` 为 `void`,适合先攻、魔攻、坚固、模仿这类纯机制;
|
||||
- `defineCommonSerializableSpecial<T>(code, defaultValue, config)`:有参数,参数会进入存档,适合连击、破甲、光环这类需要数值描述的效果。
|
||||
|
||||
`config` 的 `name`、`desc`、`color` 都可以写成字符串,也可以写成 `(special) => string` 的函数:
|
||||
`config` 的 `name`、`desc`、`color` 都可以写成字符串,也可以写成 `(special) => string` 的函数。需要根据参数变化显示不同文字时,就写成函数:
|
||||
|
||||
```ts
|
||||
// 无参:坚固
|
||||
@ -84,7 +90,7 @@ map.set(
|
||||
|
||||
## 自定义一个新特殊属性
|
||||
|
||||
以新增一个「每回合给勇士造成固定伤害」的属性为例(假设使用 code 50):
|
||||
当你需要引擎里没有的机制时,就新增一条特殊属性。以「每回合给勇士造成固定伤害」为例(假设使用 code 50):
|
||||
|
||||
1. **选一个未使用的 code**。先搜一遍 `special.ts` 中已有的 `map.set(...)`,确认没有冲突。
|
||||
2. **有参数时定义参数接口**。在文件顶部 `//#region 复合属性值类型` 内声明 `ISpecial50Value`,并在接口前写 `/** 50 - 属性名 */`。
|
||||
@ -108,7 +114,7 @@ map.set(
|
||||
|
||||
## 属性实现位置
|
||||
|
||||
`special.ts` 顶部的注释给出了内置属性的实现分工。新增属性时按这张表找落点:
|
||||
注册只是让引擎认识这条属性,真正让它生效的代码在别处。`special.ts` 顶部的注释给出了内置属性的实现分工。新增属性时按这张表找落点:
|
||||
|
||||
| 属性类型 | 实现位置 | 说明 |
|
||||
|----------|----------|------|
|
||||
|
||||
Loading…
Reference in New Issue
Block a user