template/packages-user/data-base/src/enemy/enemy.ts

109 lines
3.3 KiB
TypeScript

import { logger } from '@motajs/common';
import { SaveCompression } from '../common';
import { IEnemy, IEnemySaveState, IReadonlyEnemy, ISpecial } from './types';
export class Enemy<TAttr> implements IEnemy<TAttr> {
/** 怪物身上的特殊属性列表 */
private readonly specials: Set<ISpecial<any>> = new Set();
/** code -> ISpecial 映射,用于快速查找 */
private readonly specialMap: Map<number, ISpecial<any>> = new Map();
constructor(
readonly id: string,
readonly code: number,
private attributes: TAttr
) {}
getSpecial<T>(code: number): ISpecial<T> | null {
return (this.specialMap.get(code) as ISpecial<T>) ?? null;
}
hasSpecial(code: number): boolean {
return this.specialMap.has(code);
}
addSpecial(special: ISpecial<any>): void {
if (this.specialMap.has(special.code)) {
logger.warn(96, this.id, special.code.toString());
return;
}
this.specials.add(special);
this.specialMap.set(special.code, special);
}
deleteSpecial(special: number | ISpecial<any>): void {
const code = typeof special === 'number' ? special : special.code;
const existing = this.specialMap.get(code);
if (!existing) return;
this.specials.delete(existing);
this.specialMap.delete(code);
}
iterateSpecials(): Iterable<ISpecial<any>> {
return this.specials;
}
setAttribute<K extends keyof TAttr>(key: K, value: TAttr[K]): void {
this.attributes[key] = value;
}
addAttribute<K extends SelectKey<TAttr, number>>(
key: K,
value: number
): void {
(this.attributes[key] as number) += value;
}
getAttribute<K extends keyof TAttr>(key: K): TAttr[K] {
return this.attributes[key];
}
cloneAttributes(): TAttr {
return structuredClone(this.attributes);
}
clone(): IEnemy<TAttr> {
const cloned = new Enemy<TAttr>(
this.id,
this.code,
structuredClone(this.attributes)
);
for (const special of this.specials) {
cloned.addSpecial(special.clone());
}
return cloned;
}
copyFrom(enemy: IReadonlyEnemy<TAttr>): void {
this.attributes = enemy.cloneAttributes();
this.specials.clear();
this.specialMap.clear();
for (const special of enemy.iterateSpecials()) {
this.addSpecial(special.clone());
}
}
saveState(_compression: SaveCompression): IEnemySaveState<TAttr> {
const specials: Map<number, unknown> = new Map();
for (const special of this.specials) {
specials.set(special.code, special.saveState(_compression));
}
return { attrs: structuredClone(this.attributes), specials };
}
loadState(
state: IEnemySaveState<TAttr>,
compression: SaveCompression
): void {
this.attributes = structuredClone(state.attrs);
for (const special of this.specials) {
const saved = state.specials.get(special.code);
if (saved === undefined) {
logger.warn(120, special.code.toString(), this.id);
continue;
}
special.loadState(saved, compression);
}
}
}