diff --git a/packages-user/client-modules/src/render/map/extension/hero.ts b/packages-user/client-modules/src/render/map/extension/hero.ts index 25fb1e5..a6e3068 100644 --- a/packages-user/client-modules/src/render/map/extension/hero.ts +++ b/packages-user/client-modules/src/render/map/extension/hero.ts @@ -5,10 +5,9 @@ import { HeroAnimateDirection, IHeroState, IHeroStateHooks, - IMapLayer, - nextFaceDirection, - state -} from '@user/data-state'; + nextFaceDirection +} from '@user/data-base'; +import { IMapLayer, state } from '@user/data-state'; import { IMapRenderer, IMapRendererTicker, IMovingBlock } from '../types'; import { isNil } from 'lodash-es'; import { IHookController, logger } from '@motajs/common'; diff --git a/packages-user/client-modules/src/render/map/extension/manager.ts b/packages-user/client-modules/src/render/map/extension/manager.ts index aa5b341..a87c644 100644 --- a/packages-user/client-modules/src/render/map/extension/manager.ts +++ b/packages-user/client-modules/src/render/map/extension/manager.ts @@ -1,4 +1,5 @@ -import { IHeroState, IMapLayer } from '@user/data-state'; +import { IHeroState } from '@user/data-base'; +import { IMapLayer } from '@user/data-state'; import { IMapDoorRenderer, IMapExtensionManager, diff --git a/packages-user/client-modules/src/render/map/extension/types.ts b/packages-user/client-modules/src/render/map/extension/types.ts index 88cb6ed..98503cb 100644 --- a/packages-user/client-modules/src/render/map/extension/types.ts +++ b/packages-user/client-modules/src/render/map/extension/types.ts @@ -2,9 +2,9 @@ import { ITexture, Font } from '@motajs/render'; import { FaceDirection, HeroAnimateDirection, - IHeroState, - IMapLayer -} from '@user/data-state'; + IHeroState +} from '@user/data-base'; +import { IMapLayer } from '@user/data-state'; import { IMapRenderResult } from '../types'; diff --git a/packages-user/data-base/src/hero/attribute.ts b/packages-user/data-base/src/hero/attribute.ts new file mode 100644 index 0000000..f1bb76a --- /dev/null +++ b/packages-user/data-base/src/hero/attribute.ts @@ -0,0 +1,152 @@ +import { logger } from '@motajs/common'; +import { IHeroAttribute, IHeroModifier } from './types'; + +export abstract class BaseHeroModifier implements IHeroModifier { + abstract readonly priority: number; + + owner: IHeroAttribute | null = null; + + constructor(private currentValue: V) {} + + get value(): V { + return this.currentValue; + } + + setValue(value: V): void { + this.currentValue = value; + this.owner?.markModifierDirty(this); + } + + getValue(): V { + return this.currentValue; + } + + bindAttribute(attribute: IHeroAttribute | null): void { + this.owner = attribute; + } + + abstract modify(value: T, baseValue: T, name: string): T; + + abstract clone(): IHeroModifier; +} + +export class HeroAttribute implements IHeroAttribute { + /** 当前勇士属性修饰器 */ + private readonly modifier: Map = new Map(); + /** 当前每个修饰器对应的属性值 */ + private readonly modifierName: Map = new Map(); + /** 当前勇士最终属性 */ + private readonly finalAttribute: THero; + + /** + * @param attribute 当前勇士的基础属性 + */ + constructor(private readonly attribute: THero) { + this.finalAttribute = structuredClone(attribute); + } + + /** + * 判定修饰器结果是否同引用 + * @param curr 当前属性值 + * @param next 修饰器修饰结果 + */ + private isSameReference(curr: unknown, next: unknown) { + return typeof curr === 'object' && curr !== null && curr === next; + } + + /** + * 重新计算指定属性值 + * @param name 属性名称 + */ + private recalculateAttribute(name: K): void { + const modifierList = this.modifier.get(name); + if (!modifierList) return; + + const baseValue = this.attribute[name]; + let value = baseValue; + for (const modifier of modifierList as IHeroModifier[]) { + const nextValue = modifier.modify(value, baseValue, name); + // 部署之后就没必要弹这个警告了,额外判断反而可能会有一定的性能损失,直接 tree-shaking 优化掉 + if (import.meta.env.DEV && this.isSameReference(value, nextValue)) { + const modiferName = modifier.constructor.name; + logger.warn(109, modiferName, String(name)); + } + value = nextValue; + } + + this.finalAttribute[name] = value; + } + + getBaseAttribute(name: K): THero[K] { + return this.attribute[name]; + } + + getFinalAttribute(name: K): THero[K] { + return this.finalAttribute[name]; + } + + setBaseAttribute(name: K, value: THero[K]): void { + this.attribute[name] = value; + this.markDirty(name); + } + + addModifier( + name: K, + modifier: IHeroModifier + ): void { + if (modifier.owner) { + const modiferName = modifier.constructor.name; + logger.warn(108, modiferName, String(name)); + return; + } + + const modifierList = this.modifier.getOrInsert(name, []); + modifierList.push(modifier); + modifierList.sort((left, right) => right.priority - left.priority); + + this.modifierName.set(modifier, name); + modifier.bindAttribute(this); + this.markDirty(name); + } + + deleteModifier( + name: K, + modifier: IHeroModifier + ): void { + const modifierList = this.modifier.get(name); + if (!modifierList) return; + const index = modifierList.indexOf(modifier); + if (index === -1) return; + + modifier.bindAttribute(null); + modifierList.splice(index, 1); + this.modifierName.delete(modifier); + + this.markDirty(name); + } + + markDirty(name: keyof THero): void { + this.recalculateAttribute(name); + } + + markModifierDirty(modifier: IHeroModifier): void { + const name = this.modifierName.get(modifier); + if (name === undefined) return; + this.markDirty(name); + } + + clone(cloneModifier: boolean = true): IHeroAttribute { + const cloned = new HeroAttribute( + structuredClone(this.attribute) + ); + if (!cloneModifier) return cloned; + // 拷贝修饰器 + for (const [modifier, name] of this.modifierName) { + cloned.addModifier( + name, + modifier.clone() as IHeroModifier + ); + } + return cloned; + } +} diff --git a/packages-user/data-base/src/hero/index.ts b/packages-user/data-base/src/hero/index.ts new file mode 100644 index 0000000..8b42120 --- /dev/null +++ b/packages-user/data-base/src/hero/index.ts @@ -0,0 +1,4 @@ +export * from './attribute'; +export * from './state'; +export * from './types'; +export * from './utils'; diff --git a/packages-user/data-state/src/hero/state.ts b/packages-user/data-base/src/hero/state.ts similarity index 94% rename from packages-user/data-state/src/hero/state.ts rename to packages-user/data-base/src/hero/state.ts index 113eebd..ba121cc 100644 --- a/packages-user/data-state/src/hero/state.ts +++ b/packages-user/data-base/src/hero/state.ts @@ -1,8 +1,14 @@ import { Hookable, HookController, IHookController } from '@motajs/common'; -import { IHeroFollower, IHeroState, IHeroStateHooks } from './types'; -import { FaceDirection, getFaceMovement, nextFaceDirection } from '../common'; import { isNil } from 'lodash-es'; -import { DEFAULT_HERO_IMAGE } from '../shared'; +import { getFaceMovement, nextFaceDirection } from './utils'; +import { + FaceDirection, + IHeroFollower, + IHeroState, + IHeroStateHooks +} from './types'; + +const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png'; export class HeroState extends Hookable implements IHeroState { x: number = 0; diff --git a/packages-user/data-base/src/hero/types.ts b/packages-user/data-base/src/hero/types.ts new file mode 100644 index 0000000..99c0f4f --- /dev/null +++ b/packages-user/data-base/src/hero/types.ts @@ -0,0 +1,330 @@ +import { IHookBase, IHookable } from '@motajs/common'; + +//#region 勇士属性 + +export interface IHeroModifier { + /** 修饰器优先级 */ + readonly priority: number; + /** 修饰器参数值 */ + readonly value: V; + /** 当前修饰器所属的勇士属性对象 */ + readonly owner: IHeroAttribute | null; + + /** + * 设置修饰器参数值 + * @param value 参数值 + */ + setValue(value: V): void; + + /** + * 获取修饰器参数值 + */ + getValue(): V; + + /** + * 绑定勇士属性对象 + * @param attribute 勇士属性对象 + */ + bindAttribute(attribute: IHeroAttribute | null): void; + + /** + * 对指定属性值进行修改 + * @param value 该属性值的当前属性值,即经过了优先级更高的修饰器修饰后的属性值 + * @param baseValue 该属性值的基础属性值 + * @param name 属性名称 + */ + modify(value: T, baseValue: T, name: PropertyKey): T; + + /** + * 深拷贝此修饰器 + */ + clone(): IHeroModifier; +} + +export interface IHeroAttribute { + /** + * 获取勇士的基础属性,即未经过任何 Buff 或装备等加成的属性 + * @param name 属性名称 + */ + getBaseAttribute(name: K): THero[K]; + + /** + * 获取勇士的最终属性,即经过了 Buff 或装备等加成的属性 + * @param name 属性名称 + */ + getFinalAttribute(name: K): THero[K]; + + /** + * 设置勇士的基础属性 + * @param name 属性名称 + * @param value 要设置为的值 + */ + setBaseAttribute(name: K, value: THero[K]): void; + + /** + * 向一个属性添加属性修饰器 + * @param name 属性名称 + * @param modifier 属性修饰器 + */ + addModifier( + name: K, + modifier: IHeroModifier + ): void; + + /** + * 删除指定的属性修饰器 + * @param name 属性名称 + * @param modifier 属性修饰器 + */ + deleteModifier( + name: K, + modifier: IHeroModifier + ): void; + + /** + * 将指定属性标记为脏 + * @param name 属性名称 + */ + markDirty(name: keyof THero): void; + + /** + * 将指定属性修饰器标记为脏 + * @param modifier 属性修饰器 + */ + markModifierDirty(modifier: IHeroModifier): void; + + /** + * 深拷贝此勇士属性对象 + * @param cloneModifier 是否同时复制修饰器,默认复制 + */ + clone(cloneModifier?: boolean): IHeroAttribute; +} + +//#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 勇士状态 + +export const enum HeroAnimateDirection { + /** 正向播放动画 */ + Forward, + /** 反向播放动画 */ + Backward +} + +export interface IHeroFollower { + /** 跟随者的图块数字 */ + readonly num: number; + /** 跟随者的标识符 */ + readonly identifier: string; + /** 跟随者的不透明度 */ + alpha: number; +} + +export interface IHeroStateHooks extends IHookBase { + /** + * 当设置勇士的坐标时触发 + * @param x 勇士横坐标 + * @param y 勇士纵坐标 + */ + onSetPosition(x: number, y: number): void; + + /** + * 当设置勇士朝向时触发 + * @param direction 勇士朝向 + */ + onTurnHero(direction: FaceDirection): void; + + /** + * 当勇士开始移动时触发 + */ + onStartMove(): void; + + /** + * 当移动勇士时触发 + * @param direction 移动方向 + * @param time 移动动画时长 + */ + onMoveHero(direction: FaceDirection, time: number): Promise; + + /** + * 当停止移动时触发 + */ + onEndMove(): Promise; + + /** + * 当勇士跳跃时触发 + * @param x 目标点横坐标 + * @param y 目标点纵坐标 + * @param time 跳跃动画时长 + * @param waitFollower 是否等待跟随者跳跃完毕 + */ + onJumpHero( + x: number, + y: number, + time: number, + waitFollower: boolean + ): Promise; + + /** + * 当设置勇士图片时触发 + * @param image 勇士图片 id + */ + onSetImage(image: ImageIds): void; + + /** + * 当设置勇士不透明度时执行 + * @param alpha 不透明度 + */ + onSetAlpha(alpha: number): void; + + /** + * 添加跟随者时触发 + * @param follower 跟随者的图块数字 + * @param identifier 跟随者的标识符 + */ + onAddFollower(follower: number, identifier: string): void; + + /** + * 当移除跟随者时触发 + * @param identifier 跟随者的标识符 + * @param animate 填 `true` 的话,如果删除了中间的跟随者,后续跟随者会使用移动动画移动到下一格,否则瞬移至下一格 + */ + onRemoveFollower(identifier: string, animate: boolean): void; + + /** + * 当移除所有跟随者时触发 + */ + onRemoveAllFollowers(): void; + + /** + * 设置跟随者的不透明度 + * @param identifier 跟随者标识符 + * @param alpha 跟随者不透明度 + */ + onSetFollowerAlpha(identifier: string, alpha: number): void; +} + +export interface IHeroState extends IHookable { + /** 勇士横坐标 */ + readonly x: number; + /** 勇士纵坐标 */ + readonly y: number; + /** 勇士朝向 */ + readonly direction: FaceDirection; + /** 勇士图片 */ + readonly image?: ImageIds; + /** 跟随者列表 */ + readonly followers: readonly IHeroFollower[]; + /** 勇士当前的不透明度 */ + readonly alpha: number; + + /** + * 设置勇士位置 + * @param x 横坐标 + * @param y 纵坐标 + */ + setPosition(x: number, y: number): void; + + /** + * 设置勇士朝向 + * @param direction 勇士朝向,不填表示顺时针旋转 + */ + turn(direction?: FaceDirection): void; + + /** + * 开始勇士移动,在移动前必须先调用此方法将勇士切换为移动状态 + */ + startMove(): void; + + /** + * 移动勇士。能否移动的逻辑暂时不在这里,目前作为过渡作用,仅服务于渲染 + * @param dir 移动方向 + * @param time 移动动画时长,默认 100ms + * @returns 移动的 `Promise`,当相关的移动动画结束后兑现 + */ + move(dir: FaceDirection, time?: number): Promise; + + /** + * 结束勇士移动 + * @returns 当移动动画结束后兑现的 `Promise` + */ + endMove(): Promise; + + /** + * 跳跃勇士至目标点 + * @param x 目标点横坐标 + * @param y 目标点纵坐标 + * @param time 跳跃动画时长,默认 500ms + * @param waitFollower 是否等待跟随者跳跃完毕,默认不等待 + * @returns 跳跃的 `Promise`,当相关的移动动画结束后兑现 + */ + jumpHero( + x: number, + y: number, + time?: number, + waitFollower?: boolean + ): Promise; + + /** + * 设置勇士图片 + * @param image 图片 id + */ + setImage(image: ImageIds): void; + + /** + * 设置勇士的不透明度 + * @param alpha 不透明度 + */ + setAlpha(alpha: number): void; + + /** + * 添加一个跟随者 + * @param follower 跟随者的图块数字 + * @param identifier 跟随者的标识符,可以用来移除 + */ + addFollower(follower: number, identifier: string): void; + + /** + * 移除指定的跟随者 + * @param identifier 跟随者的标识符 + * @param animate 填 `true` 的话,如果删除了中间的跟随者,后续跟随者会使用移动动画移动到下一格,否则瞬移至下一格 + */ + removeFollower(identifier: string, animate?: boolean): void; + + /** + * 移除所有跟随者 + */ + removeAllFollowers(): void; + + /** + * 设置指定跟随者的不透明度 + * @param identifier 跟随者标识符 + * @param alpha 跟随者不透明度 + */ + setFollowerAlpha(identifier: string, alpha: number): void; +} + +//#endregion diff --git a/packages-user/data-base/src/hero/utils.ts b/packages-user/data-base/src/hero/utils.ts new file mode 100644 index 0000000..9502841 --- /dev/null +++ b/packages-user/data-base/src/hero/utils.ts @@ -0,0 +1,182 @@ +import { FaceDirection } from './types'; + +/** + * 获取指定朝向的坐标偏移量 + * @param dir 朝向 + */ +export function getFaceMovement(dir: FaceDirection): Loc { + switch (dir) { + case FaceDirection.Left: + return { x: -1, y: 0 }; + case FaceDirection.Right: + return { x: 1, y: 0 }; + case FaceDirection.Up: + return { x: 0, y: -1 }; + case FaceDirection.Down: + return { x: 0, y: 1 }; + case FaceDirection.LeftUp: + return { x: -1, y: -1 }; + case FaceDirection.RightUp: + return { x: 1, y: -1 }; + case FaceDirection.LeftDown: + return { x: -1, y: 1 }; + case FaceDirection.RightDown: + return { x: 1, y: 1 }; + case FaceDirection.Unknown: + return { x: 0, y: 0 }; + } +} + +/** + * 将八方向朝向降级为四方向朝向 + * @param dir 朝向 + * @param unknown 如果朝向是 `FaceDirection.Unknown`,那么会返回什么,默认还是未知 + */ +export function degradeFace( + dir: FaceDirection, + unknown: FaceDirection = FaceDirection.Unknown +): FaceDirection { + switch (dir) { + case FaceDirection.LeftUp: + return FaceDirection.Left; + case FaceDirection.LeftDown: + return FaceDirection.Left; + case FaceDirection.RightUp: + return FaceDirection.Right; + case FaceDirection.RightDown: + return FaceDirection.Right; + case FaceDirection.Unknown: + return unknown; + } + return dir; +} + +/** + * 获取指定朝向旋转后的朝向 + * @param dir 当前朝向 + * @param anticlockwise 是否逆时针旋转,默认顺时针 + * @param face8 是否使用八朝向。为 `false` 时,旋转为九十度旋转,即 上->右->下->左,左上->右上->右下->左下。 + * 为 `true` 时,旋转为四十五度旋转,即 上->右上->右->右下->下->左下->左->左上。逆时针反过来旋转。 + */ +export function nextFaceDirection( + dir: FaceDirection, + anticlockwise: boolean = false, + face8: boolean = false +): FaceDirection { + if (face8) { + if (anticlockwise) { + switch (dir) { + case FaceDirection.Left: + return FaceDirection.LeftDown; + case FaceDirection.LeftDown: + return FaceDirection.Down; + case FaceDirection.Down: + return FaceDirection.RightDown; + case FaceDirection.RightDown: + return FaceDirection.Right; + case FaceDirection.Right: + return FaceDirection.RightUp; + case FaceDirection.RightUp: + return FaceDirection.Up; + case FaceDirection.Up: + return FaceDirection.LeftUp; + case FaceDirection.LeftUp: + return FaceDirection.Left; + case FaceDirection.Unknown: + return FaceDirection.Unknown; + } + } else { + switch (dir) { + case FaceDirection.Left: + return FaceDirection.LeftUp; + case FaceDirection.LeftUp: + return FaceDirection.Up; + case FaceDirection.Up: + return FaceDirection.RightUp; + case FaceDirection.RightUp: + return FaceDirection.Right; + case FaceDirection.Right: + return FaceDirection.RightDown; + case FaceDirection.RightDown: + return FaceDirection.Down; + case FaceDirection.Down: + return FaceDirection.LeftDown; + case FaceDirection.LeftDown: + return FaceDirection.Left; + case FaceDirection.Unknown: + return FaceDirection.Unknown; + } + } + } else { + if (anticlockwise) { + switch (dir) { + case FaceDirection.Left: + return FaceDirection.Down; + case FaceDirection.Down: + return FaceDirection.Right; + case FaceDirection.Right: + return FaceDirection.Up; + case FaceDirection.Up: + return FaceDirection.Left; + case FaceDirection.LeftUp: + return FaceDirection.LeftDown; + case FaceDirection.LeftDown: + return FaceDirection.RightDown; + case FaceDirection.RightDown: + return FaceDirection.RightUp; + case FaceDirection.RightUp: + return FaceDirection.LeftUp; + case FaceDirection.Unknown: + return FaceDirection.Unknown; + } + } else { + switch (dir) { + case FaceDirection.Left: + return FaceDirection.Up; + case FaceDirection.Up: + return FaceDirection.Right; + case FaceDirection.Right: + return FaceDirection.Down; + case FaceDirection.Down: + return FaceDirection.Left; + case FaceDirection.LeftUp: + return FaceDirection.RightUp; + case FaceDirection.RightUp: + return FaceDirection.RightDown; + case FaceDirection.RightDown: + return FaceDirection.LeftDown; + case FaceDirection.LeftDown: + return FaceDirection.LeftUp; + case FaceDirection.Unknown: + return FaceDirection.Unknown; + } + } + } +} + +/** + * 根据朝向字符串获取朝向枚举值 + * @param dir 朝向字符串 + */ +export function fromDirectionString(dir: Dir2): FaceDirection { + switch (dir) { + case 'left': + return FaceDirection.Left; + case 'right': + return FaceDirection.Right; + case 'up': + return FaceDirection.Up; + case 'down': + return FaceDirection.Down; + case 'leftup': + return FaceDirection.LeftUp; + case 'rightup': + return FaceDirection.RightUp; + case 'leftdown': + return FaceDirection.LeftDown; + case 'rightdown': + return FaceDirection.RightDown; + default: + return FaceDirection.Unknown; + } +} diff --git a/packages-user/data-base/src/index.ts b/packages-user/data-base/src/index.ts index c23da5f..6681d98 100644 --- a/packages-user/data-base/src/index.ts +++ b/packages-user/data-base/src/index.ts @@ -1,4 +1,5 @@ export * from './enemy'; +export * from './hero'; export * from './load'; export * from './game'; diff --git a/packages-user/data-state/src/common/types.ts b/packages-user/data-state/src/common/types.ts index ef726bb..b256db0 100644 --- a/packages-user/data-state/src/common/types.ts +++ b/packages-user/data-state/src/common/types.ts @@ -1,21 +1,7 @@ -export const enum FaceDirection { - Unknown, - Left, - Up, - Right, - Down, - LeftUp, - RightUp, - LeftDown, - RightDown -} +import { FaceDirection, type IFaceData } from '@user/data-base'; -export interface IFaceData { - /** 图块数字 */ - readonly identifier: number; - /** 图块朝向 */ - readonly face: FaceDirection; -} +export { FaceDirection }; +export type { IFaceData } from '@user/data-base'; export interface IRoleFaceBinder { /** diff --git a/packages-user/data-state/src/common/utils.ts b/packages-user/data-state/src/common/utils.ts index 9502841..2e7a185 100644 --- a/packages-user/data-state/src/common/utils.ts +++ b/packages-user/data-state/src/common/utils.ts @@ -1,182 +1,6 @@ -import { FaceDirection } from './types'; - -/** - * 获取指定朝向的坐标偏移量 - * @param dir 朝向 - */ -export function getFaceMovement(dir: FaceDirection): Loc { - switch (dir) { - case FaceDirection.Left: - return { x: -1, y: 0 }; - case FaceDirection.Right: - return { x: 1, y: 0 }; - case FaceDirection.Up: - return { x: 0, y: -1 }; - case FaceDirection.Down: - return { x: 0, y: 1 }; - case FaceDirection.LeftUp: - return { x: -1, y: -1 }; - case FaceDirection.RightUp: - return { x: 1, y: -1 }; - case FaceDirection.LeftDown: - return { x: -1, y: 1 }; - case FaceDirection.RightDown: - return { x: 1, y: 1 }; - case FaceDirection.Unknown: - return { x: 0, y: 0 }; - } -} - -/** - * 将八方向朝向降级为四方向朝向 - * @param dir 朝向 - * @param unknown 如果朝向是 `FaceDirection.Unknown`,那么会返回什么,默认还是未知 - */ -export function degradeFace( - dir: FaceDirection, - unknown: FaceDirection = FaceDirection.Unknown -): FaceDirection { - switch (dir) { - case FaceDirection.LeftUp: - return FaceDirection.Left; - case FaceDirection.LeftDown: - return FaceDirection.Left; - case FaceDirection.RightUp: - return FaceDirection.Right; - case FaceDirection.RightDown: - return FaceDirection.Right; - case FaceDirection.Unknown: - return unknown; - } - return dir; -} - -/** - * 获取指定朝向旋转后的朝向 - * @param dir 当前朝向 - * @param anticlockwise 是否逆时针旋转,默认顺时针 - * @param face8 是否使用八朝向。为 `false` 时,旋转为九十度旋转,即 上->右->下->左,左上->右上->右下->左下。 - * 为 `true` 时,旋转为四十五度旋转,即 上->右上->右->右下->下->左下->左->左上。逆时针反过来旋转。 - */ -export function nextFaceDirection( - dir: FaceDirection, - anticlockwise: boolean = false, - face8: boolean = false -): FaceDirection { - if (face8) { - if (anticlockwise) { - switch (dir) { - case FaceDirection.Left: - return FaceDirection.LeftDown; - case FaceDirection.LeftDown: - return FaceDirection.Down; - case FaceDirection.Down: - return FaceDirection.RightDown; - case FaceDirection.RightDown: - return FaceDirection.Right; - case FaceDirection.Right: - return FaceDirection.RightUp; - case FaceDirection.RightUp: - return FaceDirection.Up; - case FaceDirection.Up: - return FaceDirection.LeftUp; - case FaceDirection.LeftUp: - return FaceDirection.Left; - case FaceDirection.Unknown: - return FaceDirection.Unknown; - } - } else { - switch (dir) { - case FaceDirection.Left: - return FaceDirection.LeftUp; - case FaceDirection.LeftUp: - return FaceDirection.Up; - case FaceDirection.Up: - return FaceDirection.RightUp; - case FaceDirection.RightUp: - return FaceDirection.Right; - case FaceDirection.Right: - return FaceDirection.RightDown; - case FaceDirection.RightDown: - return FaceDirection.Down; - case FaceDirection.Down: - return FaceDirection.LeftDown; - case FaceDirection.LeftDown: - return FaceDirection.Left; - case FaceDirection.Unknown: - return FaceDirection.Unknown; - } - } - } else { - if (anticlockwise) { - switch (dir) { - case FaceDirection.Left: - return FaceDirection.Down; - case FaceDirection.Down: - return FaceDirection.Right; - case FaceDirection.Right: - return FaceDirection.Up; - case FaceDirection.Up: - return FaceDirection.Left; - case FaceDirection.LeftUp: - return FaceDirection.LeftDown; - case FaceDirection.LeftDown: - return FaceDirection.RightDown; - case FaceDirection.RightDown: - return FaceDirection.RightUp; - case FaceDirection.RightUp: - return FaceDirection.LeftUp; - case FaceDirection.Unknown: - return FaceDirection.Unknown; - } - } else { - switch (dir) { - case FaceDirection.Left: - return FaceDirection.Up; - case FaceDirection.Up: - return FaceDirection.Right; - case FaceDirection.Right: - return FaceDirection.Down; - case FaceDirection.Down: - return FaceDirection.Left; - case FaceDirection.LeftUp: - return FaceDirection.RightUp; - case FaceDirection.RightUp: - return FaceDirection.RightDown; - case FaceDirection.RightDown: - return FaceDirection.LeftDown; - case FaceDirection.LeftDown: - return FaceDirection.LeftUp; - case FaceDirection.Unknown: - return FaceDirection.Unknown; - } - } - } -} - -/** - * 根据朝向字符串获取朝向枚举值 - * @param dir 朝向字符串 - */ -export function fromDirectionString(dir: Dir2): FaceDirection { - switch (dir) { - case 'left': - return FaceDirection.Left; - case 'right': - return FaceDirection.Right; - case 'up': - return FaceDirection.Up; - case 'down': - return FaceDirection.Down; - case 'leftup': - return FaceDirection.LeftUp; - case 'rightup': - return FaceDirection.RightUp; - case 'leftdown': - return FaceDirection.LeftDown; - case 'rightdown': - return FaceDirection.RightDown; - default: - return FaceDirection.Unknown; - } -} +export { + degradeFace, + fromDirectionString, + getFaceMovement, + nextFaceDirection +} from '@user/data-base'; diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 4ede50e..286a2e6 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -1,11 +1,12 @@ import { ICoreState, IStateSaveData } from './types'; -import { IHeroState, HeroState } from './hero'; import { ILayerState, LayerState } from './map'; import { IRoleFaceBinder, RoleFaceBinder } from './common'; import { DamageSystem, EnemyContext, EnemyManager, + HeroState, + IHeroState, IEnemyContext, IEnemyManager, MapDamage diff --git a/packages-user/data-state/src/hero/index.ts b/packages-user/data-state/src/hero/index.ts index 0de1d2f..fcb073f 100644 --- a/packages-user/data-state/src/hero/index.ts +++ b/packages-user/data-state/src/hero/index.ts @@ -1,2 +1 @@ -export * from './state'; export * from './types'; diff --git a/packages-user/data-state/src/hero/types.ts b/packages-user/data-state/src/hero/types.ts index 197bf7d..25d7216 100644 --- a/packages-user/data-state/src/hero/types.ts +++ b/packages-user/data-state/src/hero/types.ts @@ -1,204 +1,22 @@ -import { IHookBase, IHookable } from '@motajs/common'; -import { FaceDirection } from '../common/types'; +//#region 勇士属性 -export const enum HeroAnimateDirection { - /** 正向播放动画 */ - Forward, - /** 反向播放动画 */ - Backward +export interface IHeroAttributeObject { + /** 勇士名称 */ + name: string; + /** 勇士生命值 */ + hp: number; + /** 勇士生命值上限 */ + hpmax: number; + /** 勇士攻击力 */ + atk: number; + /** 勇士防御力 */ + def: number; + /** 勇士护盾 */ + mdef: number; + /** 勇士魔法值 */ + mana: number; + /** 勇士魔法上限 */ + manamax: number; } -export interface IHeroFollower { - /** 跟随者的图块数字 */ - readonly num: number; - /** 跟随者的标识符 */ - readonly identifier: string; - /** 跟随者的不透明度 */ - alpha: number; -} - -export interface IHeroStateHooks extends IHookBase { - /** - * 当设置勇士的坐标时触发 - * @param controller 钩子控制器 - * @param x 勇士横坐标 - * @param y 勇士纵坐标 - */ - onSetPosition(x: number, y: number): void; - - /** - * 当设置勇士朝向时触发 - * @param direction 勇士朝向 - */ - onTurnHero(direction: FaceDirection): void; - - /** - * 当勇士开始移动时触发 - */ - onStartMove(): void; - - /** - * 当移动勇士时触发 - * @param controller 钩子控制器 - * @param direction 移动方向 - * @param time 移动动画时长 - */ - onMoveHero(direction: FaceDirection, time: number): Promise; - - /** - * 当停止移动时触发 - */ - onEndMove(): Promise; - - /** - * 当勇士跳跃时触发 - * @param x 目标点横坐标 - * @param y 目标点纵坐标 - * @param time 跳跃动画时长 - * @param waitFollower 是否等待跟随者跳跃完毕 - */ - onJumpHero( - x: number, - y: number, - time: number, - waitFollower: boolean - ): Promise; - - /** - * 当设置勇士图片时触发 - * @param image 勇士图片 id - */ - onSetImage(image: ImageIds): void; - - /** - * 当设置勇士不透明度时执行 - * @param alpha 不透明度 - */ - onSetAlpha(alpha: number): void; - - /** - * 添加跟随者时触发 - * @param follower 跟随者的图块数字 - * @param identifier 跟随者的标识符 - */ - onAddFollower(follower: number, identifier: string): void; - - /** - * 当移除跟随者时触发 - * @param identifier 跟随者的标识符 - * @param animate 填 `true` 的话,如果删除了中间的跟随者,后续跟随者会使用移动动画移动到下一格,否则瞬移至下一格 - */ - onRemoveFollower(identifier: string, animate: boolean): void; - - /** - * 当移除所有跟随者时触发 - */ - onRemoveAllFollowers(): void; - - /** - * 设置跟随者的不透明度 - * @param identifier 跟随者标识符 - * @param alpha 跟随者不透明度 - */ - onSetFollowerAlpha(identifier: string, alpha: number): void; -} - -export interface IHeroState extends IHookable { - /** 勇士横坐标 */ - readonly x: number; - /** 勇士纵坐标 */ - readonly y: number; - /** 勇士朝向 */ - readonly direction: FaceDirection; - /** 勇士图片 */ - readonly image?: ImageIds; - /** 跟随者列表 */ - readonly followers: readonly IHeroFollower[]; - /** 勇士当前的不透明度 */ - readonly alpha: number; - - /** - * 设置勇士位置 - * @param x 横坐标 - * @param y 纵坐标 - */ - setPosition(x: number, y: number): void; - - /** - * 设置勇士朝向 - * @param direction 勇士朝向,不填表示顺时针旋转 - */ - turn(direction?: FaceDirection): void; - - /** - * 开始勇士移动,在移动前必须先调用此方法将勇士切换为移动状态 - */ - startMove(): void; - - /** - * 移动勇士。能否移动的逻辑暂时不在这里,目前作为过渡作用,仅服务于渲染 - * @param dir 移动方向 - * @param time 移动动画时长,默认 100ms - * @returns 移动的 `Promise`,当相关的移动动画结束后兑现 - */ - move(dir: FaceDirection, time?: number): Promise; - - /** - * 结束勇士移动 - * @returns 当移动动画结束后兑现的 `Promise` - */ - endMove(): Promise; - - /** - * 跳跃勇士至目标点 - * @param x 目标点横坐标 - * @param y 目标点纵坐标 - * @param time 跳跃动画时长,默认 500ms - * @param waitFollower 是否等待跟随者跳跃完毕,默认不等待 - * @returns 跳跃的 `Promise`,当相关的移动动画结束后兑现 - */ - jumpHero( - x: number, - y: number, - time?: number, - waitFollower?: boolean - ): Promise; - - /** - * 设置勇士图片 - * @param image 图片 id - */ - setImage(image: ImageIds): void; - - /** - * 设置勇士的不透明度 - * @param alpha 不透明度 - */ - setAlpha(alpha: number): void; - - /** - * 添加一个跟随者 - * @param follower 跟随者的图块数字 - * @param identifier 跟随者的标识符,可以用来移除 - */ - addFollower(follower: number, identifier: string): void; - - /** - * 移除指定的跟随者 - * @param identifier 跟随者的标识符 - * @param animate 填 `true` 的话,如果删除了中间的跟随者,后续跟随者会使用移动动画移动到下一格,否则瞬移至下一格 - */ - removeFollower(identifier: string, animate?: boolean): void; - - /** - * 移除所有跟随者 - */ - removeAllFollowers(): void; - - /** - * 设置指定跟随者的不透明度 - * @param identifier 跟随者标识符 - * @param alpha 跟随者不透明度 - */ - setFollowerAlpha(identifier: string, alpha: number): void; -} +//#endregion diff --git a/packages-user/data-state/src/types.ts b/packages-user/data-state/src/types.ts index 7092239..91703a8 100644 --- a/packages-user/data-state/src/types.ts +++ b/packages-user/data-state/src/types.ts @@ -1,7 +1,11 @@ import { ILayerState } from './map'; -import { IHeroFollower, IHeroState } from './hero'; import { IRoleFaceBinder } from './common'; -import { IEnemyContext, IEnemyManager } from '@user/data-base'; +import { + IEnemyContext, + IEnemyManager, + IHeroFollower, + IHeroState +} from '@user/data-base'; import { IEnemyAttributes } from './enemy/types'; export interface IGameDataState { diff --git a/packages/common/src/hook.ts b/packages/common/src/hook.ts index 6885c94..195b1a4 100644 --- a/packages/common/src/hook.ts +++ b/packages/common/src/hook.ts @@ -4,8 +4,7 @@ import { IHookable, IHookBase, IHookController, IHookObject } from './types'; export abstract class Hookable< H extends IHookBase = IHookBase, C extends IHookController = IHookController -> implements IHookable -{ +> implements IHookable { /** 加载完成的钩子列表 */ protected readonly loadedList: Set> = new Set(); diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index 38a4936..b988c93 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -163,6 +163,8 @@ "105": "No specific map damage view stored, which seems like an internal bug of map damage system.", "106": "Damage calculator is missing, damage calculation is unavailable.", "107": "Hero status is not bound, damage calculation is unavailable.", + "108": "Hero modifier '$1' has already been added once. Ignore repeated add for attribute '$2'.", + "109": "Expected a different object reference returned, but got a same reference at modifier '$1' for property '$2'.", "1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency." } }