refactor: 勇士状态
CodeQL / Analyze (javascript) (push) Has been cancelled

This commit is contained in:
AncTe committed 2026-04-16 22:31:39 +08:00
1 parent c22a51829d
commit 92ff489811
15 files changed
+310 -193

No files matched your search

@@ -0,0 +1,2 @@
export * from './types';
export * from './utils';
@@ -0,0 +1,18 @@
export const enum FaceDirection {
Unknown,
Left,
Up,
Right,
Down,
LeftUp,
RightUp,
LeftDown,
RightDown
}
export interface IFaceData {
/** 图块数字 */
readonly identifier: number;
/** 图块朝向 */
readonly face: FaceDirection;
}
@@ -30,7 +30,7 @@ export function getFaceMovement(dir: FaceDirection): Loc {
/**
* 将八方向朝向降级为四方向朝向
* @param dir 朝向
* @param unknown 如果朝向是 `FaceDirection.Unknown`,那么会返回什么,默认还是未知
* @param unknown 如果朝向是 `FaceDirection.Unknown`,那么会返回什么,默认值是未知
*/
export function degradeFace(
dir: FaceDirection,
+1 -1
View File
@@ -1,4 +1,4 @@
export * from './attribute';
export * from './mover';
export * from './state';
export * from './types';
export * from './utils';
+138
View File
@@ -0,0 +1,138 @@
import { Hookable, HookController, IHookController } from '@motajs/common';
import { isNil } from 'lodash-es';
import { getFaceMovement, nextFaceDirection } from '../common/utils';
import { IHeroFollower, IHeroMover, IHeroMovingHooks } from './types';
import { FaceDirection } from '../common';
const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png';
export class HeroMover
extends Hookable<IHeroMovingHooks>
implements IHeroMover
{
x: number = 0;
y: number = 0;
direction: FaceDirection = FaceDirection.Down;
image: ImageIds = DEFAULT_HERO_IMAGE;
/** 当前勇士是否正在移动 */
moving: boolean = false;
alpha: number = 1;
readonly followers: IHeroFollower[] = [];
protected createController(
hook: Partial<IHeroMovingHooks>
): IHookController<IHeroMovingHooks> {
return new HookController(this, hook);
}
setPosition(x: number, y: number): void {
this.x = x;
this.y = y;
this.forEachHook(hook => {
hook.onSetPosition?.(x, y);
});
}
turn(direction?: FaceDirection): void {
const next = isNil(direction)
? nextFaceDirection(this.direction)
: direction;
this.direction = next;
this.forEachHook(hook => {
hook.onTurnHero?.(next);
});
}
startMove(): void {
this.moving = true;
this.forEachHook(hook => {
hook.onStartMove?.();
});
}
async move(dir: FaceDirection, time: number = 100): Promise<void> {
await Promise.all(
this.forEachHook(hook => {
return hook.onMoveHero?.(dir, time);
})
);
const { x, y } = getFaceMovement(dir);
this.x += x;
this.y += y;
}
async endMove(): Promise<void> {
if (!this.moving) return;
await Promise.all(
this.forEachHook(hook => {
return hook.onEndMove?.();
})
);
this.moving = false;
}
async jumpHero(
x: number,
y: number,
time: number = 500,
waitFollower: boolean = false
): Promise<void> {
await Promise.all(
this.forEachHook(hook => {
return hook.onJumpHero?.(x, y, time, waitFollower);
})
);
this.x = x;
this.y = y;
}
setImage(image: ImageIds): void {
this.image = image;
this.forEachHook(hook => {
hook.onSetImage?.(image);
});
}
setAlpha(alpha: number): void {
this.alpha = alpha;
this.forEachHook(hook => {
hook.onSetAlpha?.(alpha);
});
}
setFollowerAlpha(identifier: string, alpha: number): void {
const follower = this.followers.find(v => v.identifier === identifier);
if (!follower) return;
follower.alpha = alpha;
this.forEachHook(hook => {
hook.onSetFollowerAlpha?.(identifier, alpha);
});
}
addFollower(follower: number, identifier: string): void {
this.followers.push({ num: follower, identifier, alpha: 1 });
this.forEachHook(hook => {
hook.onAddFollower?.(follower, identifier);
});
}
removeFollower(identifier: string, animate: boolean = false): void {
const index = this.followers.findIndex(
v => v.identifier === identifier
);
if (index === -1) return;
this.followers.splice(index, 1);
this.forEachHook(hook => {
hook.onRemoveFollower?.(identifier, animate);
});
}
removeAllFollowers(): void {
this.followers.length = 0;
this.forEachHook(hook => {
hook.onRemoveAllFollowers?.();
});
}
}
+20 -122
View File
@@ -1,139 +1,37 @@
import { Hookable, HookController, IHookController } from '@motajs/common';
import { isNil } from 'lodash-es';
import { getFaceMovement, nextFaceDirection } from './utils';
import {
FaceDirection,
IHeroFollower,
IHeroAttribute,
IHeroMover,
IHeroState,
IHeroStateHooks
IReadonlyHeroAttribute
} from './types';
const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png';
export class HeroState<THero> implements IHeroState<THero> {
constructor(
public mover: IHeroMover,
public attribute: IHeroAttribute<THero>
) {}
export class HeroState extends Hookable<IHeroStateHooks> implements IHeroState {
x: number = 0;
y: number = 0;
direction: FaceDirection = FaceDirection.Down;
image: ImageIds = DEFAULT_HERO_IMAGE;
/** 当前勇士是否正在移动 */
moving: boolean = false;
alpha: number = 1;
readonly followers: IHeroFollower[] = [];
protected createController(
hook: Partial<IHeroStateHooks>
): IHookController<IHeroStateHooks> {
return new HookController(this, hook);
attachMover(mover: IHeroMover): void {
this.mover = mover;
}
setPosition(x: number, y: number): void {
this.x = x;
this.y = y;
this.forEachHook(hook => {
hook.onSetPosition?.(x, y);
});
attachAttribute(attribute: IHeroAttribute<THero>): void {
this.attribute = attribute;
}
turn(direction?: FaceDirection): void {
const next = isNil(direction)
? nextFaceDirection(this.direction)
: direction;
this.direction = next;
this.forEachHook(hook => {
hook.onTurnHero?.(next);
});
getHeroMover(): IHeroMover {
return this.mover;
}
startMove(): void {
this.moving = true;
this.forEachHook(hook => {
hook.onStartMove?.();
});
getModifiableAttribute(): IHeroAttribute<THero> {
return this.attribute;
}
async move(dir: FaceDirection, time: number = 100): Promise<void> {
await Promise.all(
this.forEachHook(hook => {
return hook.onMoveHero?.(dir, time);
})
);
const { x, y } = getFaceMovement(dir);
this.x += x;
this.y += y;
getAttribute(): IReadonlyHeroAttribute<THero> {
return this.attribute;
}
async endMove(): Promise<void> {
if (!this.moving) return;
await Promise.all(
this.forEachHook(hook => {
return hook.onEndMove?.();
})
);
this.moving = false;
}
async jumpHero(
x: number,
y: number,
time: number = 500,
waitFollower: boolean = false
): Promise<void> {
await Promise.all(
this.forEachHook(hook => {
return hook.onJumpHero?.(x, y, time, waitFollower);
})
);
this.x = x;
this.y = y;
}
setImage(image: ImageIds): void {
this.image = image;
this.forEachHook(hook => {
hook.onSetImage?.(image);
});
}
setAlpha(alpha: number): void {
this.alpha = alpha;
this.forEachHook(hook => {
hook.onSetAlpha?.(alpha);
});
}
setFollowerAlpha(identifier: string, alpha: number): void {
const follower = this.followers.find(v => v.identifier === identifier);
if (!follower) return;
follower.alpha = alpha;
this.forEachHook(hook => {
hook.onSetFollowerAlpha?.(identifier, alpha);
});
}
addFollower(follower: number, identifier: string): void {
this.followers.push({ num: follower, identifier, alpha: 1 });
this.forEachHook(hook => {
hook.onAddFollower?.(follower, identifier);
});
}
removeFollower(identifier: string, animate: boolean = false): void {
const index = this.followers.findIndex(
v => v.identifier === identifier
);
if (index === -1) return;
this.followers.splice(index, 1);
this.forEachHook(hook => {
hook.onRemoveFollower?.(identifier, animate);
});
}
removeAllFollowers(): void {
this.followers.length = 0;
this.forEachHook(hook => {
hook.onRemoveAllFollowers?.();
});
getIsolatedAttribute(): IHeroAttribute<THero> {
return this.attribute.clone();
}
}
+68 -45
View File
@@ -1,4 +1,5 @@
import { IHookBase, IHookable } from '@motajs/common';
import { FaceDirection } from '@user/data-state';
//#region 勇士属性
@@ -41,7 +42,7 @@ export interface IHeroModifier<T = unknown, V = unknown> {
clone(): IHeroModifier<T, V>;
}
export interface IHeroAttribute<THero> {
export interface IReadonlyHeroAttribute<THero> {
/**
* 获取勇士的基础属性,即未经过任何 Buff 或装备等加成的属性
* @param name 属性名称
@@ -54,6 +55,26 @@ export interface IHeroAttribute<THero> {
*/
getFinalAttribute<K extends keyof THero>(name: K): THero[K];
/**
* 将指定属性标记为脏
* @param name 属性名称
*/
markDirty(name: keyof THero): void;
/**
* 将指定属性修饰器标记为脏
* @param modifier 属性修饰器
*/
markModifierDirty(modifier: IHeroModifier): void;
/**
* 深拷贝此勇士属性对象
* @param cloneModifier 是否同时复制修饰器,默认复制
*/
clone(cloneModifier?: boolean): IHeroAttribute<THero>;
}
export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
/**
* 设置勇士的基础属性
* @param name 属性名称
@@ -80,52 +101,11 @@ export interface IHeroAttribute<THero> {
name: K,
modifier: IHeroModifier<THero[K], unknown>
): void;
/**
* 将指定属性标记为脏
* @param name 属性名称
*/
markDirty(name: keyof THero): void;
/**
* 将指定属性修饰器标记为脏
* @param modifier 属性修饰器
*/
markModifierDirty(modifier: IHeroModifier): void;
/**
* 深拷贝此勇士属性对象
* @param cloneModifier 是否同时复制修饰器,默认复制
*/
clone(cloneModifier?: boolean): IHeroAttribute<THero>;
}
//#endregion
//#region 朝向
export const enum FaceDirection {
Unknown,
Left,
Up,
Right,
Down,
LeftUp,
RightUp,
LeftDown,
RightDown
}
export interface IFaceData {
/** 图块数字 */
readonly identifier: number;
/** 图块朝向 */
readonly face: FaceDirection;
}
//#endregion
//#region 勇士状态
//#region 勇士移动
export const enum HeroAnimateDirection {
/** 正向播放动画 */
@@ -143,7 +123,7 @@ export interface IHeroFollower {
alpha: number;
}
export interface IHeroStateHooks extends IHookBase {
export interface IHeroMovingHooks extends IHookBase {
/**
* 当设置勇士的坐标时触发
* @param x 勇士横坐标
@@ -227,7 +207,7 @@ export interface IHeroStateHooks extends IHookBase {
onSetFollowerAlpha(identifier: string, alpha: number): void;
}
export interface IHeroState extends IHookable<IHeroStateHooks> {
export interface IHeroMover extends IHookable<IHeroMovingHooks> {
/** 勇士横坐标 */
readonly x: number;
/** 勇士纵坐标 */
@@ -328,3 +308,46 @@ export interface IHeroState extends IHookable<IHeroStateHooks> {
}
//#endregion
//#region 勇士状态
export interface IHeroState<THero> {
/** 勇士移动对象 */
readonly mover: IHeroMover;
/** 勇士属性对象 */
readonly attribute: IReadonlyHeroAttribute<THero>;
/**
* 绑定勇士移动对象
* @param mover 勇士移动对象
*/
attachMover(mover: IHeroMover): void;
/**
* 获取勇士移动对象
*/
getHeroMover(): IHeroMover;
/**
* 绑定勇士属性对象
* @param attribute 勇士属性对象
*/
attachAttribute(attribute: IHeroAttribute<THero>): void;
/**
* 获取可修改勇士对象
*/
getModifiableAttribute(): IHeroAttribute<THero>;
/**
* 获取只读勇士对象
*/
getAttribute(): IReadonlyHeroAttribute<THero>;
/**
* 获取独立勇士属性对象,修改此对象不会影响勇士本身的属性
*/
getIsolatedAttribute(): IHeroAttribute<THero>;
}
//#endregion
+1
View File
@@ -1,3 +1,4 @@
export * from './common';
export * from './enemy';
export * from './hero';
export * from './load';