diff --git a/src/core/render/preset/hero.ts b/src/core/render/preset/hero.ts index 197ddea..3659786 100644 --- a/src/core/render/preset/hero.ts +++ b/src/core/render/preset/hero.ts @@ -168,7 +168,6 @@ export class HeroRenderer */ resetRenderable(getInfo: boolean) { this.movingFrame = 0; - console.trace(); if (this.renderable) { this.renderable.animate = 0; diff --git a/src/game/index.ts b/src/game/index.ts index 20b18ad..173af80 100644 --- a/src/game/index.ts +++ b/src/game/index.ts @@ -11,7 +11,7 @@ import * as miscMechanism from './mechanism/misc'; import * as study from './mechanism/study'; import { registerPresetState } from './state/preset'; import { ItemState } from './state/item'; -import { HeroMover, ObjectMoverBase } from './state/move'; +import { heroMoveCollection, HeroMover, ObjectMoverBase } from './state/move'; // ----- 类注册 Mota.register('class', 'DamageEnemy', damage.DamageEnemy); @@ -37,7 +37,8 @@ Mota.register('module', 'Mechanism', { Mota.register('module', 'State', { ItemState, HeroMover, - ObjectMoverBase + ObjectMoverBase, + heroMoveCollection }); main.loading = loading; diff --git a/src/game/state/move.ts b/src/game/state/move.ts index 777de45..6fd8af7 100644 --- a/src/game/state/move.ts +++ b/src/game/state/move.ts @@ -4,7 +4,7 @@ import { loading } from '../game'; import type { RenderAdapter } from '@/core/render/adapter'; import type { HeroRenderer } from '@/core/render/preset/hero'; import type { FloorViewport } from '@/core/render/preset/viewport'; -import type { KeyCode } from '@/plugin/keyCodes'; +import type { HeroKeyMover } from '@/core/main/action/move'; interface MoveStepDir { type: 'dir'; @@ -16,7 +16,7 @@ interface MoveStepSpeed { value: number; } -type MoveStep = MoveStepDir | MoveStepSpeed; +export type MoveStep = MoveStepDir | MoveStepSpeed; export interface IMoveController { /** 本次移动是否完成 */ @@ -55,6 +55,9 @@ export abstract class ObjectMoverBase extends EventEmitter { /** 当前是否正在移动 */ moving: boolean = false; + /** 当前的控制对象 */ + controller?: IMoveController; + /** * 当本次移动开始时执行的函数 * @param controller 本次移动的控制对象 @@ -133,7 +136,10 @@ export abstract class ObjectMoverBase extends EventEmitter { await this.onMoveEnd(controller); }; - const onEnd = start(); + const onEnd = start().then(() => { + this.controller = void 0; + controller.done = true; + }); const controller: IMoveController = { done: false, onEnd, @@ -146,6 +152,7 @@ export abstract class ObjectMoverBase extends EventEmitter { return onEnd; } }; + this.controller = 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', () => { // 注册按键操作 Mota.r(() => { const { HeroKeyMover } = Mota.require('module', 'Action'); const gameKey = Mota.require('var', 'gameKey'); const keyMover = new HeroKeyMover(gameKey, heroMover); + heroMoveCollection.keyMover = keyMover; }); }); diff --git a/src/game/system.ts b/src/game/system.ts index 22ab4ba..a1799af 100644 --- a/src/game/system.ts +++ b/src/game/system.ts @@ -125,6 +125,10 @@ interface ModuleInterface { ItemState: typeof ItemState; HeroMover: typeof HeroMover; ObjectMoverBase: typeof ObjectMoverBase; + heroMoveCollection: { + mover: HeroMover; + keyMover: HeroKeyMover; + }; }; Action: { HeroKeyMover: typeof HeroKeyMover; diff --git a/src/plugin/game/fallback.ts b/src/plugin/game/fallback.ts index 6a053c5..f41e76a 100644 --- a/src/plugin/game/fallback.ts +++ b/src/plugin/game/fallback.ts @@ -10,6 +10,7 @@ import { BluePalace } from '@/game/mechanism/misc'; import { backDir } from './utils'; import type { TimingFn } from 'mutate-animate'; import EventEmitter from 'eventemitter3'; +import { heroMoveCollection, MoveStep } from '@/game/state/move'; import type { FloorViewport } from '@/core/render/preset/viewport'; // 向后兼容用,会充当两个版本间过渡的作用 @@ -49,227 +50,12 @@ export function init() { adapters['viewport'] = viewport; } - let moving: boolean = false; - let stopChain: boolean = false; - let moveDir: Dir; - let stepDir: Dir; - let moveEnding: Promise = Promise.resolve([]); - let stepEnding: Promise = Promise.resolve([]); - - /** 移动事件 */ - const moveEmit = new EventEmitter(); - /** 传送门信息,下一步传送到哪 */ let portalData: BluePalace.PortalTo | undefined; /** 下一步是否步入传送门 */ let portal: boolean = false; - const pressedArrow: Set = new Set(); - // 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 是否会尝试继续移动,只有在按键操作条件下需要填true,其余填false,默认为true - */ - 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?.(); - } + const { mover: heroMover } = heroMoveCollection; function onMoveEnd( noPass: boolean, @@ -284,10 +70,6 @@ export function init() { core.setHeroLoc('direction', dir); portal = false; moveDir = before; - } else if (!noPass) { - const { nx, ny } = getNextLoc(); - core.setHeroLoc('x', nx, true); - core.setHeroLoc('y', ny, true); } var direction = core.getHeroLoc('direction'); @@ -299,30 +81,6 @@ export function init() { 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(res => { - moveEmit.once('stepEnd', () => { - now = step.shift(); - if (!now) endMove(); - console.log(now?.direction); - res(); - }); - }); - } - } - // ----- 移动 - 传送门 function checkPortal() { 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 { steps: moveSteps, start: step0 }; + return moveSteps; } /** @@ -519,12 +276,16 @@ export function init() { Mota.r(() => { // ----- 勇士移动相关 control.prototype.moveAction = async function (callback?: () => void) { - // await endMove(false); - moveEmit.once('stepEnd', () => { - stopChain = true; - endMove(false); + heroMover.clearMoveQueue(); + heroMover.oneStep('forward'); + const lock = core.status.lockControl; + 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 ( @@ -559,62 +320,28 @@ export function init() { core.doAction(); }; - const validDir = new Set(['left', 'right', 'up', 'down']); events.prototype.eventMoveHero = async function ( steps: string[], time: number = 500, callback?: () => void ) { - if (moving) return; - const { steps: moveSteps, start } = 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); + if (heroMover.moving) return; + const moveSteps = getMoveSteps(steps); - const speed = core.status.replay.speed; - time /= speed; - if (speed === 24) time = 1; - - let pointer = -1; - const len = moveSteps.length; - const list = adapters['hero-adapter']?.items ?? []; - const speedMap: Map = new Map(); - list.forEach(v => { - speedMap.set(v, v.speed); - v.setMoveSpeed(time); - }); - - let nx = core.status.hero.loc.x; - let ny = core.status.hero.loc.y; - readyMove(true, true, true); - 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 resolved = moveSteps.map(v => { + if (v.startsWith('speed')) { + return { type: 'speed', value: Number(v.slice(6)) }; + } else { + return { type: 'dir', value: v as Move2 }; } - const { x, y } = core.utils.scan[stepDir]; - nx += x; - ny += y; - await new Promise(res => { - moveEmit.once('stepEnd', res); - }); - } - endMove(false); + }); + const start: MoveStep = { type: 'speed', value: time }; - core.setHeroLoc('x', nx); - core.setHeroLoc('y', ny); - callback?.(); + heroMover.insertMove(...[start, ...resolved]); + const controller = heroMover.startMove(); + controller?.onEnd.then(() => { + callback?.(); + }); }; control.prototype.setHeroLoc = function ( @@ -629,8 +356,6 @@ export function init() { this.gatherFollowers(); } if (name === 'direction') { - moveDir = value as Dir; - stepDir = value as Dir; adapters['hero-adapter']?.sync('turn', value); } else if (name === 'x') { adapters['hero-adapter']?.sync('setHeroLoc', value); @@ -644,33 +369,31 @@ export function init() { core.stopAutomaticRoute(); core.clearContinueAutomaticRoute(); - stopChain = true; - stepEnding.then(() => { + heroMover.controller?.stop().then(() => { callback?.(); }); }; control.prototype.moveHero = async function ( - direction: Dir, - callback: () => void + direction?: Dir, + callback?: () => void ) { - // 如果正在移动,直接return - if (core.status.heroMoving != 0) return; - if (core.isset(direction)) core.setHeroLoc('direction', direction); - - const nx = core.nextX(); - const ny = core.nextY(); - if (core.status.thisMap.enemy.mapDamage[`${nx},${ny}`]?.mockery) { - core.autosave(); - } - - moveDir = direction; - stepDir = direction; - await readyMove(); - - stopChain = true; - - callback?.(); + if (heroMover.moving) return; + // const nx = core.nextX(); + // const ny = core.nextY(); + // if (core.status.thisMap.enemy.mapDamage[`${nx},${ny}`]?.mockery) { + // core.autosave(); + // } + heroMover.clearMoveQueue(); + heroMover.oneStep(direction ?? 'forward'); + const lock = core.status.lockControl; + const controller = heroMover.startMove(false, true, lock); + controller?.onEnd.then(() => { + callback?.(); + }); + heroMover.once('stepEnd', () => { + controller?.stop(); + }); }; events.prototype.setHeroIcon = function (name: ImageIds) { @@ -680,7 +403,7 @@ export function init() { }; control.prototype.isMoving = function () { - return moving; + return heroMover.moving; }; ////// 设置自动寻路路线 ////// @@ -715,18 +438,16 @@ export function init() { core.status.automaticRoute.destY = destY; this._setAutomaticRoute_drawRoute(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 }; + }); + heroMover.clearMoveQueue(); + heroMover.insertMove(...steps); + heroMover.startMove(); }; - hook.on('reset', () => { - moveDir = core.status.hero.loc.direction; - stepDir = moveDir; - }); - // ----- 开关门 events.prototype.openDoor = function ( x: number, @@ -1012,7 +733,7 @@ export function init() { time: number = 500, callback?: () => void ) { - if (moving) return; + if (heroMover.moving) return; const sx = core.getHeroLoc('x'); const sy = core.getHeroLoc('y'); adapters.viewport?.all('mutateTo', ex, ey); @@ -1077,18 +798,17 @@ export function init() { action === 'left' || action === 'right' ) { - stepDir = action; - const { noPass, canMove } = checkCanMove(); - const { nx, ny } = getNextLoc(); - if (noPass || !canMove) { - if (canMove) core.trigger(nx, ny); - } else { - core.setHeroLoc('x', nx); - core.setHeroLoc('y', ny); - core.setHeroLoc('direction', action); - } + // const { noPass, canMove } = checkCanMove(); + // const { nx, ny } = getNextLoc(); + // if (noPass || !canMove) { + // if (canMove) core.trigger(nx, ny); + // } else { + // core.setHeroLoc('x', nx); + // core.setHeroLoc('y', ny); + // core.setHeroLoc('direction', action); + // } - setTimeout(core.replay, 100); + // setTimeout(core.replay, 100); return true; } else { @@ -1096,5 +816,5 @@ export function init() { } }); - return { readyMove, endMove, move }; + // return { readyMove, endMove, move }; } diff --git a/src/types/control.d.ts b/src/types/control.d.ts index 69b30ba..471d743 100644 --- a/src/types/control.d.ts +++ b/src/types/control.d.ts @@ -193,6 +193,7 @@ interface Control { readonly noAutoEvent: boolean; /** + * @deprecated * 注册的帧动画 */ readonly renderFrameFunc: RenderFrame[]; @@ -203,11 +204,13 @@ interface Control { readonly replayActions: ReplayAction[]; /** + * @deprecated * 注册的resize操作 */ readonly resizes: ResizeAction[]; /** + * @deprecated * 注册的天气 */ readonly weathers: Record; @@ -238,6 +241,7 @@ interface Control { unregisterAnimationFrame(name: string): void; /** + * @deprecated * 进入标题画面 * @example core.showStartAnimate(); // 重启游戏但不重置bgm * @param noAnimate 是否不由黑屏淡入而是立即亮屏 @@ -246,6 +250,7 @@ interface Control { showStartAnimate(noAnimate?: boolean, callback?: () => void): void; /** + * @deprecated * 淡出标题画面 * @example core.hideStartAnimate(core.startGame); // 淡出标题画面并开始新游戏,跳过难度选择 * @param callback 标题画面完全淡出后的回调函数 @@ -309,13 +314,13 @@ interface Control { setHeroMoveInterval(callback?: () => any): void; /** + * @deprecated * 每移动一格后执行的函数 */ moveOneStep(callback?: () => any): void; /** - * @deprecated 尽量不要使用!!如果需要无视地形使用代码来移动勇士,请使用 `eventMoveHero` - * 如果需要向前前进一格或撞击,再使用本函数 + * @deprecated 尽量不要使用!!可以使用HeroMover来实现 * 尝试前进一步,如果面前不可被踏入就会直接触发该点事件 * @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理 * @param callback 走一步后的回调函数 @@ -323,11 +328,11 @@ interface Control { moveAction(callback?: () => void): void; /** - * @deprecated 尽量不要使用!!如果需要无视地形使用代码来移动勇士,请使用 `eventMoveHero` + * @deprecated 尽量不要使用!!可以使用HeroMover来实现 * 向指定方向移动一格 * @example core.moveHero(); // 连续前进 * @param direction 移动的方向,不设置就是勇士当前的方向 - * @param callback 回调函数,设置了就只走一步 + * @param callback 回调函数 */ moveHero(direction?: Dir, callback?: () => void): void;