import { logger } from '@motajs/common'; import { SaveCompression } from '../common'; import { IEnemy, IEnemySaveState, IReadonlyEnemy, ISpecial } from './types'; export class Enemy implements IEnemy { /** 怪物身上的特殊属性列表 */ private readonly specials: Set> = new Set(); /** code -> ISpecial 映射,用于快速查找 */ private readonly specialMap: Map> = new Map(); constructor( readonly id: string, readonly code: number, private attributes: TAttr ) {} getSpecial(code: number): ISpecial | null { return (this.specialMap.get(code) as ISpecial) ?? null; } hasSpecial(code: number): boolean { return this.specialMap.has(code); } addSpecial(special: ISpecial): 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): 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> { return this.specials; } setAttribute(key: K, value: TAttr[K]): void { this.attributes[key] = value; } addAttribute>( key: K, value: number ): void { (this.attributes[key] as number) += value; } getAttribute(key: K): TAttr[K] { return this.attributes[key]; } cloneAttributes(): TAttr { return structuredClone(this.attributes); } clone(): IEnemy { const cloned = new Enemy( this.id, this.code, structuredClone(this.attributes) ); for (const special of this.specials) { cloned.addSpecial(special.clone()); } return cloned; } copyFrom(enemy: IReadonlyEnemy): 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 { const specials: Map = new Map(); for (const special of this.specials) { specials.set(special.code, special.saveState(_compression)); } return { attrs: structuredClone(this.attributes), specials }; } loadState( state: IEnemySaveState, 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); } } }