mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-08-28 06:03:55 +08:00
289 lines
9.4 KiB
Markdown
289 lines
9.4 KiB
Markdown
# 怪物特殊属性
|
||
|
||
下面以特殊属性“勇士造成的伤害减少 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;
|
||
}
|
||
```
|