chore: 怪物伤害计算与特殊属性调整为样板

This commit is contained in:
unanmed 2025-10-14 13:38:39 +08:00
parent fe46a7b397
commit 7c9847aa40
24 changed files with 578 additions and 751 deletions

View File

@ -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 {
// ... 原有内容

View File

@ -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;
}
```

View File

@ -210,7 +210,7 @@ const realStatus: (keyof HeroStatus)[] = [
```ts
export function calDamageWith(
info: UserEnemyInfo,
info: EnemyInfo,
hero: Partial<HeroStatus>
): number {
// ... 原有逻辑

View File

@ -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);

View File

@ -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;

View File

@ -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',

View File

@ -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) {

View File

@ -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}%`;

View File

@ -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>;
/**

View File

@ -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();
}

View File

@ -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;
});

View File

@ -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'
);
}

View File

@ -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);
});
});
}
};
}

View File

@ -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);
}

View File

@ -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>;
/**
*
*/

View File

@ -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

View File

@ -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},

View File

@ -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 =

View File

@ -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": {

View File

@ -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"},

View File

@ -278,7 +278,7 @@ interface AnimateFrame {
/**
* number为索引的回调函数列表
*/
readonly asyncId: Record<number, () => void>;
readonly asyncId: Record<number | symbol, () => void>;
/**
* id

View File

@ -802,9 +802,6 @@ interface HeroStatus {
*/
manamax: number;
/** 魔法防御 */
magicDef: number;
/**
*
*/