template/packages-user/data-base/src/hero/mover.ts

223 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<T extends IHeroLocation>
extends ObjectMover<T>
implements IHeroMover<T>
{
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<FaceDirection>
) {
super(faceHandler);
this.state = tile.state;
}
config(config: Partial<IHeroMoverConfig>): 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<IHeroMoverConfig> {
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<ObjectMoveStep>,
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<void> {
if (!this.topImpl) {
logger.warn(144);
}
}
protected async onMoveEnd(): Promise<void> {}
protected async onStepStart(
step: Readonly<ObjectMoveStep>,
tile: IHeroLocation
): Promise<number> {
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<ObjectMoveStep>,
tile: IHeroLocation,
controller: Readonly<IMoverController>
): Promise<ITileLocator> {
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 };
}
}