refactor: 勇士移动fallback

This commit is contained in:
unanmed 2024-09-25 17:30:02 +08:00
parent 3de6aac6a1
commit 7c10af09c2
6 changed files with 101 additions and 356 deletions

View File

@ -168,7 +168,6 @@ export class HeroRenderer
*/ */
resetRenderable(getInfo: boolean) { resetRenderable(getInfo: boolean) {
this.movingFrame = 0; this.movingFrame = 0;
console.trace();
if (this.renderable) { if (this.renderable) {
this.renderable.animate = 0; this.renderable.animate = 0;

View File

@ -11,7 +11,7 @@ import * as miscMechanism from './mechanism/misc';
import * as study from './mechanism/study'; import * as study from './mechanism/study';
import { registerPresetState } from './state/preset'; import { registerPresetState } from './state/preset';
import { ItemState } from './state/item'; import { ItemState } from './state/item';
import { HeroMover, ObjectMoverBase } from './state/move'; import { heroMoveCollection, HeroMover, ObjectMoverBase } from './state/move';
// ----- 类注册 // ----- 类注册
Mota.register('class', 'DamageEnemy', damage.DamageEnemy); Mota.register('class', 'DamageEnemy', damage.DamageEnemy);
@ -37,7 +37,8 @@ Mota.register('module', 'Mechanism', {
Mota.register('module', 'State', { Mota.register('module', 'State', {
ItemState, ItemState,
HeroMover, HeroMover,
ObjectMoverBase ObjectMoverBase,
heroMoveCollection
}); });
main.loading = loading; main.loading = loading;

View File

@ -4,7 +4,7 @@ import { loading } from '../game';
import type { RenderAdapter } from '@/core/render/adapter'; import type { RenderAdapter } from '@/core/render/adapter';
import type { HeroRenderer } from '@/core/render/preset/hero'; import type { HeroRenderer } from '@/core/render/preset/hero';
import type { FloorViewport } from '@/core/render/preset/viewport'; import type { FloorViewport } from '@/core/render/preset/viewport';
import type { KeyCode } from '@/plugin/keyCodes'; import type { HeroKeyMover } from '@/core/main/action/move';
interface MoveStepDir { interface MoveStepDir {
type: 'dir'; type: 'dir';
@ -16,7 +16,7 @@ interface MoveStepSpeed {
value: number; value: number;
} }
type MoveStep = MoveStepDir | MoveStepSpeed; export type MoveStep = MoveStepDir | MoveStepSpeed;
export interface IMoveController { export interface IMoveController {
/** 本次移动是否完成 */ /** 本次移动是否完成 */
@ -55,6 +55,9 @@ export abstract class ObjectMoverBase extends EventEmitter<EObjectMovingEvent> {
/** 当前是否正在移动 */ /** 当前是否正在移动 */
moving: boolean = false; moving: boolean = false;
/** 当前的控制对象 */
controller?: IMoveController;
/** /**
* *
* @param controller * @param controller
@ -133,7 +136,10 @@ export abstract class ObjectMoverBase extends EventEmitter<EObjectMovingEvent> {
await this.onMoveEnd(controller); await this.onMoveEnd(controller);
}; };
const onEnd = start(); const onEnd = start().then(() => {
this.controller = void 0;
controller.done = true;
});
const controller: IMoveController = { const controller: IMoveController = {
done: false, done: false,
onEnd, onEnd,
@ -146,6 +152,7 @@ export abstract class ObjectMoverBase extends EventEmitter<EObjectMovingEvent> {
return onEnd; return onEnd;
} }
}; };
this.controller = controller;
return controller; return controller;
} }
@ -375,14 +382,23 @@ export class HeroMover extends ObjectMoverBase {
} }
} }
interface HeroMoveCollection {
mover: HeroMover;
keyMover?: HeroKeyMover;
}
// 初始化基础勇士移动实例 // 初始化基础勇士移动实例
export const heroMover = new HeroMover(); const heroMover = new HeroMover();
export const heroMoveCollection: HeroMoveCollection = {
mover: heroMover
};
loading.once('coreInit', () => { loading.once('coreInit', () => {
// 注册按键操作 // 注册按键操作
Mota.r(() => { Mota.r(() => {
const { HeroKeyMover } = Mota.require('module', 'Action'); const { HeroKeyMover } = Mota.require('module', 'Action');
const gameKey = Mota.require('var', 'gameKey'); const gameKey = Mota.require('var', 'gameKey');
const keyMover = new HeroKeyMover(gameKey, heroMover); const keyMover = new HeroKeyMover(gameKey, heroMover);
heroMoveCollection.keyMover = keyMover;
}); });
}); });

View File

@ -125,6 +125,10 @@ interface ModuleInterface {
ItemState: typeof ItemState; ItemState: typeof ItemState;
HeroMover: typeof HeroMover; HeroMover: typeof HeroMover;
ObjectMoverBase: typeof ObjectMoverBase; ObjectMoverBase: typeof ObjectMoverBase;
heroMoveCollection: {
mover: HeroMover;
keyMover: HeroKeyMover;
};
}; };
Action: { Action: {
HeroKeyMover: typeof HeroKeyMover; HeroKeyMover: typeof HeroKeyMover;

View File

@ -10,6 +10,7 @@ import { BluePalace } from '@/game/mechanism/misc';
import { backDir } from './utils'; import { backDir } from './utils';
import type { TimingFn } from 'mutate-animate'; import type { TimingFn } from 'mutate-animate';
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
import { heroMoveCollection, MoveStep } from '@/game/state/move';
import type { FloorViewport } from '@/core/render/preset/viewport'; import type { FloorViewport } from '@/core/render/preset/viewport';
// 向后兼容用,会充当两个版本间过渡的作用 // 向后兼容用,会充当两个版本间过渡的作用
@ -49,227 +50,12 @@ export function init() {
adapters['viewport'] = viewport; adapters['viewport'] = viewport;
} }
let moving: boolean = false;
let stopChain: boolean = false;
let moveDir: Dir;
let stepDir: Dir;
let moveEnding: Promise<any[]> = Promise.resolve([]);
let stepEnding: Promise<any[]> = Promise.resolve([]);
/** 移动事件 */
const moveEmit = new EventEmitter<MoveEvent>();
/** 传送门信息,下一步传送到哪 */ /** 传送门信息,下一步传送到哪 */
let portalData: BluePalace.PortalTo | undefined; let portalData: BluePalace.PortalTo | undefined;
/** 下一步是否步入传送门 */ /** 下一步是否步入传送门 */
let portal: boolean = false; let portal: boolean = false;
const pressedArrow: Set<Dir> = new Set(); const { mover: heroMover } = heroMoveCollection;
// Mota.r(() => {
// const gameKey = Mota.require('var', 'gameKey');
// const { moveUp, moveDown, moveLeft, moveRight } = gameKey.data;
// const symbol = Symbol.for('@key_main');
// gameKey.on('press', code => {
// if (core.status.lockControl || gameKey.scope !== symbol) return;
// if (code === moveUp.key) onMoveKeyDown('up');
// if (code === moveDown.key) onMoveKeyDown('down');
// if (code === moveLeft.key) onMoveKeyDown('left');
// if (code === moveRight.key) onMoveKeyDown('right');
// });
// gameKey.on('release', code => {
// if (code === moveUp.key) onMoveKeyUp('up');
// if (code === moveDown.key) onMoveKeyUp('down');
// if (code === moveLeft.key) onMoveKeyUp('left');
// if (code === moveRight.key) onMoveKeyUp('right');
// });
// });
function onMoveKeyDown(type: Dir) {
pressedArrow.add(type);
moveDir = type;
if (!moving) {
stepDir = moveDir;
readyMove();
}
if (moving && stopChain) {
stopChain = false;
}
}
function onMoveKeyUp(type: Dir) {
pressedArrow.delete(type);
if (pressedArrow.size === 0) {
stopChain = true;
} else {
const arr = [...pressedArrow];
moveDir = arr[0];
if (!moving) {
stepDir = moveDir;
readyMove();
}
}
}
async function readyMove(
ignoreTerrain: boolean = false,
noRoute: boolean = false,
inLockControl: boolean = false,
callback?: () => void
) {
const adapter = adapters['hero-adapter'];
if (!adapter) {
return Promise.resolve();
} else {
if (!ignoreTerrain && !checkCanMoveStatus(callback)) {
moveEmit.emit('stepEnd');
continueAfterEnd();
return Promise.resolve();
}
await adapter.all('readyMove');
moving = true;
stopChain = false;
startHeroMoveChain(ignoreTerrain, noRoute, inLockControl, callback);
}
}
/**
*
* @param tryContinue truefalsetrue
*/
async function endMove(tryContinue: boolean = true) {
const adapter = adapters['hero-adapter'];
if (!adapter) {
return Promise.resolve();
} else {
if (moving) {
stopChain = true;
moveEnding = adapter.all('endMove');
await moveEnding;
moveEmit.emit('moveEnd');
moving = false;
if (tryContinue) continueAfterEnd();
}
return Promise.resolve();
}
}
function move(dir: Dir) {
moveDir = dir;
}
function continueAfterEnd() {
requestAnimationFrame(() => {
if (pressedArrow.size === 0 || moving) {
// console.log(6);
stopChain = true;
return;
}
if (checkCanMoveStatus()) {
if (!moving) {
stepDir = moveDir;
readyMove();
}
} else {
continueAfterEnd();
}
});
}
async function startHeroMoveChain(
ignoreTerrain: boolean = false,
noRoute: boolean = false,
inLockControl: boolean = false,
callback?: () => void
) {
const adapter = adapters['hero-adapter'];
if (!adapter) {
return Promise.resolve();
} else {
while (moving || !stopChain) {
if (stopChain || (!inLockControl && core.status.lockControl)) {
break;
}
stepDir = moveDir;
if (!ignoreTerrain) {
if (!checkCanMoveStatus(callback)) break;
if (portal) renderHeroSwap();
}
stepEnding = adapter.all('move', moveDir);
if (portal && portalData) {
adapters.viewport?.all(
'mutateTo',
portalData.x,
portalData.y
);
} else {
const { nx, ny } = getNextLoc();
adapters.viewport?.all('moveTo', nx, ny);
}
await stepEnding;
moveEmit.emit('stepEnd');
if (!ignoreTerrain) onMoveEnd(false, noRoute, callback);
}
endMove();
stopChain = false;
}
}
function checkCanMoveStatus(callback?: () => void) {
core.setHeroLoc('direction', stepDir);
const { noPass, canMove } = checkCanMove();
checkPortal();
if (!portal && (noPass || !canMove)) {
onCannotMove(canMove, callback);
if (moving) endMove();
return false;
}
return true;
}
function getNextLoc() {
const { x: dx, y: dy } = core.utils.scan[stepDir];
const { x, y } = core.status.hero.loc;
const nx = x + dx;
const ny = y + dy;
return { nx, ny };
}
/**
*
*/
function checkCanMove() {
const { nx, ny } = getNextLoc();
const { x, y } = core.status.hero.loc;
const noPass = core.noPass(nx, ny);
const canMove = core.canMoveHero(x, y, stepDir);
return { noPass, canMove };
}
function onCannotMove(canMove: boolean, callback?: () => void) {
const { nx, ny } = getNextLoc();
core.status.route.push(core.getHeroLoc('direction'));
// @ts-ignore
core.status.automaticRoute.moveStepBeforeStop = [];
// @ts-ignore
core.status.automaticRoute.lastDirection = core.getHeroLoc('direction');
if (canMove) core.trigger(nx, ny);
core.drawHero();
if (core.status.automaticRoute.moveStepBeforeStop.length == 0) {
core.clearContinueAutomaticRoute();
core.stopAutomaticRoute();
}
callback?.();
}
function onMoveEnd( function onMoveEnd(
noPass: boolean, noPass: boolean,
@ -284,10 +70,6 @@ export function init() {
core.setHeroLoc('direction', dir); core.setHeroLoc('direction', dir);
portal = false; portal = false;
moveDir = before; moveDir = before;
} else if (!noPass) {
const { nx, ny } = getNextLoc();
core.setHeroLoc('x', nx, true);
core.setHeroLoc('y', ny, true);
} }
var direction = core.getHeroLoc('direction'); var direction = core.getHeroLoc('direction');
@ -299,30 +81,6 @@ export function init() {
callback?.(); callback?.();
} }
async function moveHeroAs(step: DiredLoc[]) {
if (moving) return;
// console.log(step);
let now = step.shift();
if (!now) return;
stepDir = now.direction;
await readyMove();
while (now) {
if (!moving) break;
stepDir = now.direction;
await new Promise<void>(res => {
moveEmit.once('stepEnd', () => {
now = step.shift();
if (!now) endMove();
console.log(now?.direction);
res();
});
});
}
}
// ----- 移动 - 传送门 // ----- 移动 - 传送门
function checkPortal() { function checkPortal() {
const map = BluePalace.portalMap.get(core.status.floorId); const map = BluePalace.portalMap.get(core.status.floorId);
@ -463,8 +221,7 @@ export function init() {
} }
}); });
const step0 = moveSteps.find(v => !v.startsWith('speed')) as Move2; return moveSteps;
return { steps: moveSteps, start: step0 };
} }
/** /**
@ -519,12 +276,16 @@ export function init() {
Mota.r(() => { Mota.r(() => {
// ----- 勇士移动相关 // ----- 勇士移动相关
control.prototype.moveAction = async function (callback?: () => void) { control.prototype.moveAction = async function (callback?: () => void) {
// await endMove(false); heroMover.clearMoveQueue();
moveEmit.once('stepEnd', () => { heroMover.oneStep('forward');
stopChain = true; const lock = core.status.lockControl;
endMove(false); const controller = heroMover.startMove(false, true, lock);
controller?.onEnd.then(() => {
callback?.();
});
heroMover.once('stepEnd', () => {
controller?.stop();
}); });
readyMove(false, true, true, callback);
}; };
control.prototype._moveAction_moving = function ( control.prototype._moveAction_moving = function (
@ -559,62 +320,28 @@ export function init() {
core.doAction(); core.doAction();
}; };
const validDir = new Set<string>(['left', 'right', 'up', 'down']);
events.prototype.eventMoveHero = async function ( events.prototype.eventMoveHero = async function (
steps: string[], steps: string[],
time: number = 500, time: number = 500,
callback?: () => void callback?: () => void
) { ) {
if (moving) return; if (heroMover.moving) return;
const { steps: moveSteps, start } = getMoveSteps(steps); const moveSteps = getMoveSteps(steps);
if (
moveSteps.some(v => !v.startsWith('speed') && !validDir.has(v))
) {
callback?.();
return;
}
if (start !== 'backward' && start !== 'forward') {
moveDir = start as Dir;
}
core.setHeroLoc('direction', moveDir);
const speed = core.status.replay.speed; const resolved = moveSteps.map<MoveStep>(v => {
time /= speed; if (v.startsWith('speed')) {
if (speed === 24) time = 1; return { type: 'speed', value: Number(v.slice(6)) };
} else {
let pointer = -1; return { type: 'dir', value: v as Move2 };
const len = moveSteps.length; }
const list = adapters['hero-adapter']?.items ?? [];
const speedMap: Map<HeroRenderer, number> = new Map();
list.forEach(v => {
speedMap.set(v, v.speed);
v.setMoveSpeed(time);
}); });
const start: MoveStep = { type: 'speed', value: time };
let nx = core.status.hero.loc.x; heroMover.insertMove(...[start, ...resolved]);
let ny = core.status.hero.loc.y; const controller = heroMover.startMove();
readyMove(true, true, true); controller?.onEnd.then(() => {
while (++pointer < len) {
const dir = moveSteps[pointer];
if (dir === 'backward') stepDir = backDir(stepDir);
else if (dir.startsWith('speed')) {
const speed = parseInt(dir.slice(6));
list.forEach(v => v.setMoveSpeed(speed));
} else if (dir !== 'forward') {
stepDir = dir as Dir;
}
const { x, y } = core.utils.scan[stepDir];
nx += x;
ny += y;
await new Promise<void>(res => {
moveEmit.once('stepEnd', res);
});
}
endMove(false);
core.setHeroLoc('x', nx);
core.setHeroLoc('y', ny);
callback?.(); callback?.();
});
}; };
control.prototype.setHeroLoc = function ( control.prototype.setHeroLoc = function (
@ -629,8 +356,6 @@ export function init() {
this.gatherFollowers(); this.gatherFollowers();
} }
if (name === 'direction') { if (name === 'direction') {
moveDir = value as Dir;
stepDir = value as Dir;
adapters['hero-adapter']?.sync('turn', value); adapters['hero-adapter']?.sync('turn', value);
} else if (name === 'x') { } else if (name === 'x') {
adapters['hero-adapter']?.sync('setHeroLoc', value); adapters['hero-adapter']?.sync('setHeroLoc', value);
@ -644,33 +369,31 @@ export function init() {
core.stopAutomaticRoute(); core.stopAutomaticRoute();
core.clearContinueAutomaticRoute(); core.clearContinueAutomaticRoute();
stopChain = true; heroMover.controller?.stop().then(() => {
stepEnding.then(() => {
callback?.(); callback?.();
}); });
}; };
control.prototype.moveHero = async function ( control.prototype.moveHero = async function (
direction: Dir, direction?: Dir,
callback: () => void callback?: () => void
) { ) {
// 如果正在移动直接return if (heroMover.moving) return;
if (core.status.heroMoving != 0) return; // const nx = core.nextX();
if (core.isset(direction)) core.setHeroLoc('direction', direction); // const ny = core.nextY();
// if (core.status.thisMap.enemy.mapDamage[`${nx},${ny}`]?.mockery) {
const nx = core.nextX(); // core.autosave();
const ny = core.nextY(); // }
if (core.status.thisMap.enemy.mapDamage[`${nx},${ny}`]?.mockery) { heroMover.clearMoveQueue();
core.autosave(); heroMover.oneStep(direction ?? 'forward');
} const lock = core.status.lockControl;
const controller = heroMover.startMove(false, true, lock);
moveDir = direction; controller?.onEnd.then(() => {
stepDir = direction;
await readyMove();
stopChain = true;
callback?.(); callback?.();
});
heroMover.once('stepEnd', () => {
controller?.stop();
});
}; };
events.prototype.setHeroIcon = function (name: ImageIds) { events.prototype.setHeroIcon = function (name: ImageIds) {
@ -680,7 +403,7 @@ export function init() {
}; };
control.prototype.isMoving = function () { control.prototype.isMoving = function () {
return moving; return heroMover.moving;
}; };
////// 设置自动寻路路线 ////// ////// 设置自动寻路路线 //////
@ -715,17 +438,15 @@ export function init() {
core.status.automaticRoute.destY = destY; core.status.automaticRoute.destY = destY;
this._setAutomaticRoute_drawRoute(moveStep); this._setAutomaticRoute_drawRoute(moveStep);
this._setAutomaticRoute_setAutoSteps(moveStep); this._setAutomaticRoute_setAutoSteps(moveStep);
// 立刻移动
// core.setAutoHeroMove();
console.log(moveStep);
moveHeroAs(moveStep.slice()); // 执行移动
}; const steps: MoveStep[] = moveStep.map(v => {
return { type: 'dir', value: v.direction };
hook.on('reset', () => {
moveDir = core.status.hero.loc.direction;
stepDir = moveDir;
}); });
heroMover.clearMoveQueue();
heroMover.insertMove(...steps);
heroMover.startMove();
};
// ----- 开关门 // ----- 开关门
events.prototype.openDoor = function ( events.prototype.openDoor = function (
@ -1012,7 +733,7 @@ export function init() {
time: number = 500, time: number = 500,
callback?: () => void callback?: () => void
) { ) {
if (moving) return; if (heroMover.moving) return;
const sx = core.getHeroLoc('x'); const sx = core.getHeroLoc('x');
const sy = core.getHeroLoc('y'); const sy = core.getHeroLoc('y');
adapters.viewport?.all('mutateTo', ex, ey); adapters.viewport?.all('mutateTo', ex, ey);
@ -1077,18 +798,17 @@ export function init() {
action === 'left' || action === 'left' ||
action === 'right' action === 'right'
) { ) {
stepDir = action; // const { noPass, canMove } = checkCanMove();
const { noPass, canMove } = checkCanMove(); // const { nx, ny } = getNextLoc();
const { nx, ny } = getNextLoc(); // if (noPass || !canMove) {
if (noPass || !canMove) { // if (canMove) core.trigger(nx, ny);
if (canMove) core.trigger(nx, ny); // } else {
} else { // core.setHeroLoc('x', nx);
core.setHeroLoc('x', nx); // core.setHeroLoc('y', ny);
core.setHeroLoc('y', ny); // core.setHeroLoc('direction', action);
core.setHeroLoc('direction', action); // }
}
setTimeout(core.replay, 100); // setTimeout(core.replay, 100);
return true; return true;
} else { } else {
@ -1096,5 +816,5 @@ export function init() {
} }
}); });
return { readyMove, endMove, move }; // return { readyMove, endMove, move };
} }

View File

@ -193,6 +193,7 @@ interface Control {
readonly noAutoEvent: boolean; readonly noAutoEvent: boolean;
/** /**
* @deprecated
* *
*/ */
readonly renderFrameFunc: RenderFrame[]; readonly renderFrameFunc: RenderFrame[];
@ -203,11 +204,13 @@ interface Control {
readonly replayActions: ReplayAction[]; readonly replayActions: ReplayAction[];
/** /**
* @deprecated
* resize操作 * resize操作
*/ */
readonly resizes: ResizeAction[]; readonly resizes: ResizeAction[];
/** /**
* @deprecated
* *
*/ */
readonly weathers: Record<string, WeatherAction>; readonly weathers: Record<string, WeatherAction>;
@ -238,6 +241,7 @@ interface Control {
unregisterAnimationFrame(name: string): void; unregisterAnimationFrame(name: string): void;
/** /**
* @deprecated
* *
* @example core.showStartAnimate(); // 重启游戏但不重置bgm * @example core.showStartAnimate(); // 重启游戏但不重置bgm
* @param noAnimate * @param noAnimate
@ -246,6 +250,7 @@ interface Control {
showStartAnimate(noAnimate?: boolean, callback?: () => void): void; showStartAnimate(noAnimate?: boolean, callback?: () => void): void;
/** /**
* @deprecated
* *
* @example core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择 * @example core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择
* @param callback * @param callback
@ -309,13 +314,13 @@ interface Control {
setHeroMoveInterval(callback?: () => any): void; setHeroMoveInterval(callback?: () => any): void;
/** /**
* @deprecated
* *
*/ */
moveOneStep(callback?: () => any): void; moveOneStep(callback?: () => any): void;
/** /**
* @deprecated 使使使 `eventMoveHero` * @deprecated 使使HeroMover来实现
* 使
* *
* @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理 * @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理
* @param callback * @param callback
@ -323,11 +328,11 @@ interface Control {
moveAction(callback?: () => void): void; moveAction(callback?: () => void): void;
/** /**
* @deprecated 使使使 `eventMoveHero` * @deprecated 使使HeroMover来实现
* *
* @example core.moveHero(); // 连续前进 * @example core.moveHero(); // 连续前进
* @param direction * @param direction
* @param callback * @param callback
*/ */
moveHero(direction?: Dir, callback?: () => void): void; moveHero(direction?: Dir, callback?: () => void): void;