init: 2.B

This commit is contained in:
AncTe committed 2026-02-28 20:23:34 +08:00
commit 6bf32174d8
610 files changed
+151444

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
{
"name": "@user/data-fallback",
"dependencies": {
"@motajs/legacy-common": "workspace:*",
"@user/data-state": "workspace:*",
"@user/data-base": "workspace:*",
"@user/data-utils": "workspace:*"
}
}
+264
View File
@@ -0,0 +1,264 @@
import { DamageEnemy, ensureFloorDamage, getEnemy } from '@user/data-state';
import { hook } from '@user/data-base';
import { Patch, PatchClass } from '@motajs/legacy-common';
import { isNil } from 'lodash-es';
export interface CurrentEnemy {
enemy: DamageEnemy;
// 这个是干啥的?
onMapEnemy: DamageEnemy[];
}
export function patchBattle() {
const patch = new Patch(PatchClass.Enemys);
const patch2 = new Patch(PatchClass.Events);
patch.add('canBattle', function (x, y, floorId) {
const enemy = typeof x === 'number' ? getEnemy(x, y!, floorId) : x;
if (!enemy) {
throw new Error(
`Cannot get enemy on x:${x}, y:${y}, floor: ${floorId}`
);
}
const { damage } = enemy.calDamage();
return damage < core.status.hero.hp;
});
function battle(
x: number | DamageEnemy,
y: number,
force: boolean = false,
callback?: () => void
) {
core.saveAndStopAutomaticRoute();
const isLoc = typeof x === 'number';
const enemy = isLoc ? getEnemy(x, y) : x;
if (!enemy) {
throw new Error(
`Cannot battle with enemy since no enemy on ${x},${y}`
);
}
// 非强制战斗
// @ts-expect-error 2.c 重构
if (!core.canBattle(x, y) && !force && !core.status.event.id) {
core.stopSound();
core.playSound('操作失败');
core.drawTip('你打不过此怪物!', enemy!.id);
return core.clearContinueAutomaticRoute(callback);
}
// 自动存档
if (!core.status.event.id) core.autosave(true);
// 战前事件
// 战后事件
core.afterBattle(enemy, isLoc ? x : enemy.x, y);
callback?.();
}
const getFacedId = (enemy: DamageEnemy) => {
const e = enemy.enemy;
if (e.displayIdInBook) return e.displayIdInBook;
if (e.faceIds) return e.faceIds.down;
return e.id;
};
patch.add('getCurrentEnemys', function (floorId = core.status.floorId) {
floorId = floorId || core.status.floorId;
const enemys: CurrentEnemy[] = [];
const used: Record<string, DamageEnemy[]> = {};
ensureFloorDamage(floorId);
const floor = core.status.maps[floorId];
floor.enemy.list.forEach(v => {
const id = getFacedId(v);
if (!(id in used)) {
const e = new DamageEnemy(v.enemy);
e.calAttribute();
e.getRealInfo();
e.calDamage();
const curr: CurrentEnemy = {
enemy: e,
onMapEnemy: [v]
};
enemys.push(curr);
used[id] = curr.onMapEnemy;
} else {
used[id].push(v);
}
});
return enemys.sort((a, b) => {
const ad = a.enemy.calDamage().damage;
const bd = b.enemy.calDamage().damage;
return ad - bd;
});
});
patch2.add('battle', battle);
patch2.add('_sys_battle', function (data: Block, callback?: () => void) {
// 检查战前事件
const floor = core.floors[core.status.floorId];
const beforeBattle: MotaEvent = [];
const loc = `${data.x},${data.y}` as LocString;
const enemy = getEnemy(data.x, data.y);
beforeBattle.push(...(floor.beforeBattle[loc] ?? []));
beforeBattle.push(...(enemy!.enemy.beforeBattle ?? []));
if (beforeBattle.length > 0) {
beforeBattle.push({ type: 'battle', x: data.x, y: data.y });
core.clearContinueAutomaticRoute();
// 自动存档
const inAction = core.status.event.id === 'action';
if (inAction) {
core.insertAction(beforeBattle, data.x, data.y);
core.doAction();
} else {
core.autosave(true);
core.insertAction(beforeBattle, data.x, data.y, callback);
}
} else {
battle(data.x, data.y, false, callback);
}
});
patch2.add('_action_battle', function (data, x, y, prefix) {
if (data.id) {
// const enemy = getSingleEnemy(data.id as EnemyIds);
// todo: 与不在地图上的怪物战斗
} else {
if (data.floorId !== core.status.floorId) {
core.doAction();
return;
}
const [ex, ey] = core.events.__action_getLoc(
data.loc,
x,
y,
prefix
) as LocArr;
battle(ex, ey, true, core.doAction);
}
});
patch2.add(
'afterBattle',
function (enemy: DamageEnemy, x?: number, y?: number) {
// 播放战斗动画
let animate: AnimationIds = 'hand';
// 检查当前装备是否存在攻击动画
const equipId = core.getEquip(0);
if (equipId && (core.material.items[equipId].equip || {}).animate)
animate = core.material.items[equipId].equip.animate;
// 检查该动画是否存在SE,如果不存在则使用默认音效
if (!core.material.animates[animate]?.se)
core.playSound('attack.opus');
// 战斗伤害
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;
core.updateStatusBar(false, true);
core.events.lose('战斗失败');
return;
}
// 扣减体力值并记录统计数据
core.status.hero.hp -= damage;
core.status.hero.statistics.battleDamage += damage;
core.status.hero.statistics.battle++;
// 获得金币经验
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;
core.status.hero.exp += exp;
core.status.hero.statistics.exp += 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 = [];
// 战后事件
if (!isNil(core.status.floorId)) {
const loc = `${x},${y}` as LocString;
todo.push(
...(core.floors[core.status.floorId].afterBattle[loc] ?? [])
);
}
todo.push(...(enemy.enemy.afterBattle ?? []));
// 如果事件不为空,将其插入
if (todo.length > 0) core.insertAction(todo, x, y);
if (!isNil(x) && !isNil(y)) {
core.drawAnimate(animate, x, y);
core.removeBlock(x, y);
} else core.drawHeroAnimate(animate);
// 如果已有事件正在处理中
if (isNil(core.status.event.id)) core.continueAutomaticRoute();
else core.clearContinueAutomaticRoute();
core.checkAutoEvents();
hook.emit('afterBattle', enemy, x, y);
}
);
}
declare global {
interface Enemys {
getCurrentEnemys(floorId: FloorIds): CurrentEnemy[];
canBattle(enemy: DamageEnemy, _?: number, floorId?: FloorIds): boolean;
canBattle(x: number, y: number, floorId?: FloorIds): boolean;
}
interface Events {
battle(
enemy: DamageEnemy,
y?: number,
force?: boolean,
callback?: () => void
): void;
battle(
x: number,
y?: number,
force?: boolean,
callback?: () => void
): void;
}
}
+124
View File
@@ -0,0 +1,124 @@
import { Patch, PatchClass } from '@motajs/legacy-common';
import { EnemyCollection, ensureFloorDamage } from '@user/data-state';
import { formatDamage } from '@user/data-utils';
import { isNil } from 'lodash-es';
export function patchDamage() {
const patch = new Patch(PatchClass.Control);
patch.add(
'updateDamage',
function (
floorId = core.status.floorId,
ctx,
thumbnail: boolean = false
) {
if (!floorId || core.status.gameOver || main.mode !== 'play')
return;
const onMap = isNil(ctx);
const floor = core.status.maps[floorId];
// 没有怪物手册
// if (!core.hasItem('book')) return;
core.status.damage.posX = core.bigmap.posX;
core.status.damage.posY = core.bigmap.posY;
if (!onMap) {
const width = core.floors[floorId].width,
height = core.floors[floorId].height;
// 地图过大的缩略图不绘制显伤
if (width * height > core.bigmap.threshold) return;
}
// 计算伤害
ensureFloorDamage(floorId);
floor.enemy.extract();
floor.enemy.calRealAttribute();
floor.enemy.calMapDamage();
floor.enemy.emit('calculated');
core.status.damage.data = [];
// floor.enemy.render(true);
// getItemDetail(floorId, onMap); // 宝石血瓶详细信息
if (thumbnail) {
renderThumbnailDamage(floor.enemy);
core.control.drawDamage(ctx, floorId);
}
}
);
}
function renderThumbnailDamage(col: EnemyCollection) {
if (main.replayChecking) return;
core.status.damage.data = [];
core.status.damage.extraData = [];
// 怪物伤害
col.list.forEach(v => {
const { damage } = v.calDamage();
// 伤害全部相等,绘制在怪物本身所在位置
const { damage: dam, color } = formatDamage(damage);
const critical = v.calCritical(1)[0];
core.status.damage.data.push({
text: dam,
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 1,
color: color
});
const setting = Mota.require('@motajs/legacy-ui').mainSetting;
const criGem = setting.getValue('screen.criticalGem', false);
const n = critical?.atkDelta ?? Infinity;
const ratio = core.status.maps[col.floorId].ratio;
const cri = criGem ? Math.ceil(n / ratio) : n;
core.status.damage.data.push({
text: isFinite(cri) ? cri.toString() : '?',
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 11,
color: '#fff'
});
});
// 地图伤害
const floor = core.status.maps[col.floorId];
const width = floor.width;
const height = floor.height;
const objs = core.getMapBlocksObj(col.floorId);
const startX = 0;
const endX = width;
const startY = 0;
const endY = height;
for (let x = startX; x < endX; x++) {
for (let y = startY; y < endY; y++) {
const id = `${x},${y}` as LocString;
const dam = col.mapDamage[id];
if (!dam || objs[id]?.event.noPass) continue;
// 地图伤害
if (dam.damage !== 0) {
const damage = core.formatBigNumber(dam.damage, true);
const color = dam.damage < 0 ? '#6eff6a' : '#fa3';
core.status.damage.extraData.push({
text: damage,
px: 32 * x + 16,
py: 32 * y + 16,
color,
alpha: 1
});
}
// 追猎
if (dam.ambush) {
core.status.damage.extraData.push({
text: '!',
px: 32 * x + 16,
py: 32 * (y + 1) - 14,
color: '#fd4',
alpha: 1
});
}
}
}
}
+7
View File
@@ -0,0 +1,7 @@
import { patchBattle } from './battle';
import { patchDamage } from './damage';
export function patchAll() {
patchBattle();
patchDamage();
}