chore: 调整勇士相关接口

This commit is contained in:
unanmed 2026-06-28 15:35:30 +08:00
parent 931f610d37
commit abaa5206c5
5 changed files with 101 additions and 51 deletions

View File

@ -43,8 +43,11 @@ export abstract class BaseHeroModifier<T, V> implements IHeroModifier<T, V, V> {
export class HeroAttribute<THero> implements IHeroAttribute<THero> { export class HeroAttribute<THero> implements IHeroAttribute<THero> {
/** 当前勇士属性修饰器 */ /** 当前勇士属性修饰器 */
private readonly modifier: Map<keyof THero, IHeroModifier[]> = new Map(); private readonly modifier: Map<keyof THero, IHeroModifier[]> = new Map();
/** 当前每个修饰器对应的属性值 */ /** 当前每个修饰器对应的属性名称 */
private readonly modifierName: Map<IHeroModifier, keyof THero> = new Map(); private readonly modifierName: Map<
IHeroModifier<THero[keyof THero]>,
keyof THero
> = new Map();
/** 当前勇士最终属性 */ /** 当前勇士最终属性 */
private readonly finalAttribute: THero; private readonly finalAttribute: THero;
@ -144,7 +147,7 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
addModifier<K extends keyof THero>( addModifier<K extends keyof THero>(
name: K, name: K,
modifier: IHeroModifier<THero[K], unknown> modifier: IHeroModifier<THero[K]>
): void { ): void {
if (modifier.owner) { if (modifier.owner) {
const modiferName = modifier.constructor.name; const modiferName = modifier.constructor.name;
@ -181,7 +184,7 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
this.recalculateAttribute(name); this.recalculateAttribute(name);
} }
markModifierDirty(modifier: IHeroModifier): void { markModifierDirty(modifier: IHeroModifier<THero[keyof THero]>): void {
const name = this.modifierName.get(modifier); const name = this.modifierName.get(modifier);
if (name === undefined) return; if (name === undefined) return;
this.markDirty(name); this.markDirty(name);
@ -210,9 +213,16 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
return structuredClone(this.attribute); return structuredClone(this.attribute);
} }
*iterateModifiers(): IterableIterator<[keyof THero, IHeroModifier]> { *iterateModifiers(): IterableIterator<[PropertyKey, IHeroModifier]> {
for (const [modifier, name] of this.modifierName) { for (const [modifier, name] of this.modifierName) {
yield [name, modifier]; yield [name, modifier];
} }
} }
getModifiers<K extends keyof THero>(
name: K
): Iterable<IHeroModifier<THero[K]>> {
const arr = this.modifier.get(name) as IHeroModifier<THero[K]>[];
return arr ?? [];
}
} }

View File

@ -65,6 +65,7 @@ export class HeroFollower implements IHeroFollower {
saveState(compression: SaveCompression): IHeroFollowerSave { saveState(compression: SaveCompression): IHeroFollowerSave {
return { return {
num: this.num,
rendering: this.rendering.saveState(compression), rendering: this.rendering.saveState(compression),
location: this.location.saveState(compression) location: this.location.saveState(compression)
}; };

View File

@ -1,39 +1,66 @@
import { HeroAttribute } from './attribute'; import { HeroAttribute } from './attribute';
import { HeroFollowersController } from './followersController';
import { HeroLocation } from './location';
import { HeroRendering } from './rendering';
import { import {
IHeroAttribute, IHeroAttribute,
IHeroFollowersController,
IHeroLocation,
IHeroModifier, IHeroModifier,
IHeroMoveController, IHeroRendering,
IHeroState, IHeroState,
IHeroStateSave, IHeroStateSave,
IModifierStateSave, IModifierStateSave,
IReadonlyHeroAttribute IReadonlyHeroAttribute
} from './types'; } from './types';
import { SaveCompression } from '@user/data-common'; import {
FaceDirection,
IDataCommon,
IFaceHandler,
SaveCompression
} from '@user/data-common';
import { IFacedTileLocator, logger } from '@motajs/common'; import { IFacedTileLocator, logger } from '@motajs/common';
export class HeroState<THero> implements IHeroState<THero> { export class HeroState<THero> implements IHeroState<THero> {
/** 修饰器工厂函数注册表 */ /** 修饰器工厂函数注册表 */
private readonly registry: Map<string, () => IHeroModifier> = new Map(); private readonly registry: Map<
string,
<K extends keyof THero>() => IHeroModifier<THero[K]>
> = new Map();
readonly location: IHeroLocation;
readonly rendering: IHeroRendering;
readonly followers: IHeroFollowersController;
constructor( constructor(
public mover: IHeroMoveController, state: IDataCommon,
faceHandler: IFaceHandler<FaceDirection>,
public attribute: IHeroAttribute<THero> public attribute: IHeroAttribute<THero>
) {} ) {
this.rendering = new HeroRendering(state);
attachMover(mover: IHeroMoveController): void { const defaultLoc: IFacedTileLocator = {
this.mover = mover; x: 0,
y: 0,
direction: FaceDirection.Down
};
this.location = new HeroLocation(state, defaultLoc, faceHandler);
this.followers = new HeroFollowersController(
state,
this.location,
faceHandler
);
} }
attachAttribute(attribute: IHeroAttribute<THero>): void { attachAttribute(attribute: IHeroAttribute<THero>): void {
this.attribute = attribute; this.attribute = attribute;
} }
getHeroMover(): IHeroMoveController {
return this.mover;
}
getLocation(): IFacedTileLocator { getLocation(): IFacedTileLocator {
return this.mover; return {
x: this.location.x,
y: this.location.y,
direction: this.location.mover.faceDirection
};
} }
getModifiableAttribute(): IHeroAttribute<THero> { getModifiableAttribute(): IHeroAttribute<THero> {
@ -48,7 +75,10 @@ export class HeroState<THero> implements IHeroState<THero> {
return this.attribute.getModifiableClone(); return this.attribute.getModifiableClone();
} }
registerModifier(type: string, cons: () => IHeroModifier): void { registerModifier(
type: string,
cons: <K extends keyof THero>() => IHeroModifier<THero[K]>
): void {
this.registry.set(type, cons); this.registry.set(type, cons);
} }
@ -57,9 +87,8 @@ export class HeroState<THero> implements IHeroState<THero> {
if (!cons) { if (!cons) {
logger.warn(116, type); logger.warn(116, type);
return null; return null;
} else {
return cons() as IHeroModifier<T, V>;
} }
return cons() as IHeroModifier<T, V>;
} }
createAndInsertModifier<K extends keyof THero, V>( createAndInsertModifier<K extends keyof THero, V>(
@ -73,22 +102,23 @@ export class HeroState<THero> implements IHeroState<THero> {
} }
saveState(compression: SaveCompression): IHeroStateSave<THero> { saveState(compression: SaveCompression): IHeroStateSave<THero> {
const modifiers: IModifierStateSave[] = []; const modifiers: IModifierStateSave<THero>[] = [];
for (const [name, modifier] of this.attribute.iterateModifiers()) { for (const [name, modifier] of this.attribute.iterateModifiers()) {
modifiers.push({ modifiers.push({
name, name: name as keyof THero,
type: modifier.type, type: modifier.type,
state: modifier.saveState(compression) state: modifier.saveState(compression)
}); });
} }
const followerSaves = this.followers
.getAllFollowers()
.map(v => v.saveState(compression));
return { return {
attribute: this.attribute.toStructured(), attribute: this.attribute.toStructured(),
locator: { location: this.location.saveState(compression),
x: this.mover.x, rendering: this.rendering.saveState(compression),
y: this.mover.y, followers: followerSaves,
direction: this.mover.direction
},
followers: structuredClone(this.mover.followers),
modifiers modifiers
}; };
} }
@ -102,19 +132,16 @@ export class HeroState<THero> implements IHeroState<THero> {
const cons = this.registry.get(save.type); const cons = this.registry.get(save.type);
if (!cons) continue; if (!cons) continue;
const modifier = cons(); const modifier = cons();
modifier.loadState(save.state as never, compression); modifier.loadState(save.state, compression);
newAttribute.addModifier( newAttribute.addModifier(save.name, modifier);
save.name as keyof THero,
modifier as unknown as IHeroModifier<THero[keyof THero]>
);
} }
this.attribute = newAttribute; this.attribute = newAttribute;
this.mover.setPosition(state.locator.x, state.locator.y); this.location.loadState(state.location, compression);
this.mover.turn(state.locator.direction); this.rendering.loadState(state.rendering, compression);
this.mover.removeAllFollowers(); void this.followers.removeAllFollowers();
state.followers.forEach(follower => { for (const save of state.followers) {
this.mover.addFollower(follower.num, follower.identifier); const follower = this.followers.addFollower(save.num);
this.mover.setFollowerAlpha(follower.identifier, follower.alpha); follower.loadState(save, compression);
}); }
} }
} }

View File

@ -9,9 +9,9 @@ import {
//#region 勇士属性 //#region 勇士属性
export interface IHeroModifier< export interface IHeroModifier<
K = unknown, H = unknown, // 属性值类型
V = unknown, V = unknown, // 修饰器参数类型
Save = unknown Save = unknown // 存档类型
> extends ISaveableContent<Save> { > extends ISaveableContent<Save> {
/** 修饰器类型 */ /** 修饰器类型 */
readonly type: string; readonly type: string;
@ -45,17 +45,17 @@ export interface IHeroModifier<
* @param baseValue * @param baseValue
* @param name * @param name
*/ */
modify(value: K, baseValue: K, name: PropertyKey): K; modify(value: H, baseValue: H, name: PropertyKey): H;
/** /**
* *
*/ */
clone(): IHeroModifier<K, V>; clone(): IHeroModifier<H, V>;
} }
export interface IModifierStateSave { export interface IModifierStateSave<THero> {
/** 属性名称 */ /** 属性名称 */
readonly name: PropertyKey; readonly name: keyof THero;
/** 修饰器类型 */ /** 修饰器类型 */
readonly type: string; readonly type: string;
/** 修饰器存档数据 */ /** 修饰器存档数据 */
@ -107,6 +107,14 @@ export interface IReadonlyHeroAttribute<THero> {
* *
*/ */
iterateModifiers(): Iterable<[PropertyKey, IHeroModifier]>; iterateModifiers(): Iterable<[PropertyKey, IHeroModifier]>;
/**
*
* @param name
*/
getModifiers<K extends keyof THero>(
name: K
): Iterable<IHeroModifier<THero[K]>>;
} }
export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> { export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
@ -263,6 +271,8 @@ export interface IHeroRendering
//#region 勇士跟随者 //#region 勇士跟随者
export interface IHeroFollowerSave { export interface IHeroFollowerSave {
/** 跟随者图块数字 */
readonly num: number;
/** 跟随者渲染对象保存 */ /** 跟随者渲染对象保存 */
readonly rendering: IHeroRenderingSave; readonly rendering: IHeroRenderingSave;
/** 跟随者位置保存 */ /** 跟随者位置保存 */
@ -369,11 +379,13 @@ export interface IHeroStateSave<THero> {
/** 勇士属性状态 */ /** 勇士属性状态 */
readonly attribute: THero; readonly attribute: THero;
/** 勇士当前位置 */ /** 勇士当前位置 */
readonly locator: IHeroLocationSave; readonly location: IHeroLocationSave;
/** 勇士渲染状态 */
readonly rendering: IHeroRenderingSave;
/** 勇士当前的跟随者 */ /** 勇士当前的跟随者 */
readonly followers: readonly IHeroFollowerSave[]; readonly followers: readonly IHeroFollowerSave[];
/** 勇士属性修饰器状态 */ /** 勇士属性修饰器状态 */
readonly modifiers: readonly IModifierStateSave[]; readonly modifiers: readonly IModifierStateSave<THero>[];
} }
export interface IHeroState<THero> extends ISaveableContent< export interface IHeroState<THero> extends ISaveableContent<

View File

@ -700,7 +700,7 @@ export interface IEnemyContext<TEnemy, THero> extends IReadonlyEnemyContext<
* *
* @param system * @param system
*/ */
attachDamageSystem(system: IDamageSystem<TEnemy, unknown> | null): void; attachDamageSystem(system: IDamageSystem<TEnemy, THero> | null): void;
/** /**
* *