From ab18aa3ec73bc4b3ec076658015139bf20879d21 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Thu, 17 Sep 2026 15:56:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(07-12):=20deleteModifierByIndex=20=E5=A7=94?= =?UTF-8?q?=E6=89=98=20deleteModifier=EF=BC=88WR-05=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 按索引取出修饰器后委托 deleteModifier,删除簿记收敛为一份实现 - 返回被删修饰器;索引越界或为负返回 null 且不删除任何修饰器 - 回归覆盖:迭代器产出、索引解析、final 重算、重新挂载、存盘开关清理、越界 --- .../data-base/src/hero/attribute.test.ts | 54 +++++++++++++++++++ packages-user/data-base/src/hero/attribute.ts | 9 ++-- packages-user/data-base/src/hero/types.ts | 3 +- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/packages-user/data-base/src/hero/attribute.test.ts b/packages-user/data-base/src/hero/attribute.test.ts index 406830b..0727b9b 100644 --- a/packages-user/data-base/src/hero/attribute.test.ts +++ b/packages-user/data-base/src/hero/attribute.test.ts @@ -203,6 +203,60 @@ describe('HeroAttribute modifier management', () => { expect(attribute.deleteModifierByIndex('hp', 0)).toBeNull(); }); + // 验证按索引删除后修饰器不再被迭代、索引解析为 -1 且最终属性按剩余修饰器重算 + it('removes a modifier by index from the iterator and the final attribute', () => { + const attribute = createAttribute(); + const first = new TestModifier(5, 10); + const second = new TestModifier(2, 0); + attribute.addModifier('hp', first); + attribute.addModifier('hp', second); + expect(attribute.getFinalAttribute('hp')).toBe(107); + + expect(attribute.deleteModifierByIndex('hp', 0)).toBe(first); + expect([...attribute.iterateModifiers()]).toEqual([['hp', second]]); + expect(attribute.getModifierIndex(first)).toBe(-1); + expect(attribute.getFinalAttribute('hp')).toBe(102); + }); + + // 验证被按索引删除的修饰器已解除归属,可以重新挂载且不再被码 108 拒绝 + it('allows an index-deleted modifier to be attached again', () => { + const attribute = createAttribute(); + const first = new TestModifier(5); + attribute.addModifier('hp', first); + + expect(attribute.deleteModifierByIndex('hp', 0)).toBe(first); + + const result = logger.catch(() => attribute.addModifier('hp', first)); + expect(result.info.map(info => info.code)).not.toContain(108); + expect(attribute.getModifierIndex(first)).toBe(0); + expect(attribute.getFinalAttribute('hp')).toBe(105); + }); + + // 验证索引越界或为负时返回 null 且不删除任何修饰器 + it('keeps every modifier when the index is out of range', () => { + const attribute = createAttribute(); + const first = new TestModifier(5, 10); + const second = new TestModifier(2, 0); + attribute.addModifier('hp', first); + attribute.addModifier('hp', second); + + expect(attribute.deleteModifierByIndex('hp', -1)).toBeNull(); + expect(attribute.deleteModifierByIndex('hp', 2)).toBeNull(); + expect([...attribute.getModifiers('hp')]).toEqual([first, second]); + expect(attribute.getFinalAttribute('hp')).toBe(107); + }); + + // 验证按索引删除不存档修饰器时其存盘开关记录被一并清理 + it('clears the save flag of an index-deleted modifier', () => { + const attribute = createAttribute(); + const unsaved = new TestModifier(3); + attribute.addModifier('hp', unsaved, false); + expect(attribute.getModifierSaveEnabled(unsaved)).toBe(false); + + expect(attribute.deleteModifierByIndex('hp', 0)).toBe(unsaved); + expect(attribute.getModifierSaveEnabled(unsaved)).toBe(true); + }); + // 验证存盘开关可分别设置与查询 it('tracks the per-modifier save flag', () => { const attribute = createAttribute(); diff --git a/packages-user/data-base/src/hero/attribute.ts b/packages-user/data-base/src/hero/attribute.ts index 7e95fc2..e372e8a 100644 --- a/packages-user/data-base/src/hero/attribute.ts +++ b/packages-user/data-base/src/hero/attribute.ts @@ -233,9 +233,12 @@ export class HeroAttribute implements IHeroAttribute { ): IHeroModifier | null { const arr = this.modifier.get(name); if (!arr) return null; - const modifier = arr.splice(index, 1); - if (modifier.length === 0) return null; - else return modifier[0] as IHeroModifier; + const modifier = arr[index] as IHeroModifier | undefined; + // 越界与负索引一律不删除任何修饰器,避免旧 splice 语义下误删末尾元素 + if (!modifier) return null; + // 删除簿记统一由 deleteModifier 承担,故需先取出修饰器再委托以保留返回值 + this.deleteModifier(name, modifier); + return modifier; } markDirty(name: keyof THero): void { diff --git a/packages-user/data-base/src/hero/types.ts b/packages-user/data-base/src/hero/types.ts index bfc7355..33dc0c2 100644 --- a/packages-user/data-base/src/hero/types.ts +++ b/packages-user/data-base/src/hero/types.ts @@ -219,7 +219,8 @@ export interface IHeroAttribute ): void; /** - * 根据修饰器索引删除指定修饰器 + * 根据修饰器索引删除指定修饰器。删除簿记与 `deleteModifier` 共用同一份实现, + * 修饰器会被解绑且不再属于本属性;索引越界或为负时返回 `null`,且不删除任何修饰器 * @param name 属性名称 * @param index 修饰器索引 * @returns 被删除的修饰器