mirror of
https://github.com/motajs/template.git
synced 2026-05-16 06:11:10 +08:00
109 lines
3.3 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
}
|