mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-10-15 17:32:58 +08:00
chore: 怪物伤害计算与特殊属性调整为样板
This commit is contained in:
parent
fe46a7b397
commit
7c9847aa40
@ -53,7 +53,7 @@ export function toggleSkill1() {
|
||||
import { getSkill1Enabled } from '../machanism/skill'; // [!code ++]
|
||||
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number | null {
|
||||
// ... 原有内容
|
||||
@ -241,7 +241,7 @@ export function getEnabledSkill() {
|
||||
import { getEnabledSkill, SkillType } from './skill';
|
||||
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number | null {
|
||||
// ... 原有内容
|
||||
@ -260,7 +260,7 @@ export function calDamageWith(
|
||||
import { getEnabledSkill, SkillType } from './skill';
|
||||
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number | null {
|
||||
// ... 原有内容
|
||||
|
@ -26,13 +26,13 @@ export const specials: SpecialDeclaration[] = [
|
||||
|
||||
## 实现特殊属性
|
||||
|
||||
打开 `packages-user/data-state/src/enemy/damage.ts`,在文件最后的 `calDamageWith` 函数中编写:
|
||||
打开 `packages-user/data-state/src/enemy/damage.ts`,在文件最后的 `calDamageWithTurn` 函数中编写:
|
||||
|
||||
```ts
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
export function calDamageWithTurn(
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number | null {
|
||||
): DamageWithTurn {
|
||||
// ... 原有内容
|
||||
|
||||
// 在需要降低勇士伤害的地方将勇士伤害乘以 0.9 即可
|
||||
@ -102,13 +102,13 @@ export const specials: SpecialDeclaration[] = [
|
||||
|
||||
### 属性实现
|
||||
|
||||
修改 `damage.ts` `calDamageWith` 中的实现:
|
||||
修改 `damage.ts` `calDamageWithTurn` 中的实现:
|
||||
|
||||
```ts
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number | null {
|
||||
): DamageWithTurn {
|
||||
// ... 原有内容
|
||||
|
||||
// 在乘以 1 - (myAttr / 100),除以 100 是因为 myAttr 是百分制
|
||||
@ -210,10 +210,11 @@ class DamageEnemy {
|
||||
|
||||
### 自定义形状
|
||||
|
||||
如果想要自定义光环形状,我们打开 `packages-user/data-utils/src/range.ts`,拉到最后可以看到形状定义,目前包含两个:
|
||||
如果想要自定义光环形状,我们打开 `packages-user/data-utils/src/range.ts`,拉到最后可以看到形状定义,目前默认的包含这些:
|
||||
|
||||
- `square`: 中心点+边长的正方形
|
||||
- `rect`: 左上角坐标+宽高的矩形
|
||||
- `manhattan`: 曼哈顿距离,坐标之和小于半径
|
||||
|
||||
我们以曼哈顿距离为例,展示如何自定义形状。
|
||||
|
||||
@ -262,27 +263,3 @@ col.applyHalo(
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## 拓展-输出回合数
|
||||
|
||||
样板默认的 `calDamageWith` 函数只允许输出伤害值,而有时候我们可能会需要战斗的回合数,这时候我们需要修改一下这部分内容,将伤害计算逻辑单独提出来,命名为 `calDamageWithTurn`,然后在 `calDamageWith` 中调用它。在需要回合数的时候,我们调用 `calDamageWithTurn` 函数即可,如下例所示:
|
||||
|
||||
```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;
|
||||
}
|
||||
```
|
||||
|
@ -210,7 +210,7 @@ const realStatus: (keyof HeroStatus)[] = [
|
||||
|
||||
```ts
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number {
|
||||
// ... 原有逻辑
|
||||
|
@ -969,10 +969,13 @@ export class TextContentParser {
|
||||
continue;
|
||||
} else if (char === '$') {
|
||||
// 表达式
|
||||
const next = text[pointer + 1];
|
||||
if (next === '{') {
|
||||
pointer++;
|
||||
inExpression = true;
|
||||
expStart = pointer + 1;
|
||||
continue;
|
||||
}
|
||||
} else if (char === '\n') {
|
||||
// 在这里预先将换行处理为多个 node,会比在分行时再处理更方便
|
||||
this.addTextNode(pointer + 1, true);
|
||||
|
@ -94,6 +94,8 @@ export function patchBattle() {
|
||||
});
|
||||
});
|
||||
|
||||
patch2.add('battle', battle);
|
||||
|
||||
patch2.add('_sys_battle', function (data: Block, callback?: () => void) {
|
||||
// 检查战前事件
|
||||
const floor = core.floors[core.status.floorId];
|
||||
@ -156,8 +158,9 @@ export function patchBattle() {
|
||||
core.playSound('attack.opus');
|
||||
|
||||
// 战斗伤害
|
||||
const info = enemy.calDamage(core.status.hero);
|
||||
const damage = info.damage;
|
||||
const info = enemy.getRealInfo();
|
||||
const damageInfo = enemy.calDamage(core.status.hero);
|
||||
const damage = damageInfo.damage;
|
||||
// 判定是否致死
|
||||
if (damage >= core.status.hero.hp) {
|
||||
core.status.hero.hp = 0;
|
||||
@ -171,25 +174,41 @@ export function patchBattle() {
|
||||
core.status.hero.statistics.battleDamage += damage;
|
||||
core.status.hero.statistics.battle++;
|
||||
|
||||
// 获得金币
|
||||
const money = enemy.info.money!;
|
||||
// 获得金币经验
|
||||
const money = core.hasFlag('curse') ? 0 : enemy.info.money!;
|
||||
const exp = core.hasFlag('curse') ? 0 : enemy.info.exp!;
|
||||
|
||||
core.status.hero.money += money;
|
||||
core.status.hero.statistics.money += money;
|
||||
|
||||
// 获得经验
|
||||
const exp = enemy.info.exp!;
|
||||
core.status.hero.exp += exp;
|
||||
core.status.hero.statistics.exp += exp;
|
||||
|
||||
const hint =
|
||||
'打败 ' +
|
||||
enemy.enemy.name +
|
||||
',金币+' +
|
||||
money +
|
||||
',经验+' +
|
||||
exp;
|
||||
const hint = `打败 ${enemy.enemy.name},金币+${money},经验+${exp}`;
|
||||
core.drawTip(hint, enemy.id);
|
||||
|
||||
// 毒衰咒
|
||||
if (info.special.has(12)) core.setFlag('poison', true);
|
||||
if (info.special.has(13)) core.setFlag('weak', true);
|
||||
if (info.special.has(14)) core.setFlag('curse', true);
|
||||
|
||||
// 仇恨
|
||||
if (info.special.has(17)) {
|
||||
core.setFlag('hatred', core.getFlag('hatred', 0) / 2);
|
||||
} else {
|
||||
core.addFlag('hatred', core.values.hatred);
|
||||
}
|
||||
|
||||
// 自爆
|
||||
if (info.special.has(19)) {
|
||||
core.status.hero.hp = 1;
|
||||
}
|
||||
|
||||
// 退化
|
||||
if (info.special.has(21)) {
|
||||
core.status.hero.atk -= info.atkValue ?? 0;
|
||||
core.status.hero.def -= info.defValue ?? 0;
|
||||
}
|
||||
|
||||
// 事件的处理
|
||||
const todo: MotaEvent = [];
|
||||
|
||||
@ -231,7 +250,7 @@ declare global {
|
||||
interface Events {
|
||||
battle(
|
||||
enemy: DamageEnemy,
|
||||
_?: number,
|
||||
y?: number,
|
||||
force?: boolean,
|
||||
callback?: () => void
|
||||
): void;
|
||||
|
@ -108,26 +108,10 @@ function renderThumbnailDamage(col: EnemyCollection) {
|
||||
});
|
||||
}
|
||||
|
||||
// 电摇嘲讽
|
||||
if (dam.mockery) {
|
||||
dam.mockery.sort((a, b) =>
|
||||
a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]
|
||||
);
|
||||
const [tx, ty] = dam.mockery[0];
|
||||
const dir = x > tx ? '←' : x < tx ? '→' : y > ty ? '↑' : '↓';
|
||||
core.status.damage.extraData.push({
|
||||
text: '嘲' + dir,
|
||||
px: 32 * x + 16,
|
||||
py: 32 * (y + 1) - 14,
|
||||
color: '#fd4',
|
||||
alpha: 1
|
||||
});
|
||||
}
|
||||
|
||||
// 追猎
|
||||
if (dam.hunt) {
|
||||
if (dam.ambush) {
|
||||
core.status.damage.extraData.push({
|
||||
text: '猎',
|
||||
text: '!',
|
||||
px: 32 * x + 16,
|
||||
py: 32 * (y + 1) - 14,
|
||||
color: '#fd4',
|
||||
|
@ -15,41 +15,6 @@ import {
|
||||
HaloType,
|
||||
IEnemyCollectionEvent
|
||||
} from '@motajs/types';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
export interface UserEnemyInfo extends EnemyInfo {
|
||||
togetherNum?: number;
|
||||
}
|
||||
|
||||
/** 光环属性 */
|
||||
export const haloSpecials: Set<number> = new Set([
|
||||
8, 21, 25, 26, 27, 29, 31, 32
|
||||
]);
|
||||
/** 不可被同化的属性 */
|
||||
export const unassimilatable: Set<number> = new Set(haloSpecials);
|
||||
unassimilatable.add(8).add(30).add(33);
|
||||
/** 特殊属性对应 */
|
||||
export const specialValue: Map<number, SelectKey<Enemy, number | undefined>[]> =
|
||||
new Map();
|
||||
specialValue
|
||||
.set(1, ['crit'])
|
||||
.set(6, ['n'])
|
||||
.set(7, ['hungry'])
|
||||
.set(8, ['together'])
|
||||
.set(10, ['courage'])
|
||||
.set(11, ['charge'])
|
||||
.set(15, ['value'])
|
||||
.set(18, ['value'])
|
||||
.set(20, ['ice'])
|
||||
.set(21, ['iceHalo'])
|
||||
.set(22, ['night'])
|
||||
.set(23, ['day'])
|
||||
.set(25, ['melt'])
|
||||
.set(26, ['iceCore'])
|
||||
.set(27, ['fireCore'])
|
||||
.set(28, ['paleShield'])
|
||||
.set(31, ['hpHalo'])
|
||||
.set(32, ['assimilateRange']);
|
||||
|
||||
export class EnemyCollection
|
||||
extends EventEmitter<IEnemyCollectionEvent>
|
||||
@ -68,9 +33,6 @@ export class EnemyCollection
|
||||
/** 楼层高度 */
|
||||
height: number = 0;
|
||||
|
||||
/** 乾坤挪移属性 */
|
||||
translation: [number, number] = [0, 0];
|
||||
|
||||
constructor(floorId: FloorIds) {
|
||||
super();
|
||||
this.floorId = floorId;
|
||||
@ -111,7 +73,6 @@ export class EnemyCollection
|
||||
*/
|
||||
calRealAttribute() {
|
||||
this.haloList = [];
|
||||
this.translation = [0, 0];
|
||||
this.list.forEach(v => {
|
||||
v.reset();
|
||||
});
|
||||
@ -204,7 +165,7 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
* 属性计算流程:预平衡光环(即计算加光环的光环怪的光环) -> 计算怪物在没有光环下的属性
|
||||
* -> provide inject 光环 -> 计算怪物的光环加成 -> 计算完毕
|
||||
*/
|
||||
info!: UserEnemyInfo;
|
||||
info!: EnemyInfo;
|
||||
|
||||
/** 向其他怪提供过的光环 */
|
||||
providedHalo: Set<number> = new Set();
|
||||
@ -238,10 +199,10 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
atk: enemy.atk,
|
||||
def: enemy.def,
|
||||
special: new Set(enemy.special),
|
||||
damageDecline: 0,
|
||||
atkBuff_: 0,
|
||||
defBuff_: 0,
|
||||
hpBuff_: 0,
|
||||
guard: [],
|
||||
enemy: this.enemy,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
@ -256,12 +217,6 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
}
|
||||
this.progress = 0;
|
||||
this.providedHalo.clear();
|
||||
|
||||
// 在这里计算乾坤挪移
|
||||
if (this.col && enemy.special.includes(30)) {
|
||||
this.col.translation[0] += enemy.translation![0];
|
||||
this.col.translation[1] += enemy.translation![1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,31 +227,18 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
this.progress = 2;
|
||||
const special = this.info.special;
|
||||
const info = this.info;
|
||||
const floorId = this.floorId ?? core.status.floorId;
|
||||
let [dx, dy] = [0, 0];
|
||||
const col = this.col ?? core.status.maps[this.floorId!]?.enemy;
|
||||
if (col) {
|
||||
[dx, dy] = col.translation;
|
||||
|
||||
const { atk = 0, def = 0 } = getHeroStatusOn(realStatus);
|
||||
|
||||
// 坚固
|
||||
if (special.has(3)) {
|
||||
info.def = Math.max(info.def, atk - 1);
|
||||
}
|
||||
|
||||
// 智慧之源
|
||||
if (flags.hard === 2 && special.has(14)) {
|
||||
info.atk += flags[`inte_${floorId}`] ?? 0;
|
||||
}
|
||||
|
||||
// 融化,融化不属于怪物光环,因此不能用provide和inject计算,需要在这里计算
|
||||
const melt = flags[`melt_${floorId}`];
|
||||
if (!isNil(melt) && !isNil(this.x) && !isNil(this.y)) {
|
||||
for (const [loc, per] of Object.entries(melt)) {
|
||||
const [mx, my] = loc.split(',').map(v => parseInt(v));
|
||||
if (
|
||||
Math.abs(mx + dx - this.x) <= 1 &&
|
||||
Math.abs(my + dy - this.y) <= 1
|
||||
) {
|
||||
info.atkBuff_ += per as number;
|
||||
info.defBuff_ += per as number;
|
||||
}
|
||||
}
|
||||
// 模仿
|
||||
if (special.has(10)) {
|
||||
info.atk = atk;
|
||||
info.def = def;
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,14 +257,6 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
// 此时已经inject光环,因此直接计算真实属性
|
||||
const info = this.info;
|
||||
|
||||
if (info.special.has(33)) {
|
||||
const count = this.col?.list.size ?? 0;
|
||||
const [hp, atk, def] = this.enemy.horn ?? [0, 0, 0];
|
||||
info.hpBuff_ += hp * count;
|
||||
info.atkBuff_ += atk * count;
|
||||
info.defBuff_ += def * count;
|
||||
}
|
||||
|
||||
info.atk = Math.floor(info.atk * (info.atkBuff_ / 100 + 1));
|
||||
info.def = Math.floor(info.def * (info.defBuff_ / 100 + 1));
|
||||
info.hp = Math.floor(info.hp * (info.hpBuff_ / 100 + 1));
|
||||
@ -330,19 +264,6 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
return this.info;
|
||||
}
|
||||
|
||||
getHaloSpecials(): Set<number> {
|
||||
if (!this.floorId) return new Set();
|
||||
if (!has(this.x) || !has(this.y)) return new Set();
|
||||
const special = this.info.special ?? this.enemy.special;
|
||||
const res = new Set<number>();
|
||||
special.forEach(v => {
|
||||
if (haloSpecials.has(v) && !this.providedHalo.has(v)) {
|
||||
res.add(v);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 光环预提供,用于平衡所有怪的光环属性,避免出现不同情况下光环效果不一致的现象
|
||||
*/
|
||||
@ -351,62 +272,10 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
this.progress = 1;
|
||||
if (!this.floorId) return;
|
||||
if (!has(this.x) || !has(this.y)) return;
|
||||
const special = this.getHaloSpecials();
|
||||
const col = this.col ?? core.status.maps[this.floorId!].enemy;
|
||||
let [dx, dy] = [0, 0];
|
||||
if (col) [dx, dy] = col.translation;
|
||||
|
||||
// 这里可以做优先级更高的光环,比如加光环的光环怪等,写法与 provideHalo 类似
|
||||
|
||||
// e 是被加成怪的属性,enemy 是施加光环的怪
|
||||
|
||||
for (const halo of special) {
|
||||
switch (halo) {
|
||||
case 29: {
|
||||
// 特殊光环
|
||||
const e = this.enemy;
|
||||
const type = 'square';
|
||||
const r = Math.floor(e.haloRange!);
|
||||
const d = r * 2 + 1;
|
||||
const range = { x: this.x + dx, y: this.y + dy, d };
|
||||
|
||||
// 这一句必须放到applyHalo之前
|
||||
this.providedHalo.add(29);
|
||||
const halo = (e: UserEnemyInfo, enemy: UserEnemyInfo) => {
|
||||
const s = enemy.specialHalo!;
|
||||
|
||||
for (const spe of s) {
|
||||
e.special.add(spe);
|
||||
}
|
||||
// 如果是自身,就不进行特殊属性数值处理了
|
||||
if (e === this.info) return;
|
||||
// 然后计算特殊属性数值
|
||||
for (const spec of s) {
|
||||
// 如果目标怪物拥有杀戮光环,且光环会加成此属性,则忽略
|
||||
if (e.specialHalo?.includes(spec)) continue;
|
||||
const toChange = specialValue.get(spec);
|
||||
if (!toChange) continue;
|
||||
for (const key of toChange) {
|
||||
// 这种光环应该获取怪物的原始数值,而不是真实数值
|
||||
if (enemy.enemy.specialMultiply) {
|
||||
e[key] ??= 1;
|
||||
e[key] *= enemy[key] ?? 1;
|
||||
} else {
|
||||
e[key] ??= 0;
|
||||
e[key] += enemy[key] ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
col.applyHalo(type, range, this, halo, true);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x + dx, y: this.y + dy, d },
|
||||
special: 29,
|
||||
from: this
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -419,159 +288,67 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
if (!has(this.x) || !has(this.y)) return;
|
||||
const col = this.col ?? core.status.maps[this.floorId].enemy;
|
||||
if (!col) return;
|
||||
const special = this.getHaloSpecials();
|
||||
const [dx, dy] = col.translation;
|
||||
|
||||
const square7: HaloFn[] = [];
|
||||
const square5: HaloFn[] = [];
|
||||
const special = this.info.special;
|
||||
|
||||
// e 是被加成怪的属性,enemy 是施加光环的怪
|
||||
|
||||
// 抱团
|
||||
if (special.has(8)) {
|
||||
col.applyHalo(
|
||||
'square',
|
||||
{ x: this.x, y: this.y, d: 5 },
|
||||
this,
|
||||
(e: UserEnemyInfo, enemy) => {
|
||||
if (
|
||||
e.special.has(8) &&
|
||||
(e.x !== this.x || this.y !== e.y)
|
||||
) {
|
||||
e.atkBuff_ += enemy.together ?? 0;
|
||||
e.defBuff_ += enemy.together ?? 0;
|
||||
e.togetherNum ??= 0;
|
||||
e.togetherNum++;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.providedHalo.add(8);
|
||||
}
|
||||
|
||||
// 冰封光环
|
||||
if (special.has(21)) {
|
||||
square7.push(e => {
|
||||
e.damageDecline += this.info.iceHalo ?? 0;
|
||||
});
|
||||
this.providedHalo.add(21);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x + dx, y: this.y + dy, d: 7 },
|
||||
special: 21,
|
||||
from: this
|
||||
});
|
||||
}
|
||||
|
||||
// 冰封之核
|
||||
if (special.has(26)) {
|
||||
square5.push(e => {
|
||||
e.defBuff_ += this.info.iceCore ?? 0;
|
||||
});
|
||||
this.providedHalo.add(26);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x + dx, y: this.y + dy, d: 5 },
|
||||
special: 26,
|
||||
from: this
|
||||
});
|
||||
}
|
||||
|
||||
// 火焰之核
|
||||
if (special.has(27)) {
|
||||
square5.push(e => {
|
||||
e.atkBuff_ += this.info.fireCore ?? 0;
|
||||
});
|
||||
this.providedHalo.add(27);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x + dx, y: this.y + dy, d: 5 },
|
||||
special: 27,
|
||||
from: this
|
||||
});
|
||||
}
|
||||
|
||||
// 再生光环
|
||||
if (special.has(31)) {
|
||||
square7.push(e => {
|
||||
e.hpBuff_ += this.info.hpHalo ?? 0;
|
||||
});
|
||||
this.providedHalo.add(31);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x + dx, y: this.y + dy, d: 7 },
|
||||
special: 31,
|
||||
from: this
|
||||
});
|
||||
}
|
||||
|
||||
// 同化,它不会被光环类属性影响,因此放到这
|
||||
if (special.has(32)) {
|
||||
const e = this.info;
|
||||
const type = 'square';
|
||||
const r = Math.floor(e.assimilateRange!);
|
||||
const d = r * 2 + 1;
|
||||
const range = { x: this.x, y: this.y, d };
|
||||
|
||||
col.applyHalo(type, range, this, (e, enemy) => {
|
||||
// 如果是自身,就不进行特殊属性数值处理了
|
||||
if (e === this.info) return;
|
||||
const s = e.special;
|
||||
|
||||
for (const spe of s) {
|
||||
if (unassimilatable.has(spe)) continue;
|
||||
enemy.special.add(spe);
|
||||
}
|
||||
// 然后计算特殊属性数值
|
||||
for (const spec of s) {
|
||||
if (unassimilatable.has(spec)) continue;
|
||||
const toChange = specialValue.get(spec);
|
||||
if (!toChange) continue;
|
||||
for (const key of toChange) {
|
||||
// 这种光环应该获取怪物的原始数值,而不是真实数值
|
||||
if (enemy.enemy.specialMultiply) {
|
||||
enemy[key] ??= 1;
|
||||
enemy[key] *= e[key] ?? 1;
|
||||
// 普通光环
|
||||
if (special.has(25)) {
|
||||
// 光环效果,这里直接增加 e 的 buff 属性
|
||||
const halo = (e: EnemyInfo, enemy: EnemyInfo) => {
|
||||
if (enemy.haloAdd) {
|
||||
e.hpBuff_ += enemy.hpBuff ?? 0;
|
||||
e.atkBuff_ += enemy.atkBuff ?? 0;
|
||||
e.defBuff_ += enemy.defBuff ?? 0;
|
||||
} else {
|
||||
enemy[key] ??= 0;
|
||||
enemy[key] += e[key] ?? 0;
|
||||
e.hpBuff_ = Math.max(e.hpBuff_, enemy.hpBuff ?? 0);
|
||||
e.atkBuff_ = Math.max(e.atkBuff_, enemy.atkBuff ?? 0);
|
||||
e.defBuff_ = Math.max(e.defBuff_, enemy.defBuff ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: range,
|
||||
special: 32,
|
||||
from: this
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
// 根据范围施加光环
|
||||
const range = this.info.haloRange ?? 1;
|
||||
if (this.info.haloSquare) {
|
||||
col.applyHalo(
|
||||
'square',
|
||||
{ x: this.x + dx, y: this.y + dy, d: 7 },
|
||||
{ x: this.x, y: this.y, d: range * 2 + 1 },
|
||||
this,
|
||||
square7
|
||||
halo
|
||||
);
|
||||
} else {
|
||||
col.applyHalo(
|
||||
'manhattan',
|
||||
{ x: this.x, y: this.y, d: range },
|
||||
this,
|
||||
halo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 支援也是一类光环
|
||||
if (special.has(26)) {
|
||||
col.applyHalo(
|
||||
'square',
|
||||
{ x: this.x + dx, y: this.y + dy, d: 5 },
|
||||
{ x: this.x, y: this.y, d: 3 },
|
||||
this,
|
||||
square5
|
||||
(e, enemy) => {
|
||||
e.guard.push(enemy);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受其他怪的光环
|
||||
*/
|
||||
injectHalo(halo: HaloFn, enemy: UserEnemyInfo) {
|
||||
injectHalo(halo: HaloFn, enemy: EnemyInfo) {
|
||||
halo(this.info, enemy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算怪物伤害
|
||||
*/
|
||||
calDamage(hero: Partial<HeroStatus> = core.status.hero) {
|
||||
calDamage(hero: Partial<HeroStatus> = core.status.hero): DamageInfo {
|
||||
const enemy = this.getRealInfo();
|
||||
return this.calEnemyDamageOf(hero, enemy);
|
||||
}
|
||||
@ -582,23 +359,23 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
*/
|
||||
calMapDamage(
|
||||
damage: Record<string, MapDamage> = {},
|
||||
hero: Partial<HeroStatus> = getHeroStatusOn(realStatus)
|
||||
_hero: Partial<HeroStatus> = getHeroStatusOn(realStatus)
|
||||
) {
|
||||
if (!has(this.x) || !has(this.y) || !has(this.floorId)) return damage;
|
||||
const enemy = this.enemy;
|
||||
const floor = core.status.maps[this.floorId];
|
||||
const w = floor.width;
|
||||
const h = floor.height;
|
||||
const objs = core.getMapBlocksObj(this.floorId);
|
||||
|
||||
// 突刺
|
||||
// 领域
|
||||
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 = Math.max((enemy.value ?? 0) - hero.def!, 0);
|
||||
const objs = core.getMapBlocksObj(this.floorId);
|
||||
const dam = Math.max(enemy.zone ?? 0, 0);
|
||||
|
||||
for (let x = startX; x <= endX; x++) {
|
||||
for (let y = startY; y <= endY; y++) {
|
||||
@ -606,20 +383,20 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
!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, '突刺');
|
||||
this.setMapDamage(damage, loc, dam, '领域');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 射击
|
||||
// 激光
|
||||
if (this.info.special.has(24)) {
|
||||
const dirs: Dir[] = ['left', 'down', 'up', 'right'];
|
||||
const dam = Math.max((enemy.atk ?? 0) - hero.def!, 0);
|
||||
const objs = core.getMapBlocksObj(this.floorId);
|
||||
const dam = Math.max(enemy.laser ?? 0, 0);
|
||||
|
||||
for (const dir of dirs) {
|
||||
let x = this.x;
|
||||
@ -629,70 +406,67 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
x += dx;
|
||||
y += dy;
|
||||
const loc = `${x},${y}` as LocString;
|
||||
const block = objs[loc];
|
||||
if (
|
||||
block &&
|
||||
block.event.noPass &&
|
||||
block.event.cls !== 'enemys'
|
||||
) {
|
||||
break;
|
||||
}
|
||||
this.setMapDamage(damage, loc, dam, '射击');
|
||||
if (objs[loc]?.event.noPass) continue;
|
||||
this.setMapDamage(damage, loc, dam, '激光');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 电摇嘲讽
|
||||
if (this.info.special.has(19)) {
|
||||
const objs = core.getMapBlocksObj(this.floorId);
|
||||
for (let nx = 0; nx < w; nx++) {
|
||||
const loc = `${nx},${this.y}` as LocString;
|
||||
const block = objs[loc];
|
||||
if (!block?.event.noPass) {
|
||||
damage[loc] ??= { damage: 0, type: new Set() };
|
||||
damage[loc].mockery ??= [];
|
||||
damage[loc].mockery!.push([this.x, this.y]);
|
||||
}
|
||||
}
|
||||
for (let ny = 0; ny < h; ny++) {
|
||||
const loc = `${this.x},${ny}` as LocString;
|
||||
const block = objs[loc];
|
||||
if (!block?.event.noPass) {
|
||||
damage[loc] ??= { damage: 0, type: new Set() };
|
||||
damage[loc].mockery ??= [];
|
||||
damage[loc].mockery!.push([this.x, this.y]);
|
||||
}
|
||||
// 阻击
|
||||
if (this.info.special.has(18)) {
|
||||
const dirs: Dir[] = ['left', 'down', 'up', 'right'];
|
||||
for (const dir of dirs) {
|
||||
const { x: dx, y: dy } = core.utils.scan[dir];
|
||||
const x = this.x + dx;
|
||||
const y = this.y + dy;
|
||||
const loc = `${x},${y}` as LocString;
|
||||
if (objs[loc]?.event.noPass) continue;
|
||||
this.setMapDamage(damage, loc, this.info.repulse ?? 0, '阻击');
|
||||
damage[loc].repulse ??= [];
|
||||
damage[loc].repulse.push([this.x, this.y]);
|
||||
}
|
||||
}
|
||||
|
||||
// 追猎
|
||||
if (this.info.special.has(12)) {
|
||||
const objs = core.getMapBlocksObj(this.floorId);
|
||||
for (let nx = 0; nx < w; nx++) {
|
||||
const loc = `${nx},${this.y}` as LocString;
|
||||
const block = objs[loc];
|
||||
if (!block?.event.noPass) {
|
||||
// 捕捉
|
||||
if (this.info.special.has(27)) {
|
||||
const dirs: Dir[] = ['left', 'down', 'up', 'right'];
|
||||
for (const dir of dirs) {
|
||||
const { x: dx, y: dy } = core.utils.scan[dir];
|
||||
const x = this.x + dx;
|
||||
const y = this.y + dy;
|
||||
const loc = `${x},${y}` as LocString;
|
||||
if (objs[loc]?.event.noPass) continue;
|
||||
damage[loc] ??= { damage: 0, type: new Set() };
|
||||
damage[loc].hunt ??= [];
|
||||
damage[loc].hunt!.push([
|
||||
this.x,
|
||||
this.y,
|
||||
nx < this.x ? 'left' : 'right'
|
||||
]);
|
||||
damage[loc].ambush ??= [];
|
||||
damage[loc].ambush.push([this.x, this.y]);
|
||||
}
|
||||
}
|
||||
for (let ny = 0; ny < h; ny++) {
|
||||
const loc = `${this.x},${ny}` as LocString;
|
||||
const block = objs[loc];
|
||||
if (!block?.event.noPass) {
|
||||
damage[loc] ??= { damage: 0, type: new Set() };
|
||||
damage[loc].hunt ??= [];
|
||||
damage[loc].hunt!.push([
|
||||
this.x,
|
||||
this.y,
|
||||
ny < this.y ? 'up' : 'down'
|
||||
]);
|
||||
|
||||
// 夹击
|
||||
if (this.info.special.has(16)) {
|
||||
// 只计算右方和下方的怪物,这样就可以避免一个点被重复计算两次
|
||||
const dirs: Dir[] = ['down', 'right'];
|
||||
for (const dir of dirs) {
|
||||
const { x: dx, y: dy } = core.utils.scan[dir];
|
||||
const x = this.x + dx * 2;
|
||||
const y = this.y + dy * 2;
|
||||
const e = this.col?.get(x, y);
|
||||
if (!e) continue;
|
||||
const info = e.getRealInfo();
|
||||
if (!info.special.has(16)) continue;
|
||||
const cx = this.x + dx;
|
||||
const cy = this.y + dy;
|
||||
const loc = `${cx},${cy}` as LocString;
|
||||
if (objs[loc]?.event.noPass) continue;
|
||||
const half = getHeroStatusOn('hp') / 2;
|
||||
let bt = half;
|
||||
// 夹击不超伤害值
|
||||
if (core.flags.betweenAttackMax) {
|
||||
const aDamage = this.calDamage().damage;
|
||||
const bDamage = e.calDamage().damage;
|
||||
bt = Math.min(aDamage, bDamage, half);
|
||||
}
|
||||
this.setMapDamage(damage, loc, bt, '夹击');
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,7 +484,10 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
if (type) damage[loc].type.add(type);
|
||||
}
|
||||
|
||||
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: UserEnemyInfo) {
|
||||
private calEnemyDamageOf(
|
||||
hero: Partial<HeroStatus>,
|
||||
enemy: EnemyInfo
|
||||
): DamageInfo {
|
||||
const status = getHeroStatusOf(hero, realStatus, this.floorId);
|
||||
const damage = calDamageWith(enemy, status) ?? Infinity;
|
||||
|
||||
@ -849,118 +626,75 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
*/
|
||||
getSeckillAtk(): number {
|
||||
const info = this.getRealInfo();
|
||||
const add = info.def + info.hp - core.status.hero.mana;
|
||||
|
||||
// 坚固,不可能通过攻击秒杀
|
||||
if (info.special.has(3)) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
// 列方程求解,拿笔算一下就知道了
|
||||
// 饥渴,会偷取勇士攻击
|
||||
if (info.special.has(7)) {
|
||||
if (info.damageDecline === 0) {
|
||||
return add / (1 - this.enemy.hungry! / 100);
|
||||
} else {
|
||||
return (
|
||||
(info.hp / (1 - info.damageDecline / 100) -
|
||||
core.status.hero.mana +
|
||||
info.def) /
|
||||
(1 - this.enemy.hungry! / 100)
|
||||
);
|
||||
}
|
||||
// 常规怪物秒杀攻击是怪物防御+怪物生命
|
||||
return info.def + info.hp;
|
||||
}
|
||||
}
|
||||
|
||||
// 霜冻
|
||||
if (info.special.has(20)) {
|
||||
return (
|
||||
info.def +
|
||||
info.hp / (1 - this.enemy.ice! / 100) -
|
||||
core.status.hero.mana
|
||||
);
|
||||
}
|
||||
|
||||
if (info.damageDecline !== 0) {
|
||||
return (
|
||||
info.def +
|
||||
info.hp / (1 - info.damageDecline / 100) -
|
||||
core.status.hero.mana
|
||||
);
|
||||
} else {
|
||||
return add;
|
||||
}
|
||||
}
|
||||
export interface DamageWithTurn {
|
||||
damage: number;
|
||||
turn: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算伤害时会用到的勇士属性,攻击防御,其余的不会有buff加成,直接从core.status.hero取
|
||||
* 如果有属性不会被 buff 加成请在这里去除,有助于提高性能表现
|
||||
*/
|
||||
const realStatus: (keyof HeroStatus)[] = [
|
||||
'atk',
|
||||
'def',
|
||||
'hpmax',
|
||||
'mana',
|
||||
'magicDef'
|
||||
];
|
||||
const realStatus: (keyof HeroStatus)[] = ['atk', 'def', 'mdef', 'hpmax'];
|
||||
|
||||
/** 当前是否正在计算支援怪的伤害 */
|
||||
let inGuard = false;
|
||||
|
||||
/**
|
||||
* 计算怪物伤害
|
||||
* 计算伤害,返回值包含伤害与回合数
|
||||
* @param info 怪物信息
|
||||
* @param hero 勇士信息
|
||||
* @param hero 勇士真实属性
|
||||
*/
|
||||
export function calDamageWith(
|
||||
info: UserEnemyInfo,
|
||||
export function calDamageWithTurn(
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number {
|
||||
const { mdef } = core.status.hero;
|
||||
const { def, mana, magicDef } = hero as HeroStatus;
|
||||
const { hp: monHp, def: monDef, special, enemy } = info;
|
||||
let { atk, hpmax } = hero as HeroStatus;
|
||||
let { atk: monAtk } = info;
|
||||
): DamageWithTurn {
|
||||
const { hp } = core.status.hero;
|
||||
const { atk, def, mdef } = hero as HeroStatus;
|
||||
const { atk: monAtk, def: monDef, special } = info;
|
||||
let { hp: monHp } = info;
|
||||
|
||||
// 赏金,优先级最高
|
||||
if (special.has(34)) return 0;
|
||||
|
||||
hpmax = Math.min(hpmax, def / 10);
|
||||
// 无敌
|
||||
if (special.has(20) && core.itemCount('cross') < 1) {
|
||||
return { damage: Infinity, turn: 0 };
|
||||
}
|
||||
|
||||
/** 怪物会对勇士造成的总伤害 */
|
||||
let damage = 0;
|
||||
|
||||
// 饥渴
|
||||
if (special.has(7)) {
|
||||
const delta = Math.floor((atk * info.hungry!) / 100);
|
||||
atk -= delta;
|
||||
monAtk += delta;
|
||||
/** 勇士每轮造成的伤害 */
|
||||
let heroPerDamage: number = 0;
|
||||
/** 怪物每轮造成的伤害 */
|
||||
let enemyPerDamage: number = 0;
|
||||
|
||||
// 勇士每轮伤害为勇士攻击减去怪物防御
|
||||
heroPerDamage += atk - monDef;
|
||||
|
||||
// 吸血
|
||||
if (special.has(11)) {
|
||||
const vampire = info.vampire ?? 0;
|
||||
const value = (vampire / 100) * hp;
|
||||
damage += value;
|
||||
// 如果吸血加到自身
|
||||
if (info.add) {
|
||||
monHp += value;
|
||||
}
|
||||
|
||||
let heroPerDamage: number;
|
||||
|
||||
// 绝对防御
|
||||
if (special.has(9)) {
|
||||
heroPerDamage = atk + mana - monDef;
|
||||
if (heroPerDamage <= 0) return Infinity;
|
||||
} else if (special.has(3)) {
|
||||
// 由于坚固的特性,只能放到这来计算了
|
||||
if (atk > enemy.def) heroPerDamage = 1 + mana;
|
||||
else return Infinity;
|
||||
} else {
|
||||
heroPerDamage = atk - monDef;
|
||||
if (heroPerDamage > 0) heroPerDamage += mana;
|
||||
else return Infinity;
|
||||
}
|
||||
|
||||
// 霜冻
|
||||
if (special.has(20)) {
|
||||
heroPerDamage *= 1 - info.ice! / 100;
|
||||
}
|
||||
|
||||
heroPerDamage *= 1 - info.damageDecline / 100;
|
||||
|
||||
let enemyPerDamage: number;
|
||||
|
||||
// 魔攻
|
||||
if (special.has(2) || special.has(13)) {
|
||||
if (special.has(2)) {
|
||||
enemyPerDamage = monAtk;
|
||||
enemyPerDamage -= magicDef;
|
||||
} else {
|
||||
enemyPerDamage = monAtk - def;
|
||||
}
|
||||
@ -972,50 +706,81 @@ export function calDamageWith(
|
||||
|
||||
if (enemyPerDamage < 0) enemyPerDamage = 0;
|
||||
|
||||
// 苍蓝刻
|
||||
if (special.has(28)) {
|
||||
heroPerDamage *= 1 - info.paleShield! / 100;
|
||||
}
|
||||
|
||||
let turn = Math.ceil(monHp / heroPerDamage);
|
||||
|
||||
// 致命一击
|
||||
if (special.has(1)) {
|
||||
const times = Math.floor(turn / 5);
|
||||
damage += ((times * (info.crit! - 100)) / 100) * enemyPerDamage;
|
||||
// 支援,当怪物被支援且不包含支援标记时执行,因为支援怪不能再被支援了
|
||||
if (info.guard.length > 0 && !inGuard) {
|
||||
inGuard = true;
|
||||
// 支援中魔防只会被计算一次,因此除了当前怪物,计算其他怪物伤害时魔防为 0
|
||||
const status = { ...hero, mdef: 0 };
|
||||
// 计算支援怪的伤害,同时把打支援怪花费的回合数加到当前怪物上,因为打支援怪的时候当前怪物也会打你
|
||||
// 因此回合数需要加上打支援怪的回合数
|
||||
for (const enemy of info.guard) {
|
||||
// 直接把 enemy 传过去,因此支援的 enemy 会吃到其原本所在位置的光环加成
|
||||
const extraInfo = calDamageWithTurn(enemy, status);
|
||||
turn += extraInfo.turn;
|
||||
damage += extraInfo.damage;
|
||||
}
|
||||
|
||||
// 勇气之刃
|
||||
if (turn > 1 && special.has(10)) {
|
||||
damage += (info.courage! / 100 - 1) * enemyPerDamage;
|
||||
}
|
||||
|
||||
// 勇气冲锋
|
||||
if (special.has(11)) {
|
||||
damage += (info.charge! / 100) * enemyPerDamage;
|
||||
turn += 5;
|
||||
inGuard = false;
|
||||
}
|
||||
|
||||
// 先攻
|
||||
if (special.has(17)) {
|
||||
if (special.has(1)) {
|
||||
damage += enemyPerDamage;
|
||||
}
|
||||
|
||||
// 破甲
|
||||
if (special.has(7)) {
|
||||
const value = info.breakArmor ?? core.values.breakArmor;
|
||||
damage += (value / 100) * def;
|
||||
}
|
||||
|
||||
// 反击
|
||||
if (special.has(8)) {
|
||||
const value = info.counterAttack ?? core.values.counterAttack;
|
||||
// 反击是每回合生效,因此加到 enemyPerDamage 上
|
||||
enemyPerDamage += (value / 100) * atk;
|
||||
}
|
||||
|
||||
// 净化
|
||||
if (special.has(9)) {
|
||||
const value = info.purify ?? core.values.purify;
|
||||
damage += mdef * value;
|
||||
}
|
||||
|
||||
damage += (turn - 1) * enemyPerDamage;
|
||||
// 无上之盾
|
||||
if (flags.superSheild) {
|
||||
damage -= mdef / 10;
|
||||
}
|
||||
// 生命回复
|
||||
damage -= hpmax * turn;
|
||||
if (flags.hard === 1) damage *= 0.9;
|
||||
|
||||
if (flags.chapter > 1 && damage < 0) {
|
||||
const dm = -info.hp * 0.25;
|
||||
if (damage < dm) damage = dm;
|
||||
// 魔防
|
||||
damage -= mdef;
|
||||
|
||||
// 未开启负伤时,如果伤害为负,则设为 0
|
||||
if (!core.flags.enableNegativeDamage && damage < 0) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
return Math.floor(damage);
|
||||
// 固伤,无法被魔防减伤
|
||||
if (special.has(22)) {
|
||||
damage += info.damage ?? 0;
|
||||
}
|
||||
|
||||
// 仇恨,无法被魔防减伤
|
||||
if (special.has(17)) {
|
||||
damage += core.getFlag('hatred', 0);
|
||||
}
|
||||
|
||||
return { damage: Math.floor(damage), turn };
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算怪物伤害
|
||||
* @param info 怪物信息
|
||||
* @param hero 勇士信息
|
||||
*/
|
||||
export function calDamageWith(
|
||||
info: EnemyInfo,
|
||||
hero: Partial<HeroStatus>
|
||||
): number {
|
||||
return calDamageWithTurn(info, hero).damage;
|
||||
}
|
||||
|
||||
export function ensureFloorDamage(floorId: FloorIds) {
|
||||
|
@ -1,13 +1,26 @@
|
||||
import { EnemyInfo } from '@motajs/types';
|
||||
import { getHeroStatusOn } from '../state/hero';
|
||||
import { UserEnemyInfo } from './damage';
|
||||
|
||||
export interface SpecialDeclaration {
|
||||
code: number;
|
||||
name: string | ((enemy: UserEnemyInfo) => string);
|
||||
desc: string | ((enemy: UserEnemyInfo) => string);
|
||||
name: string | ((enemy: EnemyInfo) => string);
|
||||
desc: string | ((enemy: EnemyInfo) => string);
|
||||
color: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 怪物特殊属性列表,当前版本中 code 最好与索引保持一致,不然可能会出现问题
|
||||
* 属性实现位置一览('./'表示当前文件夹 '../'表示上一级文件夹):
|
||||
* 1. 调参类属性 / 仅影响战斗过程的属性:./damage.ts calDamageWithTurn 函数
|
||||
* 2. 地图伤害:./damage.ts DamageEnemy.calMapDamage 方法,搜索 calMapDamage 即可搜到
|
||||
* 3. 光环属性:./damage.ts DamageEnemy.provideHalo 方法,搜索 provideHalo 即可搜到
|
||||
* 4. 仇恨 / 退化 等战后效果:packages-user/data-fallback/src/battle.ts 中的 afterBattle
|
||||
* 5. 中毒的每步效果:../state/move.ts HeroMover.onStepEnd 方法,在约 590 行
|
||||
* 6. 中毒的瞬移效果:还在脚本编辑的 moveDirectly
|
||||
* 7. 衰弱效果:../state/hero.ts getHeroStatusOf 方法
|
||||
* 8. 重生属性:还在脚本编辑的 changingFloor
|
||||
* 9. 阻击 / 捕捉 的每步效果:packages-user/legacy-plugin-data/src/enemy/checkblock.ts
|
||||
*/
|
||||
export const specials: SpecialDeclaration[] = [
|
||||
{
|
||||
code: 0,
|
||||
@ -48,7 +61,7 @@ export const specials: SpecialDeclaration[] = [
|
||||
{
|
||||
code: 6,
|
||||
name: enemy => `${enemy.n ?? 4}连击`,
|
||||
desc: enemy => `怪物每回合攻击${enemy.n}次。`,
|
||||
desc: enemy => `怪物每回合攻击${enemy.n ?? 4}次。`,
|
||||
color: '#fe7'
|
||||
},
|
||||
{
|
||||
@ -82,7 +95,7 @@ export const specials: SpecialDeclaration[] = [
|
||||
code: 11,
|
||||
name: '吸血',
|
||||
desc: enemy => {
|
||||
const vampire = enemy.vampire ?? 10;
|
||||
const vampire = enemy.vampire ?? 0;
|
||||
return (
|
||||
`战斗前,怪物首先吸取角色的${vampire}%生命` +
|
||||
`(约${Math.floor((vampire / 100) * getHeroStatusOn('hp'))}点)作为伤害` +
|
||||
@ -101,7 +114,14 @@ export const specials: SpecialDeclaration[] = [
|
||||
{
|
||||
code: 13,
|
||||
name: '衰弱',
|
||||
desc: '怪物攻击无视勇士的防御。',
|
||||
desc: () => {
|
||||
const weak = core.values.weakValue;
|
||||
if (weak < 1) {
|
||||
return `战斗后,角色陷入衰弱状态,攻防暂时下降${Math.floor(weak * 100)}%`;
|
||||
} else {
|
||||
return `战斗后,角色陷入衰弱状态,攻防暂时下降${weak}点`;
|
||||
}
|
||||
},
|
||||
color: '#f0bbcc'
|
||||
},
|
||||
{
|
||||
@ -114,7 +134,7 @@ export const specials: SpecialDeclaration[] = [
|
||||
code: 15,
|
||||
name: '领域',
|
||||
desc: enemy =>
|
||||
`经过怪物周围${enemy.zoneSquare ? '九宫格' : '十字'}范围内${enemy.range}格时自动减生命${enemy.zone}点。`,
|
||||
`经过怪物周围${enemy.zoneSquare ? '九宫格' : '十字'}范围内${enemy.range ?? 1}格时自动减生命${enemy.zone ?? 0}点。`,
|
||||
color: '#c677dd'
|
||||
},
|
||||
{
|
||||
@ -126,14 +146,15 @@ export const specials: SpecialDeclaration[] = [
|
||||
{
|
||||
code: 17,
|
||||
name: '仇恨',
|
||||
desc: `战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得${core.values.hatred}点仇恨值)。`,
|
||||
desc: () =>
|
||||
`战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得${core.values.hatred}点仇恨值)。`,
|
||||
color: '#b0b666'
|
||||
},
|
||||
{
|
||||
code: 18,
|
||||
name: '阻击',
|
||||
desc: enemy =>
|
||||
`经过怪物十字范围内时怪物后退一格,同时对勇士造成${enemy.repulse}点伤害。`,
|
||||
`经过怪物十字范围内时怪物后退一格,同时对勇士造成${enemy.repulse ?? 0}点伤害。`,
|
||||
color: '#8888e6'
|
||||
},
|
||||
{
|
||||
@ -193,7 +214,7 @@ export const specials: SpecialDeclaration[] = [
|
||||
str += `,生命提升${enemy.hpBuff}%`;
|
||||
}
|
||||
if (enemy.atkBuff) {
|
||||
str += `,攻击提升${enemy.hpBuff}%`;
|
||||
str += `,攻击提升${enemy.atkBuff}%`;
|
||||
}
|
||||
if (enemy.defBuff) {
|
||||
str += `,防御提升${enemy.defBuff}%`;
|
||||
|
@ -20,7 +20,7 @@ export function getHeroStatusOn(
|
||||
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
|
||||
floorId?: FloorIds
|
||||
) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error 暂时无法推导
|
||||
return getHeroStatusOf(core.status.hero, name, floorId);
|
||||
}
|
||||
|
||||
@ -96,9 +96,22 @@ function getRealStatus(
|
||||
s *= core.status.hero.buff[name] ?? 1;
|
||||
s = Math.floor(s);
|
||||
|
||||
// 衰弱效果
|
||||
if ((name === 'atk' || name === 'def') && flags.weak) {
|
||||
const weak = core.values.weakValue;
|
||||
if (weak < 1) {
|
||||
// 百分比衰弱
|
||||
s *= 1 - weak;
|
||||
} else {
|
||||
s -= weak;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// 下面的内容暂时无用
|
||||
|
||||
export interface IHeroStatusDefault {
|
||||
atk: number;
|
||||
def: number;
|
||||
@ -224,7 +237,7 @@ export class HeroState<
|
||||
refreshStatus(key?: keyof T): boolean {
|
||||
if (key === void 0) {
|
||||
for (const [key, value] of Object.entries(this.status)) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error 暂时无法推导
|
||||
this.computedStatus[key] = HeroState.cal(this, key, value);
|
||||
}
|
||||
return true;
|
||||
@ -257,7 +270,7 @@ export class HeroState<
|
||||
}
|
||||
}
|
||||
|
||||
interface IHeroItem {
|
||||
interface _IHeroItem {
|
||||
items: Map<AllIdsOf<'items'>, number>;
|
||||
|
||||
/**
|
||||
|
@ -459,10 +459,11 @@ export class HeroMover extends ObjectMoverBase {
|
||||
const map = core.status.thisMap.enemy.mapDamage;
|
||||
const dam = map[index];
|
||||
const nextDam = map[nIndex];
|
||||
if (nextDam?.mockery || (!dam?.hunt && nextDam?.hunt)) {
|
||||
core.autosave();
|
||||
return true;
|
||||
}
|
||||
if (!dam || !nextDam) return;
|
||||
// 可以在这里判断地图伤害,并进行自动存档,例如在进入或离开地图伤害时存档
|
||||
// if (dam.damage > 0 || nextDam.damage > 0) {
|
||||
// core.autosave()
|
||||
// }
|
||||
}
|
||||
|
||||
protected async onMoveStart(controller: IMoveController): Promise<void> {
|
||||
@ -586,6 +587,12 @@ export class HeroMover extends ObjectMoverBase {
|
||||
core.control._moveAction_popAutomaticRoute();
|
||||
if (!this.noRoute) core.status.route.push(direction);
|
||||
|
||||
// 中毒处理
|
||||
if (core.hasFlag('poison')) {
|
||||
core.status.hero.hp -= core.values.poisonDamage;
|
||||
core.updateStatusBar();
|
||||
}
|
||||
|
||||
core.moveOneStep();
|
||||
core.checkRouteFolding();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { isNil } from 'lodash-es';
|
||||
interface RangeTypeData {
|
||||
square: { x: number; y: number; d: number };
|
||||
rect: { x: number; y: number; w: number; h: number };
|
||||
manhattan: { x: number; y: number; d: number };
|
||||
}
|
||||
|
||||
type InRangeFn<E extends Partial<Loc>, T> = (item: E, data: T) => boolean;
|
||||
@ -79,3 +80,7 @@ Range.register('rect', (item, { x, y, w, h }) => {
|
||||
const ey = y + h;
|
||||
return item.x >= x && item.y >= y && item.x < ex && item.y < ey;
|
||||
});
|
||||
Range.register('manhattan', (item, { x, y, d }) => {
|
||||
if (isNil(item.x) || isNil(item.y)) return false;
|
||||
return Math.abs(item.x - x) + Math.abs(item.y - y) < d;
|
||||
});
|
||||
|
@ -168,12 +168,12 @@ export function boundary(arr: any, key?: any) {
|
||||
* @param from 初始坐标
|
||||
* @param to 指向坐标
|
||||
*/
|
||||
export function findDir(from: Loc, to: Loc): Dir2 | 'none' {
|
||||
const dx = to.x - from.x;
|
||||
const dy = to.y - from.y;
|
||||
export function findDir(from: Loc, to: Loc): Dir | 'none' {
|
||||
const dx = Math.sign(to.x - from.x);
|
||||
const dy = Math.sign(to.y - from.y);
|
||||
return (
|
||||
(Object.entries(core.utils.scan2).find(v => {
|
||||
v[1].x === dx && v[1].y === dy;
|
||||
})?.[0] as Dir2) ?? 'none'
|
||||
(Object.entries(core.utils.scan).find(v => {
|
||||
return v[1].x === dx && v[1].y === dy;
|
||||
})?.[0] as Dir) ?? 'none'
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
import { DamageEnemy } from '@user/data-state';
|
||||
import { findDir, ofDir } from '@user/data-utils';
|
||||
|
||||
export function createCheckBlock() {
|
||||
// 伤害弹出
|
||||
// 复写阻激夹域检测
|
||||
// 地图伤害在这实现。2.C 会修改实现方式
|
||||
control.prototype.checkBlock = function () {
|
||||
const x = core.getHeroLoc('x'),
|
||||
y = core.getHeroLoc('y'),
|
||||
loc = x + ',' + y;
|
||||
const info = core.status.thisMap.enemy.mapDamage[loc];
|
||||
const damage = info?.damage;
|
||||
const heroLoc = core.status.hero.loc;
|
||||
const { x, y } = heroLoc;
|
||||
const loc = `${x},${y}`;
|
||||
const col = core.status.thisMap.enemy;
|
||||
const info = col.mapDamage[loc];
|
||||
if (!info) return;
|
||||
const damage = info.damage;
|
||||
|
||||
// 阻击夹域伤害
|
||||
if (damage) {
|
||||
core.status.hero.hp -= damage;
|
||||
const type = [...info.type];
|
||||
@ -24,5 +30,62 @@ export function createCheckBlock() {
|
||||
core.updateStatusBar();
|
||||
}
|
||||
}
|
||||
|
||||
const actions: MotaAction[] = [];
|
||||
|
||||
// 阻击效果
|
||||
if (info.repulse) {
|
||||
for (const [x, y] of info.repulse) {
|
||||
const loc2 = { x, y };
|
||||
const dir = findDir(heroLoc, loc2);
|
||||
if (dir === 'none') continue;
|
||||
const [nx, ny] = ofDir(x, y, dir);
|
||||
if (core.noPass(nx, ny) || !core.canMoveHero(x, y, dir)) {
|
||||
continue;
|
||||
}
|
||||
actions.push({
|
||||
type: 'move',
|
||||
time: 250,
|
||||
keep: true,
|
||||
loc: [x, y],
|
||||
steps: [`${dir}:1`],
|
||||
async: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** 存储要和哪些捕捉怪战斗 */
|
||||
const ambushEnemies: DamageEnemy[] = [];
|
||||
|
||||
// 捕捉效果
|
||||
if (info.ambush) {
|
||||
for (const [x, y] of info.ambush) {
|
||||
const loc2 = { x, y };
|
||||
const dir = findDir(loc2, heroLoc);
|
||||
if (dir === 'none') continue;
|
||||
actions.push({
|
||||
type: 'move',
|
||||
time: 250,
|
||||
keep: false,
|
||||
loc: [x, y],
|
||||
steps: [`${dir}:1`],
|
||||
async: true
|
||||
});
|
||||
const enemy = col.get(x, y);
|
||||
if (enemy) {
|
||||
ambushEnemies.push(enemy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actions.length > 0) {
|
||||
actions.push({ type: 'waitAsync' });
|
||||
// 与捕捉怪战斗
|
||||
core.insertAction(actions, void 0, void 0, () => {
|
||||
ambushEnemies.forEach(v => {
|
||||
core.battle(v, v.y, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -514,10 +514,19 @@ export function initFallback() {
|
||||
mover.insertMove(...[start, ...resolved]);
|
||||
const controller = mover.startMove();
|
||||
|
||||
const id = fallbackIds++;
|
||||
core.animateFrame.asyncId[id] = () => {
|
||||
if (!keep) {
|
||||
core.removeBlock(mover.x, mover.y);
|
||||
}
|
||||
};
|
||||
|
||||
if (controller) {
|
||||
await controller.onEnd;
|
||||
}
|
||||
|
||||
delete core.animateFrame.asyncId[id];
|
||||
|
||||
if (!keep) {
|
||||
core.removeBlock(mover.x, mover.y);
|
||||
}
|
||||
@ -566,7 +575,18 @@ export function initFallback() {
|
||||
|
||||
core.updateStatusBar();
|
||||
core.removeBlock(sx, sy);
|
||||
|
||||
const id = fallbackIds++;
|
||||
core.animateFrame.asyncId[id] = () => {
|
||||
if (keep) {
|
||||
core.setBlock(block.id, ex, ey);
|
||||
}
|
||||
};
|
||||
|
||||
await promise;
|
||||
|
||||
delete core.animateFrame.asyncId[id];
|
||||
|
||||
if (keep) {
|
||||
core.setBlock(block.id, ex, ey);
|
||||
}
|
||||
|
@ -5,11 +5,11 @@ export interface EnemyInfo extends Partial<Omit<Enemy, 'special'>> {
|
||||
def: number;
|
||||
hp: number;
|
||||
special: Set<number>;
|
||||
damageDecline: number;
|
||||
atkBuff_: number;
|
||||
defBuff_: number;
|
||||
hpBuff_: number;
|
||||
enemy: Enemy;
|
||||
guard: EnemyInfo[];
|
||||
x?: number;
|
||||
y?: number;
|
||||
floorId?: FloorIds;
|
||||
@ -18,8 +18,8 @@ export interface EnemyInfo extends Partial<Omit<Enemy, 'special'>> {
|
||||
export interface MapDamage {
|
||||
damage: number;
|
||||
type: Set<string>;
|
||||
mockery?: LocArr[];
|
||||
hunt?: [x: number, y: number, dir: Dir][];
|
||||
repulse?: LocArr[];
|
||||
ambush?: LocArr[];
|
||||
}
|
||||
|
||||
export interface DamageDelta {
|
||||
@ -93,11 +93,6 @@ export interface IDamageEnemy {
|
||||
*/
|
||||
getRealInfo(): EnemyInfo;
|
||||
|
||||
/**
|
||||
* 获取这个怪物的所有光环属性
|
||||
*/
|
||||
getHaloSpecials(): Set<number>;
|
||||
|
||||
/**
|
||||
* 计算怪物伤害
|
||||
*/
|
||||
|
@ -207,139 +207,125 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
||||
},
|
||||
"_data": "特殊属性"
|
||||
},
|
||||
"crit": {
|
||||
"n": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "致命一击",
|
||||
"_data": "致命一击"
|
||||
"_docs": "连击数",
|
||||
"_data": "连击数"
|
||||
},
|
||||
"charge": {
|
||||
"breakArmor": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "勇气冲锋",
|
||||
"_data": "勇气冲锋"
|
||||
"_docs": "破甲百分比",
|
||||
"_data": "破甲百分比"
|
||||
},
|
||||
"courage": {
|
||||
"counterAttack": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "勇气之刃",
|
||||
"_data": "勇气之刃"
|
||||
"_docs": "反击百分比",
|
||||
"_data": "反击百分比"
|
||||
},
|
||||
"together": {
|
||||
"purify": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "抱团",
|
||||
"_data": "抱团"
|
||||
"_docs": "净化倍率",
|
||||
"_data": "净化倍率"
|
||||
},
|
||||
"hungry": {
|
||||
"vampire": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "饥渴",
|
||||
"_data": "饥渴"
|
||||
"_docs": "吸血百分比",
|
||||
"_data": "吸血百分比"
|
||||
},
|
||||
"ice": {
|
||||
"add": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "霜冻",
|
||||
"_data": "霜冻"
|
||||
"_docs": "吸血加到自身",
|
||||
"_data": "吸血是否加到怪物自身血量上"
|
||||
},
|
||||
"iceHalo": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "冰封光环",
|
||||
"_data": "冰封光环"
|
||||
},
|
||||
"night": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "永夜",
|
||||
"_data": "永夜"
|
||||
},
|
||||
"day": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "极昼",
|
||||
"_data": "极昼"
|
||||
},
|
||||
"melt": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "融化",
|
||||
"_data": "融化"
|
||||
},
|
||||
"iceCore": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "冰封之核",
|
||||
"_data": "冰封之核"
|
||||
},
|
||||
"fireCore": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "火焰之核",
|
||||
"_data": "火焰之核"
|
||||
},
|
||||
"paleShield": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "苍蓝刻",
|
||||
"_data": "苍蓝刻"
|
||||
},
|
||||
"translation": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "乾坤挪移",
|
||||
"_data": "乾坤挪移"
|
||||
},
|
||||
"hpHalo": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "再生光环",
|
||||
"_data": "再生光环"
|
||||
},
|
||||
"assimilateRange": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "同化范围",
|
||||
"_data": "同化范围"
|
||||
},
|
||||
"horn": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "战争号角",
|
||||
"_data": "战争号角"
|
||||
},
|
||||
"specialHalo": {
|
||||
"_leaf": true,
|
||||
"_type": "popCheckboxSet",
|
||||
"_checkboxSet": function () {
|
||||
var array = Mota.require('@user/data-state').specials;
|
||||
var b = [],
|
||||
c = [];
|
||||
for (var index = 0; index < array.length; index++) {
|
||||
b.push(index);
|
||||
var name = array[index].name;
|
||||
if (name instanceof Function) name = name({});
|
||||
c.push(name + "(" + index + ")");
|
||||
}
|
||||
return {
|
||||
"prefix": c,
|
||||
"key": b
|
||||
}
|
||||
},
|
||||
"_data": "杀戮光环"
|
||||
},
|
||||
"specialMultiply": {
|
||||
"zoneSquare": {
|
||||
"_leaf": true,
|
||||
"_type": "checkbox",
|
||||
"_docs": "光环叠加",
|
||||
"_data": "光环叠加是否为乘算"
|
||||
"_docs": "领域九宫格",
|
||||
"_data": "领域九宫格"
|
||||
},
|
||||
"range": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "范围",
|
||||
"_data": "领域或光环范围"
|
||||
},
|
||||
"zone": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "领域伤害",
|
||||
"_data": "领域伤害"
|
||||
},
|
||||
"repulse": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "阻击伤害",
|
||||
"_data": "阻击伤害"
|
||||
},
|
||||
"atkValue": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "退化攻击",
|
||||
"_data": "退化攻击"
|
||||
},
|
||||
"defValue": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "退化防御",
|
||||
"_data": "退化防御"
|
||||
},
|
||||
"damage": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "固伤伤害",
|
||||
"_data": "固伤伤害"
|
||||
},
|
||||
"laser": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "激光伤害",
|
||||
"_data": "激光伤害"
|
||||
},
|
||||
"haloRange": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "光环范围",
|
||||
"_data": "光环范围"
|
||||
"_data": "光环范围,不填表示 1"
|
||||
},
|
||||
"haloSquare": {
|
||||
"_leaf": true,
|
||||
"_type": "checkbox",
|
||||
"_docs": "光环九宫格",
|
||||
"_data": "光环九宫格"
|
||||
},
|
||||
"hpBuff": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "光环加血",
|
||||
"_data": "光环加血百分比"
|
||||
},
|
||||
"atkBuff": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "光环加攻",
|
||||
"_data": "光环加攻百分比"
|
||||
},
|
||||
"defBuff": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "光环加防",
|
||||
"_data": "光环加防百分比"
|
||||
},
|
||||
"haloAdd": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_docs": "光环叠加",
|
||||
"_data": "光环是否叠加"
|
||||
},
|
||||
"value": {
|
||||
"_leaf": true,
|
||||
@ -347,66 +333,12 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
||||
"_docs": "特殊属性数值",
|
||||
"_data": "特殊属性的数值\n如:领域/阻激/激光怪的伤害值;吸血怪的吸血比例;光环怪增加生命的比例"
|
||||
},
|
||||
"zoneSquare": {
|
||||
"_leaf": true,
|
||||
"_type": "checkbox",
|
||||
"_docs": "九宫格",
|
||||
"_data": "领域、阻击、光环或捕捉怪是否九宫格"
|
||||
},
|
||||
"range": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null",
|
||||
"_docs": "领域范围",
|
||||
"_data": "领域或光环的范围;领域不加默认为1,光环不加则为全图效果"
|
||||
},
|
||||
"notBomb": {
|
||||
"_leaf": true,
|
||||
"_type": "checkbox",
|
||||
"_docs": "不可炸",
|
||||
"_data": "该怪物不可被炸"
|
||||
},
|
||||
"n": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null",
|
||||
"_docs": "连击数",
|
||||
"_data": "多连击的连击数,净化怪的净化倍率"
|
||||
},
|
||||
"add": {
|
||||
"_leaf": true,
|
||||
"_type": "checkbox",
|
||||
"_docs": "吸血加到自身",
|
||||
"_data": "吸血后是否加到自身;光环是否叠加"
|
||||
},
|
||||
"atkValue": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_range": "thiseval==~~thiseval||thiseval==null",
|
||||
"_docs": "退化扣攻",
|
||||
"_data": "退化时勇士下降的攻击力点数;光环怪增加攻击的比例;反击的比例"
|
||||
},
|
||||
"defValue": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_range": "thiseval==~~thiseval||thiseval==null",
|
||||
"_docs": "退化扣防",
|
||||
"_data": "退化时勇士下降的防御力点数;光环怪增加防御的比例;破甲的比例"
|
||||
},
|
||||
"damage": {
|
||||
"_leaf": true,
|
||||
"_type": "textarea",
|
||||
"_range": "thiseval==~~thiseval||thiseval==null",
|
||||
"_docs": "固伤",
|
||||
"_data": "战前扣血的点数"
|
||||
},
|
||||
"beforeBattle": {
|
||||
"_leaf": true,
|
||||
"_type": "event",
|
||||
"_event": "beforeBattle",
|
||||
"_docs": "战前事件",
|
||||
"_data": "和该怪物战斗前触发的事件列表"
|
||||
},
|
||||
"afterBattle": {
|
||||
"_leaf": true,
|
||||
"_type": "event",
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,7 +4,7 @@ var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
|
||||
"redSlime": {"name":"红头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[16,18],"value":10},
|
||||
"blackSlime": {"name":"青头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
"slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[1,9]},
|
||||
"bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1]},
|
||||
"bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1,27]},
|
||||
"bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
"redBat": {"name":"红蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
"vampire": {"name":"冥灵魔王","hp":888,"atk":888,"def":888,"money":888,"exp":888,"point":0,"special":[6],"n":8},
|
||||
|
@ -150,9 +150,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
// 根据分区信息自动砍层与恢复
|
||||
Mota.require('@user/legacy-plugin-data')?.autoRemoveMaps?.(floorId);
|
||||
|
||||
// 重置画布尺寸
|
||||
core.maps.resizeMap(floorId);
|
||||
|
||||
// ---------- 重绘新地图;这一步将会设置core.status.floorId ---------- //
|
||||
core.drawMap(floorId);
|
||||
|
||||
@ -192,6 +189,25 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
|
||||
core.updateDamage();
|
||||
|
||||
// 检查重生怪并重置
|
||||
if (!fromLoad) {
|
||||
core.extractBlocks(floorId);
|
||||
const obj = core.getMapBlocksObj(floorId);
|
||||
const floor = core.status.maps[floorId];
|
||||
const col = floor.enemy;
|
||||
col.list.forEach(v => {
|
||||
const info = v.getRealInfo();
|
||||
if (info.special.has(23)) {
|
||||
const block = obj[`${v.x},${v.y}`];
|
||||
if (block.disable) {
|
||||
block.disable = false;
|
||||
core.setMapBlockDisabled(floorId, v.x, v.y, false);
|
||||
core.maps._updateMapArray(floorId, v.x, v.y);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等
|
||||
},
|
||||
afterChangeFloor: function (floorId) {
|
||||
@ -347,6 +363,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
core.updateDamage();
|
||||
},
|
||||
moveOneStep: function (callback) {
|
||||
// 注意,此函数在 2.C 将会被弃用,并移动至 packages-user/data-state/src/state/move.ts 的 HeroMover 中
|
||||
// 一些样板内置内容已经移动至上述函数,包括中毒行为等
|
||||
|
||||
// 勇士每走一步后执行的操作。callback为行走完毕后的回调
|
||||
// 这个函数执行在“刚走完”的时候,即还没有检查该点的事件和领域伤害等。
|
||||
// 请注意:瞬间移动不会执行该函数。如果要控制能否瞬间移动有三种方法:
|
||||
@ -412,6 +431,13 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
// 判定能否瞬移到该点
|
||||
if (ignoreSteps == null) ignoreSteps = core.canMoveDirectly(x, y);
|
||||
if (ignoreSteps >= 0) {
|
||||
// 中毒也允许瞬移
|
||||
if (core.hasFlag('poison')) {
|
||||
const damage = ignoreSteps * core.values.poisonDamage;
|
||||
if (damage >= core.status.hero.hp) return false;
|
||||
core.status.hero.statistics.poisonDamage += damage;
|
||||
core.status.hero.hp -= damage;
|
||||
}
|
||||
core.clearMap('hero');
|
||||
// 获得勇士最后的朝向
|
||||
var lastDirection =
|
||||
|
@ -417,28 +417,28 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
|
||||
"cls": "tools",
|
||||
"name": "解毒药水",
|
||||
"text": "可以解除中毒状态",
|
||||
"useItemEffect": "core.triggerDebuff('remove', 'poison');",
|
||||
"useItemEffect": "core.removeFlag('poison')",
|
||||
"canUseItemEffect": "core.hasFlag('poison');"
|
||||
},
|
||||
"weakWine": {
|
||||
"cls": "tools",
|
||||
"name": "解衰药水",
|
||||
"text": "可以解除衰弱状态",
|
||||
"useItemEffect": "core.triggerDebuff('remove', 'weak');",
|
||||
"useItemEffect": "core.removeFlag('weak');",
|
||||
"canUseItemEffect": "core.hasFlag('weak');"
|
||||
},
|
||||
"curseWine": {
|
||||
"cls": "tools",
|
||||
"name": "解咒药水",
|
||||
"text": "可以解除诅咒状态",
|
||||
"useItemEffect": "core.triggerDebuff('remove', 'curse');",
|
||||
"useItemEffect": "core.removeFlag('curse');",
|
||||
"canUseItemEffect": "core.hasFlag('curse');"
|
||||
},
|
||||
"superWine": {
|
||||
"cls": "tools",
|
||||
"name": "万能药水",
|
||||
"text": "可以解除所有不良状态",
|
||||
"useItemEffect": "core.triggerDebuff('remove', ['poison', 'weak', 'curse']);",
|
||||
"useItemEffect": "core.removeFlag('poison');\ncore.removeFlag('weak');\ncore.removeFlag('curse');",
|
||||
"canUseItemEffect": "(function() {\n\treturn core.hasFlag('poison') || core.hasFlag('weak') || core.hasFlag('curse');\n})();"
|
||||
},
|
||||
"hammer": {
|
||||
|
@ -11,9 +11,9 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e =
|
||||
"9": {"cls":"terrains","id":"pinkShopLeft"},
|
||||
"10": {"cls":"terrains","id":"pinkShopRight"},
|
||||
"11": {"cls":"animates","id":"lavaNet","canPass":true,"trigger":"null","script":"(function () {\n\t// 血网的伤害效果移动到 checkBlock 中处理\n\n\t// 如果要做一次性血网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();","name":"血网"},
|
||||
"12": {"cls":"animates","id":"poisonNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'poison');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"毒网"},
|
||||
"13": {"cls":"animates","id":"weakNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'weak');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"衰网"},
|
||||
"14": {"cls":"animates","id":"curseNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.triggerDebuff('get', 'curse');\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"咒网"},
|
||||
"12": {"cls":"animates","id":"poisonNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.setFlag('poison', true);\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"毒网"},
|
||||
"13": {"cls":"animates","id":"weakNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.setFlag('weak', true);\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"衰网"},
|
||||
"14": {"cls":"animates","id":"curseNet","canPass":true,"trigger":"null","script":"(function () {\n\tif (!core.hasItem('amulet')) {\n\t\tcore.setFlag('curse', true);\n\t\tcore.updateStatusBar();\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()","name":"咒网"},
|
||||
"15": {"cls":"animates","id":"blueLava"},
|
||||
"16": {"cls":"animates","id":"water"},
|
||||
"20": {"cls":"autotile","id":"autotile"},
|
||||
|
2
src/types/declaration/core.d.ts
vendored
2
src/types/declaration/core.d.ts
vendored
@ -278,7 +278,7 @@ interface AnimateFrame {
|
||||
/**
|
||||
* 异步信息,想不到吧,这玩意是一个以number为索引的回调函数列表
|
||||
*/
|
||||
readonly asyncId: Record<number, () => void>;
|
||||
readonly asyncId: Record<number | symbol, () => void>;
|
||||
|
||||
/**
|
||||
* 上一个异步事件的id
|
||||
|
3
src/types/declaration/status.d.ts
vendored
3
src/types/declaration/status.d.ts
vendored
@ -802,9 +802,6 @@ interface HeroStatus {
|
||||
*/
|
||||
manamax: number;
|
||||
|
||||
/** 魔法防御 */
|
||||
magicDef: number;
|
||||
|
||||
/**
|
||||
* 勇士的名称
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user