mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-11-04 15:12: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 === '$') {
 | 
			
		||||
                // 表达式
 | 
			
		||||
                pointer++;
 | 
			
		||||
                inExpression = true;
 | 
			
		||||
                expStart = pointer + 1;
 | 
			
		||||
                continue;
 | 
			
		||||
                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)) {
 | 
			
		||||
        // 普通光环
 | 
			
		||||
        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 {
 | 
			
		||||
                    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);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            // 根据范围施加光环
 | 
			
		||||
            const range = this.info.haloRange ?? 1;
 | 
			
		||||
            if (this.info.haloSquare) {
 | 
			
		||||
                col.applyHalo(
 | 
			
		||||
                    'square',
 | 
			
		||||
                    { x: this.x, y: this.y, d: range * 2 + 1 },
 | 
			
		||||
                    this,
 | 
			
		||||
                    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, y: this.y, d: 5 },
 | 
			
		||||
                { x: this.x, y: this.y, d: 3 },
 | 
			
		||||
                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++;
 | 
			
		||||
                    }
 | 
			
		||||
                (e, enemy) => {
 | 
			
		||||
                    e.guard.push(enemy);
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
            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;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            enemy[key] ??= 0;
 | 
			
		||||
                            enemy[key] += e[key] ?? 0;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            col.haloList.push({
 | 
			
		||||
                type: 'square',
 | 
			
		||||
                data: range,
 | 
			
		||||
                special: 32,
 | 
			
		||||
                from: this
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        col.applyHalo(
 | 
			
		||||
            'square',
 | 
			
		||||
            { x: this.x + dx, y: this.y + dy, d: 7 },
 | 
			
		||||
            this,
 | 
			
		||||
            square7
 | 
			
		||||
        );
 | 
			
		||||
        col.applyHalo(
 | 
			
		||||
            'square',
 | 
			
		||||
            { x: this.x + dx, y: this.y + dy, d: 5 },
 | 
			
		||||
            this,
 | 
			
		||||
            square5
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 接受其他怪的光环
 | 
			
		||||
     */
 | 
			
		||||
    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) {
 | 
			
		||||
                    damage[loc] ??= { damage: 0, type: new Set() };
 | 
			
		||||
                    damage[loc].hunt ??= [];
 | 
			
		||||
                    damage[loc].hunt!.push([
 | 
			
		||||
                        this.x,
 | 
			
		||||
                        this.y,
 | 
			
		||||
                        nx < this.x ? 'left' : 'right'
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
        // 捕捉
 | 
			
		||||
        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].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)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 霜冻
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
        // 常规怪物秒杀攻击是怪物防御+怪物生命
 | 
			
		||||
        return info.def + info.hp;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 (turn > 1 && special.has(10)) {
 | 
			
		||||
        damage += (info.courage! / 100 - 1) * enemyPerDamage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 勇气冲锋
 | 
			
		||||
    if (special.has(11)) {
 | 
			
		||||
        damage += (info.charge! / 100) * enemyPerDamage;
 | 
			
		||||
        turn += 5;
 | 
			
		||||
    // 支援,当怪物被支援且不包含支援标记时执行,因为支援怪不能再被支援了
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
        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