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 a6e3068..039ed77 100644 --- a/packages-user/client-modules/src/render/map/extension/hero.ts +++ b/packages-user/client-modules/src/render/map/extension/hero.ts @@ -3,8 +3,8 @@ import { FaceDirection, getFaceMovement, HeroAnimateDirection, - IHeroState, - IHeroStateHooks, + IHeroMover, + IHeroMovingHooks, nextFaceDirection } from '@user/data-base'; import { IMapLayer, state } from '@user/data-state'; @@ -54,7 +54,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { new TextureRowSplitter(); /** 勇士钩子 */ - readonly controller: IHookController; + readonly controller: IHookController; /** 勇士每个朝向的贴图对象 */ readonly textureMap: Map = new Map(); /** 勇士渲染实体,与 `entities[0]` 同引用 */ @@ -72,7 +72,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { constructor( readonly renderer: IMapRenderer, readonly layer: IMapLayer, - readonly hero: IHeroState + readonly hero: IHeroMover ) { this.controller = hero.addHook(new MapHeroHook(this)); this.controller.load(); @@ -106,7 +106,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { private addHeroMoving( renderer: IMapRenderer, layer: IMapLayer, - hero: IHeroState + hero: IHeroMover ) { if (isNil(hero.image)) { logger.warn(88); @@ -450,7 +450,7 @@ export class MapHeroRenderer implements IMapHeroRenderer { } } -class MapHeroHook implements Partial { +class MapHeroHook implements Partial { constructor(readonly hero: MapHeroRenderer) {} onSetImage(image: ImageIds): void { 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 a87c644..41c6481 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,4 @@ -import { IHeroState } from '@user/data-base'; +import { IHeroMover } from '@user/data-base'; import { IMapLayer } from '@user/data-state'; import { IMapDoorRenderer, @@ -14,7 +14,7 @@ import { IOnMapTextRenderer } from './types'; export class MapExtensionManager implements IMapExtensionManager { /** 勇士状态至勇士渲染器的映射 */ - readonly heroMap: Map = new Map(); + readonly heroMap: Map = new Map(); /** 地图图层到门渲染器的映射 */ readonly doorMap: Map = new Map(); /** 单例的文字渲染拓展(独立图层) */ @@ -22,7 +22,7 @@ export class MapExtensionManager implements IMapExtensionManager { constructor(readonly renderer: IMapRenderer) {} - addHero(state: IHeroState, layer: IMapLayer): IMapHeroRenderer | null { + addHero(state: IHeroMover, layer: IMapLayer): IMapHeroRenderer | null { if (this.heroMap.has(state)) { logger.error(45, 'hero renderer'); return null; @@ -32,7 +32,7 @@ export class MapExtensionManager implements IMapExtensionManager { return heroRenderer; } - removeHero(state: IHeroState): void { + removeHero(state: IHeroMover): void { const renderer = this.heroMap.get(state); if (!renderer) return; renderer.destroy(); 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 98503cb..82577d0 100644 --- a/packages-user/client-modules/src/render/map/extension/types.ts +++ b/packages-user/client-modules/src/render/map/extension/types.ts @@ -2,7 +2,7 @@ import { ITexture, Font } from '@motajs/render'; import { FaceDirection, HeroAnimateDirection, - IHeroState + IHeroMover } from '@user/data-base'; import { IMapLayer } from '@user/data-state'; @@ -10,7 +10,7 @@ import { IMapRenderResult } from '../types'; export interface IMapExtensionManager { /** 勇士状态至勇士渲染器的映射 */ - readonly heroMap: Map; + readonly heroMap: Map; /** 地图图层到门渲染器的映射 */ readonly doorMap: Map; /** 单例的文字渲染拓展(独立图层) */ @@ -21,13 +21,13 @@ export interface IMapExtensionManager { * @param state 勇士状态 * @param layer 勇士所在图层 */ - addHero(state: IHeroState, layer: IMapLayer): IMapHeroRenderer | null; + addHero(state: IHeroMover, layer: IMapLayer): IMapHeroRenderer | null; /** * 移除勇士渲染拓展 * @param state 勇士状态 */ - removeHero(state: IHeroState): void; + removeHero(state: IHeroMover): void; /** * 添加开门动画拓展 diff --git a/packages-user/data-base/src/common/index.ts b/packages-user/data-base/src/common/index.ts new file mode 100644 index 0000000..6d5a6ef --- /dev/null +++ b/packages-user/data-base/src/common/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export * from './utils'; diff --git a/packages-user/data-base/src/common/types.ts b/packages-user/data-base/src/common/types.ts new file mode 100644 index 0000000..196a18f --- /dev/null +++ b/packages-user/data-base/src/common/types.ts @@ -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; +} diff --git a/packages-user/data-base/src/hero/utils.ts b/packages-user/data-base/src/common/utils.ts similarity index 99% rename from packages-user/data-base/src/hero/utils.ts rename to packages-user/data-base/src/common/utils.ts index 9502841..eff640e 100644 --- a/packages-user/data-base/src/hero/utils.ts +++ b/packages-user/data-base/src/common/utils.ts @@ -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, diff --git a/packages-user/data-base/src/hero/index.ts b/packages-user/data-base/src/hero/index.ts index 8b42120..b53355f 100644 --- a/packages-user/data-base/src/hero/index.ts +++ b/packages-user/data-base/src/hero/index.ts @@ -1,4 +1,4 @@ export * from './attribute'; +export * from './mover'; export * from './state'; export * from './types'; -export * from './utils'; diff --git a/packages-user/data-base/src/hero/mover.ts b/packages-user/data-base/src/hero/mover.ts new file mode 100644 index 0000000..c578f30 --- /dev/null +++ b/packages-user/data-base/src/hero/mover.ts @@ -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 + 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 + ): IHookController { + 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 { + 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 { + 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 { + 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?.(); + }); + } +} diff --git a/packages-user/data-base/src/hero/state.ts b/packages-user/data-base/src/hero/state.ts index ba121cc..2960ab0 100644 --- a/packages-user/data-base/src/hero/state.ts +++ b/packages-user/data-base/src/hero/state.ts @@ -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 implements IHeroState { + constructor( + public mover: IHeroMover, + public attribute: IHeroAttribute + ) {} -export class HeroState extends Hookable 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 - ): IHookController { - 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): 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 { + return this.attribute; } - async move(dir: FaceDirection, time: number = 100): Promise { - await Promise.all( - this.forEachHook(hook => { - return hook.onMoveHero?.(dir, time); - }) - ); - const { x, y } = getFaceMovement(dir); - this.x += x; - this.y += y; + getAttribute(): IReadonlyHeroAttribute { + return this.attribute; } - async endMove(): Promise { - 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 { - 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 { + return this.attribute.clone(); } } diff --git a/packages-user/data-base/src/hero/types.ts b/packages-user/data-base/src/hero/types.ts index 99c0f4f..876471e 100644 --- a/packages-user/data-base/src/hero/types.ts +++ b/packages-user/data-base/src/hero/types.ts @@ -1,4 +1,5 @@ import { IHookBase, IHookable } from '@motajs/common'; +import { FaceDirection } from '@user/data-state'; //#region 勇士属性 @@ -41,7 +42,7 @@ export interface IHeroModifier { clone(): IHeroModifier; } -export interface IHeroAttribute { +export interface IReadonlyHeroAttribute { /** * 获取勇士的基础属性,即未经过任何 Buff 或装备等加成的属性 * @param name 属性名称 @@ -54,6 +55,26 @@ export interface IHeroAttribute { */ getFinalAttribute(name: K): THero[K]; + /** + * 将指定属性标记为脏 + * @param name 属性名称 + */ + markDirty(name: keyof THero): void; + + /** + * 将指定属性修饰器标记为脏 + * @param modifier 属性修饰器 + */ + markModifierDirty(modifier: IHeroModifier): void; + + /** + * 深拷贝此勇士属性对象 + * @param cloneModifier 是否同时复制修饰器,默认复制 + */ + clone(cloneModifier?: boolean): IHeroAttribute; +} + +export interface IHeroAttribute extends IReadonlyHeroAttribute { /** * 设置勇士的基础属性 * @param name 属性名称 @@ -80,52 +101,11 @@ export interface IHeroAttribute { 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 勇士状态 +//#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 { +export interface IHeroMover extends IHookable { /** 勇士横坐标 */ readonly x: number; /** 勇士纵坐标 */ @@ -328,3 +308,46 @@ export interface IHeroState extends IHookable { } //#endregion + +//#region 勇士状态 + +export interface IHeroState { + /** 勇士移动对象 */ + readonly mover: IHeroMover; + /** 勇士属性对象 */ + readonly attribute: IReadonlyHeroAttribute; + + /** + * 绑定勇士移动对象 + * @param mover 勇士移动对象 + */ + attachMover(mover: IHeroMover): void; + + /** + * 获取勇士移动对象 + */ + getHeroMover(): IHeroMover; + + /** + * 绑定勇士属性对象 + * @param attribute 勇士属性对象 + */ + attachAttribute(attribute: IHeroAttribute): void; + + /** + * 获取可修改勇士对象 + */ + getModifiableAttribute(): IHeroAttribute; + + /** + * 获取只读勇士对象 + */ + getAttribute(): IReadonlyHeroAttribute; + + /** + * 获取独立勇士属性对象,修改此对象不会影响勇士本身的属性 + */ + getIsolatedAttribute(): IHeroAttribute; +} + +//#endregion diff --git a/packages-user/data-base/src/index.ts b/packages-user/data-base/src/index.ts index 6681d98..4cbfdc8 100644 --- a/packages-user/data-base/src/index.ts +++ b/packages-user/data-base/src/index.ts @@ -1,3 +1,4 @@ +export * from './common'; export * from './enemy'; export * from './hero'; export * from './load'; diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 286a2e6..a794295 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -5,11 +5,13 @@ import { DamageSystem, EnemyContext, EnemyManager, - HeroState, - IHeroState, + HeroMover, IEnemyContext, IEnemyManager, - MapDamage + MapDamage, + HeroAttribute, + HeroState, + IHeroState } from '@user/data-base'; import { IEnemyAttributes } from './enemy/types'; import { @@ -21,21 +23,22 @@ import { MainMapDamageReducer, registerSpecials } from './enemy'; -import { TILE_HEIGHT, TILE_WIDTH } from './shared'; +import { HERO_DEFAULT_ATTRIBUTE, TILE_HEIGHT, TILE_WIDTH } from './shared'; +import { IHeroAttributeObject } from './hero'; export class CoreState implements ICoreState { readonly layer: ILayerState; - readonly hero: IHeroState; readonly roleFace: IRoleFaceBinder; readonly idNumberMap: Map; readonly numberIdMap: Map; + readonly hero: IHeroState; + readonly enemyManager: IEnemyManager; readonly enemyContext: IEnemyContext; constructor() { this.layer = new LayerState(); - this.hero = new HeroState(); this.roleFace = new RoleFaceBinder(); this.idNumberMap = new Map(); this.numberIdMap = new Map(); @@ -67,18 +70,27 @@ export class CoreState implements ICoreState { this.enemyContext = enemyContext; //#endregion + + //#region 勇士初始化 + + const heroMover = new HeroMover(); + const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE); + const heroState = new HeroState(heroMover, heroAttribute); + this.hero = heroState; + + //#endregion } saveState(): IStateSaveData { return structuredClone({ - followers: this.hero.followers + followers: this.hero.mover.followers }); } loadState(data: IStateSaveData): void { - this.hero.removeAllFollowers(); + this.hero.mover.removeAllFollowers(); data.followers.forEach(v => { - this.hero.addFollower(v.num, v.identifier); + this.hero.mover.addFollower(v.num, v.identifier); }); } } diff --git a/packages-user/data-state/src/hero/types.ts b/packages-user/data-state/src/hero/types.ts index 25d7216..bc86406 100644 --- a/packages-user/data-state/src/hero/types.ts +++ b/packages-user/data-state/src/hero/types.ts @@ -17,6 +17,10 @@ export interface IHeroAttributeObject { mana: number; /** 勇士魔法上限 */ manamax: number; + /** 勇士拥有的金币 */ + money: number; + /** 勇士拥有的经验 */ + exp: number; } //#endregion diff --git a/packages-user/data-state/src/shared.ts b/packages-user/data-state/src/shared.ts index bb2d38c..f08416d 100644 --- a/packages-user/data-state/src/shared.ts +++ b/packages-user/data-state/src/shared.ts @@ -1,7 +1,27 @@ +import { IHeroAttributeObject } from './hero'; + /** 每个地图的默认宽度 */ export const TILE_WIDTH = 13; /** 每个地图的默认高度 */ export const TILE_HEIGHT = 13; +//#region 勇士相关 + /** 默认的勇士图片 */ export const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png'; + +/** 勇士的初始属性,数值填多少目前都无所谓,因为最终会从旧样板读取,但是必须得填 */ +export const HERO_DEFAULT_ATTRIBUTE: IHeroAttributeObject = { + name: '', + hp: 1, + hpmax: 0, + atk: 0, + def: 0, + mdef: 0, + mana: 0, + manamax: 0, + money: 0, + exp: 0 +}; + +//#endregion diff --git a/packages-user/data-state/src/types.ts b/packages-user/data-state/src/types.ts index 91703a8..5f01f1e 100644 --- a/packages-user/data-state/src/types.ts +++ b/packages-user/data-state/src/types.ts @@ -7,6 +7,7 @@ import { IHeroState } from '@user/data-base'; import { IEnemyAttributes } from './enemy/types'; +import { IHeroAttributeObject } from './hero'; export interface IGameDataState { /** 怪物管理器 */ @@ -22,7 +23,7 @@ export interface ICoreState { /** 地图状态 */ readonly layer: ILayerState; /** 勇士状态 */ - readonly hero: IHeroState; + readonly hero: IHeroState; /** 朝向绑定 */ readonly roleFace: IRoleFaceBinder; /** id 到图块数字的映射 */