mirror of
https://github.com/motajs/template.git
synced 2026-07-17 01:11:09 +08:00
Compare commits
No commits in common. "1ce02837438388dda755e7c33838ac2121b5417c" and "0508dfb9e4f2bca6e920a4f66f7603dbcd424d7d" have entirely different histories.
1ce0283743
...
0508dfb9e4
@ -1,21 +1,18 @@
|
||||
import { ITileLocator } from '@motajs/common';
|
||||
import {
|
||||
FaceDirection,
|
||||
getFaceMovement,
|
||||
IFaceHandler,
|
||||
IMoverController,
|
||||
ObjectMoveStep,
|
||||
ObjectMover,
|
||||
ObjectMoveType,
|
||||
IDataCommon
|
||||
ObjectMoveStepType
|
||||
} from '@user/data-common';
|
||||
import {
|
||||
HeroMoveCode,
|
||||
IHeroHitAction,
|
||||
IHeroHitHandler,
|
||||
IHeroLocation,
|
||||
IHeroMover,
|
||||
IHeroMoverConfig,
|
||||
IPassCheckerHandler,
|
||||
ITerrainPassChecker
|
||||
} from './types';
|
||||
import { isNil } from 'lodash-es';
|
||||
@ -24,26 +21,20 @@ 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 terrainChecker: ITerrainPassChecker | null = null;
|
||||
/** 撞击行为对象 */
|
||||
private hitAction: IHeroHitAction | null = null;
|
||||
|
||||
constructor(
|
||||
readonly tile: T,
|
||||
faceHandler: IFaceHandler<FaceDirection>
|
||||
) {
|
||||
super(faceHandler);
|
||||
this.state = tile.state;
|
||||
}
|
||||
|
||||
config(config: Readonly<IHeroMoverConfig>): this {
|
||||
@ -71,108 +62,38 @@ export class HeroMover<T extends IHeroLocation>
|
||||
this.terrainChecker = checker;
|
||||
}
|
||||
|
||||
useHitAction(action: IHeroHitAction | null): void {
|
||||
this.hitAction = action;
|
||||
}
|
||||
protected async onMoveStart(
|
||||
_tile: IHeroLocation,
|
||||
_controller: Readonly<IMoverController>
|
||||
): Promise<void> {}
|
||||
|
||||
/**
|
||||
* 创建通行性检查信息对象
|
||||
* @param curr 勇士所在位置
|
||||
* @param dir 勇士移动方向
|
||||
* @param floorId 当前楼层 id
|
||||
*/
|
||||
private createPassHandler(
|
||||
curr: ITileLocator,
|
||||
dir: FaceDirection,
|
||||
floorId: string | undefined
|
||||
): IPassCheckerHandler {
|
||||
const { x, y } = this.faceHandler.movement(dir);
|
||||
const nx = curr.x + x;
|
||||
const ny = curr.y + y;
|
||||
return {
|
||||
currLoc: curr,
|
||||
nextLoc: { x: nx, y: ny },
|
||||
direction: dir,
|
||||
floorId,
|
||||
face: this.faceHandler,
|
||||
state: this.state
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通行性检查信息对象
|
||||
* @param curr 勇士所在位置
|
||||
* @param dir 勇士移动方向
|
||||
* @param floorId 当前楼层 id
|
||||
*/
|
||||
private createHitHandler(
|
||||
curr: ITileLocator,
|
||||
dir: FaceDirection,
|
||||
floorId: string | undefined
|
||||
): IHeroHitHandler {
|
||||
const { x, y } = this.faceHandler.movement(dir);
|
||||
const nx = curr.x + x;
|
||||
const ny = curr.y + y;
|
||||
return {
|
||||
currLoc: curr,
|
||||
nextLoc: { x: nx, y: ny },
|
||||
direction: dir,
|
||||
floorId,
|
||||
state: this.state
|
||||
};
|
||||
}
|
||||
|
||||
protected async onMoveStart(): Promise<void> {}
|
||||
|
||||
protected async onMoveEnd(): Promise<void> {}
|
||||
protected async onMoveEnd(
|
||||
_tile: IHeroLocation,
|
||||
_controller: Readonly<IMoverController>
|
||||
): Promise<void> {}
|
||||
|
||||
protected async onStepStart(
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
tile: IHeroLocation
|
||||
step: ObjectMoveStep,
|
||||
tile: IHeroLocation,
|
||||
controller: Readonly<IMoverController>
|
||||
): Promise<number> {
|
||||
if (!this.terrainChecker) return HeroMoveCode.CannotMove;
|
||||
|
||||
const type = step.type;
|
||||
|
||||
if (
|
||||
type === ObjectMoveType.Dir ||
|
||||
type === ObjectMoveType.DirFace ||
|
||||
type === ObjectMoveType.Special
|
||||
type !== ObjectMoveStepType.Dir &&
|
||||
type !== ObjectMoveStepType.DirFace &&
|
||||
type !== ObjectMoveStepType.Special
|
||||
) {
|
||||
const dir = this.moveDirection;
|
||||
const handler = this.createPassHandler(tile, dir, tile.floorId);
|
||||
const { x: nx, y: ny } = handler.nextLoc;
|
||||
const inBound = this.terrainChecker.inBound(nx, ny, tile.floorId);
|
||||
// 目标点不在地图内
|
||||
if (!inBound) return HeroMoveCode.CannotMove;
|
||||
|
||||
if (this.ignoreTerrain) {
|
||||
return HeroMoveCode.Step;
|
||||
} else {
|
||||
const canPass = this.terrainChecker.canPass(handler);
|
||||
if (canPass) {
|
||||
const hit = this.terrainChecker.shouldHit(handler);
|
||||
if (hit) return HeroMoveCode.Hit;
|
||||
else return HeroMoveCode.Step;
|
||||
} else {
|
||||
return HeroMoveCode.CannotMove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 跳跃和瞬移仅需要进行边界判断
|
||||
if (type === ObjectMoveType.Jump || type === ObjectMoveType.Teleport) {
|
||||
const nx = step.rel ? tile.x + step.x : step.x;
|
||||
const ny = step.rel ? tile.y + step.y : step.y;
|
||||
if (this.ignoreTerrain) {
|
||||
return HeroMoveCode.Step;
|
||||
} else {
|
||||
if (this.terrainChecker.inBound(nx, ny, tile.floorId)) {
|
||||
return HeroMoveCode.Step;
|
||||
} else {
|
||||
return HeroMoveCode.Stop;
|
||||
}
|
||||
if (!this.ignoreTerrain) {
|
||||
const result = this.checkTerrain(tile, controller);
|
||||
if (result !== HeroMoveCode.Step) return result;
|
||||
}
|
||||
|
||||
if (this.autoSave) {
|
||||
// TODO: 检查当前步是否需要触发自动存档(如进入/离开地图伤害区域)
|
||||
}
|
||||
|
||||
return HeroMoveCode.Step;
|
||||
@ -180,60 +101,89 @@ export class HeroMover<T extends IHeroLocation>
|
||||
|
||||
protected async onStepEnd(
|
||||
code: number,
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
step: ObjectMoveStep,
|
||||
tile: IHeroLocation,
|
||||
controller: Readonly<IMoverController>
|
||||
): Promise<ITileLocator> {
|
||||
const type = step.type;
|
||||
|
||||
// 不能移动或停止
|
||||
if (code === HeroMoveCode.CannotMove || code === HeroMoveCode.Stop) {
|
||||
// 这里不能 await,因为其 Promise 会在当前步结束后兑现,如果 await 就会卡住
|
||||
controller.stop();
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
const isMovementStep =
|
||||
type === ObjectMoveStepType.Dir ||
|
||||
type === ObjectMoveStepType.DirFace ||
|
||||
type === ObjectMoveStepType.Special;
|
||||
|
||||
// 撞击时也使用当前位置,同时处理撞击行为
|
||||
if (code === HeroMoveCode.Hit) {
|
||||
// 执行撞击行为
|
||||
if (this.hitAction) {
|
||||
const dir = this.moveDirection;
|
||||
const handler = this.createHitHandler(tile, dir, tile.floorId);
|
||||
await this.hitAction.hit(handler);
|
||||
}
|
||||
// 这里同样不能 await
|
||||
controller.stop();
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
|
||||
// 正常移动
|
||||
if (code === HeroMoveCode.Step) {
|
||||
if (
|
||||
type === ObjectMoveType.Teleport ||
|
||||
type === ObjectMoveType.Jump
|
||||
!isMovementStep &&
|
||||
type !== ObjectMoveStepType.Teleport &&
|
||||
type !== ObjectMoveStepType.Jump
|
||||
) {
|
||||
// 瞬移行为
|
||||
if (step.rel) {
|
||||
return { x: tile.x + step.x, y: tile.y + step.y };
|
||||
} else {
|
||||
return { x: step.x, y: step.y };
|
||||
}
|
||||
} else if (
|
||||
type === ObjectMoveType.Dir ||
|
||||
type === ObjectMoveType.DirFace ||
|
||||
type === ObjectMoveType.Special
|
||||
) {
|
||||
// 移动行为
|
||||
const { x, y } = this.faceHandler.movement(this.moveDirection);
|
||||
const nx = tile.x + x;
|
||||
const ny = tile.y + y;
|
||||
return { x: nx, y: ny };
|
||||
} else {
|
||||
// 其他行为
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
}
|
||||
|
||||
if (code === HeroMoveCode.CannotMove || code === HeroMoveCode.Hit) {
|
||||
void controller.stop();
|
||||
// TODO: 处理撞击图块逻辑
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
|
||||
if (code === HeroMoveCode.Stop) {
|
||||
void controller.stop();
|
||||
// TODO: 清除自动寻路
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
|
||||
if (type === ObjectMoveStepType.Teleport) {
|
||||
return this.calcPos(tile, step.x, step.y, step.rel);
|
||||
}
|
||||
|
||||
if (type === ObjectMoveStepType.Jump) {
|
||||
return this.calcPos(tile, step.x, step.y, step.rel);
|
||||
}
|
||||
|
||||
const offset = getFaceMovement(this.moveDirection);
|
||||
const nx = tile.x + offset.x;
|
||||
const ny = tile.y + offset.y;
|
||||
|
||||
// TODO: 路线记录
|
||||
// TODO: 中毒伤害处理
|
||||
// TODO: 步数累计
|
||||
|
||||
return { x: nx, y: ny };
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算传送 / 跳跃的目标坐标
|
||||
* @param tile 当前所在图块
|
||||
* @param x 目标横坐标或偏移量
|
||||
* @param y 目标纵坐标或偏移量
|
||||
* @param rel 是否使用相对模式
|
||||
*/
|
||||
private calcPos(
|
||||
tile: IHeroLocation,
|
||||
x: number,
|
||||
y: number,
|
||||
rel: boolean
|
||||
): ITileLocator {
|
||||
if (rel) {
|
||||
return { x: tile.x + x, y: tile.y + y };
|
||||
}
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测当前移动方向前方一格的地形是否可通行
|
||||
*/
|
||||
private checkTerrain(
|
||||
tile: IHeroLocation,
|
||||
_controller: Readonly<IMoverController>
|
||||
): HeroMoveCode {
|
||||
if (!this.terrainChecker) return HeroMoveCode.CannotMove;
|
||||
const locator: ITileLocator = { x: tile.x, y: tile.y };
|
||||
const pass = this.terrainChecker.canPass(
|
||||
locator,
|
||||
this.moveDirection,
|
||||
tile.floorId
|
||||
);
|
||||
return pass ? HeroMoveCode.Step : HeroMoveCode.CannotMove;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
import {
|
||||
FaceDirection,
|
||||
IDataCommonExtended,
|
||||
IFaceHandler,
|
||||
IObjectMovable,
|
||||
IObjectMover,
|
||||
ISaveableContent
|
||||
@ -212,7 +211,7 @@ export interface IHeroLocation
|
||||
/** 当前所在楼层 id,undefined 表示尚不处于任何楼层 */
|
||||
readonly floorId: string | undefined;
|
||||
/** 勇士的移动对象 */
|
||||
readonly mover: IHeroMover<this>;
|
||||
readonly mover: IObjectMover<this>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@ -230,52 +229,21 @@ export const enum HeroMoveCode {
|
||||
CannotMove
|
||||
}
|
||||
|
||||
export interface IHeroMoveHandlerBase extends IDataCommonExtended {
|
||||
/** 当前位置 */
|
||||
readonly currLoc: ITileLocator;
|
||||
/** 要移动至的位置 */
|
||||
readonly nextLoc: ITileLocator;
|
||||
/** 移动方向 */
|
||||
readonly direction: FaceDirection;
|
||||
/** 当前楼层 id */
|
||||
readonly floorId: string | undefined;
|
||||
}
|
||||
|
||||
export interface IPassCheckerHandler extends IHeroMoveHandlerBase {
|
||||
/** 朝向管理对象 */
|
||||
readonly face: IFaceHandler<FaceDirection>;
|
||||
}
|
||||
|
||||
export interface IHeroHitHandler extends IHeroMoveHandlerBase {}
|
||||
|
||||
export interface ITerrainPassChecker {
|
||||
/**
|
||||
* 检查目标位置是否在地图范围内
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
* @param floorId 楼层 id
|
||||
*/
|
||||
inBound(x: number, y: number, floorId: string | undefined): boolean;
|
||||
/** 对应的勇士状态对象 */
|
||||
readonly hero: IHeroState<unknown>;
|
||||
|
||||
/**
|
||||
* 判断在指定楼层中,从指定坐标向指定方向移动一格是否可通行
|
||||
* @param handler 通行性检查对象
|
||||
* @param locator 当前位置
|
||||
* @param direction 移动方向
|
||||
* @param floorId 当前楼层 id,可能为 undefined
|
||||
*/
|
||||
canPass(handler: IPassCheckerHandler): boolean;
|
||||
|
||||
/**
|
||||
* 判断在指定楼层中,从指定坐标向指定方向移动时是否应该产生撞击,撞击将会触发目标位置的触发器
|
||||
* @param handler 通行性检查对象
|
||||
*/
|
||||
shouldHit(handler: IPassCheckerHandler): boolean;
|
||||
}
|
||||
|
||||
export interface IHeroHitAction {
|
||||
/**
|
||||
* 勇士撞击某一个图块时执行的内容,一般用于触发目标位置的触发器
|
||||
* @param handler 撞击行为对象
|
||||
*/
|
||||
hit(handler: IHeroHitHandler): Promise<void>;
|
||||
canPass(
|
||||
locator: ITileLocator,
|
||||
direction: FaceDirection,
|
||||
floorId: string | undefined
|
||||
): boolean;
|
||||
}
|
||||
|
||||
export interface IHeroMoverConfig {
|
||||
@ -287,8 +255,7 @@ export interface IHeroMoverConfig {
|
||||
autoSave?: boolean;
|
||||
}
|
||||
|
||||
export interface IHeroMover<T extends IHeroLocation>
|
||||
extends IObjectMover<T>, IDataCommonExtended {
|
||||
export interface IHeroMover<T extends IHeroLocation> extends IObjectMover<T> {
|
||||
/**
|
||||
* 配置本次移动的行为模式
|
||||
* @param config 配置对象,未传入的字段保持当前值
|
||||
@ -301,16 +268,10 @@ export interface IHeroMover<T extends IHeroLocation>
|
||||
getConfig(): Readonly<IHeroMoverConfig>;
|
||||
|
||||
/**
|
||||
* 设置地形通行判定器,传入 `null` 移表示移除
|
||||
* @param checker 地形判定器
|
||||
* 设置地形通行判定器,传入 null 移除判定器
|
||||
* @param checker 地形判定器,null 表示移除
|
||||
*/
|
||||
useTerrainChecker(checker: ITerrainPassChecker | null): void;
|
||||
|
||||
/**
|
||||
* 设置勇士撞击行为的执行对象,传入 `null` 表示移除。
|
||||
* @param action 撞击行为对象
|
||||
*/
|
||||
useHitAction(action: IHeroHitAction | null): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
getFaceMovement,
|
||||
ObjectMover,
|
||||
ObjectMoveStep,
|
||||
ObjectMoveType
|
||||
ObjectMoveStepType
|
||||
} from '@user/data-common';
|
||||
import { IDynamicTile } from './types';
|
||||
import { DYNAMIC_MOVER_FACE } from '../shared';
|
||||
@ -47,33 +47,33 @@ export class DynamicTileMover extends ObjectMover<IDynamicTile> {
|
||||
y: tile.y
|
||||
};
|
||||
switch (step.type) {
|
||||
case ObjectMoveType.Dir: {
|
||||
case ObjectMoveStepType.Dir: {
|
||||
const { x, y } = getFaceMovement(step.move);
|
||||
tile.setFaceDirection(step.move);
|
||||
locator.x += x;
|
||||
locator.y += y;
|
||||
break;
|
||||
}
|
||||
case ObjectMoveType.DirFace: {
|
||||
case ObjectMoveStepType.DirFace: {
|
||||
const { x, y } = getFaceMovement(step.move);
|
||||
tile.setFaceDirection(step.face);
|
||||
locator.x += x;
|
||||
locator.y += y;
|
||||
break;
|
||||
}
|
||||
case ObjectMoveType.Face: {
|
||||
case ObjectMoveStepType.Face: {
|
||||
tile.setFaceDirection(step.value);
|
||||
break;
|
||||
}
|
||||
case ObjectMoveType.Special: {
|
||||
case ObjectMoveStepType.Special: {
|
||||
const { x, y } = getFaceMovement(this.moveDirection);
|
||||
tile.setFaceDirection(this.faceDirection);
|
||||
locator.x += x;
|
||||
locator.y += y;
|
||||
break;
|
||||
}
|
||||
case ObjectMoveType.Teleport:
|
||||
case ObjectMoveType.Jump: {
|
||||
case ObjectMoveStepType.Teleport:
|
||||
case ObjectMoveStepType.Jump: {
|
||||
const { x, y, rel } = step;
|
||||
tile.setFaceDirection(this.faceDirection);
|
||||
if (rel) {
|
||||
|
||||
@ -40,7 +40,7 @@ export interface IStateBase extends IDataCommon {
|
||||
getSaveableContent<T>(id: string): ISaveableContent<T> | null;
|
||||
}
|
||||
|
||||
export interface IDataBaseExtended {
|
||||
export interface IStateBaseExtended {
|
||||
/** 当前对象对应的数据层对象(Layer 1 对象) */
|
||||
readonly state: IStateBase;
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import { IFaceHandler } from './faceManager';
|
||||
|
||||
//#region 对象移动
|
||||
|
||||
export const enum ObjectMoveType {
|
||||
export const enum ObjectMoveStepType {
|
||||
/** 绝对方向步,同步更新移动方向与朝向 */
|
||||
Dir,
|
||||
/** 绝对方向步,显式指定朝向 */
|
||||
@ -65,14 +65,14 @@ export interface IObjectMovable {
|
||||
|
||||
export interface IObjectMoveStepDir {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.Dir;
|
||||
type: ObjectMoveStepType.Dir;
|
||||
/** 本步移动方向 */
|
||||
move: FaceDirection;
|
||||
}
|
||||
|
||||
export interface IObjectMoveStepDirFace {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.DirFace;
|
||||
type: ObjectMoveStepType.DirFace;
|
||||
/** 本步移动方向 */
|
||||
move: FaceDirection;
|
||||
/** 本步显式朝向 */
|
||||
@ -81,35 +81,35 @@ export interface IObjectMoveStepDirFace {
|
||||
|
||||
export interface IObjectMoveStepSpeed {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.Speed;
|
||||
type: ObjectMoveStepType.Speed;
|
||||
/** 后续移动的每格耗时,单位为 ms */
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface IObjectMoveStepFace {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.Face;
|
||||
type: ObjectMoveStepType.Face;
|
||||
/** 要设置的朝向 */
|
||||
value: FaceDirection;
|
||||
}
|
||||
|
||||
export interface IObjectMoveStepSpecial {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.Special;
|
||||
type: ObjectMoveStepType.Special;
|
||||
/** 特殊步方向 */
|
||||
direction: ObjectSpecialStep;
|
||||
}
|
||||
|
||||
export interface IObjectMoveAnimDir {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.AnimDir;
|
||||
type: ObjectMoveStepType.AnimDir;
|
||||
/** 动画播放方向 */
|
||||
dir: ObjectAnimDirection;
|
||||
}
|
||||
|
||||
export interface IObjectMoveTP {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.Teleport;
|
||||
type: ObjectMoveStepType.Teleport;
|
||||
/** 目标横坐标 */
|
||||
x: number;
|
||||
/** 目标纵坐标 */
|
||||
@ -120,7 +120,7 @@ export interface IObjectMoveTP {
|
||||
|
||||
export interface IObjectMoveJump {
|
||||
/** 步骤类型 */
|
||||
type: ObjectMoveType.Jump;
|
||||
type: ObjectMoveStepType.Jump;
|
||||
/** 目标横坐标 */
|
||||
x: number;
|
||||
/** 目标纵坐标 */
|
||||
@ -149,13 +149,13 @@ export interface IMoverController {
|
||||
* 向当前移动队列末尾追加步骤
|
||||
* @param steps 要追加的步骤列表
|
||||
*/
|
||||
push(...steps: Readonly<ObjectMoveStep>[]): void;
|
||||
push(...steps: ObjectMoveStep[]): void;
|
||||
|
||||
/**
|
||||
* 在当前步移动之后立刻插入指定步,顺序为参数传入的顺序
|
||||
* @param steps 要插入的步骤列表
|
||||
*/
|
||||
insert(...steps: Readonly<ObjectMoveStep>[]): void;
|
||||
insert(...steps: ObjectMoveStep[]): void;
|
||||
|
||||
/**
|
||||
* 停止当前移动,在当前步骤完成后兑现
|
||||
@ -187,7 +187,7 @@ export interface IObjectMoverHooks<T extends IObjectMovable> extends IHookBase {
|
||||
*/
|
||||
onStepStart?(
|
||||
code: number,
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
step: ObjectMoveStep,
|
||||
tile: T,
|
||||
mover: IObjectMover<T>
|
||||
): Promise<void>;
|
||||
@ -201,7 +201,7 @@ export interface IObjectMoverHooks<T extends IObjectMovable> extends IHookBase {
|
||||
*/
|
||||
onStepEnd?(
|
||||
code: number,
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
step: ObjectMoveStep,
|
||||
tile: T,
|
||||
mover: IObjectMover<T>
|
||||
): Promise<void>;
|
||||
@ -409,7 +409,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
* @param controller 移动控制器
|
||||
*/
|
||||
protected abstract onStepStart(
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
step: ObjectMoveStep,
|
||||
tile: T,
|
||||
controller: Readonly<IMoverController>
|
||||
): Promise<number>;
|
||||
@ -423,7 +423,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
*/
|
||||
protected abstract onStepEnd(
|
||||
code: number,
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
step: ObjectMoveStep,
|
||||
tile: T,
|
||||
controller: Readonly<IMoverController>
|
||||
): Promise<ITileLocator>;
|
||||
@ -451,20 +451,20 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
* 根据步骤内容预先同步移动器内部状态
|
||||
* @param step 当前步骤
|
||||
*/
|
||||
private prepareStep(step: Readonly<ObjectMoveStep>): void {
|
||||
private prepareStep(step: ObjectMoveStep): void {
|
||||
switch (step.type) {
|
||||
case ObjectMoveType.Dir:
|
||||
case ObjectMoveStepType.Dir:
|
||||
this.moveDirection = step.move;
|
||||
this.faceDirection = step.move;
|
||||
break;
|
||||
case ObjectMoveType.DirFace:
|
||||
case ObjectMoveStepType.DirFace:
|
||||
this.moveDirection = step.move;
|
||||
this.faceDirection = step.face;
|
||||
break;
|
||||
case ObjectMoveType.Face:
|
||||
case ObjectMoveStepType.Face:
|
||||
this.faceDirection = step.value;
|
||||
break;
|
||||
case ObjectMoveType.Special: {
|
||||
case ObjectMoveStepType.Special: {
|
||||
const dir = this.getCurrentDirection();
|
||||
if (step.direction === ObjectSpecialStep.Backward) {
|
||||
const opposite = this.faceHandler.opposite(dir);
|
||||
@ -476,10 +476,10 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectMoveType.AnimDir:
|
||||
case ObjectMoveStepType.AnimDir:
|
||||
this.currAnimDir = step.dir;
|
||||
break;
|
||||
case ObjectMoveType.Speed:
|
||||
case ObjectMoveStepType.Speed:
|
||||
this.currentSpeed = step.value;
|
||||
break;
|
||||
}
|
||||
@ -508,7 +508,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
|
||||
tp(x: number, y: number, rel: boolean = false): this {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Teleport,
|
||||
type: ObjectMoveStepType.Teleport,
|
||||
x,
|
||||
y,
|
||||
rel
|
||||
@ -518,7 +518,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
|
||||
jump(x: number, y: number, rel: boolean = false): this {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Jump,
|
||||
type: ObjectMoveStepType.Jump,
|
||||
x,
|
||||
y,
|
||||
rel
|
||||
@ -529,7 +529,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
step(dir: FaceDirection, count: number = 1): this {
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Dir,
|
||||
type: ObjectMoveStepType.Dir,
|
||||
move: dir
|
||||
});
|
||||
}
|
||||
@ -543,7 +543,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
): this {
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.DirFace,
|
||||
type: ObjectMoveStepType.DirFace,
|
||||
move,
|
||||
face
|
||||
});
|
||||
@ -554,7 +554,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
forward(count: number = 1): this {
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Special,
|
||||
type: ObjectMoveStepType.Special,
|
||||
direction: ObjectSpecialStep.Forward
|
||||
});
|
||||
}
|
||||
@ -564,7 +564,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
backward(count: number = 1): this {
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Special,
|
||||
type: ObjectMoveStepType.Special,
|
||||
direction: ObjectSpecialStep.Backward
|
||||
});
|
||||
}
|
||||
@ -573,7 +573,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
|
||||
speed(value: number): this {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Speed,
|
||||
type: ObjectMoveStepType.Speed,
|
||||
value
|
||||
});
|
||||
return this;
|
||||
@ -581,7 +581,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
|
||||
face(dir: FaceDirection): this {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.Face,
|
||||
type: ObjectMoveStepType.Face,
|
||||
value: dir
|
||||
});
|
||||
return this;
|
||||
@ -589,7 +589,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
|
||||
animDir(dir: ObjectAnimDirection): this {
|
||||
this.pushStep({
|
||||
type: ObjectMoveType.AnimDir,
|
||||
type: ObjectMoveStepType.AnimDir,
|
||||
dir
|
||||
});
|
||||
return this;
|
||||
@ -605,7 +605,7 @@ export abstract class ObjectMover<T extends IObjectMovable>
|
||||
* @param queue 移动队列
|
||||
*/
|
||||
private async moveProgress(
|
||||
queue: Readonly<ObjectMoveStep>[],
|
||||
queue: ObjectMoveStep[],
|
||||
controller: Readonly<IMoverController>
|
||||
) {
|
||||
// 移动开始
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { FaceDirection } from './types';
|
||||
|
||||
/**
|
||||
* @deprecated 用 `IFaceHandler` 接口
|
||||
* 获取指定朝向的坐标偏移量
|
||||
* @param dir 朝向
|
||||
*/
|
||||
@ -29,7 +28,6 @@ export function getFaceMovement(dir: FaceDirection): Loc {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 用 `IFaceHandler` 接口
|
||||
* 将八方向朝向降级为四方向朝向
|
||||
* @param dir 朝向
|
||||
* @param unknown 如果朝向是 `FaceDirection.Unknown`,那么会返回什么,默认值是未知
|
||||
@ -54,7 +52,6 @@ export function degradeFace(
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 用 `IFaceHandler` 接口
|
||||
* 获取指定朝向旋转后的朝向
|
||||
* @param dir 当前朝向
|
||||
* @param anticlockwise 是否逆时针旋转,默认顺时针
|
||||
@ -158,7 +155,6 @@ export function nextFaceDirection(
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 用 `IFaceHandler` 接口
|
||||
* 根据朝向字符串获取朝向枚举值
|
||||
* @param dir 朝向字符串
|
||||
*/
|
||||
|
||||
@ -52,11 +52,6 @@ export interface ITileRawData {
|
||||
readonly type: TileType;
|
||||
/** 图块的通行性对象 */
|
||||
readonly pass: ITilePassData;
|
||||
/**
|
||||
* 事件可通行性,当为 `false` 时,玩家会通过撞击触发图块的触发器,
|
||||
* 当为 `true` 时,玩家会通过走上去触发图块的触发器。类似于旧样板的 `noPass`
|
||||
*/
|
||||
readonly eventPass: boolean;
|
||||
}
|
||||
|
||||
export interface ITileLegacyConverter<TLegacy> {
|
||||
|
||||
@ -66,8 +66,6 @@ import { ILoadProgressTotal, LoadProgressTotal } from '@motajs/loader';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { logger } from '@motajs/common';
|
||||
import { ISaveSystem, SaveSystem } from './save';
|
||||
import { DefaultPassChecker } from './hero';
|
||||
import { DefaultHitAction } from './hero/hitAction';
|
||||
|
||||
export class CoreState implements ICoreState {
|
||||
// Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据
|
||||
@ -210,16 +208,6 @@ export class CoreState implements ICoreState {
|
||||
);
|
||||
});
|
||||
|
||||
// 勇士顶层初始化
|
||||
const passChecker = new DefaultPassChecker(this.maps);
|
||||
const hitAction = new DefaultHitAction(
|
||||
this.maps,
|
||||
this.triggerCollector,
|
||||
this
|
||||
);
|
||||
this.hero.location.mover.useTerrainChecker(passChecker);
|
||||
this.hero.location.mover.useHitAction(hitAction);
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
import {
|
||||
IHeroHitAction,
|
||||
IHeroHitHandler,
|
||||
IMapStore,
|
||||
IStateBase
|
||||
} from '@user/data-base';
|
||||
import { ITriggerCollector, ITriggerHandler } from '@user/data-system';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
export class DefaultHitAction implements IHeroHitAction {
|
||||
constructor(
|
||||
readonly maps: IMapStore,
|
||||
readonly collector: ITriggerCollector,
|
||||
readonly state: IStateBase
|
||||
) {}
|
||||
|
||||
async hit(handler: IHeroHitHandler): Promise<void> {
|
||||
if (isNil(handler.floorId)) return;
|
||||
|
||||
const map = this.maps.getLayerState(handler.floorId);
|
||||
if (!map) return;
|
||||
const event = map.eventLayer;
|
||||
if (!event) return;
|
||||
|
||||
const { x, y } = handler.nextLoc;
|
||||
const triggers = this.collector.collect(x, y, event);
|
||||
|
||||
const triggerHandler: ITriggerHandler = {
|
||||
state: this.state,
|
||||
layer: map,
|
||||
mapLayer: event,
|
||||
locator: handler.nextLoc
|
||||
};
|
||||
|
||||
return triggers.trigger(triggerHandler);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,72 @@
|
||||
import { ITileLocator } from '@motajs/common';
|
||||
import { FaceDirection, PassBit } from '@user/data-common';
|
||||
import {
|
||||
IMapStore,
|
||||
IPassCheckerHandler,
|
||||
ITerrainPassChecker
|
||||
} from '@user/data-base';
|
||||
import { IHeroState, IStateBase, ITerrainPassChecker } from '@user/data-base';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
export class DefaultPassChecker implements ITerrainPassChecker {
|
||||
constructor(readonly maps: IMapStore) {}
|
||||
constructor(
|
||||
readonly state: IStateBase,
|
||||
readonly hero: IHeroState<unknown>
|
||||
) {}
|
||||
|
||||
canPass(
|
||||
locator: ITileLocator,
|
||||
direction: FaceDirection,
|
||||
floorId: string | undefined
|
||||
): boolean {
|
||||
if (isNil(floorId)) return false;
|
||||
if (
|
||||
direction === FaceDirection.LeftDown ||
|
||||
direction === FaceDirection.LeftUp ||
|
||||
direction === FaceDirection.RightDown ||
|
||||
direction === FaceDirection.RightUp
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const layerState = this.state.maps.getLayerState(floorId);
|
||||
if (!layerState) return false;
|
||||
const eventLayer = layerState.eventLayer;
|
||||
if (!eventLayer) return false;
|
||||
|
||||
const { x, y } = locator;
|
||||
const face = this.hero.location.mover.faceHandler;
|
||||
const { x: dx, y: dy } = face.movement(direction);
|
||||
const nx = x + dx;
|
||||
const ny = y + dy;
|
||||
const opposite = face.opposite(direction);
|
||||
const leaveMask = this.directionToPassBit(direction);
|
||||
const enterMask = this.directionToPassBit(opposite);
|
||||
|
||||
let canLeave = true;
|
||||
let canEnter = true;
|
||||
|
||||
const eventCurr = eventLayer.getLocationData(x, y);
|
||||
const eventNext = eventLayer.getLocationData(nx, ny);
|
||||
if (eventCurr && eventCurr.raw) {
|
||||
canLeave = !!(leaveMask & eventCurr.raw.pass.outPass);
|
||||
}
|
||||
if (eventNext && eventNext.raw) {
|
||||
canEnter = !!(enterMask & eventNext.raw.pass.inPass);
|
||||
}
|
||||
|
||||
if (!canLeave || !canEnter) return false;
|
||||
|
||||
for (const layer of layerState.layerList) {
|
||||
if (layer === eventLayer) continue;
|
||||
const curr = layer.getLocationData(x, y);
|
||||
const next = layer.getLocationData(nx, ny);
|
||||
if (curr && curr.raw && curr.raw.pass.onlyEvents) {
|
||||
canLeave = !!(leaveMask & curr.raw.pass.outPass);
|
||||
}
|
||||
if (next && next.raw && next.raw.pass.onlyEvents) {
|
||||
canEnter = !!(enterMask & next.raw.pass.inPass);
|
||||
}
|
||||
if (!canLeave || !canEnter) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将朝向转换为对应的通行性位掩码。
|
||||
@ -27,87 +86,4 @@ export class DefaultPassChecker implements ITerrainPassChecker {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inBound(x: number, y: number, floorId: string | undefined): boolean {
|
||||
if (isNil(floorId)) return false;
|
||||
const layerState = this.maps.getLayerState(floorId);
|
||||
if (!layerState) return false;
|
||||
const { width, height } = layerState;
|
||||
return x >= 0 && y >= 0 && x < width && y < height;
|
||||
}
|
||||
|
||||
canPass(handler: IPassCheckerHandler): boolean {
|
||||
const { currLoc, nextLoc, direction, floorId, face } = handler;
|
||||
if (isNil(floorId)) return false;
|
||||
|
||||
// 四角朝向直接判定为可通行
|
||||
if (
|
||||
direction === FaceDirection.LeftDown ||
|
||||
direction === FaceDirection.LeftUp ||
|
||||
direction === FaceDirection.RightDown ||
|
||||
direction === FaceDirection.RightUp
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const map = this.maps.getLayerState(floorId);
|
||||
if (!map) return false;
|
||||
const event = map.eventLayer;
|
||||
if (!event) return false;
|
||||
|
||||
const { x, y } = currLoc;
|
||||
const { x: nx, y: ny } = nextLoc;
|
||||
|
||||
const opposite = face.opposite(direction);
|
||||
const leaveMask = this.directionToPassBit(direction);
|
||||
const enterMask = this.directionToPassBit(opposite);
|
||||
|
||||
let canLeave = true;
|
||||
let canEnter = true;
|
||||
|
||||
// 判断事件层
|
||||
const curr = event.getLocationData(x, y);
|
||||
const next = event.getLocationData(nx, ny);
|
||||
if (curr && curr.raw) {
|
||||
canLeave = !!(leaveMask & curr.raw.pass.outPass);
|
||||
}
|
||||
if (next && next.raw) {
|
||||
canEnter = !!(enterMask & next.raw.pass.inPass);
|
||||
}
|
||||
|
||||
if (!canLeave || !canEnter) return false;
|
||||
|
||||
// 判断其他层
|
||||
for (const layer of map.layerList) {
|
||||
if (layer === event) continue;
|
||||
const curr = layer.getLocationData(x, y);
|
||||
const next = layer.getLocationData(nx, ny);
|
||||
let canLeave = true;
|
||||
let canEnter = true;
|
||||
if (curr && curr.raw && curr.raw.pass.onlyEvents) {
|
||||
canLeave = !!(leaveMask & curr.raw.pass.outPass);
|
||||
}
|
||||
if (next && next.raw && next.raw.pass.onlyEvents) {
|
||||
canEnter = !!(enterMask & next.raw.pass.inPass);
|
||||
}
|
||||
if (!canLeave || !canEnter) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
shouldHit(handler: IPassCheckerHandler): boolean {
|
||||
const { nextLoc, floorId } = handler;
|
||||
if (isNil(floorId)) return false;
|
||||
const layerState = this.maps.getLayerState(floorId);
|
||||
if (!layerState) return false;
|
||||
const eventLayer = layerState.eventLayer;
|
||||
if (!eventLayer) return false;
|
||||
|
||||
const { x: nx, y: ny } = nextLoc;
|
||||
|
||||
const next = eventLayer.getLocationData(nx, ny);
|
||||
if (!next || !next.raw) return false;
|
||||
return !next.raw.eventPass;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,22 +36,25 @@ export class TileLegacyBridge implements ITileLegacyConverter<LegacyTileData> {
|
||||
|
||||
private getPass(num: number, legacy: LegacyTileData): ITilePassData {
|
||||
if (num === 0) {
|
||||
// 空图块
|
||||
return {
|
||||
onlyEvents: true,
|
||||
inPass: 0b1111,
|
||||
outPass: 0b1111
|
||||
};
|
||||
} else if (num === 17) {
|
||||
// 空气墙
|
||||
return {
|
||||
onlyEvents: false,
|
||||
inPass: 0b0000,
|
||||
outPass: 0b0000
|
||||
};
|
||||
} else {
|
||||
// 正常图块
|
||||
if (legacy.cannotIn && legacy.cannotOut) {
|
||||
if (legacy.noPass) {
|
||||
return {
|
||||
onlyEvents: true,
|
||||
inPass: 0b0000,
|
||||
outPass: 0b1111
|
||||
};
|
||||
} else if (legacy.cannotIn && legacy.cannotOut) {
|
||||
let inPass = 0b1111;
|
||||
let outPass = 0b1111;
|
||||
if (legacy.cannotIn.includes('up')) {
|
||||
@ -99,8 +102,7 @@ export class TileLegacyBridge implements ITileLegacyConverter<LegacyTileData> {
|
||||
id: legacy.id,
|
||||
trigger: -1,
|
||||
type: this.getTileType(num, legacy),
|
||||
pass: this.getPass(num, legacy),
|
||||
eventPass: !legacy.noPass
|
||||
pass: this.getPass(num, legacy)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,13 +5,14 @@ import {
|
||||
ISpecial,
|
||||
IReadonlyHeroAttribute,
|
||||
IHeroAttribute,
|
||||
IDataBaseExtended
|
||||
IStateBase,
|
||||
IStateBaseExtended
|
||||
} from '@user/data-base';
|
||||
import { ILocationHelper } from '@user/data-common';
|
||||
|
||||
//#region 辅助接口
|
||||
|
||||
export interface IEnemyHandler<TEnemy, THero> extends IDataBaseExtended {
|
||||
export interface IEnemyHandler<TEnemy, THero> {
|
||||
/** 怪物属性信息 */
|
||||
readonly enemy: IEnemy<TEnemy>;
|
||||
/** 怪物上下文 */
|
||||
@ -20,12 +21,11 @@ export interface IEnemyHandler<TEnemy, THero> extends IDataBaseExtended {
|
||||
readonly locator: ITileLocator;
|
||||
/** 勇士属性信息 */
|
||||
readonly hero: IReadonlyHeroAttribute<THero>;
|
||||
/** 当前全局状态对象 */
|
||||
readonly state: IStateBase;
|
||||
}
|
||||
|
||||
export interface IReadonlyEnemyHandler<
|
||||
TEnemy,
|
||||
THero
|
||||
> extends IDataBaseExtended {
|
||||
export interface IReadonlyEnemyHandler<TEnemy, THero> {
|
||||
/** 怪物属性信息 */
|
||||
readonly enemy: IReadonlyEnemy<TEnemy>;
|
||||
/** 怪物上下文 */
|
||||
@ -34,6 +34,8 @@ export interface IReadonlyEnemyHandler<
|
||||
readonly locator: ITileLocator;
|
||||
/** 勇士属性信息 */
|
||||
readonly hero: IReadonlyHeroAttribute<THero>;
|
||||
/** 当前全局状态对象 */
|
||||
readonly state: IStateBase;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@ -281,7 +283,7 @@ export interface IMapDamageReducer {
|
||||
): Readonly<IMapDamageInfo>;
|
||||
}
|
||||
|
||||
export interface IMapDamage<TEnemy, THero> extends IDataBaseExtended {
|
||||
export interface IMapDamage<TEnemy, THero> extends IStateBaseExtended {
|
||||
/** 当前绑定的怪物上下文 */
|
||||
readonly context: IEnemyContext<TEnemy, THero>;
|
||||
|
||||
@ -404,7 +406,7 @@ export interface IDamageCalculator<TEnemy, THero> {
|
||||
): number;
|
||||
}
|
||||
|
||||
export interface IDamageContext<TEnemy, THero> extends IDataBaseExtended {
|
||||
export interface IDamageContext<TEnemy, THero> extends IStateBaseExtended {
|
||||
/**
|
||||
* 获取战斗伤害信息
|
||||
* @param enemy 怪物视图
|
||||
@ -497,7 +499,7 @@ export interface IDamageSystem<TEnemy, THero> extends IDamageContext<
|
||||
export interface IReadonlyEnemyContext<
|
||||
TEnemy,
|
||||
THero
|
||||
> extends IDataBaseExtended {
|
||||
> extends IStateBaseExtended {
|
||||
/** 怪物上下文宽度 */
|
||||
readonly width: number;
|
||||
/** 怪物上下文高度 */
|
||||
@ -737,7 +739,7 @@ export interface IEnemyContext<TEnemy, THero> extends IReadonlyEnemyContext<
|
||||
|
||||
//#region 战斗流程
|
||||
|
||||
export interface ICombatFlowHandler<TEnemy, THero> extends IDataBaseExtended {
|
||||
export interface ICombatFlowHandler<TEnemy, THero> {
|
||||
/** 战斗的怪物是否在地图上 */
|
||||
readonly onMap: boolean;
|
||||
/** 可修改勇士对象 */
|
||||
@ -748,6 +750,8 @@ export interface ICombatFlowHandler<TEnemy, THero> extends IDataBaseExtended {
|
||||
readonly context: IEnemyContext<TEnemy, THero>;
|
||||
/** 怪物位置 */
|
||||
readonly locator: ITileLocator;
|
||||
/** 全局状态对象 */
|
||||
readonly state: IStateBase;
|
||||
}
|
||||
|
||||
export interface ICombatFlowHook<TEnemy, THero> extends IHookBase {
|
||||
@ -799,7 +803,7 @@ export interface ICombatResult<TEnemy, THero> {
|
||||
}
|
||||
|
||||
export interface ICombatFlow<TEnemy, THero>
|
||||
extends IHookable<ICombatFlowHook<TEnemy, THero>>, IDataBaseExtended {
|
||||
extends IHookable<ICombatFlowHook<TEnemy, THero>>, IStateBaseExtended {
|
||||
/** 勇士属性对象 */
|
||||
readonly hero: IReadonlyHeroAttribute<THero> | null;
|
||||
/** 怪物上下文对象 */
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
ILayerState,
|
||||
IMapLayer,
|
||||
IStateBase,
|
||||
IDataBaseExtended
|
||||
IStateBaseExtended
|
||||
} from '@user/data-base';
|
||||
|
||||
export interface ITriggerHandler {
|
||||
@ -19,7 +19,7 @@ export interface ITriggerHandler {
|
||||
|
||||
export type TriggerFactory = (type: number, state: IStateBase) => ITrigger;
|
||||
|
||||
export interface ITrigger extends IDataBaseExtended {
|
||||
export interface ITrigger extends IStateBaseExtended {
|
||||
/** 触发器类型标识 */
|
||||
readonly type: number;
|
||||
/** 触发器优先级 */
|
||||
@ -37,7 +37,7 @@ export interface ITrigger extends IDataBaseExtended {
|
||||
collection(): ITriggerCollection;
|
||||
}
|
||||
|
||||
export interface ITriggerRegistry extends IDataBaseExtended {
|
||||
export interface ITriggerRegistry extends IStateBaseExtended {
|
||||
/**
|
||||
* 注册一个按类型创建的触发器工厂
|
||||
* @param type 触发器类型
|
||||
|
||||
40
prompt.md
40
prompt.md
@ -1,32 +1,26 @@
|
||||
# 核心规则
|
||||
|
||||
你的身份不是架构设计者,而是长期维护该项目的核心开发者。
|
||||
|
||||
该项目的架构已经经过大量设计与验证,你不需要深刻理解架构,当前架构已经是经过深思熟虑的结果。除非明确要求,否则不要试图重新设计架构,也不要主动优化已有设计。你的职责是在保持整体风格和架构稳定的前提下,完成需求并自然扩展已有系统。
|
||||
|
||||
1. **遵循最小修改原则**:
|
||||
- 默认不要重新设计已有代码。
|
||||
- 默认不要重构已有实现。
|
||||
- 默认不要为了"更优雅"而改变已有模式。
|
||||
- 新代码应当尽可能像是在原有代码基础上的自然延续,而不是重新实现
|
||||
2. **现有代码最重要**。当前文件就是最高优先级规范。当 Prompt、已有代码、自身知识冲突时,优先遵循已有代码。
|
||||
3. **不要主动创造新的模式**。遵循已有的设计与代码风格,不要在不同风格之间来回跳跃。
|
||||
4. **优先优化维护成本,而不是代码长度**。允许局部重复,允许多几个变量,允许多几个 if。但绝不鼓励:为了减少几行代码增加抽象;为了减少重复增加函数跳转;为了"现代 TS"使用复杂语法。
|
||||
5. **当不确定时,选择最保守的方案**。如果两种方案都能实现需求,默认选择修改最少、风险最低、最接近已有实现的方案。
|
||||
|
||||
# 必须规则
|
||||
|
||||
以下规则必须时刻遵守,任何情况下都不允许违反。
|
||||
|
||||
1. **不擅自修改已有内容**:将我已经写好的内容视为绝对正确。除非我**明确允许**,否则**不允许任何修改**,哪怕因为接口变化或其他原因导致其中出现类型错误。若认为我的代码存在错误,应在对话中提出,而不是直接修改。
|
||||
2. **不恢复我的修改**:我做的任何代码修改都是有原因的。若我在两次对话期间新增、删除或修改了部分代码,不要将其恢复。
|
||||
3. **遇到歧义立即提问**:若思考或实现时遇到任何问题——例如描述模糊、接口不清晰、某些地方存在歧义等——应立即向我提问,而不是按自己的想法去写。若完成需求所需的接口尚不存在,也应当立即停止实现,向我提出疑问,而不是擅自新增接口来推进。
|
||||
4. **修改文件前重读文件,修改后说明内容**:修改文件前务必重新阅读以确保内容是最新版。修改后必须在对话中说明所有改动的文件及具体改动内容。在修改文件时,**不得修改**文档的 `涉及文件` 节未提及的文件。
|
||||
5. **关于 `dev.md` 的强度升级**:`dev.md` 中"一般不建议/尽量避免"的条目在此处视为**绝对禁止**;"建议/最好"的条目在此处视为**必须遵守**。
|
||||
6. **渲染端永远处于被动**:任何情况下都不可能会出现数据端主动通知渲染端进行更新的场景,渲染端仅通过钩子与数据端通信,通过钩子被动获取信息,并在某些情况下影响数据端行为。
|
||||
7. **关于非空判断**:对于对象,直接使用 `if (!object)` 的方式进行判断,对于字面量,使用 `lodash` 接口 `isNil` 判断 `if (isNil(value))`,不要使用 `if (value === undefined)` 这种方式。
|
||||
8. **方法顺序**:对于类的方法,根据功能进行排序,而不是按照修饰词进行排序。对于较长或功能较多的类,使用 `#region` 进行分区,分区按照方法功能进行划分。所有类的成员必须写到类的开头,并按照功能排序。
|
||||
9. **注释要求**:所有私有方法和私有成员必须添加 jsDoc 注释,受保护、公共成员与方法必须在源头处添加注释,继承而来的,除非功能或描述有变化,否则不得添加注释。即要求注释具有唯一性:已经在某些地方添加过注释的,不在其他地方添加。
|
||||
3. **以目的驱动,而非以接口驱动**:实现前先想清楚我为什么要这样设计接口、这个接口设计的目的是什么,而不是单纯地以将接口填满为目标。
|
||||
4. **遇到歧义立即提问**:若思考或实现时遇到任何问题——例如描述模糊、接口不清晰、某些地方存在歧义等——应立即向我提问,而不是按自己的想法去写。
|
||||
5. **接口缺失时停止并提问**:若完成需求所需的接口尚不存在,应立即停止实现,向我提出疑问,而不是擅自新增接口来推进。
|
||||
6. **按我说的方式重构**:重构时**不要**擅自新增任何参数、方法或接口,**不要**仅通过新增兼容层应对重构。
|
||||
- **彻底性重构**(新旧接口完全没有重合):按正常方式全新实现,旧代码仅作逻辑参考。
|
||||
- **结构性重构**(新旧接口基本一致,细节有差距):将旧代码搬移到新接口上后进行微调。
|
||||
7. **不要有任何"顺手"的想法**:任何时候,都不要出现顺手的想法,包括但不限于发现了一个 bug 然后**顺手**修复、发现一处类型错误然后**顺手**修复等,这种情况下应当遵循规则 1。
|
||||
8. **修改文件前重读文件,修改后说明内容**:修改文件前务必重新阅读以确保内容是最新版。修改后必须在对话中说明所有改动的文件及具体改动内容。在修改文件时,**不得修改**文档的 `涉及文件` 节未提及的文件。
|
||||
9. **关于 `dev.md` 的强度升级**:`dev.md` 中"一般不建议/尽量避免"的条目在此处视为**绝对禁止**;"建议/最好"的条目在此处视为**必须遵守**。`as` 关键字不受此升级约束。
|
||||
10. **参数最小化 / 级联获取**:不从外部传入可通过已有引用获取的对象。若对象已持有对父对象或相关对象的引用(如 `controller`),应直接通过该引用获取所需数据(如 `controller.state`),不应再单独传递参数。即"非必要不传参"。
|
||||
11. **转换下沉**:数据的格式或类型转换(如 `string` 的 id 转 `number`)应放在最终消费该值的对象中完成,而非在调用方预先转换后再传入。这样可以减少调用方的职责并避免重复转换。
|
||||
12. **通过公有接口操作,不直接赋值内部属性**:对某个对象内部状态的修改,必须通过该对象对外暴露的公有方法进行(如用 `mover.face(dir).start()` 设置朝向并发起移动),而不是通过类型断言访问其内部属性直接赋值(如 `mover.faceDirection = dir`)。
|
||||
13. **map/filter 优于 for 循环**:对数组进行数据收集或转换时(如收集 Promise),使用 `map`、`filter` 等函数式写法,比 `for` 循环 + `push` 的可读性更高。
|
||||
14. **渲染端永远处于被动**:任何情况下都不可能会出现数据端主动通知渲染端进行更新的场景,渲染端仅通过钩子与数据端通信,通过钩子被动获取信息,并在某些情况下影响数据端行为。
|
||||
15. **按设计实现,勿自由发挥**:我已想好功能的基本思路和代码结构,你的任务是还原它,而非自行创造。
|
||||
16. **代码首先得看着舒服**:代码的第一读者是人,必须一眼就能看清整体结构,不需要反复来回读才能理顺逻辑。不出现过长的表达式,不进行不必要的函数拆分。不允许出现 3 层以上的 `if` 嵌套。
|
||||
17. **关于非空判断**:对于对象,直接使用 `if (!object)` 的方式进行判断,对于字面量,使用 `lodash` 接口 `isNil` 判断 `if (isNil(value))`,不要使用 `if (value === undefined)` 这种方式。
|
||||
|
||||
# 建议规则
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user