HumanBreak/docs/guide/battle.md
2024-02-08 16:50:58 +08:00

170 lines
7.3 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.

# 战斗系统
新样板对战斗系统进行了完全性重构,大大提高了代码的结构性与执行效率。对于大部分情况,你只需要注重于战斗脚本与光环处理,就能做出任何你想要的怪物特殊属性,或者更改战斗流程,同时不会引起任何副作用。
## 战斗脚本
与旧样板不同的是,新样板中的战斗函数不再接收楼层、坐标等信息,取而代之的是怪物信息。返回值也变成了一个伤害值,不再需要返回一系列属性。
```ts
function calDamageWith(info: EnemyInfo, hero: Partial<HeroStatus>): number | null
```
你可以通过`info`来获取的怪物的真实信息,`info.x`与`info.y`获取怪物坐标,`info.floorId`获取怪物所在楼层,这三者可能是`undefined`,表示怪物不在任何楼层中。除此之外它还包含所有编辑器中可以编辑的怪物属性(如果存在且不为`null`和`undefined`),以及下面几个属性:
- `atkBuff`: buff 攻击加成
- `defBuff`: buff 防御加成
- `hpBuff`: buff 生命加成
- `enemy`: 怪物原始信息,即编辑器中的怪物原始信息,不能修改,只可以读取
- `guard`: 支援信息
对于战斗流程,新样板并没有进行过多改动,与旧样板相差不大。
战斗脚本可以在插件`battle`中修改。
## 光环处理
新样板的光环处理可以说是非常强大了,它通过`provide`和`inject`的方式进行光环处理,这使得光环处理不会产生副作用,同时提供了`preProvideHalo`函数,可以对那些能给其他怪物增加特殊属性甚至光环的光环进行处理。只要你的光环没有副作用(施加顺序不会影响结果,顺序无关性),就可以实现加光环的光环,甚至是加光环的光环的光环。同时,借助于`Range`类,你可以自定义你的光环范围。光环范围扫描只会扫描所有的怪物,因此性能相比于旧样板大有提升。
在处理光环时,会用到下列函数:
```ts
// 光环函数,第一个参数是被施加光环的怪物,第二个参数是施加光环的怪物
type HaloFn = (e: EnemyInfo, enemy: EnemyInfo) => void
// 在 DamageEnemy 类上,即每个怪物
function provideHalo(): void
function preProvideHalo(): void
function injectHalo(halo: HaloFn, enemy: Enemy): void
// 在 EnemyCollection 类上,即每一层的怪物集合
function applyHalo(
type: string,
data: any,
enemy: DamageEnemy,
halo: HaloFn | HaloFn[],
recursion: boolean = false
): void
```
对于光环,其施加流程大致如下:
1. 定义光环范围,使用`Range.registerRangeType`函数,见[范围处理](#范围处理)
2. 在`provideHalo`或`preProvideHalo`函数中添加对应的光环处理
3. 在光环处理中编写光环函数,并使用`applyHalo`施加光环
对于光环函数,它会接收被施加的怪物和施加光环的怪物作为参数,`e`是被施加光环的怪物,`enemy`是施加光环的怪物。在处理时,不应当更改后者的信息,仅应当更改前者的信息。
示例
```js
// 在 provideHalo 中,以下内容应放在样板自带的光环处理函数的循环中
// 施加光环
col.applyHalo(
'square', // 方形范围
{ x: this.x, y: this.y, d: 7 }, // 方形以怪物为中心7为边长
this, // 施加光环的怪,一般就是自己
(e, enemy) => {
e.hp += 100; // 光环效果是被施加的怪物增加100点生命值注意光环也可以作用于自身因此e和enemy有可能相等
}
);
```
## 范围处理
范围处理是一个通用接口,在当前版本样板中主要用于光环处理。它是一个类,因此应该通过`Mota.require('class', 'Range')`获取。它的类型如下:
```ts
class Range<C extends Partial<Loc>> {
collection: RangeCollection<C>;
cache: Record<string, any>;
static rangeType: Record<string, RangeType<Partial<Loc>>>;
constructor(collection: RangeCollection<C>);
/**
* 扫描 collection 中在范围内的物品
* @param type 范围类型
* @param data 范围数据
* @returns 在范围内的物品列表
*/
scan(type: string, data: any): C[];
inRange(type: string, data: any, item: Partial<Loc>): boolean;
clearCache(): void;
static registerRangeType(type: string, scan: RangeScanFn<Partial<Loc>>, inRange: InRangeFn<Partial<Loc>>): void;
}
```
当然,大部分情况下,你不需要理解每个 api 及其类型,在这里,我们重点关注`registerRangeType`函数。
它用于注册一个你自己的范围类型,接收三个参数,分别是:
- `type`: 范围类型名称
- `scan`: 范围扫描函数,用于获取`collection`中所有在范围内的元素
- `inRange`: 范围判断函数,用于判断一个元素是否在范围内
对于范围扫描函数,它接收`collection`和范围参数作为参数,返回一个数组,表示在范围内的元素列表。
对于范围判断函数,它会比范围扫描函数多一个参数,表示要判断的元素。返回布尔值,表示是否在范围内。
这里`collection`描述的是一个列表,表示所有要判断的元素。范围参数指的是该范围类型的参数,例如上面提到过的方形范围,其范围参数就是`{ x: this.x, y: this.y, d: 7 }`。值得注意的是,每个元素不一定包含横纵坐标两个属性。
系统自带两种范围,方形范围与曼哈顿范围(横纵坐标相加小于一个值),我们来看看方形范围是如何注册的:
```js
Range.registerRangeType(
'square',
(col, { x, y, d }) => {
// 获取要判断的列表
const list = col.collection.list;
const r = Math.floor(d / 2);
// 获取在范围内的列表,对每个元素进行判断是否在范围内即可
return list.filter(v => {
return (
has(v.x) && // 这两个用于判断是否存在横纵坐标参数
has(v.y) &&
Math.abs(v.x - x) <= r &&
Math.abs(v.y - y) <= r
);
});
},
(col, { x, y, d }, item) => {
const r = Math.floor(d / 2);
return (
has(item.x) &&
has(item.y) &&
Math.abs(item.x - x) <= r &&
Math.abs(item.y - y) <= r
);
}
);
```
## 地图伤害
与旧样板相比,地图伤害的处理并没有进行过多改动,其核心大致相同。计算地图伤害的流程是:
1. 遍历每个怪物,对于单个怪物,执行下列行为
2. 判断是否存在有地图伤害的属性,有则处理
3. 将地图伤害加入列表中
在计算地图伤害过程中,会用到下列函数:
```ts
// 在 DamageEnemy 上
function calMapDamage(
damage: Record<string, MapDamage> = {},
hero: Partial<HeroStatus> = getHeroStatusOn(Damage.realStatus)
): void
function setMapDamage(
damage: Record<string, MapDamage>,
loc: string,
dam: number,
type?: string
): void
```
前者是计算地图伤害的函数,也就是在插件中被复写的函数,一般不需要调用。它传入`damage`和`hero`作为参数,表示地图伤害要存入的对象,以及勇士信息。
后者是设置地图伤害的函数,当我们讲该怪物在改点的伤害计算完毕后,应该调用它将这一点的伤害信息记录下来。
示例请参考插件中的地图伤害计算。