feat: IEnemyHandler 引入 IStateBase 成员

This commit is contained in:
unanmed 2026-05-12 17:26:57 +08:00
parent 0da19e1697
commit 43ad3fd83e
7 changed files with 232 additions and 197 deletions

View File

@ -18,66 +18,73 @@ import { IReadonlyHeroAttribute } from '../hero';
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy'; import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
import { EnemyView } from './enemy'; import { EnemyView } from './enemy';
import { ILocationIndexer, MapLocIndexer } from '../common/indexer'; import { ILocationIndexer, MapLocIndexer } from '../common/indexer';
import { IStateBase } from '../types';
export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> { export class EnemyContext<TEnemy, THero> implements IEnemyContext<
TEnemy,
THero
> {
/** 坐标索引 -> 怪物视图 */ /** 坐标索引 -> 怪物视图 */
private readonly enemyViewMap: Map<number, EnemyView<TAttr>> = new Map(); private readonly enemyViewMap: Map<number, EnemyView<TEnemy>> = new Map();
/** 坐标索引 -> 计算前怪物对象 */ /** 坐标索引 -> 计算前怪物对象 */
private readonly enemyMap: Map<number, IEnemy<TAttr>> = new Map(); private readonly enemyMap: Map<number, IEnemy<TEnemy>> = new Map();
/** 怪物视图 -> 坐标索引 */ /** 怪物视图 -> 坐标索引 */
private readonly locatorViewMap: Map<IEnemyView<TAttr>, number> = new Map(); private readonly locatorViewMap: Map<IEnemyView<TEnemy>, number> =
new Map();
/** 计算前怪物对象 -> 坐标索引 */ /** 计算前怪物对象 -> 坐标索引 */
private readonly locatorEnemyMap: Map<IEnemy<TAttr>, number> = new Map(); private readonly locatorEnemyMap: Map<IEnemy<TEnemy>, number> = new Map();
/** 计算后怪物对象 -> 怪物视图 */ /** 计算后怪物对象 -> 怪物视图 */
private readonly computedToView: Map< private readonly computedToView: Map<
IReadonlyEnemy<TAttr>, IReadonlyEnemy<TEnemy>,
EnemyView<TAttr> EnemyView<TEnemy>
> = new Map(); > = new Map();
/** 当前已注册的光环转换器 */ /** 当前已注册的光环转换器 */
private readonly auraConverter: Set<IAuraConverter<TAttr, THero>> = private readonly auraConverter: Set<IAuraConverter<TEnemy, THero>> =
new Set(); new Set();
/** 光环转换器是否启用 */ /** 光环转换器是否启用 */
private readonly converterStatus: Map< private readonly converterStatus: Map<
IAuraConverter<TAttr, THero>, IAuraConverter<TEnemy, THero>,
boolean boolean
> = new Map(); > = new Map();
/** 所有已被转换的光环 */ /** 所有已被转换的光环 */
private readonly convertedAura: Map<ISpecial<any>, IAuraView<TAttr>> = private readonly convertedAura: Map<ISpecial<any>, IAuraView<TEnemy>> =
new Map(); new Map();
/** 普通查询效果注册,特殊属性 -> 此特殊属性的查询效果列表,按照优先级从高到低排序 */ /** 普通查询效果注册,特殊属性 -> 此特殊属性的查询效果列表,按照优先级从高到低排序 */
private readonly commonQueryMap: Map< private readonly commonQueryMap: Map<
number, number,
IEnemyCommonQueryEffect<TAttr, THero>[] IEnemyCommonQueryEffect<TEnemy, THero>[]
> = new Map(); > = new Map();
/** 特殊查询效果注册,特殊属性 -> 此特殊属性的特殊查询效果列表,按照优先级从高到低排序 */ /** 特殊查询效果注册,特殊属性 -> 此特殊属性的特殊查询效果列表,按照优先级从高到低排序 */
private readonly specialQueryEffects: Map< private readonly specialQueryEffects: Map<
number, number,
IEnemySpecialQueryEffect<TAttr, THero>[] IEnemySpecialQueryEffect<TEnemy, THero>[]
> = new Map(); > = new Map();
/** 最终效果列表,按照优先级从高到低排列 */ /** 最终效果列表,按照优先级从高到低排列 */
private readonly finalEffects: IEnemyFinalEffect<TAttr, THero>[] = []; private readonly finalEffects: IEnemyFinalEffect<TEnemy, THero>[] = [];
/** 添加的无来源全局光环列表 */ /** 添加的无来源全局光环列表 */
private readonly globalAuraList: Set<IAuraView<TAttr>> = new Set(); private readonly globalAuraList: Set<IAuraView<TEnemy>> = new Set();
/** 排序后的光环视图,视图优先级 -> 光环视图列表 */ /** 排序后的光环视图,视图优先级 -> 光环视图列表 */
private readonly sortedAura: Map<number, Set<IAuraView<TAttr>>> = new Map(); private readonly sortedAura: Map<number, Set<IAuraView<TEnemy>>> =
new Map();
/** 当怪物更新后,需要对上下文进行全量刷新的怪物列表 */ /** 当怪物更新后,需要对上下文进行全量刷新的怪物列表 */
private readonly needTotallyRefresh: Set<IEnemyView<TAttr>> = new Set(); private readonly needTotallyRefresh: Set<IEnemyView<TEnemy>> = new Set();
/** 所有实际查询了上下文的常规查询效果,这些怪物需要在上下文或其他怪物刷新时一并刷新 */ /** 所有实际查询了上下文的常规查询效果,这些怪物需要在上下文或其他怪物刷新时一并刷新 */
private readonly requestedCommonContext: Set<IEnemyView<TAttr>> = new Set(); private readonly requestedCommonContext: Set<IEnemyView<TEnemy>> =
new Set();
/** 所有需要被标记为脏的怪物 */ /** 所有需要被标记为脏的怪物 */
private readonly dirtyEnemy: Set<IEnemyView<TAttr>> = new Set(); private readonly dirtyEnemy: Set<IEnemyView<TEnemy>> = new Set();
/** 当前绑定的勇士属性对象 */ /** 当前绑定的勇士属性对象 */
private bindedHero: IReadonlyHeroAttribute<THero> | null = null; private bindedHero: IReadonlyHeroAttribute<THero> | null = null;
/** 地图伤害对象 */ /** 地图伤害对象 */
private mapDamage: IMapDamage<TAttr, THero> | null = null; private mapDamage: IMapDamage<TEnemy, THero> | null = null;
/** 伤害系统对象 */ /** 伤害系统对象 */
private damageSystem: IDamageSystem<TAttr, THero> | null = null; private damageSystem: IDamageSystem<TEnemy, THero> | null = null;
/** 索引工具 */ /** 索引工具 */
readonly indexer: ILocationIndexer = new MapLocIndexer(); readonly indexer: ILocationIndexer = new MapLocIndexer();
@ -89,6 +96,8 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
width: number = 0; width: number = 0;
height: number = 0; height: number = 0;
constructor(readonly dataState: IStateBase<TEnemy, THero>) {}
resize(width: number, height: number): void { resize(width: number, height: number): void {
this.clear(); this.clear();
this.width = width; this.width = width;
@ -97,20 +106,20 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
this.needUpdate = true; this.needUpdate = true;
} }
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void { registerAuraConverter(converter: IAuraConverter<TEnemy, THero>): void {
this.auraConverter.add(converter); this.auraConverter.add(converter);
this.converterStatus.set(converter, true); this.converterStatus.set(converter, true);
this.needUpdate = true; this.needUpdate = true;
} }
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void { unregisterAuraConverter(converter: IAuraConverter<TEnemy, THero>): void {
this.auraConverter.delete(converter); this.auraConverter.delete(converter);
this.converterStatus.delete(converter); this.converterStatus.delete(converter);
this.needUpdate = true; this.needUpdate = true;
} }
setAuraConverterEnabled( setAuraConverterEnabled(
converter: IAuraConverter<TAttr, THero>, converter: IAuraConverter<TEnemy, THero>,
enabled: boolean enabled: boolean
): void { ): void {
if (!this.auraConverter.has(converter)) return; if (!this.auraConverter.has(converter)) return;
@ -120,7 +129,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
registerCommonQueryEffect( registerCommonQueryEffect(
code: number, code: number,
effect: IEnemyCommonQueryEffect<TAttr, THero> effect: IEnemyCommonQueryEffect<TEnemy, THero>
): void { ): void {
const array = this.commonQueryMap.getOrInsert(code, []); const array = this.commonQueryMap.getOrInsert(code, []);
array.push(effect); array.push(effect);
@ -130,7 +139,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
unregisterCommonQueryEffect( unregisterCommonQueryEffect(
code: number, code: number,
effect: IEnemyCommonQueryEffect<TAttr, THero> effect: IEnemyCommonQueryEffect<TEnemy, THero>
): void { ): void {
const array = this.commonQueryMap.get(code); const array = this.commonQueryMap.get(code);
if (!array) return; if (!array) return;
@ -141,7 +150,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
} }
registerSpecialQueryEffect( registerSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero> effect: IEnemySpecialQueryEffect<TEnemy, THero>
): void { ): void {
const list = this.specialQueryEffects.getOrInsert(effect.priority, []); const list = this.specialQueryEffects.getOrInsert(effect.priority, []);
list.push(effect); list.push(effect);
@ -149,7 +158,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
} }
unregisterSpecialQueryEffect( unregisterSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero> effect: IEnemySpecialQueryEffect<TEnemy, THero>
): void { ): void {
const list = this.specialQueryEffects.get(effect.priority); const list = this.specialQueryEffects.get(effect.priority);
if (!list) return; if (!list) return;
@ -163,13 +172,13 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
this.needUpdate = true; this.needUpdate = true;
} }
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void { registerFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void {
this.finalEffects.push(effect); this.finalEffects.push(effect);
this.finalEffects.sort((a, b) => b.priority - a.priority); this.finalEffects.sort((a, b) => b.priority - a.priority);
this.needUpdate = true; this.needUpdate = true;
} }
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void { unregisterFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void {
const index = this.finalEffects.indexOf(effect); const index = this.finalEffects.indexOf(effect);
if (index !== -1) { if (index !== -1) {
this.finalEffects.splice(index, 1); this.finalEffects.splice(index, 1);
@ -194,37 +203,39 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* @param locator * @param locator
*/ */
private createHandler( private createHandler(
enemy: IEnemy<TAttr>, enemy: IEnemy<TEnemy>,
locator: ITileLocator locator: ITileLocator
): IEnemyHandler<TAttr, THero> { ): IEnemyHandler<TEnemy, THero> {
return { enemy, locator, hero: this.bindedHero! }; return { enemy, locator, hero: this.bindedHero!, data: this.dataState };
} }
getEnemyLocator(enemy: IEnemy<TAttr>): Readonly<ITileLocator> | null { getEnemyLocator(enemy: IEnemy<TEnemy>): Readonly<ITileLocator> | null {
const index = this.locatorEnemyMap.get(enemy); const index = this.locatorEnemyMap.get(enemy);
if (index === undefined) return null; if (index === undefined) return null;
return this.indexer.indexToLocator(index); return this.indexer.indexToLocator(index);
} }
getEnemyLocatorByView( getEnemyLocatorByView(
view: IEnemyView<TAttr> view: IEnemyView<TEnemy>
): Readonly<ITileLocator> | null { ): Readonly<ITileLocator> | null {
const index = this.locatorViewMap.get(view); const index = this.locatorViewMap.get(view);
if (index === undefined) return null; if (index === undefined) return null;
return this.indexer.indexToLocator(index); return this.indexer.indexToLocator(index);
} }
getEnemyByLocator(locator: ITileLocator): IEnemyView<TAttr> | null { getEnemyByLocator(locator: ITileLocator): IEnemyView<TEnemy> | null {
const index = this.indexer.locToIndex(locator.x, locator.y); const index = this.indexer.locToIndex(locator.x, locator.y);
return this.enemyViewMap.get(index) ?? null; return this.enemyViewMap.get(index) ?? null;
} }
getEnemyByLoc(x: number, y: number): IEnemyView<TAttr> | null { getEnemyByLoc(x: number, y: number): IEnemyView<TEnemy> | null {
const index = this.indexer.locToIndex(x, y); const index = this.indexer.locToIndex(x, y);
return this.enemyViewMap.get(index) ?? null; return this.enemyViewMap.get(index) ?? null;
} }
getViewByComputed(enemy: IReadonlyEnemy<TAttr>): IEnemyView<TAttr> | null { getViewByComputed(
enemy: IReadonlyEnemy<TEnemy>
): IEnemyView<TEnemy> | null {
return this.computedToView.get(enemy) ?? null; return this.computedToView.get(enemy) ?? null;
} }
@ -256,11 +267,11 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
this.locatorEnemyMap.delete(enemy); this.locatorEnemyMap.delete(enemy);
} }
setEnemyAt(locator: ITileLocator, enemy: IEnemy<TAttr>): void { setEnemyAt(locator: ITileLocator, enemy: IEnemy<TEnemy>): void {
const index = this.indexer.locToIndex(locator.x, locator.y); const index = this.indexer.locToIndex(locator.x, locator.y);
this.deleteEnemyAt(index); this.deleteEnemyAt(index);
const view = new EnemyView<TAttr>(enemy, this); const view = new EnemyView<TEnemy>(enemy, this);
this.enemyMap.set(index, enemy); this.enemyMap.set(index, enemy);
this.enemyViewMap.set(index, view); this.enemyViewMap.set(index, view);
this.locatorEnemyMap.set(enemy, index); this.locatorEnemyMap.set(enemy, index);
@ -290,7 +301,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
private *internalScanRange<T>( private *internalScanRange<T>(
range: IRange<T>, range: IRange<T>,
param: T param: T
): Iterable<[ITileLocator, EnemyView<TAttr>]> { ): Iterable<[ITileLocator, EnemyView<TEnemy>]> {
range.bindHost(this); range.bindHost(this);
const keys = new Set(this.enemyViewMap.keys()); const keys = new Set(this.enemyViewMap.keys());
const matched = range.autoDetect(keys, param); const matched = range.autoDetect(keys, param);
@ -307,46 +318,46 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
scanRange<T>( scanRange<T>(
range: IRange<T>, range: IRange<T>,
param: T param: T
): Iterable<[ITileLocator, IEnemyView<TAttr>]> { ): Iterable<[ITileLocator, IEnemyView<TEnemy>]> {
return this.internalScanRange(range, param); return this.internalScanRange(range, param);
} }
*iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TAttr>]> { *iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TEnemy>]> {
for (const [index, view] of this.enemyViewMap) { for (const [index, view] of this.enemyViewMap) {
const locator = this.indexer.indexToLocator(index); const locator = this.indexer.indexToLocator(index);
yield [locator, view]; yield [locator, view];
} }
} }
addAura(aura: IAuraView<TAttr>): void { addAura(aura: IAuraView<TEnemy>): void {
this.globalAuraList.add(aura); this.globalAuraList.add(aura);
this.needUpdate = true; this.needUpdate = true;
} }
deleteAura(aura: IAuraView<TAttr>): void { deleteAura(aura: IAuraView<TEnemy>): void {
this.globalAuraList.delete(aura); this.globalAuraList.delete(aura);
this.needUpdate = true; this.needUpdate = true;
} }
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void { attachMapDamage(damage: IMapDamage<TEnemy, THero> | null): void {
this.mapDamage = damage; this.mapDamage = damage;
if (damage) { if (damage) {
damage.refreshAll(); damage.refreshAll();
} }
} }
getMapDamage(): IMapDamage<TAttr, THero> | null { getMapDamage(): IMapDamage<TEnemy, THero> | null {
return this.mapDamage; return this.mapDamage;
} }
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void { attachDamageSystem(system: IDamageSystem<TEnemy, THero> | null): void {
this.damageSystem = system; this.damageSystem = system;
if (system) { if (system) {
system.bindHeroStatus(this.bindedHero); system.bindHeroStatus(this.bindedHero);
} }
} }
getDamageSystem(): IDamageSystem<TAttr, THero> | null { getDamageSystem(): IDamageSystem<TEnemy, THero> | null {
return this.damageSystem; return this.damageSystem;
} }
@ -358,9 +369,9 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
*/ */
private convertSpecial( private convertSpecial(
special: ISpecial<any>, special: ISpecial<any>,
handler: IReadonlyEnemyHandler<TAttr, THero> handler: IReadonlyEnemyHandler<TEnemy, THero>
): IEnemyAuraView<TAttr, any, any> | null { ): IEnemyAuraView<TEnemy, any, any> | null {
let matched: IAuraConverter<TAttr, THero> | null = null; let matched: IAuraConverter<TEnemy, THero> | null = null;
for (const converter of this.auraConverter) { for (const converter of this.auraConverter) {
if (!this.converterStatus.get(converter)) continue; if (!this.converterStatus.get(converter)) continue;
if (converter.shouldConvert(special, handler)) { if (converter.shouldConvert(special, handler)) {
@ -380,7 +391,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* *
* @param aura * @param aura
*/ */
private insertIntoSortedAura(aura: IAuraView<TAttr>): void { private insertIntoSortedAura(aura: IAuraView<TEnemy>): void {
const set = this.sortedAura.getOrInsertComputed( const set = this.sortedAura.getOrInsertComputed(
aura.priority, aura.priority,
() => new Set() () => new Set()
@ -392,7 +403,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* *
* @param aura * @param aura
*/ */
private removeFromSortedAura(aura: IAuraView<TAttr>): void { private removeFromSortedAura(aura: IAuraView<TEnemy>): void {
const set = this.sortedAura.get(aura.priority); const set = this.sortedAura.get(aura.priority);
if (set) { if (set) {
set.delete(aura); set.delete(aura);
@ -410,12 +421,12 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* @param currentPriority * @param currentPriority
*/ */
private processSpecialModifier( private processSpecialModifier(
modifier: IEnemySpecialModifier<TAttr>, modifier: IEnemySpecialModifier<TEnemy>,
handler: IEnemyHandler<TAttr, THero>, handler: IEnemyHandler<TEnemy, THero>,
currentPriority: number currentPriority: number
): Set<IAuraView<TAttr>> { ): Set<IAuraView<TEnemy>> {
const enemy = handler.enemy; const enemy = handler.enemy;
const affectedAuras = new Set<IAuraView<TAttr>>(); const affectedAuras = new Set<IAuraView<TEnemy>>();
const toAdd = modifier.add(handler); const toAdd = modifier.add(handler);
const toDelete = modifier.delete(handler); const toDelete = modifier.delete(handler);
@ -487,7 +498,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* @param currentPriority * @param currentPriority
*/ */
private processSpecialQuery( private processSpecialQuery(
effect: IEnemySpecialQueryEffect<TAttr, THero>, effect: IEnemySpecialQueryEffect<TEnemy, THero>,
currentPriority: number currentPriority: number
): void { ): void {
const modifier = effect.for(this); const modifier = effect.for(this);
@ -519,7 +530,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* @param currentPriority * @param currentPriority
*/ */
private processAuraSpecial( private processAuraSpecial(
aura: IAuraView<TAttr>, aura: IAuraView<TEnemy>,
currentPriority: number currentPriority: number
): void { ): void {
const param = aura.getRangeParam(); const param = aura.getRangeParam();
@ -694,7 +705,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
} }
} }
markDirty(view: IEnemyView<TAttr>): void { markDirty(view: IEnemyView<TEnemy>): void {
if (!this.locatorViewMap.has(view)) return; if (!this.locatorViewMap.has(view)) return;
this.dirtyEnemy.add(view); this.dirtyEnemy.add(view);
if (this.damageSystem) { if (this.damageSystem) {
@ -709,8 +720,8 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* @param locator * @param locator
*/ */
private refreshSpecialModifier( private refreshSpecialModifier(
modifier: IEnemySpecialModifier<TAttr>, modifier: IEnemySpecialModifier<TEnemy>,
handler: IEnemyHandler<TAttr, THero> handler: IEnemyHandler<TEnemy, THero>
): void { ): void {
const enemy = handler.enemy; const enemy = handler.enemy;
const toAdd = modifier.add(handler); const toAdd = modifier.add(handler);
@ -756,7 +767,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
* *
* @param view * @param view
*/ */
private refreshEnemy(view: EnemyView<TAttr>): void { private refreshEnemy(view: EnemyView<TEnemy>): void {
const locator = this.getEnemyLocatorByView(view); const locator = this.getEnemyLocatorByView(view);
if (!locator) return; if (!locator) return;
@ -851,7 +862,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
} }
} }
requestRefresh(view: IEnemyView<TAttr>): void { requestRefresh(view: IEnemyView<TEnemy>): void {
if (!this.dirtyEnemy.has(view)) return; if (!this.dirtyEnemy.has(view)) return;
if (this.needTotallyRefresh.has(view)) { if (this.needTotallyRefresh.has(view)) {
this.needUpdate = true; this.needUpdate = true;
@ -861,11 +872,11 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
return; return;
} }
this.refreshEnemy(view as EnemyView<TAttr>); this.refreshEnemy(view as EnemyView<TEnemy>);
for (const requestedView of this.requestedCommonContext) { for (const requestedView of this.requestedCommonContext) {
if (requestedView === view) continue; if (requestedView === view) continue;
this.refreshEnemy(requestedView as EnemyView<TAttr>); this.refreshEnemy(requestedView as EnemyView<TEnemy>);
} }
} }

View File

@ -13,6 +13,7 @@ import {
} from './types'; } from './types';
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero'; import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
import { IReadonlyEnemy } from '../enemy'; import { IReadonlyEnemy } from '../enemy';
import { IStateBase } from '../types';
interface ICriticalSearchResult { interface ICriticalSearchResult {
/** 此临界点的属性值 */ /** 此临界点的属性值 */
@ -21,20 +22,23 @@ interface ICriticalSearchResult {
readonly info: IEnemyDamageInfo; readonly info: IEnemyDamageInfo;
} }
export class DamageContext<TAttr, THero> implements IDamageContext< export class DamageContext<TEnemy, THero> implements IDamageContext<
TAttr, TEnemy,
THero THero
> { > {
/** 当前正在使用的计算器 */ /** 当前正在使用的计算器 */
protected calculator: IDamageCalculator<TAttr, THero> | null; protected calculator: IDamageCalculator<TEnemy, THero> | null;
/** 当前勇士属性 */ /** 当前勇士属性 */
protected heroStatus: IReadonlyHeroAttribute<THero> | null; protected heroStatus: IReadonlyHeroAttribute<THero> | null;
readonly dataState: IStateBase<TEnemy, THero>;
constructor( constructor(
readonly context: IEnemyContext<TAttr, THero>, readonly context: IEnemyContext<TEnemy, THero>,
calculator: IDamageCalculator<TAttr, THero> | null = null, calculator: IDamageCalculator<TEnemy, THero> | null = null,
heroStatus: IReadonlyHeroAttribute<THero> | null = null heroStatus: IReadonlyHeroAttribute<THero> | null = null
) { ) {
this.dataState = context.dataState;
this.calculator = calculator; this.calculator = calculator;
this.heroStatus = heroStatus; this.heroStatus = heroStatus;
} }
@ -46,14 +50,14 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
* @param hero * @param hero
*/ */
private createReadonlyHandler( private createReadonlyHandler(
enemy: IReadonlyEnemy<TAttr>, enemy: IReadonlyEnemy<TEnemy>,
locator: ITileLocator, locator: ITileLocator,
hero: IReadonlyHeroAttribute<THero> hero: IReadonlyHeroAttribute<THero>
): IReadonlyEnemyHandler<TAttr, THero> { ): IReadonlyEnemyHandler<TEnemy, THero> {
return { enemy, locator, hero }; return { enemy, locator, hero, data: this.dataState };
} }
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null { getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null {
if (!this.heroStatus) { if (!this.heroStatus) {
logger.warn(107); logger.warn(107);
return null; return null;
@ -73,7 +77,7 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
} }
getDamageInfoByComputed( getDamageInfoByComputed(
enemy: IReadonlyEnemy<TAttr> enemy: IReadonlyEnemy<TEnemy>
): IEnemyDamageInfo | null { ): IEnemyDamageInfo | null {
if (!this.heroStatus) { if (!this.heroStatus) {
logger.warn(107); logger.warn(107);
@ -96,7 +100,7 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
} }
*calculateCritical( *calculateCritical(
view: IEnemyView<TAttr>, view: IEnemyView<TEnemy>,
attribute: CriticalableHeroStatus<THero>, attribute: CriticalableHeroStatus<THero>,
precision: number = 12 precision: number = 12
): Generator<IEnemyCritical, void, void> { ): Generator<IEnemyCritical, void, void> {
@ -168,7 +172,7 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
* @param maxIterations * @param maxIterations
*/ */
private findNextCritical( private findNextCritical(
handler: IReadonlyEnemyHandler<TAttr, THero>, handler: IReadonlyEnemyHandler<TEnemy, THero>,
hero: IHeroAttribute<THero>, hero: IHeroAttribute<THero>,
attribute: CriticalableHeroStatus<THero>, attribute: CriticalableHeroStatus<THero>,
currentValue: number, currentValue: number,
@ -206,24 +210,24 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
} }
} }
export class DamageSystem<TAttr, THero> export class DamageSystem<TEnemy, THero>
extends DamageContext<TAttr, THero> extends DamageContext<TEnemy, THero>
implements IDamageSystem<TAttr, THero> implements IDamageSystem<TEnemy, THero>
{ {
/** 怪物伤害缓存 */ /** 怪物伤害缓存 */
private readonly cache: Map<IEnemyView<TAttr>, IEnemyDamageInfo> = private readonly cache: Map<IEnemyView<TEnemy>, IEnemyDamageInfo> =
new Map(); new Map();
constructor(context: IEnemyContext<TAttr, THero>) { constructor(context: IEnemyContext<TEnemy, THero>) {
super(context); super(context);
} }
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void { useCalculator(calculator: IDamageCalculator<TEnemy, THero>): void {
this.calculator = calculator; this.calculator = calculator;
this.markAllDirty(); this.markAllDirty();
} }
getCalculator(): IDamageCalculator<TAttr, THero> | null { getCalculator(): IDamageCalculator<TEnemy, THero> | null {
return this.calculator; return this.calculator;
} }
@ -232,7 +236,7 @@ export class DamageSystem<TAttr, THero>
this.markAllDirty(); this.markAllDirty();
} }
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null { getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null {
const cached = this.cache.get(enemy); const cached = this.cache.get(enemy);
if (cached) { if (cached) {
return cached; return cached;
@ -246,7 +250,7 @@ export class DamageSystem<TAttr, THero>
} }
getDamageInfoByComputed( getDamageInfoByComputed(
enemy: IReadonlyEnemy<TAttr> enemy: IReadonlyEnemy<TEnemy>
): IEnemyDamageInfo | null { ): IEnemyDamageInfo | null {
const view = this.context.getViewByComputed(enemy); const view = this.context.getViewByComputed(enemy);
if (view) { if (view) {
@ -264,11 +268,11 @@ export class DamageSystem<TAttr, THero>
return info; return info;
} }
markDirty(enemy: IEnemyView<TAttr>): void { markDirty(enemy: IEnemyView<TEnemy>): void {
this.cache.delete(enemy); this.cache.delete(enemy);
} }
deleteEnemy(enemy: IEnemyView<TAttr>): void { deleteEnemy(enemy: IEnemyView<TEnemy>): void {
this.cache.delete(enemy); this.cache.delete(enemy);
} }
@ -276,7 +280,7 @@ export class DamageSystem<TAttr, THero>
this.cache.clear(); this.cache.clear();
} }
with(hero: IHeroAttribute<THero>): IDamageContext<TAttr, THero> { with(hero: IHeroAttribute<THero>): IDamageContext<TEnemy, THero> {
return new DamageContext(this.context, this.calculator, hero); return new DamageContext(this.context, this.calculator, hero);
} }
} }

View File

@ -10,6 +10,7 @@ import {
IMapDamageView IMapDamageView
} from './types'; } from './types';
import { ILocationHelper } from '../common/indexer'; import { ILocationHelper } from '../common/indexer';
import { IStateBase } from '../types';
interface IPointInfo { interface IPointInfo {
/** 该点所有的地图伤害 */ /** 该点所有的地图伤害 */
@ -34,9 +35,9 @@ interface IDamageStore<TAttr> {
readonly index: number; readonly index: number;
} }
export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> { export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
/** 当前使用的地图伤害转换器 */ /** 当前使用的地图伤害转换器 */
private converter: IMapDamageConverter<TAttr, THero> | null = null; private converter: IMapDamageConverter<TEnemy, THero> | null = null;
/** 当前使用的地图伤害合并器 */ /** 当前使用的地图伤害合并器 */
private reducer: IMapDamageReducer | null = null; private reducer: IMapDamageReducer | null = null;
@ -45,14 +46,14 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
/** 有来源地图伤害,坐标 -> 点伤害信息 */ /** 有来源地图伤害,坐标 -> 点伤害信息 */
private readonly sourcedDamage: Map<number, IPointInfo> = new Map(); private readonly sourcedDamage: Map<number, IPointInfo> = new Map();
/** 地图伤害视图 -> 其信息对象 */ /** 地图伤害视图 -> 其信息对象 */
private readonly viewStore: Map<IMapDamageView<any>, IViewStore<TAttr>> = private readonly viewStore: Map<IMapDamageView<any>, IViewStore<TEnemy>> =
new Map(); new Map();
/** 地图伤害信息 -> 其信息对象 */ /** 地图伤害信息 -> 其信息对象 */
private readonly damageStore: Map<IMapDamageInfo, IDamageStore<TAttr>> = private readonly damageStore: Map<IMapDamageInfo, IDamageStore<TEnemy>> =
new Map(); new Map();
/** 怪物视图 -> 其影响对象 */ /** 怪物视图 -> 其影响对象 */
private readonly enemyStore: Map< private readonly enemyStore: Map<
IEnemyView<TAttr>, IEnemyView<TEnemy>,
Set<IMapDamageView<any>> Set<IMapDamageView<any>>
> = new Map(); > = new Map();
/** 需要延迟刷新的坐标索引 */ /** 需要延迟刷新的坐标索引 */
@ -63,11 +64,14 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
/** 坐标索引对象 */ /** 坐标索引对象 */
private readonly indexer: ILocationHelper; private readonly indexer: ILocationHelper;
constructor(readonly context: IEnemyContext<TAttr, THero>) { readonly dataState: IStateBase<TEnemy, THero>;
constructor(readonly context: IEnemyContext<TEnemy, THero>) {
this.indexer = context.indexer; this.indexer = context.indexer;
this.dataState = context.dataState;
} }
useConverter(converter: IMapDamageConverter<TAttr, THero>): void { useConverter(converter: IMapDamageConverter<TEnemy, THero>): void {
this.converter = converter; this.converter = converter;
this.refreshAll(); this.refreshAll();
} }
@ -78,15 +82,16 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
* @param locator * @param locator
*/ */
private createReadonlyHandler( private createReadonlyHandler(
view: IEnemyView<TAttr>, view: IEnemyView<TEnemy>,
locator: ITileLocator locator: ITileLocator
): IReadonlyEnemyHandler<TAttr, THero> | null { ): IReadonlyEnemyHandler<TEnemy, THero> | null {
const hero = this.context.getBindedHero(); const hero = this.context.getBindedHero();
if (!hero) return null; if (!hero) return null;
return { return {
enemy: view.getComputedEnemy(), enemy: view.getComputedEnemy(),
locator, locator,
hero hero,
data: this.context.dataState
}; };
} }
@ -126,7 +131,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
this.markDirtyIndex(this.indexer.locaterToIndex(locator)); this.markDirtyIndex(this.indexer.locaterToIndex(locator));
} }
markEnemyDirty(view: IEnemyView<TAttr>): void { markEnemyDirty(view: IEnemyView<TEnemy>): void {
const store = this.enemyStore.get(view); const store = this.enemyStore.get(view);
const locator = this.context.getEnemyLocatorByView(view); const locator = this.context.getEnemyLocatorByView(view);
if (!store) { if (!store) {
@ -141,7 +146,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
this.refreshEnemyAndClearCache(view, locator); this.refreshEnemyAndClearCache(view, locator);
} }
deleteEnemy(view: IEnemyView<TAttr>): void { deleteEnemy(view: IEnemyView<TEnemy>): void {
const store = this.enemyStore.get(view); const store = this.enemyStore.get(view);
if (!store) return; if (!store) return;
const collection = new Set<number>(); const collection = new Set<number>();
@ -231,7 +236,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
* *
* @param view * @param view
*/ */
private removeEnemyAffecting(view: IEnemyView<TAttr>) { private removeEnemyAffecting(view: IEnemyView<TEnemy>) {
const views = this.enemyStore.get(view); const views = this.enemyStore.get(view);
if (!views) return; if (!views) return;
views.forEach(viewItem => { views.forEach(viewItem => {
@ -253,7 +258,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
* *
*/ */
private refreshEnemyAndClearCache( private refreshEnemyAndClearCache(
view: IEnemyView<TAttr>, view: IEnemyView<TEnemy>,
locator: ITileLocator locator: ITileLocator
) { ) {
this.removeEnemyAffecting(view); this.removeEnemyAffecting(view);
@ -295,7 +300,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
/** /**
* *
*/ */
private refreshEnemy(view: IEnemyView<TAttr>, locator: ITileLocator) { private refreshEnemy(view: IEnemyView<TEnemy>, locator: ITileLocator) {
this.removeEnemyAffecting(view); this.removeEnemyAffecting(view);
if (!this.converter) return; if (!this.converter) return;
const handler = this.createReadonlyHandler(view, locator); const handler = this.createReadonlyHandler(view, locator);

View File

@ -2,34 +2,39 @@ import { ITileLocator, IRange } from '@motajs/common';
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy'; import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
import { IReadonlyHeroAttribute, IHeroAttribute } from '../hero'; import { IReadonlyHeroAttribute, IHeroAttribute } from '../hero';
import { ILocationHelper } from '../common/indexer'; import { ILocationHelper } from '../common/indexer';
import { IStateBase } from '../types';
//#region 辅助接口 //#region 辅助接口
export interface IEnemyHandler<TAttr, THero> { export interface IEnemyHandler<TEnemy, THero> {
/** 怪物属性信息 */ /** 怪物属性信息 */
readonly enemy: IEnemy<TAttr>; readonly enemy: IEnemy<TEnemy>;
/** 怪物定位符 */ /** 怪物定位符 */
readonly locator: ITileLocator; readonly locator: ITileLocator;
/** 勇士属性信息 */ /** 勇士属性信息 */
readonly hero: IReadonlyHeroAttribute<THero>; readonly hero: IReadonlyHeroAttribute<THero>;
/** 当前全局状态对象 */
readonly data: IStateBase<TEnemy, THero>;
} }
export interface IReadonlyEnemyHandler<TAttr, THero> { export interface IReadonlyEnemyHandler<TEnemy, THero> {
/** 怪物属性信息 */ /** 怪物属性信息 */
readonly enemy: IReadonlyEnemy<TAttr>; readonly enemy: IReadonlyEnemy<TEnemy>;
/** 怪物定位符 */ /** 怪物定位符 */
readonly locator: ITileLocator; readonly locator: ITileLocator;
/** 勇士属性信息 */ /** 勇士属性信息 */
readonly hero: IReadonlyHeroAttribute<THero>; readonly hero: IReadonlyHeroAttribute<THero>;
/** 当前全局状态对象 */
readonly data: IStateBase<TEnemy, THero>;
} }
//#endregion //#endregion
//#region 怪物对象 //#region 怪物对象
export interface IEnemyView<TAttr> { export interface IEnemyView<TEnemy> {
/** 怪物视图所属的上下文 */ /** 怪物视图所属的上下文 */
readonly context: IEnemyContext<TAttr, unknown>; readonly context: IEnemyContext<TEnemy, unknown>;
/** /**
* *
@ -39,18 +44,18 @@ export interface IEnemyView<TAttr> {
/** /**
* *
*/ */
getBaseEnemy(): IReadonlyEnemy<TAttr>; getBaseEnemy(): IReadonlyEnemy<TEnemy>;
/** /**
* *
*/ */
getComputedEnemy(): IReadonlyEnemy<TAttr>; getComputedEnemy(): IReadonlyEnemy<TEnemy>;
/** /**
* *
* markDirty * markDirty
*/ */
getModifiableEnemy(): IEnemy<TAttr>; getModifiableEnemy(): IEnemy<TEnemy>;
/** /**
* *
@ -62,18 +67,18 @@ export interface IEnemyView<TAttr> {
//#region 光环与查询 //#region 光环与查询
export interface IEnemySpecialModifier<TAttr> { export interface IEnemySpecialModifier<TEnemy> {
/** /**
* *
* @param handler * @param handler
*/ */
add(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[]; add(handler: IReadonlyEnemyHandler<TEnemy, unknown>): ISpecial<any>[];
/** /**
* *
* @param handler * @param handler
*/ */
delete(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[]; delete(handler: IReadonlyEnemyHandler<TEnemy, unknown>): ISpecial<any>[];
/** /**
* true false * true false
@ -81,16 +86,16 @@ export interface IEnemySpecialModifier<TAttr> {
* @param special * @param special
*/ */
modify( modify(
handler: IEnemyHandler<TAttr, unknown>, handler: IEnemyHandler<TEnemy, unknown>,
special: ISpecial<any> special: ISpecial<any>
): boolean; ): boolean;
} }
export interface IAuraView<TAttr, T = any> { export interface IAuraView<TEnemy, TRange = any> {
/** 此光环视图的优先级 */ /** 此光环视图的优先级 */
readonly priority: number; readonly priority: number;
/** 此光环视图的影响范围 */ /** 此光环视图的影响范围 */
readonly range: IRange<T>; readonly range: IRange<TRange>;
/** 这个光环视图是否有可能修改怪物的基本属性 */ /** 这个光环视图是否有可能修改怪物的基本属性 */
readonly couldApplyBase: boolean; readonly couldApplyBase: boolean;
@ -100,7 +105,7 @@ export interface IAuraView<TAttr, T = any> {
/** /**
* *
*/ */
getRangeParam(): T; getRangeParam(): TRange;
/** /**
* *
@ -108,8 +113,8 @@ export interface IAuraView<TAttr, T = any> {
* @param baseEnemy * @param baseEnemy
*/ */
apply( apply(
handler: IEnemyHandler<TAttr, unknown>, handler: IEnemyHandler<TEnemy, unknown>,
baseEnemy: IReadonlyEnemy<TAttr> baseEnemy: IReadonlyEnemy<TEnemy>
): void; ): void;
/** /**
@ -118,21 +123,24 @@ export interface IAuraView<TAttr, T = any> {
* @param baseEnemy * @param baseEnemy
*/ */
applySpecial( applySpecial(
handler: IEnemyHandler<TAttr, unknown>, handler: IEnemyHandler<TEnemy, unknown>,
baseEnemy: IReadonlyEnemy<TAttr> baseEnemy: IReadonlyEnemy<TEnemy>
): IEnemySpecialModifier<TAttr> | null; ): IEnemySpecialModifier<TEnemy> | null;
} }
export interface IEnemyAuraView<TAttr, R, S> extends IAuraView<TAttr, R> { export interface IEnemyAuraView<TEnemy, TRange, TSpecial> extends IAuraView<
TEnemy,
TRange
> {
/** 此光环视图所属的怪物 */ /** 此光环视图所属的怪物 */
readonly enemy: IReadonlyEnemy<TAttr>; readonly enemy: IReadonlyEnemy<TEnemy>;
/** 此光环视图所属的特殊属性 */ /** 此光环视图所属的特殊属性 */
readonly special: ISpecial<S>; readonly special: ISpecial<TSpecial>;
/** 此光环视图所属怪物的定位符 */ /** 此光环视图所属怪物的定位符 */
readonly locator: ITileLocator; readonly locator: ITileLocator;
} }
export interface IAuraConverter<TAttr, THero> { export interface IAuraConverter<TEnemy, THero> {
/** /**
* *
* @param special * @param special
@ -140,7 +148,7 @@ export interface IAuraConverter<TAttr, THero> {
*/ */
shouldConvert( shouldConvert(
special: ISpecial<any>, special: ISpecial<any>,
handler: IReadonlyEnemyHandler<TAttr, THero> handler: IReadonlyEnemyHandler<TEnemy, THero>
): boolean; ): boolean;
/** /**
@ -148,22 +156,22 @@ export interface IAuraConverter<TAttr, THero> {
*/ */
convert( convert(
special: ISpecial<any>, special: ISpecial<any>,
handler: IReadonlyEnemyHandler<TAttr, THero>, handler: IReadonlyEnemyHandler<TEnemy, THero>,
context: IEnemyContext<TAttr, THero> context: IEnemyContext<TEnemy, THero>
): IEnemyAuraView<TAttr, any, any>; ): IEnemyAuraView<TEnemy, any, any>;
} }
export interface IEnemySpecialQueryModifier< export interface IEnemySpecialQueryModifier<
TAttr, TEnemy,
THero THero
> extends IEnemySpecialModifier<TAttr> { > extends IEnemySpecialModifier<TEnemy> {
/** /**
* *
*/ */
shouldQuery(handler: IReadonlyEnemyHandler<TAttr, THero>): boolean; shouldQuery(handler: IReadonlyEnemyHandler<TEnemy, THero>): boolean;
} }
export interface IEnemySpecialQueryEffect<TAttr, THero> { export interface IEnemySpecialQueryEffect<TEnemy, THero> {
/** 效果优先级,与光环属性共用 */ /** 效果优先级,与光环属性共用 */
readonly priority: number; readonly priority: number;
@ -171,11 +179,11 @@ export interface IEnemySpecialQueryEffect<TAttr, THero> {
* *
*/ */
for( for(
ctx: IEnemyContext<TAttr, THero> ctx: IEnemyContext<TEnemy, THero>
): IEnemySpecialQueryModifier<TAttr, THero>; ): IEnemySpecialQueryModifier<TEnemy, THero>;
} }
export interface IEnemyCommonQueryEffect<TAttr, THero> { export interface IEnemyCommonQueryEffect<TEnemy, THero> {
/** 优先级,越高的越先执行 */ /** 优先级,越高的越先执行 */
readonly priority: number; readonly priority: number;
@ -183,20 +191,20 @@ export interface IEnemyCommonQueryEffect<TAttr, THero> {
* *
*/ */
apply( apply(
handler: IEnemyHandler<TAttr, THero>, handler: IEnemyHandler<TEnemy, THero>,
special: ISpecial<any>, special: ISpecial<any>,
query: () => IEnemyContext<TAttr, THero> query: () => IEnemyContext<TEnemy, THero>
): void; ): void;
} }
export interface IEnemyFinalEffect<TAttr, THero> { export interface IEnemyFinalEffect<TEnemy, THero> {
/** 效果优先级,越高会越先被执行 */ /** 效果优先级,越高会越先被执行 */
readonly priority: number; readonly priority: number;
/** /**
* *
*/ */
apply(handler: IEnemyHandler<TAttr, THero>): void; apply(handler: IEnemyHandler<TEnemy, THero>): void;
} }
//#endregion //#endregion
@ -245,13 +253,13 @@ export interface IMapDamageView<T = any> {
): Readonly<IMapDamageInfo> | null; ): Readonly<IMapDamageInfo> | null;
} }
export interface IMapDamageConverter<TAttr, THero> { export interface IMapDamageConverter<TEnemy, THero> {
/** /**
* *
*/ */
convert( convert(
handler: IReadonlyEnemyHandler<TAttr, THero>, handler: IReadonlyEnemyHandler<TEnemy, THero>,
context: IEnemyContext<TAttr, THero> context: IEnemyContext<TEnemy, THero>
): IMapDamageView<any>[]; ): IMapDamageView<any>[];
} }
@ -265,15 +273,17 @@ export interface IMapDamageReducer {
): Readonly<IMapDamageInfo>; ): Readonly<IMapDamageInfo>;
} }
export interface IMapDamage<TAttr, THero> { export interface IMapDamage<TEnemy, THero> {
/** 当前绑定的怪物上下文 */ /** 当前绑定的怪物上下文 */
readonly context: IEnemyContext<TAttr, THero>; readonly context: IEnemyContext<TEnemy, THero>;
/** 地图伤害系统绑定的全局状态对象 */
readonly dataState: IStateBase<TEnemy, THero>;
/** /**
* *
* @param converter * @param converter
*/ */
useConverter(converter: IMapDamageConverter<TAttr, THero>): void; useConverter(converter: IMapDamageConverter<TEnemy, THero>): void;
/** /**
* *
@ -305,7 +315,7 @@ export interface IMapDamage<TAttr, THero> {
* *
* @param view * @param view
*/ */
markEnemyDirty(view: IEnemyView<TAttr>): void; markEnemyDirty(view: IEnemyView<TEnemy>): void;
/** /**
* *
@ -316,7 +326,7 @@ export interface IMapDamage<TAttr, THero> {
* *
* @param view * @param view
*/ */
deleteEnemy(view: IEnemyView<TAttr>): void; deleteEnemy(view: IEnemyView<TEnemy>): void;
/** /**
* *
@ -363,12 +373,12 @@ export type CriticalableHeroStatus<THero> = keyof {
[P in keyof THero as THero[P] extends number ? P : never]: number; [P in keyof THero as THero[P] extends number ? P : never]: number;
}; };
export interface IDamageCalculator<TAttr, THero> { export interface IDamageCalculator<TEnemy, THero> {
/** /**
* *
* @param handler * @param handler
*/ */
calculate(handler: IReadonlyEnemyHandler<TAttr, THero>): IEnemyDamageInfo; calculate(handler: IReadonlyEnemyHandler<TEnemy, THero>): IEnemyDamageInfo;
/** /**
* *
@ -376,24 +386,27 @@ export interface IDamageCalculator<TAttr, THero> {
* @param attribute * @param attribute
*/ */
getCriticalLimit( getCriticalLimit(
handler: IReadonlyEnemyHandler<TAttr, THero>, handler: IReadonlyEnemyHandler<TEnemy, THero>,
attribute: CriticalableHeroStatus<THero> attribute: CriticalableHeroStatus<THero>
): number; ): number;
} }
export interface IDamageContext<TAttr, THero> { export interface IDamageContext<TEnemy, THero> {
/** 伤害上下文所属的全局状态对象 */
readonly dataState: IStateBase<TEnemy, THero>;
/** /**
* *
* @param enemy * @param enemy
*/ */
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null; getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null;
/** /**
* *
* @param enemy * @param enemy
*/ */
getDamageInfoByComputed( getDamageInfoByComputed(
enemy: IReadonlyEnemy<TAttr> enemy: IReadonlyEnemy<TEnemy>
): IEnemyDamageInfo | null; ): IEnemyDamageInfo | null;
/** /**
@ -403,29 +416,29 @@ export interface IDamageContext<TAttr, THero> {
* @param precision `12-16` 12 * @param precision `12-16` 12
*/ */
calculateCritical( calculateCritical(
enemy: IEnemyView<TAttr>, enemy: IEnemyView<TEnemy>,
attribute: CriticalableHeroStatus<THero>, attribute: CriticalableHeroStatus<THero>,
precision?: number precision?: number
): Generator<IEnemyCritical, void, void>; ): Generator<IEnemyCritical, void, void>;
} }
export interface IDamageSystem<TAttr, THero> extends IDamageContext< export interface IDamageSystem<TEnemy, THero> extends IDamageContext<
TAttr, TEnemy,
THero THero
> { > {
/** 伤害系统所属的上下文 */ /** 伤害系统所属的上下文 */
readonly context: IEnemyContext<TAttr, THero>; readonly context: IEnemyContext<TEnemy, THero>;
/** /**
* 使 * 使
* @param calculator * @param calculator
*/ */
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void; useCalculator(calculator: IDamageCalculator<TEnemy, THero>): void;
/** /**
* 使 * 使
*/ */
getCalculator(): IDamageCalculator<TAttr, THero> | null; getCalculator(): IDamageCalculator<TEnemy, THero> | null;
/** /**
* *
@ -437,13 +450,13 @@ export interface IDamageSystem<TAttr, THero> extends IDamageContext<
* *
* @param enemy * @param enemy
*/ */
markDirty(enemy: IEnemyView<TAttr>): void; markDirty(enemy: IEnemyView<TEnemy>): void;
/** /**
* *
* @param enemy * @param enemy
*/ */
deleteEnemy(enemy: IEnemyView<TAttr>): void; deleteEnemy(enemy: IEnemyView<TEnemy>): void;
/** /**
* *
@ -454,20 +467,22 @@ export interface IDamageSystem<TAttr, THero> extends IDamageContext<
* *
* @param modify * @param modify
*/ */
with(hero: IHeroAttribute<THero>): IDamageContext<TAttr, THero>; with(hero: IHeroAttribute<THero>): IDamageContext<TEnemy, THero>;
} }
//#endregion //#endregion
//#region 上下文 //#region 上下文
export interface IEnemyContext<TAttr, THero> { export interface IEnemyContext<TEnemy, THero> {
/** 怪物上下文宽度 */ /** 怪物上下文宽度 */
readonly width: number; readonly width: number;
/** 怪物上下文高度 */ /** 怪物上下文高度 */
readonly height: number; readonly height: number;
/** 此上下文使用的索引对象 */ /** 此上下文使用的索引对象 */
readonly indexer: ILocationHelper; readonly indexer: ILocationHelper;
/** 当前怪物上下文绑定的全局状态对象 */
readonly dataState: IStateBase<TEnemy, THero>;
/** /**
* *
@ -480,13 +495,13 @@ export interface IEnemyContext<TAttr, THero> {
* *
* @param converter * @param converter
*/ */
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void; registerAuraConverter(converter: IAuraConverter<TEnemy, THero>): void;
/** /**
* *
* @param converter * @param converter
*/ */
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void; unregisterAuraConverter(converter: IAuraConverter<TEnemy, THero>): void;
/** /**
* *
@ -494,7 +509,7 @@ export interface IEnemyContext<TAttr, THero> {
* @param enabled * @param enabled
*/ */
setAuraConverterEnabled( setAuraConverterEnabled(
converter: IAuraConverter<TAttr, THero>, converter: IAuraConverter<TEnemy, THero>,
enabled: boolean enabled: boolean
): void; ): void;
@ -503,7 +518,7 @@ export interface IEnemyContext<TAttr, THero> {
* @param effect * @param effect
*/ */
registerSpecialQueryEffect( registerSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero> effect: IEnemySpecialQueryEffect<TEnemy, THero>
): void; ): void;
/** /**
@ -511,7 +526,7 @@ export interface IEnemyContext<TAttr, THero> {
* @param effect * @param effect
*/ */
unregisterSpecialQueryEffect( unregisterSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero> effect: IEnemySpecialQueryEffect<TEnemy, THero>
): void; ): void;
/** /**
@ -521,7 +536,7 @@ export interface IEnemyContext<TAttr, THero> {
*/ */
registerCommonQueryEffect( registerCommonQueryEffect(
code: number, code: number,
effect: IEnemyCommonQueryEffect<TAttr, THero> effect: IEnemyCommonQueryEffect<TEnemy, THero>
): void; ): void;
/** /**
@ -531,20 +546,20 @@ export interface IEnemyContext<TAttr, THero> {
*/ */
unregisterCommonQueryEffect( unregisterCommonQueryEffect(
code: number, code: number,
effect: IEnemyCommonQueryEffect<TAttr, THero> effect: IEnemyCommonQueryEffect<TEnemy, THero>
): void; ): void;
/** /**
* *
* @param effect * @param effect
*/ */
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void; registerFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void;
/** /**
* *
* @param effect * @param effect
*/ */
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void; unregisterFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void;
/** /**
* *
@ -561,41 +576,41 @@ export interface IEnemyContext<TAttr, THero> {
* *
* @param enemy * @param enemy
*/ */
getEnemyLocator(enemy: IEnemy<TAttr>): Readonly<ITileLocator> | null; getEnemyLocator(enemy: IEnemy<TEnemy>): Readonly<ITileLocator> | null;
/** /**
* *
* @param view * @param view
*/ */
getEnemyLocatorByView( getEnemyLocatorByView(
view: IEnemyView<TAttr> view: IEnemyView<TEnemy>
): Readonly<ITileLocator> | null; ): Readonly<ITileLocator> | null;
/** /**
* *
* @param locator * @param locator
*/ */
getEnemyByLocator(locator: ITileLocator): IEnemyView<TAttr> | null; getEnemyByLocator(locator: ITileLocator): IEnemyView<TEnemy> | null;
/** /**
* *
* @param x * @param x
* @param y * @param y
*/ */
getEnemyByLoc(x: number, y: number): IEnemyView<TAttr> | null; getEnemyByLoc(x: number, y: number): IEnemyView<TEnemy> | null;
/** /**
* *
* @param enemy * @param enemy
*/ */
getViewByComputed(enemy: IReadonlyEnemy<TAttr>): IEnemyView<TAttr> | null; getViewByComputed(enemy: IReadonlyEnemy<TEnemy>): IEnemyView<TEnemy> | null;
/** /**
* *
* @param locator * @param locator
* @param enemy * @param enemy
*/ */
setEnemyAt(locator: ITileLocator, enemy: IEnemy<TAttr>): void; setEnemyAt(locator: ITileLocator, enemy: IEnemy<TEnemy>): void;
/** /**
* *
@ -611,46 +626,46 @@ export interface IEnemyContext<TAttr, THero> {
scanRange<T>( scanRange<T>(
range: IRange<T>, range: IRange<T>,
param: T param: T
): Iterable<[ITileLocator, IEnemyView<TAttr>]>; ): Iterable<[ITileLocator, IEnemyView<TEnemy>]>;
/** /**
* *
*/ */
iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TAttr>]>; iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TEnemy>]>;
/** /**
* *
* @param aura * @param aura
*/ */
addAura(aura: IAuraView<TAttr>): void; addAura(aura: IAuraView<TEnemy>): void;
/** /**
* *
* @param aura * @param aura
*/ */
deleteAura(aura: IAuraView<TAttr>): void; deleteAura(aura: IAuraView<TEnemy>): void;
/** /**
* *
* @param damage * @param damage
*/ */
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void; attachMapDamage(damage: IMapDamage<TEnemy, THero> | null): void;
/** /**
* *
*/ */
getMapDamage(): IMapDamage<TAttr, THero> | null; getMapDamage(): IMapDamage<TEnemy, THero> | null;
/** /**
* *
* @param system * @param system
*/ */
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void; attachDamageSystem(system: IDamageSystem<TEnemy, unknown> | null): void;
/** /**
* *
*/ */
getDamageSystem(): IDamageSystem<TAttr, THero> | null; getDamageSystem(): IDamageSystem<TEnemy, THero> | null;
/** /**
* *
@ -666,13 +681,13 @@ export interface IEnemyContext<TAttr, THero> {
* *
* @param view * @param view
*/ */
markDirty(view: IEnemyView<TAttr>): void; markDirty(view: IEnemyView<TEnemy>): void;
/** /**
* *
* @param view * @param view
*/ */
requestRefresh(view: IEnemyView<TAttr>): void; requestRefresh(view: IEnemyView<TEnemy>): void;
/** /**
* *

View File

@ -1,6 +1,6 @@
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { SaveCompression } from '../common';
import { IEnemy, IEnemySaveState, IReadonlyEnemy, ISpecial } from './types'; import { IEnemy, IEnemySaveState, IReadonlyEnemy, ISpecial } from './types';
import { SaveCompression } from '../common/types';
export class Enemy<TAttr> implements IEnemy<TAttr> { export class Enemy<TAttr> implements IEnemy<TAttr> {
/** 怪物身上的特殊属性列表 */ /** 怪物身上的特殊属性列表 */

View File

@ -1,5 +1,5 @@
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { SaveCompression } from '../common/types'; import { SaveCompression } from '../common';
import { ISpecial, SpecialCreation } from './types'; import { ISpecial, SpecialCreation } from './types';
// TODO: 颜色参数 // TODO: 颜色参数

View File

@ -1,4 +1,4 @@
import { ISaveableContent } from '../common/types'; import { ISaveableContent } from '../common';
//#region 怪物基础 //#region 怪物基础