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> {
/** 当前勇士属性修饰器 */
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;
@ -144,7 +147,7 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
addModifier<K extends keyof THero>(
name: K,
modifier: IHeroModifier<THero[K], unknown>
modifier: IHeroModifier<THero[K]>
): void {
if (modifier.owner) {
const modiferName = modifier.constructor.name;
@ -181,7 +184,7 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
this.recalculateAttribute(name);
}
markModifierDirty(modifier: IHeroModifier): void {
markModifierDirty(modifier: IHeroModifier<THero[keyof THero]>): void {
const name = this.modifierName.get(modifier);
if (name === undefined) return;
this.markDirty(name);
@ -210,9 +213,16 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
return structuredClone(this.attribute);
}
*iterateModifiers(): IterableIterator<[keyof THero, IHeroModifier]> {
*iterateModifiers(): IterableIterator<[PropertyKey, IHeroModifier]> {
for (const [modifier, name] of this.modifierName) {
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 {
return {
num: this.num,
rendering: this.rendering.saveState(compression),
location: this.location.saveState(compression)
};

View File

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

View File

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

View File

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