diff --git a/src/game/index.ts b/src/game/index.ts index 173af80..80105fb 100644 --- a/src/game/index.ts +++ b/src/game/index.ts @@ -11,7 +11,12 @@ import * as miscMechanism from './mechanism/misc'; import * as study from './mechanism/study'; import { registerPresetState } from './state/preset'; import { ItemState } from './state/item'; -import { heroMoveCollection, HeroMover, ObjectMoverBase } from './state/move'; +import { + BlockMover, + heroMoveCollection, + HeroMover, + ObjectMoverBase +} from './state/move'; // ----- 类注册 Mota.register('class', 'DamageEnemy', damage.DamageEnemy); @@ -37,6 +42,7 @@ Mota.register('module', 'Mechanism', { Mota.register('module', 'State', { ItemState, HeroMover, + BlockMover, ObjectMoverBase, heroMoveCollection }); diff --git a/src/game/state/move.ts b/src/game/state/move.ts index 6fd8af7..06e9b92 100644 --- a/src/game/state/move.ts +++ b/src/game/state/move.ts @@ -5,6 +5,12 @@ 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 { HeroKeyMover } from '@/core/main/action/move'; +import type { + FloorLayer, + Layer, + LayerMovingRenderable +} from '@/core/render/preset/layer'; +import type { LayerFloorBinder } from '@/core/render/preset/floor'; interface MoveStepDir { type: 'dir'; @@ -115,8 +121,8 @@ export abstract class ObjectMoverBase extends EventEmitter { const start = async () => { // 等待宏任务执行完成,不然controller会在首次调用中未定义 await new Promise(res => res()); - this.emit('moveStart', queue); await this.onMoveStart(controller); + this.emit('moveStart', queue); while (queue.length > 0) { if (stopMove || !this.moving) break; const step = queue.shift(); @@ -126,7 +132,9 @@ export abstract class ObjectMoverBase extends EventEmitter { const code = await this.onStepStart(step, controller); await this.onStepEnd(step, code, controller); } else { - this.moveSpeed = step.value; + const replay = core.status.replay.speed; + const speed = replay === 24 ? 1 : step.value / replay; + this.moveSpeed = speed; } this.emit('stepEnd', step); } @@ -189,6 +197,171 @@ export abstract class ObjectMoverBase extends EventEmitter { } } +const enum BlockMoveCode { + Step +} + +export class BlockMover extends ObjectMoverBase { + /** 楼层渲染适配器,用于显示动画 */ + static adapter?: RenderAdapter; + + x: number; + y: number; + floorId: FloorIds; + layer: FloorLayer; + + /** 本次移动中需要进行动画移动的楼层渲染组件 */ + private layerItems: Layer[] = []; + /** 本次移动过程中的移动renderable实例 */ + private renderable?: LayerMovingRenderable; + /** 本次移动的图块id */ + private blockNum: number = 0; + + constructor( + x: number, + y: number, + floorId: FloorIds, + layer: FloorLayer, + dir: Dir = 'down' + ) { + super(); + + this.x = x; + this.y = y; + this.floorId = floorId; + this.moveDir = dir; + this.layer = layer; + } + + /** + * 绑定移动点 + * @param x 绑定点横坐标 + * @param y 绑定点纵坐标 + * @param floorId 绑定点楼层 + * @returns 是否绑定成功,例如如果当前绑定点正在移动,那么就会绑定失败 + */ + bind( + x: number, + y: number, + floorId: FloorIds, + layer: FloorLayer, + dir: Dir = 'down' + ) { + if (this.moving) return false; + this.x = x; + this.y = y; + this.floorId = floorId; + this.moveDir = dir; + this.layer = layer; + return true; + } + + protected async onMoveStart(controller: IMoveController): Promise { + const adapter = BlockMover.adapter; + if (adapter) { + const list = adapter.items; + const items = [...list].filter(v => { + if (v.layer !== this.layer) return false; + const ex = v.getExtends('floor-binder') as LayerFloorBinder; + if (!ex) return false; + return ex.getFloor() === core.status.floorId; + }); + this.layerItems = items; + } + + let blockNum: number = 0; + if (this.layer === 'event') { + blockNum = core.status.maps[this.floorId].map[this.y][this.x]; + } else { + const array = core.maps._getBgFgMapArray(this.layer, this.floorId); + blockNum = array[this.y][this.x]; + } + this.blockNum = blockNum; + + Mota.r(() => { + const { Layer } = Mota.require('module', 'Render'); + const r = Layer.getMovingRenderable(blockNum, this.x, this.y); + + if (r) { + this.renderable = r; + this.layerItems.forEach(v => { + v.moving.add(r); + }); + } + }); + + if (this.layer === 'event') { + core.removeBlock(this.x, this.y, this.floorId); + } + } + + protected async onMoveEnd(controller: IMoveController): Promise { + if (this.renderable) { + this.layerItems.forEach(v => { + v.moving.delete(this.renderable!); + }); + } + + this.layerItems = []; + this.renderable = void 0; + + if (this.layer === 'event') { + core.setBlock(this.blockNum as AllNumbers, this.x, this.y); + } + } + + protected async onStepStart( + step: MoveStepDir, + controller: IMoveController + ): Promise { + await this.moveAnimate(step); + const { x: dx, y: dy } = core.utils.scan2[this.moveDir]; + this.x += dx; + this.y += dy; + + return BlockMoveCode.Step; + } + + protected async onStepEnd( + step: MoveStepDir, + code: BlockMoveCode, + controller: IMoveController + ): Promise {} + + private moveAnimate(step: MoveStepDir) { + const layer = this.layerItems[0]; + if (!layer) return; + if (!this.renderable) return; + const data = this.renderable; + const fx = this.x; + const fy = this.y; + const { x: dx, y: dy } = core.utils.scan2[this.moveDir]; + const start = Date.now(); + const time = this.moveSpeed; + + return new Promise(res => { + layer.delegateTicker( + () => { + const now = Date.now() - start; + const progress = now / time; + data.x = fx + dx * progress; + data.y = fy + dy * progress; + this.layerItems.forEach(v => { + v.update(v); + }); + }, + this.moveSpeed, + () => { + data.x = fx + dx; + data.y = fy + dy; + data.zIndex = fy + dy; + res(); + } + ); + }); + } +} + interface CanMoveStatus { /** 由CannotIn和CannotOut计算出的信息,不可移动时不会触发触发器 */ canMove: boolean; @@ -408,6 +581,8 @@ loading.once('coreInit', () => { const Adapter = Mota.require('module', 'Render').RenderAdapter; const adapter = Adapter.get('hero-adapter'); const viewport = Adapter.get('viewport'); + const layerAdapter = Adapter.get('layer'); HeroMover.adapter = adapter; HeroMover.viewport = viewport; + BlockMover.adapter = layerAdapter; }); diff --git a/src/game/system.ts b/src/game/system.ts index a1799af..a980085 100644 --- a/src/game/system.ts +++ b/src/game/system.ts @@ -38,7 +38,7 @@ import type { ItemState } from './state/item'; import type { Layer } from '@/core/render/preset/layer'; import type { LayerGroupFloorBinder } from '@/core/render/preset/floor'; import type { HeroKeyMover } from '@/core/main/action/move'; -import type { HeroMover, ObjectMoverBase } from './state/move'; +import type { BlockMover, HeroMover, ObjectMoverBase } from './state/move'; interface ClassInterface { // 渲染进程与游戏进程通用 @@ -124,6 +124,7 @@ interface ModuleInterface { State: { ItemState: typeof ItemState; HeroMover: typeof HeroMover; + BlockMover: typeof BlockMover; ObjectMoverBase: typeof ObjectMoverBase; heroMoveCollection: { mover: HeroMover; diff --git a/src/plugin/game/fallback.ts b/src/plugin/game/fallback.ts index f41e76a..e18df01 100644 --- a/src/plugin/game/fallback.ts +++ b/src/plugin/game/fallback.ts @@ -9,8 +9,7 @@ import type { Layer, LayerMovingRenderable } from '@/core/render/preset/layer'; 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 { BlockMover, heroMoveCollection, MoveStep } from '@/game/state/move'; import type { FloorViewport } from '@/core/render/preset/viewport'; // 向后兼容用,会充当两个版本间过渡的作用 @@ -23,11 +22,6 @@ interface Adapters { viewport?: RenderAdapter; } -interface MoveEvent { - stepEnd: []; - moveEnd: []; -} - const adapters: Adapters = {}; export function init() { @@ -62,15 +56,15 @@ export function init() { noRoute: boolean = false, callback?: () => void ) { - if (portal && portalData) { - const before = moveDir; - const { x, y, dir } = portalData; - core.setHeroLoc('x', x); - core.setHeroLoc('y', y); - core.setHeroLoc('direction', dir); - portal = false; - moveDir = before; - } + // if (portal && portalData) { + // const before = moveDir; + // const { x, y, dir } = portalData; + // core.setHeroLoc('x', x); + // core.setHeroLoc('y', y); + // core.setHeroLoc('direction', dir); + // portal = false; + // moveDir = before; + // } var direction = core.getHeroLoc('direction'); core.control._moveAction_popAutomaticRoute(); @@ -619,61 +613,30 @@ export function init() { callback?.(); return; } - const speed = core.status.replay.speed; - time /= speed; - if (speed === 24) time = 1; const block = core.getBlock(x, y); if (!block) { callback?.(); return; } - core.removeBlock(x, y); - const list = adapters.layer?.items ?? []; - const items = [...list].filter(v => { - if (v.layer !== 'event') return false; - const ex = v.getExtends('floor-binder') as LayerFloorBinder; - if (!ex) return false; - return ex.getFloor() === core.status.floorId; + const mover = new BlockMover(x, y, core.status.floorId, 'event'); + const moveSteps = getMoveSteps(steps); + 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 start: MoveStep = { type: 'speed', value: time }; + mover.insertMove(...[start, ...resolved]); + const controller = mover.startMove(); - const { steps: moveSteps, start } = getMoveSteps(steps); - if (!start || items.length === 0) { - callback?.(); - return; - } - let stepDir: Dir2; - let nx = x; - let ny = y; - if (start === 'backward' || start === 'forward') stepDir = 'down'; - else stepDir = start; - - const { Layer } = Mota.require('module', 'Render'); - const moving = Layer.getMovingRenderable(block.id, x, y); - if (!moving) { - callback?.(); - return; - } - items.forEach(v => v.moving.add(moving)); - - for (const step of moveSteps) { - if (step === 'backward') stepDir = backDir(stepDir); - else if (step.startsWith('speed')) { - time = parseInt(step.slice(6)); - continue; - } else stepDir = step as Dir2; - - const { x, y } = core.utils.scan2[stepDir]; - const tx = nx + x; - const ty = ny + y; - await moveRenderable(items[0], moving, time, tx, ty); - nx = tx; - ny = ty; - moving.zIndex = ty; + if (controller) { + await controller.onEnd; } - items.forEach(v => v.moving.delete(moving)); - if (keep) { - core.setBlock(block.id, nx, ny); + if (!keep) { + core.removeBlock(mover.x, mover.y); } callback?.(); };