refactor: 怪物系统与勇士属性系统联动

This commit is contained in:
AncTe committed 2026-04-17 15:34:44 +08:00
1 parent 92ff489811
commit eaa725553b
23 files changed
+473 -381

No files matched your search

+139 -102
View File
@@ -8,17 +8,20 @@ import {
IEnemyCommonQueryEffect,
IEnemyContext,
IEnemyFinalEffect,
IEnemyHandler,
IEnemySpecialModifier,
IEnemySpecialQueryEffect,
IEnemyView,
IMapDamage,
IReadonlyEnemy,
IReadonlyEnemyHandler,
ISpecial
} from './types';
import { EnemyView } from './enemy';
import { MapLocIndexer } from './utils';
import { IReadonlyHeroAttribute } from '../hero';
export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
private readonly enemyViewMap: Map<number, EnemyView<TAttr>> = new Map();
private readonly enemyMap: Map<number, IEnemy<TAttr>> = new Map();
private readonly locatorViewMap: Map<IEnemyView<TAttr>, number> = new Map();
@@ -28,23 +31,26 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
EnemyView<TAttr>
> = new Map();
private readonly auraConverter: Set<IAuraConverter<TAttr>> = new Set();
private readonly converterStatus: Map<IAuraConverter<TAttr>, boolean> =
new Map();
private readonly auraConverter: Set<IAuraConverter<TAttr, THero>> =
new Set();
private readonly converterStatus: Map<
IAuraConverter<TAttr, THero>,
boolean
> = new Map();
private readonly convertedAura: Map<ISpecial<any>, IAuraView<TAttr>> =
new Map();
private readonly commonQueryMap: Map<
number,
IEnemyCommonQueryEffect<TAttr>[]
IEnemyCommonQueryEffect<TAttr, THero>[]
> = new Map();
private readonly specialQueryEffects: Map<
number,
IEnemySpecialQueryEffect<TAttr>[]
IEnemySpecialQueryEffect<TAttr, THero>[]
> = new Map();
private readonly finalEffects: IEnemyFinalEffect<TAttr>[] = [];
private readonly finalEffects: IEnemyFinalEffect<TAttr, THero>[] = [];
private readonly globalAuraList: Set<IAuraView<TAttr>> = new Set();
private readonly sortedAura: Map<number, Set<IAuraView<TAttr>>> = new Map();
@@ -52,10 +58,17 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
private readonly requestedCommonContext: Set<IEnemyView<TAttr>> = new Set();
private readonly dirtyEnemy: Set<IEnemyView<TAttr>> = new Set();
private mapDamage: IMapDamage<TAttr> | null = null;
private damageSystem: IDamageSystem<TAttr, unknown> | null = null;
/** 当前绑定的勇士属性对象 */
private bindedHero: IReadonlyHeroAttribute<THero> | null = null;
/** 地图伤害对象 */
private mapDamage: IMapDamage<TAttr, THero> | null = null;
/** 伤害系统对象 */
private damageSystem: IDamageSystem<TAttr, THero> | null = null;
/** 索引工具 */
readonly indexer: MapLocIndexer = new MapLocIndexer();
/** 当前是否需要全量刷新 */
private needUpdate: boolean = true;
built: boolean = false;
@@ -70,20 +83,20 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
this.needUpdate = true;
}
registerAuraConverter(converter: IAuraConverter<TAttr>): void {
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void {
this.auraConverter.add(converter);
this.converterStatus.set(converter, true);
this.needUpdate = true;
}
unregisterAuraConverter(converter: IAuraConverter<TAttr>): void {
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void {
this.auraConverter.delete(converter);
this.converterStatus.delete(converter);
this.needUpdate = true;
}
setAuraConverterEnabled(
converter: IAuraConverter<TAttr>,
converter: IAuraConverter<TAttr, THero>,
enabled: boolean
): void {
if (!this.auraConverter.has(converter)) return;
@@ -93,7 +106,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
registerCommonQueryEffect(
code: number,
effect: IEnemyCommonQueryEffect<TAttr>
effect: IEnemyCommonQueryEffect<TAttr, THero>
): void {
const array = this.commonQueryMap.getOrInsert(code, []);
array.push(effect);
@@ -103,7 +116,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
unregisterCommonQueryEffect(
code: number,
effect: IEnemyCommonQueryEffect<TAttr>
effect: IEnemyCommonQueryEffect<TAttr, THero>
): void {
const array = this.commonQueryMap.get(code);
if (!array) return;
@@ -113,14 +126,16 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
this.needUpdate = true;
}
registerSpecialQueryEffect(effect: IEnemySpecialQueryEffect<TAttr>): void {
registerSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero>
): void {
const list = this.specialQueryEffects.getOrInsert(effect.priority, []);
list.push(effect);
this.needUpdate = true;
}
unregisterSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr>
effect: IEnemySpecialQueryEffect<TAttr, THero>
): void {
const list = this.specialQueryEffects.get(effect.priority);
if (!list) return;
@@ -134,13 +149,13 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
this.needUpdate = true;
}
registerFinalEffect(effect: IEnemyFinalEffect<TAttr>): void {
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void {
this.finalEffects.push(effect);
this.finalEffects.sort((a, b) => b.priority - a.priority);
this.needUpdate = true;
}
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr>): void {
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void {
const index = this.finalEffects.indexOf(effect);
if (index !== -1) {
this.finalEffects.splice(index, 1);
@@ -148,6 +163,29 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
this.needUpdate = true;
}
bindHero(hero: IReadonlyHeroAttribute<THero> | null): void {
this.bindedHero = hero;
this.needUpdate = true;
this.damageSystem?.bindHeroStatus(hero);
this.mapDamage?.refreshAll();
}
getBindedHero(): IReadonlyHeroAttribute<THero> | null {
return this.bindedHero;
}
/**
* 创建可修改信息对象
* @param enemy 怪物对象
* @param locator 怪物位置
*/
private createHandler(
enemy: IEnemy<TAttr>,
locator: ITileLocator
): IEnemyHandler<TAttr, THero> {
return { enemy, locator, hero: this.bindedHero! };
}
getEnemyLocator(enemy: IEnemy<TAttr>): Readonly<ITileLocator> | null {
const index = this.locatorEnemyMap.get(enemy);
if (index === undefined) return null;
@@ -177,7 +215,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 删除指定索引位置的怪物以及与之关联的所有运行时状态。
* 删除指定索引位置的怪物以及与之关联的所有运行时状态
* @param index 地图索引
*/
private deleteEnemyAt(index: number) {
@@ -231,14 +269,14 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 在指定范围内筛选出当前上下文中的怪物视图。
* 在指定范围内筛选出当前上下文中的怪物视图
* @param range 范围对象
* @param param 范围参数
*/
private *internalScanRange<T>(
range: IRange<T>,
param: T
): Iterable<EnemyView<TAttr>> {
): Iterable<[ITileLocator, EnemyView<TAttr>]> {
range.bindHost(this);
const keys = new Set(this.enemyViewMap.keys());
const matched = range.autoDetect(keys, param);
@@ -246,12 +284,16 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const index of matched) {
const view = viewMap.get(index);
if (view) {
yield view;
const locator = this.indexer.indexToLocator(index);
yield [locator, view];
}
}
}
scanRange<T>(range: IRange<T>, param: T): Iterable<IEnemyView<TAttr>> {
scanRange<T>(
range: IRange<T>,
param: T
): Iterable<[ITileLocator, IEnemyView<TAttr>]> {
return this.internalScanRange(range, param);
}
@@ -272,44 +314,42 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
this.needUpdate = true;
}
attachMapDamage(damage: IMapDamage<TAttr> | null): void {
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void {
this.mapDamage = damage;
if (damage) {
damage.refreshAll();
}
}
getMapDamage(): IMapDamage<TAttr> | null {
getMapDamage(): IMapDamage<TAttr, THero> | null {
return this.mapDamage;
}
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void {
this.damageSystem = system;
if (system) {
system.markAllDirty();
system.bindHeroStatus(this.bindedHero);
}
}
getDamageSystem<THero>(): IDamageSystem<TAttr, THero> | null {
return this.damageSystem as IDamageSystem<TAttr, THero> | null;
getDamageSystem(): IDamageSystem<TAttr, THero> | null {
return this.damageSystem;
}
/**
* 将怪物身上的特殊属性尝试转换为光环视图。
* 将怪物身上的特殊属性尝试转换为光环视图
* @param special 特殊属性
* @param enemy 怪物对象
* @param locator 怪物位置
*/
private convertSpecial(
special: ISpecial<any>,
enemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator
handler: IReadonlyEnemyHandler<TAttr, THero>
): IEnemyAuraView<TAttr, any, any> | null {
let matched: IAuraConverter<TAttr> | null = null;
let matched: IAuraConverter<TAttr, THero> | null = null;
for (const converter of this.auraConverter) {
if (!this.converterStatus.get(converter)) continue;
if (converter.shouldConvert(special, enemy, locator)) {
if (converter.shouldConvert(special, handler)) {
if (matched) {
logger.warn(97, special.code.toString());
return null;
@@ -319,11 +359,11 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
if (!matched) return null;
return matched.convert(special, enemy, locator, this);
return matched.convert(special, handler, this);
}
/**
* 将光环按优先级插入到有序表中。
* 将光环按优先级插入到有序表中
* @param aura 光环视图
*/
private insertIntoSortedAura(aura: IAuraView<TAttr>): void {
@@ -335,7 +375,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 从优先级表中移除一个光环。
* 从优先级表中移除一个光环
* @param aura 光环视图
*/
private removeFromSortedAura(aura: IAuraView<TAttr>): void {
@@ -349,7 +389,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 执行特殊属性修饰器,并返回因此受到影响的光环集合。
* 执行特殊属性修饰器,并返回因此受到影响的光环集合
* @param modifier 特殊属性修饰器
* @param enemy 目标怪物
* @param locator 怪物位置
@@ -357,14 +397,13 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
*/
private processSpecialModifier(
modifier: IEnemySpecialModifier<TAttr>,
enemy: IEnemy<TAttr>,
locator: ITileLocator,
handler: IEnemyHandler<TAttr, THero>,
currentPriority: number
): Set<IAuraView<TAttr>> {
const toAdd = modifier.add(enemy, locator);
const toDelete = modifier.delete(enemy, locator);
const enemy = handler.enemy;
const affectedAuras = new Set<IAuraView<TAttr>>();
const toAdd = modifier.add(handler);
const toDelete = modifier.delete(handler);
if (toAdd.length > 0 && toDelete.length > 0) {
logger.warn(100);
@@ -372,9 +411,9 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
for (const adding of toAdd) {
const aura = this.convertSpecial(adding, enemy, locator);
const aura = this.convertSpecial(adding, handler);
if (aura) {
// 新生成的光环只能影响之后的阶段,不能反过来影响当前优先级链。
// 新生成的光环只能影响之后的阶段,不能反过来影响当前优先级链
if (import.meta.env.DEV && aura.priority > currentPriority) {
logger.warn(
99,
@@ -410,7 +449,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
for (const special of enemy.iterateSpecials()) {
const success = modifier.modify(enemy, special, locator);
const success = modifier.modify(handler, special);
if (!success) continue;
const aura = this.convertedAura.get(special);
if (!aura) continue;
@@ -429,12 +468,12 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 执行单个特殊查询效果。
* 执行单个特殊查询效果
* @param effect 特殊查询效果
* @param currentPriority 当前处理的优先级
*/
private processSpecialQuery(
effect: IEnemySpecialQueryEffect<TAttr>,
effect: IEnemySpecialQueryEffect<TAttr, THero>,
currentPriority: number
): void {
const modifier = effect.for(this);
@@ -442,13 +481,13 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const [index, view] of this.enemyViewMap) {
const locator = this.indexer.indexToLocator(index);
const enemy = view.getComputingEnemy();
const handler = this.createHandler(enemy, locator);
if (!modifier.shouldQuery(enemy, locator)) continue;
if (!modifier.shouldQuery(handler)) continue;
const affectedAuras = this.processSpecialModifier(
modifier,
enemy,
locator,
handler,
currentPriority
);
@@ -461,7 +500,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 执行光环带来的特殊属性修饰效果。
* 执行光环带来的特殊属性修饰效果
* @param aura 光环视图
* @param currentPriority 当前处理的优先级
*/
@@ -470,30 +509,22 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
currentPriority: number
): void {
const param = aura.getRangeParam();
const iter = this.internalScanRange(aura.range, param);
for (const enemyView of this.internalScanRange(aura.range, param)) {
const locator = this.getEnemyLocatorByView(enemyView);
if (!locator) continue;
for (const [locator, enemyView] of iter) {
const enemy = enemyView.getComputingEnemy();
const base = enemyView.getBaseEnemy();
const modifier = aura.applySpecial(enemy, base, locator);
const handler = this.createHandler(enemy, locator);
const modifier = aura.applySpecial(handler, base);
if (!modifier) continue;
this.processSpecialModifier(
modifier,
enemy,
locator,
currentPriority
);
this.processSpecialModifier(modifier, handler, currentPriority);
this.needTotallyRefresh.add(enemyView);
}
}
/**
* 构建所有由特殊属性衍生出的光环与特殊查询结果。
* 构建所有由特殊属性衍生出的光环与特殊查询结果
*/
private buildupSpecials(): void {
for (const aura of this.globalAuraList) {
@@ -503,9 +534,10 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const [index, view] of this.enemyViewMap) {
const enemy = view.getComputingEnemy();
const locator = this.indexer.indexToLocator(index);
const handler = this.createHandler(enemy, locator);
for (const special of enemy.iterateSpecials()) {
const aura = this.convertSpecial(special, enemy, locator);
const aura = this.convertSpecial(special, handler);
if (!aura) continue;
this.convertedAura.set(special, aura);
this.insertIntoSortedAura(aura);
@@ -554,7 +586,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 按优先级执行所有基础属性光环效果。
* 按优先级执行所有基础属性光环效果
*/
private buildupBase(): void {
const priorities = [...this.sortedAura.keys()].sort((a, b) => b - a);
@@ -563,23 +595,25 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
if (!auras) continue;
for (const aura of auras) {
const param = aura.getRangeParam();
for (const view of this.internalScanRange(aura.range, param)) {
const iter = this.internalScanRange(aura.range, param);
for (const [locator, view] of iter) {
const enemy = view.getComputingEnemy();
const base = view.getBaseEnemy();
const locator = this.getEnemyLocatorByView(view)!;
aura.apply(enemy, base, locator);
const handler = this.createHandler(enemy, locator);
aura.apply(handler, base);
}
}
}
}
/**
* 执行常规查询效果,并记录哪些怪物查询了上下文。
* 执行常规查询效果,并记录哪些怪物查询了上下文
*/
private buildupQuery(): void {
for (const [index, view] of this.enemyViewMap) {
const enemy = view.getComputingEnemy();
const locator = this.indexer.indexToLocator(index);
const handler = this.createHandler(enemy, locator);
let queried = false;
const query = () => {
queried = true;
@@ -589,7 +623,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
const effects = this.commonQueryMap.get(special.code);
if (!effects) continue;
for (const effect of effects) {
effect.apply(enemy, special, query, locator);
effect.apply(handler, special, query);
}
}
if (queried) {
@@ -599,20 +633,25 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 执行最终效果阶段。
* 执行最终效果阶段
*/
private buildupFinal(): void {
for (const [index, view] of this.enemyViewMap) {
const enemy = view.getComputingEnemy();
const locator = this.indexer.indexToLocator(index);
const handler = this.createHandler(enemy, locator);
for (const effect of this.finalEffects) {
effect.apply(enemy, locator);
effect.apply(handler);
}
}
}
buildup(): void {
if (!this.needUpdate) return;
if (!this.bindedHero) {
logger.warn(110);
return;
}
this.needUpdate = false;
this.sortedAura.clear();
this.convertedAura.clear();
@@ -650,18 +689,18 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 在局部刷新期间执行特殊属性修饰器,但不重建光环拓扑。
* 在局部刷新期间执行特殊属性修饰器,但不重建光环拓扑
* @param modifier 特殊属性修饰器
* @param enemy 目标怪物
* @param locator 怪物位置
*/
private refreshSpecialModifier(
modifier: IEnemySpecialModifier<TAttr>,
enemy: IEnemy<TAttr>,
locator: ITileLocator
handler: IEnemyHandler<TAttr, THero>
): void {
const toAdd = modifier.add(enemy, locator);
const toDelete = modifier.delete(enemy, locator);
const enemy = handler.enemy;
const toAdd = modifier.add(handler);
const toDelete = modifier.delete(handler);
if (toAdd.length > 0 && toDelete.length > 0) {
logger.warn(100);
@@ -671,7 +710,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const adding of toAdd) {
enemy.addSpecial(adding);
if (import.meta.env.DEV) {
const aura = this.convertSpecial(adding, enemy, locator);
const aura = this.convertSpecial(adding, handler);
if (aura) {
logger.warn(101, adding.code.toString());
}
@@ -681,7 +720,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const deleting of toDelete) {
enemy.deleteSpecial(deleting);
if (import.meta.env.DEV) {
const aura = this.convertSpecial(deleting, enemy, locator);
const aura = this.convertSpecial(deleting, handler);
if (aura) {
logger.warn(101, deleting.code.toString());
}
@@ -689,7 +728,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
for (const special of enemy.iterateSpecials()) {
const success = modifier.modify(enemy, special, locator);
const success = modifier.modify(handler, special);
if (import.meta.env.DEV && success) {
const aura = this.convertedAura.get(special);
if (aura) {
@@ -700,7 +739,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
/**
* 刷新单个怪物视图的计算结果。
* 刷新单个怪物视图的计算结果
* @param view 怪物视图
*/
private refreshEnemy(view: EnemyView<TAttr>): void {
@@ -710,6 +749,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
view.reset();
const enemy = view.getComputingEnemy();
const base = view.getBaseEnemy();
const handler = this.createHandler(enemy, locator);
const specialPriorities = new Set<number>();
for (const priority of this.sortedAura.keys()) {
@@ -725,30 +765,28 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const priority of orderedSpecialPriorities) {
const auras = this.sortedAura.get(priority);
const effects = this.specialQueryEffects.get(priority);
if (auras) {
for (const aura of auras) {
if (!aura.couldApplySpecial) continue;
const param = aura.getRangeParam();
aura.range.bindHost(this);
// 局部刷新只重新判断“这个怪物是否被该光环命中”。
const inRange = aura.range.inRange(
locator.x,
locator.y,
param
);
if (!inRange) continue;
const modifier = aura.applySpecial(enemy, base, locator);
// 局部刷新只重新判断“这个怪物是否被该光环命中”
if (!aura.range.inRange(locator.x, locator.y, param)) {
continue;
}
const modifier = aura.applySpecial(handler, base);
if (!modifier) continue;
this.refreshSpecialModifier(modifier, enemy, locator);
this.refreshSpecialModifier(modifier, handler);
}
}
const effects = this.specialQueryEffects.get(priority);
if (effects) {
for (const effect of effects) {
const modifier = effect.for(this);
if (!modifier.shouldQuery(enemy, locator)) continue;
this.refreshSpecialModifier(modifier, enemy, locator);
if (!modifier.shouldQuery(handler)) continue;
this.refreshSpecialModifier(modifier, handler);
}
}
}
@@ -762,10 +800,8 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
for (const aura of auras) {
const param = aura.getRangeParam();
aura.range.bindHost(this);
if (!aura.range.inRange(locator.x, locator.y, param)) {
continue;
}
aura.apply(enemy, base, locator);
if (!aura.range.inRange(locator.x, locator.y, param)) continue;
aura.apply(handler, base);
}
}
@@ -779,7 +815,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
const effects = this.commonQueryMap.get(special.code);
if (!effects) continue;
for (const effect of effects) {
effect.apply(enemy, special, query, locator);
effect.apply(handler, special, query);
}
}
if (queried) {
@@ -787,7 +823,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
}
for (const effect of this.finalEffects) {
effect.apply(enemy, locator);
effect.apply(handler);
}
this.dirtyEnemy.delete(view);
@@ -846,5 +882,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
this.commonQueryMap.clear();
this.specialQueryEffects.clear();
this.finalEffects.length = 0;
this.bindedHero = null;
}
}
+48 -52
View File
@@ -1,4 +1,4 @@
import { logger } from '@motajs/common';
import { ITileLocator, logger } from '@motajs/common';
import {
CriticalableHeroStatus,
IDamageCalculator,
@@ -6,9 +6,11 @@ import {
IEnemyContext,
IEnemyCritical,
IEnemyDamageInfo,
IReadonlyEnemyHandler,
IEnemyView,
IReadonlyEnemy
} from './types';
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
interface ICriticalSearchResult {
/** 此临界点的属性值 */
@@ -21,12 +23,12 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
/** 当前正在使用的计算器 */
private calculator: IDamageCalculator<TAttr, THero> | null = null;
/** 当前勇士属性 */
private heroStatus: Readonly<THero> | null = null;
private heroStatus: IReadonlyHeroAttribute<THero> | null = null;
/** 怪物伤害缓存 */
private readonly cache: Map<IEnemyView<TAttr>, IEnemyDamageInfo> =
new Map();
constructor(readonly context: IEnemyContext<TAttr>) {}
constructor(readonly context: IEnemyContext<TAttr, THero>) {}
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void {
this.calculator = calculator;
@@ -37,35 +39,23 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
return this.calculator;
}
bindHeroStatus(hero: Readonly<THero>): void {
bindHeroStatus(hero: IReadonlyHeroAttribute<THero>): void {
this.heroStatus = hero;
this.markAllDirty();
}
/**
* 深拷贝勇士属性
* 创建只读信息对象
* @param enemy 怪物对象
* @param locator 怪物位置
* @param hero 勇士属性对象
*/
private cloneHeroStatus(): THero | null {
if (!this.heroStatus) return null;
else return structuredClone(this.heroStatus);
}
/**
* 在修改勇士属性的情况下计算怪物伤害
* @param enemy 怪物属性
* @param attribute 修改的属性键名
* @param value 修改为的属性值
* @returns
*/
private calculateDamageWithModified(
private createReadonlyHandler(
enemy: IReadonlyEnemy<TAttr>,
attribute: CriticalableHeroStatus<THero>,
value: number
): IEnemyDamageInfo {
const hero = this.cloneHeroStatus()!;
// @ts-expect-error 之后会进行修复
hero[attribute] = value;
return this.calculator!.calculate(hero, enemy);
locator: ITileLocator,
hero: IReadonlyHeroAttribute<THero>
): IReadonlyEnemyHandler<TAttr, THero> {
return { enemy, locator, hero };
}
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null {
@@ -77,15 +67,20 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
logger.warn(106);
return null;
}
const hero = this.cloneHeroStatus()!;
const hero = this.heroStatus;
const locator = this.context.getEnemyLocatorByView(enemy);
if (!hero || !locator) return null;
const cached = this.cache.get(enemy);
if (cached) {
return cached;
}
const info = this.calculator.calculate(hero, enemy.getComputedEnemy());
const computed = enemy.getComputedEnemy();
const handler = this.createReadonlyHandler(computed, locator, hero);
const info = this.calculator.calculate(handler);
this.cache.set(enemy, info);
return info;
}
@@ -104,7 +99,7 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
*calculateCritical(
view: IEnemyView<TAttr>,
attribute: CriticalableHeroStatus<THero>,
precision: number
precision: number = 12
): Generator<IEnemyCritical, void, void> {
if (!this.heroStatus) {
logger.warn(107);
@@ -115,15 +110,19 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
return;
}
const currentInfo = this.getDamageInfo(view);
if (!currentInfo) return;
const locator = this.context.getEnemyLocatorByView(view);
if (!locator) return;
const enemy = view.getComputedEnemy();
const hero = this.cloneHeroStatus()!;
const currentValue = hero[attribute] as number;
const hero = this.heroStatus.getModifiableClone();
const handler = this.createReadonlyHandler(enemy, locator, hero);
const currentInfo = this.calculator.calculate(handler);
if (!currentInfo) return;
const currentValue = hero.getBaseAttribute(attribute) as number;
const upperLimit = Math.floor(
this.calculator.getCriticalLimit(hero, enemy, attribute)
this.calculator.getCriticalLimit(handler, attribute)
);
if (currentValue >= upperLimit) return;
@@ -134,7 +133,8 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
while (baseValue < upperLimit) {
const next = this.findNextCritical(
enemy,
handler,
hero,
attribute,
baseValue,
upperLimit,
@@ -159,7 +159,8 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
/**
* 计算下一个临界点
* @param enemy 怪物对象
* @param handler 信息对象,其中的 `hero` 成员与 `hero` 参数同引用
* @param hero 可修改勇士属性对象,与 `handler` 中的 `hero` 成员同引用
* @param attribute 勇士属性名
* @param currentValue 当前勇士属性值
* @param upperLimit 二分上界
@@ -167,7 +168,8 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
* @param maxIterations 最大迭代数量
*/
private findNextCritical(
enemy: IReadonlyEnemy<TAttr>,
handler: IReadonlyEnemyHandler<TAttr, THero>,
hero: IHeroAttribute<THero>,
attribute: CriticalableHeroStatus<THero>,
currentValue: number,
upperLimit: number,
@@ -176,36 +178,30 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
): ICriticalSearchResult | null {
let left = currentValue;
let right = upperLimit;
let rightInfo = this.calculateDamageWithModified(
enemy,
attribute,
right
);
if (rightInfo.damage >= referenceDamage) return null;
hero.setBaseAttribute(attribute, right as THero[typeof attribute]);
let targetInfo = this.calculator!.calculate(handler);
if (targetInfo.damage >= referenceDamage) return null;
let iter = 0;
while (iter < maxIterations) {
while (iter++ < maxIterations) {
const middle = Math.floor((left + right) / 2);
const middleInfo = this.calculateDamageWithModified(
enemy,
attribute,
middle
);
hero.setBaseAttribute(attribute, middle as THero[typeof attribute]);
const middleInfo = this.calculator!.calculate(handler);
if (middleInfo.damage < referenceDamage) {
right = middle;
rightInfo = middleInfo;
} else {
left = middle;
targetInfo = middleInfo;
}
if (right - left <= 1) break;
iter++;
}
return {
value: right,
info: rightInfo
info: targetInfo
};
}
}
+1 -1
View File
@@ -93,7 +93,7 @@ export class EnemyView<TAttr> implements IEnemyView<TAttr> {
constructor(
readonly baseEnemy: IEnemy<TAttr>,
readonly context: IEnemyContext<TAttr>
readonly context: IEnemyContext<TAttr, unknown>
) {
this.computedEnemy = baseEnemy.clone();
}
+34 -10
View File
@@ -1,6 +1,7 @@
import { logger, ITileLocator } from '@motajs/common';
import {
IEnemyContext,
IReadonlyEnemyHandler,
IEnemyView,
IMapDamage,
IMapDamageConverter,
@@ -33,9 +34,9 @@ interface IDamageStore<TAttr> {
readonly index: number;
}
export class MapDamage<TAttr> implements IMapDamage<TAttr> {
export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
/** 当前使用的地图伤害转换器 */
private converter: IMapDamageConverter<TAttr> | null = null;
private converter: IMapDamageConverter<TAttr, THero> | null = null;
/** 当前使用的地图伤害合并器 */
private reducer: IMapDamageReducer | null = null;
@@ -62,15 +63,33 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
/** 坐标索引对象 */
private readonly indexer: IMapLocIndexer;
constructor(readonly context: IEnemyContext<TAttr>) {
constructor(readonly context: IEnemyContext<TAttr, THero>) {
this.indexer = context.indexer;
}
useConverter(converter: IMapDamageConverter<TAttr>): void {
useConverter(converter: IMapDamageConverter<TAttr, THero>): void {
this.converter = converter;
this.refreshAll();
}
/**
* 创建只读信息对象
* @param view 怪物视图
* @param locator 怪物位置
*/
private createReadonlyHandler(
view: IEnemyView<TAttr>,
locator: ITileLocator
): IReadonlyEnemyHandler<TAttr, THero> | null {
const hero = this.context.getBindedHero();
if (!hero) return null;
return {
enemy: view.getComputedEnemy(),
locator,
hero
};
}
useReducer(reducer: IMapDamageReducer): void {
this.reducer = reducer;
this.reducedCache.clear();
@@ -174,6 +193,7 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
const sourced = this.sourcedDamage.get(index);
if (sourceless) {
if (sourced) {
// 大集合 union 小集合会更快,一般有来源伤害更多,所以 source union sourceless
return sourced.damages.union(sourceless.damages);
} else {
return sourceless.damages;
@@ -237,9 +257,11 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
locator: ITileLocator
) {
this.removeEnemyAffecting(view);
const enemy = view.getComputedEnemy();
const views = this.converter!.convert(enemy, locator, this.context);
const set = new Set(views);
if (!this.converter) return;
const handler = this.createReadonlyHandler(view, locator);
if (!handler) return;
const views = this.converter.convert(handler, this.context);
const set = new Set<IMapDamageView<any>>(views);
if (set.size === 0) return;
this.enemyStore.set(view, set);
const collection = new Set<number>();
@@ -275,9 +297,11 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
*/
private refreshEnemy(view: IEnemyView<TAttr>, locator: ITileLocator) {
this.removeEnemyAffecting(view);
const enemy = view.getComputedEnemy();
const views = this.converter!.convert(enemy, locator, this.context);
const set = new Set(views);
if (!this.converter) return;
const handler = this.createReadonlyHandler(view, locator);
if (!handler) return;
const views = this.converter.convert(handler, this.context);
const set = new Set<IMapDamageView<any>>(views);
if (set.size === 0) return;
this.enemyStore.set(view, set);
set.forEach(viewItem => {
+98 -77
View File
@@ -1,4 +1,5 @@
import { IRange, ITileLocator } from '@motajs/common';
import { IReadonlyHeroAttribute } from '../hero';
//#region 怪物基础
@@ -246,13 +247,31 @@ export interface IMapLocIndexer extends IMapLocHelper {
setWidth(width: number): void;
}
export interface IEnemyHandler<TAttr, THero> {
/** 怪物属性信息 */
readonly enemy: IEnemy<TAttr>;
/** 怪物定位符 */
readonly locator: ITileLocator;
/** 勇士属性信息 */
readonly hero: IReadonlyHeroAttribute<THero>;
}
export interface IReadonlyEnemyHandler<TAttr, THero> {
/** 怪物属性信息 */
readonly enemy: IReadonlyEnemy<TAttr>;
/** 怪物定位符 */
readonly locator: ITileLocator;
/** 勇士属性信息 */
readonly hero: IReadonlyHeroAttribute<THero>;
}
//#endregion
//#region 怪物对象
export interface IEnemyView<TAttr> {
/** 怪物视图所属的上下文 */
readonly context: IEnemyContext<TAttr>;
readonly context: IEnemyContext<TAttr, unknown>;
/**
* 重置此怪物视图的状态,将计算后怪物对象恢复至初始状态
@@ -288,31 +307,24 @@ export interface IEnemyView<TAttr> {
export interface IEnemySpecialModifier<TAttr> {
/**
* 获取要添加到指定怪物身上的特殊属性
* @param enemy 怪物对象
* @param locator 怪物定位符
* @param handler 信息对象
*/
add(enemy: IReadonlyEnemy<TAttr>, locator: ITileLocator): ISpecial<any>[];
add(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[];
/**
* 获取制定怪物身上要删除的特殊属性
* @param enemy 怪物对象
* @param locator 怪物定位符
* @param handler 信息对象
*/
delete(
enemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator
): ISpecial<any>[];
delete(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[];
/**
* 修改一个怪物的特殊属性,如果真正进行了修改则返回 true,否则返回 false
* @param enemy 怪物对象
* @param handler 信息对象
* @param special 要修改的怪物特殊属性
* @param locator 怪物定位符
*/
modify(
enemy: IReadonlyEnemy<TAttr>,
special: ISpecial<any>,
locator: ITileLocator
handler: IEnemyHandler<TAttr, unknown>,
special: ISpecial<any>
): boolean;
}
@@ -334,26 +346,22 @@ export interface IAuraView<TAttr, T = any> {
/**
* 对指定怪物对象施加修饰器
* @param enemy 怪物对象
* @param handler 信息对象
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
* @param locator 怪物定位符
*/
apply(
enemy: IEnemy<TAttr>,
baseEnemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator
handler: IEnemyHandler<TAttr, unknown>,
baseEnemy: IReadonlyEnemy<TAttr>
): void;
/**
* 对指定怪物对象添加特殊属性修饰器
* @param enemy 怪物对象
* @param handler 信息对象
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
* @param locator 怪物定位符
*/
applySpecial(
enemy: IReadonlyEnemy<TAttr>,
baseEnemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator
handler: IEnemyHandler<TAttr, unknown>,
baseEnemy: IReadonlyEnemy<TAttr>
): IEnemySpecialModifier<TAttr> | null;
}
@@ -366,14 +374,15 @@ export interface IEnemyAuraView<TAttr, R, S> extends IAuraView<TAttr, R> {
readonly locator: ITileLocator;
}
export interface IAuraConverter<TAttr> {
export interface IAuraConverter<TAttr, THero> {
/**
* 判断一个特殊属性是否应该被当前光环转换器执行转换
* @param special 要转换的特殊属性
* @param handler 信息对象
*/
shouldConvert(
special: ISpecial<any>,
enemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator
handler: IReadonlyEnemyHandler<TAttr, THero>
): boolean;
/**
@@ -381,32 +390,34 @@ export interface IAuraConverter<TAttr> {
*/
convert(
special: ISpecial<any>,
enemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator,
context: IEnemyContext<TAttr>
handler: IReadonlyEnemyHandler<TAttr, THero>,
context: IEnemyContext<TAttr, THero>
): IEnemyAuraView<TAttr, any, any>;
}
export interface IEnemySpecialQueryModifier<
TAttr
TAttr,
THero
> extends IEnemySpecialModifier<TAttr> {
/**
* 判断一个怪物是否应该查询外部状态
*/
shouldQuery(enemy: IReadonlyEnemy<TAttr>, locator: ITileLocator): boolean;
shouldQuery(handler: IReadonlyEnemyHandler<TAttr, THero>): boolean;
}
export interface IEnemySpecialQueryEffect<TAttr> {
export interface IEnemySpecialQueryEffect<TAttr, THero> {
/** 效果优先级,与光环属性共用 */
readonly priority: number;
/**
* 根据传入的怪物上下文,获取对应的怪物特殊属性修饰器
*/
for(ctx: IEnemyContext<TAttr>): IEnemySpecialQueryModifier<TAttr>;
for(
ctx: IEnemyContext<TAttr, THero>
): IEnemySpecialQueryModifier<TAttr, THero>;
}
export interface IEnemyCommonQueryEffect<TAttr> {
export interface IEnemyCommonQueryEffect<TAttr, THero> {
/** 优先级,越高的越先执行 */
readonly priority: number;
@@ -414,21 +425,20 @@ export interface IEnemyCommonQueryEffect<TAttr> {
* 对怪物的某个特殊属性施加常规查询效果
*/
apply(
enemy: IEnemy<TAttr>,
handler: IEnemyHandler<TAttr, THero>,
special: ISpecial<any>,
query: () => IEnemyContext<TAttr>,
locator: ITileLocator
query: () => IEnemyContext<TAttr, THero>
): void;
}
export interface IEnemyFinalEffect<TAttr> {
export interface IEnemyFinalEffect<TAttr, THero> {
/** 效果优先级,越高会越先被执行 */
readonly priority: number;
/**
* 向怪物施加最终修饰效果
*/
apply(enemy: IEnemy<TAttr>, locator: ITileLocator): void;
apply(handler: IEnemyHandler<TAttr, THero>): void;
}
//#endregion
@@ -477,14 +487,13 @@ export interface IMapDamageView<T = any> {
): Readonly<IMapDamageInfo> | null;
}
export interface IMapDamageConverter<TAttr> {
export interface IMapDamageConverter<TAttr, THero> {
/**
* 转换地图伤害视图
*/
convert(
enemy: IReadonlyEnemy<TAttr>,
locator: ITileLocator,
context: IEnemyContext<TAttr>
handler: IReadonlyEnemyHandler<TAttr, THero>,
context: IEnemyContext<TAttr, THero>
): IMapDamageView<any>[];
}
@@ -498,15 +507,15 @@ export interface IMapDamageReducer {
): Readonly<IMapDamageInfo>;
}
export interface IMapDamage<TAttr> {
export interface IMapDamage<TAttr, THero> {
/** 当前绑定的怪物上下文 */
readonly context: IEnemyContext<TAttr>;
readonly context: IEnemyContext<TAttr, THero>;
/**
* 设置地图伤害转换器,并基于当前上下文重建所有地图伤害视图
* @param converter 地图伤害转换器
*/
useConverter(converter: IMapDamageConverter<TAttr>): void;
useConverter(converter: IMapDamageConverter<TAttr, THero>): void;
/**
* 设置地图伤害合并器
@@ -593,36 +602,30 @@ export interface IEnemyCritical {
}
export type CriticalableHeroStatus<THero> = keyof {
[P in keyof THero as THero[P] extends number ? P : never]: unknown;
[P in keyof THero as THero[P] extends number ? P : never]: number;
};
export interface IDamageCalculator<TAttr, THero> {
/**
* 计算战斗伤害信息
* @param hero 勇士信息
* @param enemy 怪物信息
* @param handler 信息对象
*/
calculate(
hero: Readonly<THero>,
enemy: IReadonlyEnemy<TAttr>
): IEnemyDamageInfo;
calculate(handler: IReadonlyEnemyHandler<TAttr, THero>): IEnemyDamageInfo;
/**
* 获取临界计算的上界
* @param hero 勇士信息
* @param enemy 怪物信息
* @param handler 信息对象
* @param attribute 勇士的临界属性
*/
getCriticalLimit(
hero: Readonly<THero>,
enemy: IReadonlyEnemy<TAttr>,
handler: IReadonlyEnemyHandler<TAttr, THero>,
attribute: CriticalableHeroStatus<THero>
): number;
}
export interface IDamageSystem<TAttr, THero> {
/** 伤害系统所属的上下文 */
readonly context: IEnemyContext<TAttr>;
readonly context: IEnemyContext<TAttr, THero>;
/**
* 设置当前伤害计算系统使用的伤害计算器
@@ -639,7 +642,7 @@ export interface IDamageSystem<TAttr, THero> {
* 绑定勇士信息
* @param hero 勇士信息
*/
bindHeroStatus(hero: Readonly<THero>): void;
bindHeroStatus(hero: IReadonlyHeroAttribute<THero> | null): void;
/**
* 获取战斗伤害信息
@@ -668,12 +671,12 @@ export interface IDamageSystem<TAttr, THero> {
* 计算怪物在指定勇士属性下的临界
* @param enemy 怪物视图
* @param attribute 计算临界的目标勇士属性,比如计算攻击临界、自定义属性的临界等等
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可,默认是 12
*/
calculateCritical(
enemy: IEnemyView<TAttr>,
attribute: CriticalableHeroStatus<THero>,
precision: number
precision?: number
): Generator<IEnemyCritical, void, void>;
}
@@ -681,7 +684,7 @@ export interface IDamageSystem<TAttr, THero> {
//#region 上下文
export interface IEnemyContext<TAttr> {
export interface IEnemyContext<TAttr, THero> {
/** 怪物上下文宽度 */
readonly width: number;
/** 怪物上下文高度 */
@@ -700,13 +703,13 @@ export interface IEnemyContext<TAttr> {
* 注册一个光环转换器
* @param converter 光环转换器
*/
registerAuraConverter(converter: IAuraConverter<TAttr>): void;
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void;
/**
* 注销一个光环转换器
* @param converter 光环转换器
*/
unregisterAuraConverter(converter: IAuraConverter<TAttr>): void;
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void;
/**
* 设置光环转换器的启用状态
@@ -714,7 +717,7 @@ export interface IEnemyContext<TAttr> {
* @param enabled 是否启用
*/
setAuraConverterEnabled(
converter: IAuraConverter<TAttr>,
converter: IAuraConverter<TAttr, THero>,
enabled: boolean
): void;
@@ -722,13 +725,17 @@ export interface IEnemyContext<TAttr> {
* 注册一个特殊属性查询效果
* @param effect 特殊属性查询效果
*/
registerSpecialQueryEffect(effect: IEnemySpecialQueryEffect<TAttr>): void;
registerSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero>
): void;
/**
* 注销一个特殊属性查询效果
* @param effect 特殊属性查询效果
*/
unregisterSpecialQueryEffect(effect: IEnemySpecialQueryEffect<TAttr>): void;
unregisterSpecialQueryEffect(
effect: IEnemySpecialQueryEffect<TAttr, THero>
): void;
/**
* 为指定特殊属性代码注册常规查询效果
@@ -737,7 +744,7 @@ export interface IEnemyContext<TAttr> {
*/
registerCommonQueryEffect(
code: number,
effect: IEnemyCommonQueryEffect<TAttr>
effect: IEnemyCommonQueryEffect<TAttr, THero>
): void;
/**
@@ -747,20 +754,31 @@ export interface IEnemyContext<TAttr> {
*/
unregisterCommonQueryEffect(
code: number,
effect: IEnemyCommonQueryEffect<TAttr>
effect: IEnemyCommonQueryEffect<TAttr, THero>
): void;
/**
* 注册一个最终效果
* @param effect 最终效果
*/
registerFinalEffect(effect: IEnemyFinalEffect<TAttr>): void;
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void;
/**
* 注销一个最终效果
* @param effect 最终效果
*/
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr>): void;
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void;
/**
* 绑定勇士对象
* @param hero 勇士属性对象
*/
bindHero(hero: IReadonlyHeroAttribute<THero> | null): void;
/**
* 获取当前绑定的勇士属性对象
*/
getBindedHero(): IReadonlyHeroAttribute<THero> | null;
/**
* 获取指定怪物对象当前所在位置
@@ -813,7 +831,10 @@ export interface IEnemyContext<TAttr> {
* @param range 范围对象
* @param param 范围参数
*/
scanRange<T>(range: IRange<T>, param: T): Iterable<IEnemyView<TAttr>>;
scanRange<T>(
range: IRange<T>,
param: T
): Iterable<[ITileLocator, IEnemyView<TAttr>]>;
/**
* 迭代上下文中的全部怪物
@@ -836,12 +857,12 @@ export interface IEnemyContext<TAttr> {
* 绑定地图伤害管理器
* @param damage 地图伤害管理器
*/
attachMapDamage(damage: IMapDamage<TAttr> | null): void;
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void;
/**
* 获取当前绑定的地图伤害管理器
*/
getMapDamage(): IMapDamage<TAttr> | null;
getMapDamage(): IMapDamage<TAttr, THero> | null;
/**
* 绑定伤害计算系统
@@ -852,7 +873,7 @@ export interface IEnemyContext<TAttr> {
/**
* 获取当前绑定的伤害计算系统
*/
getDamageSystem<THero>(): IDamageSystem<TAttr, THero> | null;
getDamageSystem(): IDamageSystem<TAttr, THero> | null;
/**
* 重建当前上下文中的全部怪物计算结果
@@ -149,4 +149,8 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
}
return cloned;
}
getModifiableClone(): IHeroAttribute<THero> {
return this.clone();
}
}
+1 -1
View File
@@ -32,6 +32,6 @@ export class HeroState<THero> implements IHeroState<THero> {
}
getIsolatedAttribute(): IHeroAttribute<THero> {
return this.attribute.clone();
return this.attribute.getModifiableClone();
}
}
+12 -1
View File
@@ -71,7 +71,12 @@ export interface IReadonlyHeroAttribute<THero> {
* 深拷贝此勇士属性对象
* @param cloneModifier 是否同时复制修饰器,默认复制
*/
clone(cloneModifier?: boolean): IHeroAttribute<THero>;
clone(cloneModifier?: boolean): IReadonlyHeroAttribute<THero>;
/**
* 获取此勇士属性对象的可修改副本
*/
getModifiableClone(): IHeroAttribute<THero>;
}
export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
@@ -101,6 +106,12 @@ export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
name: K,
modifier: IHeroModifier<THero[K], unknown>
): void;
/**
* 深拷贝此勇士属性对象
* @param cloneModifier 是否同时复制修饰器,默认复制
*/
clone(cloneModifier?: boolean): IHeroAttribute<THero>;
}
//#endregion