HumanBreak/docs/guide/implements/special.md
2025-08-22 16:45:47 +08:00

289 lines
9.4 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.

# 怪物特殊属性
下面以特殊属性“勇士造成的伤害减少 10%”为例,展示如何自定义一个仅修改伤害计算的特殊属性。
:::warning
伤害计算将会在 2.C 中重构,不过逻辑并不会有大幅变动。
:::
## 编写属性定义
打开 `packages-user/data-state/src/enemy/special.ts`,在最后添加一个属性定义:
```ts
export const specials: SpecialDeclaration[] = [
// ... 原有属性定义
// 自定义属性
{
code: 30, // 特殊属性代码,用于 hasSpecial 判断 // [!code ++]
name: '自定义特殊属性', // 特殊属性名称 // [!code ++]
desc: '勇士对该怪物造成的伤害减少 10%', // 特殊属性描述 // [!code ++]
color: '#ffd' // 特殊属性显示的颜色 // [!code ++]
}
];
```
## 实现特殊属性
打开 `packages-user/data-state/src/enemy/damage.ts`,在文件最后的 `calDamageWith` 函数中编写:
```ts
export function calDamageWith(
info: UserEnemyInfo,
hero: Partial<HeroStatus>
): number | null {
// ... 原有内容
// 在需要降低勇士伤害的地方将勇士伤害乘以 0.9 即可
// 注意需要再回合计算前乘,不然没有效果
heroPerDamage *= 0.9; // [!code ++]
// ... 原有内容
}
```
## 拓展-用函数声明属性
特殊属性的属性名和介绍可以使用函数来声明,这允许属性名和描述根据怪物属性变化。下面我们以特殊属性“勇士伤害减少`n%`”为例,展示如何声明这种类型的属性。
### 编辑表格
首先我们打开编辑器,选中任意一个怪物,在左侧属性栏上方找到`编辑表格`,然后点击它打开,找到`【怪物】相关的表格配置`,在 `_data` 属性下仿照攻击或其他属性新增一项,注意不要忘记了逗号:
```js {4-10}
"enemys": {
"_data": {
// 属性名为 myAttr
"myAttr": {
"_leaf": true,
"_type": "textarea",
// 属性说明
"_docs": "伤害减免",
"_data": "伤害减免"
},
}
}
```
### 类型声明
然后打开 `src/types/declaration/event.d.ts`,找到开头的 `type PartialNumbericEnemyProperty =`,在后面新增一行:
```ts
type PartialNumbericEnemyProperty =
| 'value'
// ... 其他属性声明
// 新增自己的 myAttr 属性
// 注意不要忘记删除前一行最后的分号
| 'myAttr'; // [!code ++]
```
### 属性定义
最后在 `special.ts` 中新增属性定义即可:
```ts
export const specials: SpecialDeclaration[] = [
// ... 原有属性定义
// 自定义属性
{
code: 30, // 特殊属性代码,用于 hasSpecial 判断
name: enemy => `${enemy.myAttr ?? 0}%减伤`, // 特殊属性名称 // [!code ++]
desc: enemy => `勇士对该怪物造成的伤害减少${enemy.myAttr ?? 0}%`, // 特殊属性描述 // [!code ++]
color: '#ffd' // 特殊属性显示的颜色
}
];
```
此时,如果给怪物的 `myAttr` 栏填写 `10`,那么特殊属性名称就会显示 `10%减伤`,属性描述会显示 `勇士对该怪物造成的伤害减少10%`
### 属性实现
修改 `damage.ts` `calDamageWith` 中的实现:
```ts
export function calDamageWith(
info: UserEnemyInfo,
hero: Partial<HeroStatus>
): number | null {
// ... 原有内容
// 在乘以 1 - (myAttr / 100),除以 100 是因为 myAttr 是百分制
heroPerDamage *= 1 - (info.myAttr ?? 0) / 100; // [!code ++]
// ... 原有内容
}
```
## 拓展-地图伤害
同样在 `damage.ts`,找到 `DamageEnemy.calMapDamage` 方法,直接 `ctrl+F` 搜索 `calMapDamage` 即可找到,然后在其中编写地图伤害即可。以领域为例,它是这么写的:
```ts
class DamageEnemy {
calMapDamage(
damage: Record<string, MapDamage> = {},
hero: Partial<HeroStatus> = getHeroStatusOn(realStatus)
) {
// 判断是否包含领域属性
if (this.info.special.has(15)) {
// 计算领域范围
const range = enemy.range ?? 1;
const startX = Math.max(0, this.x - range);
const startY = Math.max(0, this.y - range);
const endX = Math.min(floor.width - 1, this.x + range);
const endY = Math.min(floor.height - 1, this.y + range);
// 伤害量
const dam = enemy.value ?? 0;
const objs = core.getMapBlocksObj(this.floorId);
for (let x = startX; x <= endX; x++) {
for (let y = startY; y <= endY; y++) {
if (
!enemy.zoneSquare &&
// 判断非九宫格领域,使用曼哈顿距离判断
manhattan(x, y, this.x, this.y) > range
) {
continue;
}
const loc = `${x},${y}` as LocString;
if (objs[loc]?.event.noPass) continue;
// 存储地图伤害
this.setMapDamage(damage, loc, dam, '领域');
}
}
}
}
}
```
## 拓展-光环属性
光环计算目前分为两个优先级,高优先级的可以影响低优先级的,这意味着你可以做出来加光环的光环属性。不过高级光环的逻辑比较绕,而且需求不高,这里不再介绍。如果需要的话可以自行理解这部分逻辑或在造塔群里询问。这里以攻击光环为例,展示如何制作一个普通光环。
我们假设使用 `atkHalo` 作为光环增幅,`haloRange` 作为光环范围,属性代码为 `30`,九宫格光环。我们在 `damage.ts` 中找到 `DamageEnemy.provideHalo` 方法,直接 `ctrl+F` 搜索 `provideHalo` 就能找到。
### 光环逻辑
我们直接调用 `applyHalo` 即可,如下编写代码:
```ts
class DamageEnemy {
provideHalo() {
// ... 原有逻辑
// 施加光环
col.applyHalo(
// 光环形状为正方形。目前支持 square 矩形和 rect 矩形
'square',
// 正方形形状参数
{
x: this.x, // 中心横坐标
y: this.y, // 中心纵坐标
d: this.info.haloRange * 2 + 1 // 边长
},
this, // 填 this 即可
(e, enemy) => {
// 这里的 e 是指被加成的怪物enemy 是当前施加光环的怪物
// 直接加到 atkBuff_ 属性上即可
e.atkBuff_ += enemy.atkHalo;
}
);
// 在地图上显示光环,这部分可选,如果不想显示也可以不写
col.haloList.push({
// 光环形状
type: 'square',
// 形状参数
data: { x: this.x, y: this.y, d: this.info.haloRange * 2 + 1 },
// 特殊属性代码
special: 30,
// 施加的怪物
from: this
});
}
}
```
### 自定义形状
如果想要自定义光环形状,我们打开 `packages-user/data-utils/src/range.ts`,拉到最后可以看到形状定义,目前包含两个:
- `square`: 中心点+边长的正方形
- `rect`: 左上角坐标+宽高的矩形
我们以曼哈顿距离为例,展示如何自定义形状。
首先在开头找到 `interface RangeTypeData`,在其中添加必要的参数类型:
```ts
interface RangeTypeData {
// ... 原有内容
// 自定义的曼哈顿范围参数,包含中心坐标和半径
manhattan: { x: number; y: number; dis: number }; // [!code ++]
}
```
然后在文件最后定义形状即可:
```ts
// 这里的第一个参数就是我们的形状名称,填 manhattan 即可
// 第二个参数是一个函数,目的是判断 item 是否在范围内
Range.register('manhattan', (item, { x, y, dis }) => {
// 如果 item 连坐标属性都不存在,那么直接判定不在范围内
if (isNil(item.x) || isNil(item.y)) return false;
// 计算与中心的坐标差
const dx = Math.abs(item.x - x);
const dy = Math.abs(item.y - y);
// 坐标差之和小于半径则在范围内,否则在范围外
return dx + dy < dis;
});
```
在光环中,我们就可以直接使用这种形状了:
```ts {2-9}
col.applyHalo(
// 使用自定义形状
'manhattan',
// 自定义形状的参数
{
x: this.x, // 中心横坐标
y: this.y, // 中心纵坐标
dis: this.info.haloRange // 半径
},
this,
(e, enemy) => {
e.atkBuff_ += enemy.atkHalo;
}
);
```
## 拓展-输出回合数
样板默认的 `calDamageWith` 函数只允许输出伤害值,而有时候我们可能会需要战斗的回合数,这时候我们需要修改一下这部分内容,将伤害计算逻辑单独提出来,然后在 `calDamageWith` 中调用它。在需要回合数的时候,我们调用提出了的函数即可,如下例所示:
```ts
/** 包含回合数的伤害计算 */
export function calDamageWithTurn(
info: UserEnemyInfo,
hero: Partial<HeroStatus>
) {
// ... 原本 calDamageWith 的计算逻辑,记得删除最后返回伤害的那一行返回值
// 返回回合数和伤害
return { turn, damage };
}
export function calDamageWith(info: UserEnemyInfo, hero: Partial<HeroStatus>) {
// 调用单独提出的函数计算伤害值
const damageInfo = calDamageWithTurn(info, hero);
// 如果伤害不存在,那么返回无穷大
return damageInfo?.damage ?? Infinity;
}
```