mirror of
https://github.com/motajs/template.git
synced 2026-11-03 15:34:31 +08:00
init: 2.B
This commit is contained in:
commit
6bf32174d8
610 files changed
+151444
No files matched your search
@@ -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:*"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { patchBattle } from './battle';
|
||||
import { patchDamage } from './damage';
|
||||
|
||||
export function patchAll() {
|
||||
patchBattle();
|
||||
patchDamage();
|
||||
}
|
||||
Reference in new issue
Block a user