9.4 KiB
怪物特殊属性
下面以特殊属性“勇士造成的伤害减少 10%”为例,展示如何自定义一个仅修改伤害计算的特殊属性。
:::warning 伤害计算将会在 2.C 中重构,不过逻辑并不会有大幅变动。 :::
编写属性定义
打开 packages-user/data-state/src/enemy/special.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
函数中编写:
export function calDamageWith(
info: UserEnemyInfo,
hero: Partial<HeroStatus>
): number | null {
// ... 原有内容
// 在需要降低勇士伤害的地方将勇士伤害乘以 0.9 即可
// 注意需要再回合计算前乘,不然没有效果
heroPerDamage *= 0.9; // [!code ++]
// ... 原有内容
}
拓展-用函数声明属性
特殊属性的属性名和介绍可以使用函数来声明,这允许属性名和描述根据怪物属性变化。下面我们以特殊属性“勇士伤害减少n%
”为例,展示如何声明这种类型的属性。
编辑表格
首先我们打开编辑器,选中任意一个怪物,在左侧属性栏上方找到编辑表格
,然后点击它打开,找到【怪物】相关的表格配置
,在 _data
属性下仿照攻击或其他属性新增一项,注意不要忘记了逗号:
"enemys": {
"_data": {
// 属性名为 myAttr
"myAttr": {
"_leaf": true,
"_type": "textarea",
// 属性说明
"_docs": "伤害减免",
"_data": "伤害减免"
},
}
}
类型声明
然后打开 src/types/declaration/event.d.ts
,找到开头的 type PartialNumbericEnemyProperty =
,在后面新增一行:
type PartialNumbericEnemyProperty =
| 'value'
// ... 其他属性声明
// 新增自己的 myAttr 属性
// 注意不要忘记删除前一行最后的分号
| 'myAttr'; // [!code ++]
属性定义
最后在 special.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
中的实现:
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
即可找到,然后在其中编写地图伤害即可。以领域为例,它是这么写的:
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
即可,如下编写代码:
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
,在其中添加必要的参数类型:
interface RangeTypeData {
// ... 原有内容
// 自定义的曼哈顿范围参数,包含中心坐标和半径
manhattan: { x: number; y: number; dis: number }; // [!code ++]
}
然后在文件最后定义形状即可:
// 这里的第一个参数就是我们的形状名称,填 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;
});
在光环中,我们就可以直接使用这种形状了:
col.applyHalo(
// 使用自定义形状
'manhattan',
// 自定义形状的参数
{
x: this.x, // 中心横坐标
y: this.y, // 中心纵坐标
dis: this.info.haloRange // 半径
},
this,
(e, enemy) => {
e.atkBuff_ += enemy.atkHalo;
}
);
拓展-输出回合数
样板默认的 calDamageWith
函数只允许输出伤害值,而有时候我们可能会需要战斗的回合数,这时候我们需要修改一下这部分内容,将伤害计算逻辑单独提出来,然后在 calDamageWith
中调用它。在需要回合数的时候,我们调用提出了的函数即可,如下例所示:
/** 包含回合数的伤害计算 */
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;
}