import { ITileLocator, logger } from '@motajs/common'; import { FaceDirection, IFaceHandler, IMoverController, ObjectMoveStep, ObjectMover, ObjectMoveType, IDataCommon } from '@user/data-common'; import { HeroMoveCode, IHeroLocation, IHeroMover, IHeroMoverConfig, IHeroMoveTopHandler, IHeroMoveTopImpl } from './types'; import { isNil } from 'lodash-es'; export class HeroMover extends ObjectMover implements IHeroMover { readonly state: IDataCommon; /** 是否不记录进路线系统 */ private noRoute: boolean = false; /** 是否忽略地形碰撞检测 */ private ignoreTerrain: boolean = false; /** 是否在特定时机触发自动存档 */ private autoSave: boolean = false; /** 是否允许到达地图外 */ private allowOutBound: boolean = false; /** 勇士移动顶层实现对象 */ private topImpl: IHeroMoveTopImpl | null = null; constructor( readonly tile: T, faceHandler: IFaceHandler ) { super(faceHandler); this.state = tile.state; } config(config: Partial): this { if (!isNil(config.noRoute)) { this.noRoute = config.noRoute; } if (!isNil(config.ignoreTerrain)) { this.ignoreTerrain = config.ignoreTerrain; } if (!isNil(config.autoSave)) { this.autoSave = config.autoSave; } if (!isNil(config.allowOutBound)) { this.allowOutBound = config.allowOutBound; } return this; } getConfig(): Readonly { return { noRoute: this.noRoute, ignoreTerrain: this.ignoreTerrain, autoSave: this.autoSave, allowOutBound: this.allowOutBound }; } useTopImplementation(impl: IHeroMoveTopImpl | null): void { this.topImpl = impl; } /** * 创建顶层信息对象 * @param curr 勇士所在位置 * @param dir 勇士移动方向 * @param floorId 当前楼层 id */ private createHandler( curr: ITileLocator, step: Readonly, floorId: string | undefined ): IHeroMoveTopHandler { switch (step.type) { case ObjectMoveType.Dir: case ObjectMoveType.DirFace: case ObjectMoveType.Special: { const { x, y } = this.faceHandler.movement(this.moveDirection); const nx = curr.x + x; const ny = curr.y + y; return { currLoc: { x: curr.x, y: curr.y }, nextLoc: { x: nx, y: ny }, direction: this.moveDirection, floorId, face: this.faceHandler, state: this.state }; } case ObjectMoveType.Jump: case ObjectMoveType.Teleport: { const nx = step.rel ? curr.x + step.x : step.x; const ny = step.rel ? curr.y + step.y : step.y; return { currLoc: { x: curr.x, y: curr.y }, nextLoc: { x: nx, y: ny }, direction: this.moveDirection, floorId, face: this.faceHandler, state: this.state }; } case ObjectMoveType.AnimDir: case ObjectMoveType.Face: case ObjectMoveType.Speed: { return { currLoc: { x: curr.x, y: curr.y }, nextLoc: { x: curr.x, y: curr.y }, direction: this.moveDirection, floorId, face: this.faceHandler, state: this.state }; } } } protected async onMoveStart(): Promise { if (!this.topImpl) { logger.warn(144); } } protected async onMoveEnd(): Promise {} protected async onStepStart( step: Readonly, tile: IHeroLocation ): Promise { if (!this.topImpl) return HeroMoveCode.Stop; const type = step.type; const handler = this.createHandler(tile, step, tile.floorId); // 边界判断 const { x: nx, y: ny } = handler.nextLoc; const inBound = this.topImpl.inBound(nx, ny, handler.floorId); if (!this.allowOutBound && !inBound) return HeroMoveCode.CannotMove; // 移动步判断 if ( type === ObjectMoveType.Dir || type === ObjectMoveType.DirFace || type === ObjectMoveType.Special ) { if (this.ignoreTerrain) { return HeroMoveCode.Step; } else { const canPass = this.topImpl.canPass(handler); if (canPass) { const hit = this.topImpl.shouldHit(handler); if (hit) return HeroMoveCode.Hit; else return HeroMoveCode.Step; } else { return HeroMoveCode.CannotMove; } } } // 跳跃和瞬移 if (type === ObjectMoveType.Jump || type === ObjectMoveType.Teleport) { if (this.ignoreTerrain) { return HeroMoveCode.Step; } else { if (inBound) { return HeroMoveCode.Step; } else { return HeroMoveCode.Stop; } } } // 其他的一律可以直接执行 return HeroMoveCode.Step; } protected async onStepEnd( code: number, step: Readonly, tile: IHeroLocation, controller: Readonly ): Promise { if (!this.topImpl) return { x: tile.x, y: tile.y }; const handler = this.createHandler(tile, step, tile.floorId); // 不能移动或停止 if (code === HeroMoveCode.CannotMove || code === HeroMoveCode.Stop) { // 这里不能 await,因为其 Promise 会在当前步结束后兑现,如果 await 就会卡住 controller.stop(); return { x: tile.x, y: tile.y }; } // 撞击时也使用当前位置,同时处理撞击行为 if (code === HeroMoveCode.Hit) { await this.topImpl.hit(handler); // 这里同样不能 await controller.stop(); return { x: tile.x, y: tile.y }; } // 正常移动 if (code === HeroMoveCode.Step) { return handler.nextLoc; } return { x: tile.x, y: tile.y }; } }