diff --git a/packages-user/data-base/src/enemy/manager.ts b/packages-user/data-base/src/enemy/manager.ts index de37a69..95407bb 100644 --- a/packages-user/data-base/src/enemy/manager.ts +++ b/packages-user/data-base/src/enemy/manager.ts @@ -13,10 +13,8 @@ import { isNil } from 'lodash-es'; export class EnemyManager implements IEnemyManager { /** 特殊属性注册表,code -> 创建函数 */ - private readonly specialRegistry: Map< - number, - SpecialCreation - > = new Map(); + private readonly specialRegistry: Map> = + new Map(); /** 怪物模板表,code -> IEnemy */ private readonly prefabByCode: Map> = new Map(); /** 复用映射,reusedCode -> sourceCode */ @@ -32,10 +30,14 @@ export class EnemyManager implements IEnemyManager { constructor(private readonly tileStore: ITileStore) {} - registerSpecial(code: number, cons: SpecialCreation): void { + registerSpecial(code: number, cons: SpecialCreation): void { this.specialRegistry.set(code, cons); } + getSpecial(code: number): SpecialCreation | null { + return this.specialRegistry.get(code) ?? null; + } + /** * 根据怪物的图块数字或 id 获取其模板 * @param token 怪物的图块数字或 id diff --git a/packages-user/data-base/src/enemy/special.ts b/packages-user/data-base/src/enemy/special.ts index 20ec999..1978fc2 100644 --- a/packages-user/data-base/src/enemy/special.ts +++ b/packages-user/data-base/src/enemy/special.ts @@ -133,18 +133,18 @@ export class NonePropertySpecial implements ISpecial { } } -export function defineCommonSerializableSpecial( +export function defineCommonSerializableSpecial( code: number, value: T, config: ICommonSpecialConfig -): SpecialCreation { +): SpecialCreation { return () => new CommonSerializableSpecial(code, structuredClone(value), config); } -export function defineNonePropertySpecial( +export function defineNonePropertySpecial( code: number, config: ICommonSpecialConfig -): SpecialCreation { +): SpecialCreation { return () => new NonePropertySpecial(code, config); } diff --git a/packages-user/data-base/src/enemy/types.ts b/packages-user/data-base/src/enemy/types.ts index 1ca16ab..0fb4345 100644 --- a/packages-user/data-base/src/enemy/types.ts +++ b/packages-user/data-base/src/enemy/types.ts @@ -161,7 +161,7 @@ export interface IEnemy //#region 怪物管理器 -export type SpecialCreation = (enemy: IEnemy) => ISpecial; +export type SpecialCreation = () => ISpecial; export interface IEnemyManager extends ISaveableContent< IEnemyManagerSaveState @@ -171,7 +171,13 @@ export interface IEnemyManager extends ISaveableContent< * @param code 特殊属性代码 * @param cons 特殊属性创建函数 */ - registerSpecial(code: number, cons: SpecialCreation): void; + registerSpecial(code: number, cons: SpecialCreation): void; + + /** + * 获取一个特殊属性的注册函数 + * @param code 特殊属性代码 + */ + getSpecial(code: number): SpecialCreation | null; /** * 创建怪物对象,如果对应数字的怪物不存在则会返回 `null` diff --git a/packages-user/data-state/__test__/enemyCombination.test.ts b/packages-user/data-state/__test__/enemyCombination.test.ts index c5f6ab2..32b9d6a 100644 --- a/packages-user/data-state/__test__/enemyCombination.test.ts +++ b/packages-user/data-state/__test__/enemyCombination.test.ts @@ -204,8 +204,8 @@ function createPipelineSpecials(haloAtkBuff: number): ISpecial[] { createSpecial(17, undefined), createSpecial(22, 7), createSpecial(25, { - haloRange: 0, - haloSquare: false, + range: 0, + square: false, hpBuff: 0, atkBuff: haloAtkBuff, defBuff: 0 @@ -256,8 +256,8 @@ describe('enemy combination stage 1 - component baselines', () => { attrs: { atk: 20 }, specials: [ createSpecial(25, { - haloRange: 0, - haloSquare: false, + range: 0, + square: false, hpBuff: 0, atkBuff: 50, defBuff: 0 @@ -490,8 +490,8 @@ describe('enemy combination stage 2 - multi-special and system pipelines', () => code: 25, specials: [ createSpecial(25, { - haloRange: 0, - haloSquare: false, + range: 0, + square: false, hpBuff: 0, atkBuff: 50, defBuff: 0 diff --git a/packages-user/data-state/__test__/mapScenario.perf.ts b/packages-user/data-state/__test__/mapScenario.perf.ts index 023b4cd..1a0cf2a 100644 --- a/packages-user/data-state/__test__/mapScenario.perf.ts +++ b/packages-user/data-state/__test__/mapScenario.perf.ts @@ -157,8 +157,8 @@ const MONSTER_PORTFOLIO: readonly IMonsterPrefab[] = [ specials: [ // haloRange 为 0 时 25 会落到 FullRange,即同楼层全体光环 createSpecial(25, { - haloRange: 0, - haloSquare: false, + range: 0, + square: false, hpBuff: 0, atkBuff: 30, defBuff: 0 @@ -172,8 +172,8 @@ const MONSTER_PORTFOLIO: readonly IMonsterPrefab[] = [ specials: [ // 十字范围光环,让光环拓扑随怪物坐标变化而非全图连通 createSpecial(25, { - haloRange: 3, - haloSquare: false, + range: 3, + square: false, hpBuff: 20, atkBuff: 0, defBuff: 0 @@ -187,8 +187,8 @@ const MONSTER_PORTFOLIO: readonly IMonsterPrefab[] = [ specials: [ // 九宫格范围光环,与十字范围共用同一转换器但走不同 range 实现 createSpecial(25, { - haloRange: 2, - haloSquare: true, + range: 2, + square: true, hpBuff: 0, atkBuff: 0, defBuff: 25 @@ -212,7 +212,7 @@ const MONSTER_PORTFOLIO: readonly IMonsterPrefab[] = [ specials: [ createSpecial(15, { zone: 5, - zoneSquare: false, + square: false, range: 2 }) ] @@ -224,7 +224,7 @@ const MONSTER_PORTFOLIO: readonly IMonsterPrefab[] = [ specials: [ createSpecial(15, { zone: 8, - zoneSquare: true, + square: true, range: 1 }), createSpecial(2, undefined) diff --git a/packages-user/data-state/src/enemy/__test__/aura.test.ts b/packages-user/data-state/src/enemy/__test__/aura.test.ts index 6c8670f..8176ac3 100644 --- a/packages-user/data-state/src/enemy/__test__/aura.test.ts +++ b/packages-user/data-state/src/enemy/__test__/aura.test.ts @@ -182,8 +182,8 @@ function createWritableHandler( */ function createHalo(overrides: Partial = {}): ISpecial23Value { return { - haloRange: 0, - haloSquare: false, + range: 0, + square: false, hpBuff: 0, atkBuff: 0, defBuff: 0, @@ -227,7 +227,7 @@ describe('CommonAura range', () => { const { enemy } = createEnemy(); const aura = new modules.CommonAura( enemy, - createSpecial(25, createHalo({ haloRange: 0 })), + createSpecial(25, createHalo({ range: 0 })), { x: 3, y: 4 } ); @@ -240,7 +240,7 @@ describe('CommonAura range', () => { const { enemy } = createEnemy(); const aura = new modules.CommonAura( enemy, - createSpecial(25, createHalo({ haloRange: 2, haloSquare: true })), + createSpecial(25, createHalo({ range: 2, square: true })), { x: 3, y: 4 } ); @@ -253,7 +253,7 @@ describe('CommonAura range', () => { const { enemy } = createEnemy(); const aura = new modules.CommonAura( enemy, - createSpecial(25, createHalo({ haloRange: 2, haloSquare: false })), + createSpecial(25, createHalo({ range: 2, square: false })), { x: 3, y: 4 } ); diff --git a/packages-user/data-state/src/enemy/__test__/mapDamage.test.ts b/packages-user/data-state/src/enemy/__test__/mapDamage.test.ts index 23d2852..d2441e6 100644 --- a/packages-user/data-state/src/enemy/__test__/mapDamage.test.ts +++ b/packages-user/data-state/src/enemy/__test__/mapDamage.test.ts @@ -217,7 +217,7 @@ describe('ZoneDamageView', () => { it('builds a manhattan range and zone damage for a cross zone', () => { const special = createSpecial(15, { zone: 7, - zoneSquare: false, + square: false, range: 1 }); const view = new modules.ZoneDamageView( @@ -237,7 +237,7 @@ describe('ZoneDamageView', () => { it('builds a rect range and zone damage for a square zone', () => { const special = createSpecial(15, { zone: 4, - zoneSquare: true, + square: true, range: 2 }); const view = new modules.ZoneDamageView( @@ -257,7 +257,7 @@ describe('ZoneDamageView', () => { it('checks coordinates against the zone range', () => { const special = createSpecial(15, { zone: 7, - zoneSquare: false, + square: false, range: 1 }); const view = new modules.ZoneDamageView( diff --git a/packages-user/data-state/src/enemy/__test__/special.test.ts b/packages-user/data-state/src/enemy/__test__/special.test.ts index 66f8aae..5429367 100644 --- a/packages-user/data-state/src/enemy/__test__/special.test.ts +++ b/packages-user/data-state/src/enemy/__test__/special.test.ts @@ -147,8 +147,8 @@ describe('registerSpecials', () => { expect(halo.getDescription()).toContain('线性叠加。'); const value: ISpecial23Value = { - haloRange: 2, - haloSquare: true, + range: 2, + square: true, hpBuff: 10, atkBuff: 0, defBuff: 0 diff --git a/packages-user/data-state/src/enemy/aura.ts b/packages-user/data-state/src/enemy/aura.ts index a6f66a8..d879b96 100644 --- a/packages-user/data-state/src/enemy/aura.ts +++ b/packages-user/data-state/src/enemy/aura.ts @@ -63,7 +63,7 @@ export class CommonAura implements IEnemyAuraView< private createRange(): IRange< IRectRangeParam | IManhattanRangeParam | void > { - const { haloRange, haloSquare } = this.special.value; + const { range: haloRange, square: haloSquare } = this.special.value; if (haloRange <= 0) { return FULL_RANGE; } @@ -71,7 +71,7 @@ export class CommonAura implements IEnemyAuraView< } getRangeParam(): IRectRangeParam | IManhattanRangeParam | void { - const { haloRange, haloSquare } = this.special.value; + const { range: haloRange, square: haloSquare } = this.special.value; if (haloRange <= 0) { return undefined; } diff --git a/packages-user/data-state/src/enemy/mapDamage.ts b/packages-user/data-state/src/enemy/mapDamage.ts index 447626a..2ac8cf6 100644 --- a/packages-user/data-state/src/enemy/mapDamage.ts +++ b/packages-user/data-state/src/enemy/mapDamage.ts @@ -93,11 +93,11 @@ export class ZoneDamageView extends BaseMapDamageView< } getRange(): IRange { - return this.special.value.zoneSquare ? RECT_RANGE : MANHATTAN_RANGE; + return this.special.value.square ? RECT_RANGE : MANHATTAN_RANGE; } getRangeParam(): IRectRangeParam | IManhattanRangeParam { - if (this.special.value.zoneSquare) { + if (this.special.value.square) { return { h: this.special.value.range * 2 + 1, w: this.special.value.range * 2 + 1, diff --git a/packages-user/data-state/src/enemy/special.ts b/packages-user/data-state/src/enemy/special.ts index 9ec580f..7af0b84 100644 --- a/packages-user/data-state/src/enemy/special.ts +++ b/packages-user/data-state/src/enemy/special.ts @@ -3,7 +3,6 @@ import { defineNonePropertySpecial, SpecialCreation } from '@user/data-base'; -import { IEnemyAttr } from '@user/data-common'; import { IStateSystem } from '@user/data-system'; // 重要!!!下面这一行的注释不要删 @@ -25,7 +24,7 @@ export interface ISpecial13Value { /** 领域伤害值 */ zone: number; /** 矩形领域(true)还是十字形领域(false),当范围大于 1 时,十字型领域采用曼哈顿距离计算 */ - zoneSquare: boolean; + square: boolean; /** 领域范围值 */ range: number; } @@ -38,11 +37,12 @@ export interface ISpecial21Value { defValue: number; } +/** 23 - 光环 */ export interface ISpecial23Value { /** 光环范围值 */ - haloRange: number; + range: number; /** 光环是矩形范围(true)还是十字型范围(false),当范围大于 1 时,十字型范围采用曼哈顿距离计算 */ - haloSquare: boolean; + square: boolean; /** 生命值增加比例 */ hpBuff: number; /** 攻击力增加比例 */ @@ -51,6 +51,18 @@ export interface ISpecial23Value { defBuff: number; } +/** 26 - 特殊光环 */ +export interface ISpecial26Value { + /** 光环范围值 */ + range: number; + /** 光环是矩形范围(true)还是十字型范围(false),当范围大于 1 时,十字型范围采用曼哈顿距离计算 */ + square: boolean; + /** 当怪物受到多个特殊光环加成时,是乘算叠加(true),还是加算叠加(false) */ + mulAdd: boolean; + /** 增加的特殊属性列表 */ + specials: [number, any][]; +} + // 重要!!!下面这一行的注释不要删 //#endregion @@ -69,10 +81,10 @@ export interface ISpecial23Value { */ export function createSpecials( state: IStateSystem -): ReadonlyMap> { +): ReadonlyMap> { const hero = state.hero; - const map = new Map>(); + const map = new Map>(); // 注意,不需要参数的特殊属性,如先攻、魔攻、坚固这些纯机制, // 不需要一个数值来描述的特殊属性,使用 defineNonePropertySpecial 定义。 @@ -250,11 +262,11 @@ export function createSpecials( 13, defineCommonSerializableSpecial( 13, - { zone: 0, zoneSquare: false, range: 1 }, + { zone: 0, square: false, range: 1 }, { name: '领域', desc: special => { - const { zone, zoneSquare, range } = special.value; + const { zone, square: zoneSquare, range } = special.value; return `经过怪物周围${zoneSquare ? '九宫格' : '十字'}范围内${range}格时自动减生命${zone}点。`; }, color: '#C677DD' @@ -368,8 +380,8 @@ export function createSpecials( defineCommonSerializableSpecial( 23, { - haloRange: 0, - haloSquare: false, + range: 0, + square: false, hpBuff: 0, atkBuff: 0, defBuff: 0 @@ -377,8 +389,13 @@ export function createSpecials( { name: '光环', desc: special => { - const { haloRange, haloSquare, hpBuff, atkBuff, defBuff } = - special.value; + const { + range: haloRange, + square: haloSquare, + hpBuff, + atkBuff, + defBuff + } = special.value; let str = ''; if (haloRange > 0) { if (haloSquare) { @@ -420,14 +437,46 @@ export function createSpecials( // 25 - 捕捉 map.set( 25, - defineNonePropertySpecial(27, { + defineNonePropertySpecial(25, { name: '捕捉', desc: '当走到怪物十字范围内时会进行强制战斗。', color: '#C0DDBB' }) ); - // TODO: 26 - 特殊光环 + // 26 - 特殊光环 + map.set( + 26, + defineCommonSerializableSpecial( + 26, + { + range: 0, + square: false, + mulAdd: false, + specials: [] + }, + { + name: '特殊光环', + desc: special => { + const { range, square, specials, mulAdd } = special.value; + const prefix = `在怪物周围${square ? '正方形' : '十字'} ${range} 格范围内的怪物获得如下特殊属性,${mulAdd ? '乘算' : '加算'}叠加:\n`; + const list: string[] = []; + for (const [code, param] of specials) { + const special = state.enemyManager.getSpecial(code)?.(); + if (!special) continue; + special.setValue(param); + const name = special.getSpecialName(); + const color = special.getNameColor(); + const desc = special.getDescription(); + const item = ` ${list.length + 1}. \\r[${color}]${name}\\r: ${desc}`; + list.push(item); + } + return prefix + list.join('\n'); + }, + color: '#e945ff' + } + ) + ); // 重要!!!下面这一行的注释不要删 //#endregion