fix(07-01): #06-01-2 clear enemy-sourced map damage on delete

- register viewStore/damageStore reverse indexes when sourced damage is written
  and re-register them after a refreshIndex recompute (route A)
- deleteEnemy now finds and removes the enemy-sourced damage instead of leaving
  stale entries behind
- un-skip the correct-expectation case in mapDamage.test.ts (#06-01-2)
This commit is contained in:
unanmed 2026-09-15 17:00:44 +08:00
parent 8225ef129c
commit 24534fbca6
2 changed files with 56 additions and 7 deletions

View File

@ -528,8 +528,8 @@ describe('MapDamage sourced conversion and reduction', () => {
]).toHaveLength(before);
});
// 疑似 bug:deleteEnemy 应移除该怪物带来的有来源地图伤害,详见 06-TEST-FINDINGS.md #06-01-2,修复后取消 skip
it.skip('removes enemy-sourced damage when the enemy is deleted', () => {
// 验证 deleteEnemy 会移除该怪物带来的有来源地图伤害
it('removes enemy-sourced damage when the enemy is deleted', () => {
const fixture = createFixture();
fixture.damage.useReducer(fixture.reducer);
fixture.damage.useConverter(fixture.converter);

View File

@ -255,6 +255,35 @@ export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
this.enemyStore.delete(view);
}
/**
* 登记有来源伤害的反向索引,供删除与局部刷新时按来源清理
* @param point 坐标点伤害信息
* @param viewItem 产生伤害的视图
* @param sourceEnemy 该视图所属的怪物视图
* @param index 坐标索引
* @param damage 该点上的伤害信息
*/
private registerSourcedDamage(
point: IPointInfo,
viewItem: IMapDamageView<any>,
sourceEnemy: IEnemyView<TEnemy>,
index: number,
damage: Readonly<IMapDamageInfo>
): void {
point.affectedBy.add(viewItem);
point.damages.add(damage);
this.damageStore.set(damage, {
sourceView: viewItem,
sourceEnemy,
index
});
const viewStore = this.viewStore.getOrInsertComputed(viewItem, () => ({
damages: new Map(),
enemy: sourceEnemy
}));
viewStore.damages.set(index, damage);
}
/**
* 刷新指定位置的怪物地图伤害,并执行刷新缓存的操作
*/
@ -286,8 +315,13 @@ export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
);
const damage = viewItem.getDamageWithoutCheck(loc);
if (damage) {
point.affectedBy.add(viewItem);
point.damages.add(damage);
this.registerSourcedDamage(
point,
viewItem,
view,
index,
damage
);
collection.add(index);
}
}
@ -325,8 +359,13 @@ export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
);
const damage = viewItem.getDamageWithoutCheck(loc);
if (damage) {
point.affectedBy.add(viewItem);
point.damages.add(damage);
this.registerSourcedDamage(
point,
viewItem,
view,
index,
damage
);
}
}
});
@ -373,7 +412,17 @@ export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
point.affectedBy.forEach(view => {
const damage = view.getDamageWithoutCheck(locator);
if (damage) point.damages.add(damage);
if (!damage) return;
// 视图已被删除时不再重建其伤害,避免删除怪物后残留来源伤害
const viewStore = this.viewStore.get(view);
if (!viewStore) return;
this.registerSourcedDamage(
point,
view,
viewStore.enemy,
index,
damage
);
});
}
}