mirror of
https://github.com/motajs/template.git
synced 2026-05-14 04:41:10 +08:00
Compare commits
4 Commits
6389fd4f0b
...
43583e0e31
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43583e0e31 | ||
| 43ad3fd83e | |||
| 0da19e1697 | |||
| 8ea0c12024 |
85
docs/dev/common/face-manager.md
Normal file
85
docs/dev/common/face-manager.md
Normal file
@ -0,0 +1,85 @@
|
||||
# 需求综述
|
||||
|
||||
动态图块移动功能完成后,发现 `FaceDirection` 朝向相关操作(位移、反方向、旋转方向、降级)分散在独立工具函数中(`getFaceMovement`、`nextFaceDirection`、`degradeFace` 等),缺乏统一抽象,导致调用点分散、扩展性不足(例如:四方向与八方向切换时需要手动处理,不同移动场景难以共享同一套逻辑)。
|
||||
|
||||
目标是设计一套统一的朝向管理接口:`IFaceHandler<T>` 代表一组朝向并提供相应操作,`IFaceManager` 作为注册中心管理多个 handler,同时内置两个 handler 分别对应四方向和八方向。新接口位于 `@user/data-base/src/common/`,与 `FaceDirection` 同包,同时取代现有的 `DirectionMapper`。
|
||||
|
||||
# 实现思路
|
||||
|
||||
## 1. 设计 IFaceHandler 接口
|
||||
|
||||
`IFaceHandler<T extends number>` 代表**一组朝向**,泛型 `T` 表示本组的朝向类型(通常为 `FaceDirection`,也可以是自定义枚举以支持拓展)。内部隐含一个该组支持的方向集合,通过 `directions` 成员暴露。对于不在集合内的输入方向,通过 `degrade` 方法将其映射到集合内最合适的方向,其余操作方法在调用时**均先执行 degrade**。
|
||||
|
||||
`mapDirection` 同时取代 `DirectionMapper.map()` 的功能,不再需要单独维护 `DirectionMapper`。
|
||||
|
||||
主要成员与方法:
|
||||
|
||||
- `degrade(dir: number): T`:将任意朝向值(含其他枚举类型或 `FaceDirection`)降级为本组支持的方向。对于无法合理降级的方向(包括 `Unknown`),返回 `FaceDirection.Unknown`(数值 `0`,兼容所有 `T`)。
|
||||
|
||||
- `movement(dir: number): IFaceDescriptor`:获取指定方向的坐标偏移量,输入先经过 `degrade`,`Unknown` 返回 `{ x: 0, y: 0 }`。
|
||||
|
||||
- `move(dir: number, count: number): IFaceDescriptor`:获取指定方向走 `count` 步的坐标偏移量,等价于 `movement * count`。`count` 允许为负数,表示反向位移。输入先经过 `degrade`。
|
||||
|
||||
- `opposite(dir: number): T`:获取本组内的反方向,输入先经过 `degrade`,`Unknown` 返回 `Unknown`。
|
||||
|
||||
- `next(dir: number, anticlockwise?: boolean): T`:在本组方向集合内,顺时针(默认)或逆时针旋转一步,输入先经过 `degrade`,`Unknown` 返回 `Unknown`。
|
||||
|
||||
- `mapDirection(): Iterable<T>`:迭代本组支持的所有朝向,包含 `Unknown`(其与其他方向一视同仁,不作特例处理)。
|
||||
|
||||
- `mapMovement(): Iterable<[T, IFaceDescriptor]>`:迭代本组所有朝向及其对应的坐标描述器,包含 `Unknown`(对应 `{ x: 0, y: 0 }`)。
|
||||
|
||||
## 2. 设计 IFaceManager 接口
|
||||
|
||||
`IFaceManager` 是 handler 的注册中心,同时支持数字 key 与字符串 id 两种注册与查找方式。数字 key 适合内置组的高频调用,字符串 id 适合使用频率较低的自定义场景:
|
||||
|
||||
- `register(group: number, handler: IFaceHandler<number>): void`:以数字 key 注册一个 handler。
|
||||
- `registerById(id: string, handler: IFaceHandler<number>): void`:以字符串 id 注册一个 handler。
|
||||
- `get<T extends number>(group: number): IFaceHandler<T> | null`:按数字 key 查找 handler,未找到返回 `null`。
|
||||
- `getById<T extends number>(id: string): IFaceHandler<T> | null`:按字符串 id 查找 handler,未找到返回 `null`。
|
||||
|
||||
内置的数字 key 组用新增的 `InternalFaceGroup` 枚举标识(与现有的 `InternalDirectionGroup` 风格一致)。
|
||||
|
||||
## 3. 内置 Handler 实现
|
||||
|
||||
### Dir8FaceHandler(八方向)
|
||||
|
||||
- `directions`:包含全部八个有效方向与 `Unknown`,共九个成员。
|
||||
- `degrade`:直接返回输入(转为 `T`),无需降级。
|
||||
- `next`:`Unknown` 返回 `Unknown`;按 45° 步进,顺时针顺序:Up → RightUp → Right → RightDown → Down → LeftDown → Left → LeftUp → Up。
|
||||
- `opposite`:`Unknown` 返回 `Unknown`;Up↔Down,Left↔Right,LeftUp↔RightDown,RightUp↔LeftDown。
|
||||
- `movement`:与现有 `getFaceMovement` 一致。
|
||||
|
||||
### Dir4FaceHandler(四方向)
|
||||
|
||||
- `directions`:包含 Up、Down、Left、Right 四个方向与 `Unknown`,共五个成员。
|
||||
- `degrade`:四方向不变;斜向降级为水平分量(LeftUp/LeftDown → Left,RightUp/RightDown → Right);`Unknown` → `Unknown`。与现有 `degradeFace` 行为一致。
|
||||
- `next`:先 degrade,`Unknown` 返回 `Unknown`;再按 90° 步进,顺时针:Up → Right → Down → Left → Up。
|
||||
- `opposite`:先 degrade,`Unknown` 返回 `Unknown`;Up↔Down,Left↔Right。
|
||||
- `movement`:先 degrade,再返回偏移量。
|
||||
|
||||
## 4. 实现 FaceManager 类
|
||||
|
||||
实现 `IFaceManager`,不导出全局单例,应将实例挂载到游戏实例下。两个内置 handler(`Dir8FaceHandler` 与 `Dir4FaceHandler`)不在构造时注册,而在游戏实例初始化阶段注册,key 分别为 `InternalFaceGroup.Dir8` 和 `InternalFaceGroup.Dir4`。
|
||||
|
||||
## 5. 现有代码处理
|
||||
|
||||
- 现有 `getFaceMovement`、`nextFaceDirection`、`degradeFace`、`fromDirectionString` 等工具函数**暂时保留**,不做删改;新代码直接使用新接口,旧代码的迁移视后续情况另行处理。
|
||||
- `@motajs/common` 中的 `DirectionMapper` 及 `IDirectionMapper` 接口将被废弃,其调用方(如 `range.ts`)的迁移视后续情况另行处理。
|
||||
|
||||
# 涉及文件
|
||||
|
||||
## 需要修改的文件
|
||||
|
||||
### `@user/data-base/src/common/faceManager.ts`(新增文件)
|
||||
|
||||
- [ ] 新增 `InternalFaceGroup` 枚举:包含 `Dir4` 与 `Dir8` 两个成员,作为 `IFaceManager` 的内置数字 key
|
||||
- [ ] 新增 `IFaceDescriptor` 接口:描述一个方向的坐标增量,包含 `x` 与 `y` 两个只读成员
|
||||
- [ ] 新增 `IFaceHandler<T extends number>` 接口:包含 `degrade`、`movement`、`move`、`opposite`、`next`、`mapDirection`、`mapMovement` 七个成员
|
||||
- [ ] 新增 `IFaceManager` 接口:包含 `register`、`registerById`、`get`、`getById` 四个方法
|
||||
- [ ] 实现 `Dir8FaceHandler` 类(`implements IFaceHandler<FaceDirection>`)
|
||||
- [ ] 实现 `Dir4FaceHandler` 类(`implements IFaceHandler<FaceDirection>`)
|
||||
- [ ] 实现 `FaceManager` 类(`implements IFaceManager`)
|
||||
|
||||
### `@user/data-base/src/common/index.ts`
|
||||
|
||||
- [ ] 导出新增的枚举、接口与类
|
||||
54
docs/dev/enemy/enemy-types-restructure.md
Normal file
54
docs/dev/enemy/enemy-types-restructure.md
Normal file
@ -0,0 +1,54 @@
|
||||
# 怪物系统文件结构重构
|
||||
|
||||
**目的:** 使 `IEnemyHandler` 能持有 `IStateBase` 引用而不产生循环引用。
|
||||
|
||||
**循环根源:** `enemy/types.ts` 混写了两层接口,`data-base/src/types.ts` 经由 `./enemy` index 引入 `IEnemyManager`,导致无法反向引用。
|
||||
|
||||
## 接口归属
|
||||
|
||||
**`enemy/types.ts`(Layer 1 — 存档/管理层,保留)**
|
||||
|
||||
只保留以下接口,其余全部迁出:
|
||||
|
||||
- `IEnemySaveState`、`IEnemyManagerSaveState`、`IEnemyComparer`
|
||||
- `ISpecial`、`IReadonlyEnemy`、`IEnemy`
|
||||
- `SpecialCreation`、`IEnemyLegacyBridge`、`IEnemyManager`
|
||||
|
||||
只允许引用 `@motajs/common`、`../common`,不得引用 `../types`。
|
||||
|
||||
**`enemy/utils.ts`(Layer 0 — 工具层,与实现类并列)**
|
||||
|
||||
从 `enemy/types.ts` 迁出以下接口(实现类 `MapLocIndexer` 已在此文件):
|
||||
|
||||
- `IMapLocHelper`、`IMapLocIndexer`
|
||||
|
||||
**`combat/types.ts`(Layer 2 — 战斗/上下文层,现为空文件)**
|
||||
|
||||
从 `enemy/types.ts` 迁入以下接口:
|
||||
|
||||
- `IEnemyHandler`、`IReadonlyEnemyHandler`、`IEnemyView`
|
||||
- `IEnemySpecialModifier`、`IAuraView`、`IEnemyAuraView`、`IAuraConverter`
|
||||
- `IEnemySpecialQueryModifier`、`IEnemySpecialQueryEffect`、`IEnemyCommonQueryEffect`、`IEnemyFinalEffect`
|
||||
- `IMapDamageInfoExtra`、`IMapDamageInfo`、`IMapDamageView`、`IMapDamageConverter`、`IMapDamageReducer`、`IMapDamage`
|
||||
- `IEnemyDamageInfo`、`IEnemyCritical`、`CriticalableHeroStatus`、`IDamageCalculator`、`IDamageContext`、`IDamageSystem`
|
||||
- `IEnemyContext`
|
||||
|
||||
需引用 `../enemy/types`(Layer 1)、`../enemy/utils`(Layer 0)、`../types`(`IStateBase`,现在无循环)。在 `IEnemyHandler` 和 `IReadonlyEnemyHandler` 中新增 `readonly state: IStateBase<TAttr, THero>`。
|
||||
|
||||
## 文件修改清单
|
||||
|
||||
| 文件 | 操作 |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `combat/types.ts` | 迁入所有 Layer 2 接口;新增三条 import(`../enemy/types`、`../enemy/utils`、`../types`);`IEnemyHandler` 系列加 `state` 字段 |
|
||||
| `combat/context.ts` | 从 `enemy/context.ts` **移动**;import 路径改为从 `./types`、`../enemy/types`、`../enemy/utils` 引入 |
|
||||
| `combat/damage.ts` | 从 `enemy/damage.ts` **移动**;import 路径同上 |
|
||||
| `combat/mapDamage.ts` | 从 `enemy/mapDamage.ts` **移动**;import 路径同上 |
|
||||
| `combat/index.ts` | 新建,`export * from './types/context/damage/mapDamage'` |
|
||||
| `enemy/types.ts` | 删除所有 Layer 2 接口(迁出后仅剩 Layer 1 内容) |
|
||||
| `enemy/utils.ts` | 新增 `IMapLocHelper`、`IMapLocIndexer` 接口定义 |
|
||||
| `enemy/index.ts` | 删除对 `context`、`damage`、`mapDamage` 的导出 |
|
||||
| `data-base/src/index.ts` | 新增 `export * from './combat'` |
|
||||
| `data-base/src/types.ts` | `import { IEnemyManager }` 改从 `./enemy/types` 直接引入(`enemy/types.ts` 不再引用 `../types`,无循环) |
|
||||
| `enemy/enemy.ts` | import 路径无需改动(`IEnemy` 等仍在 `./types`) |
|
||||
| `enemy/manager.ts` | import 路径无需改动(全部 Layer 1 接口仍在 `./types`) |
|
||||
| `enemy/special.ts` | import 路径无需改动 |
|
||||
@ -3,7 +3,6 @@ import {
|
||||
IAuraConverter,
|
||||
IAuraView,
|
||||
IDamageSystem,
|
||||
IEnemy,
|
||||
IEnemyAuraView,
|
||||
IEnemyCommonQueryEffect,
|
||||
IEnemyContext,
|
||||
@ -13,76 +12,82 @@ import {
|
||||
IEnemySpecialQueryEffect,
|
||||
IEnemyView,
|
||||
IMapDamage,
|
||||
IReadonlyEnemy,
|
||||
IReadonlyEnemyHandler,
|
||||
ISpecial
|
||||
IReadonlyEnemyHandler
|
||||
} from './types';
|
||||
import { EnemyView } from './enemy';
|
||||
import { MapLocIndexer } from './utils';
|
||||
import { IReadonlyHeroAttribute } from '../hero';
|
||||
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
|
||||
import { EnemyView } from './enemy';
|
||||
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<
|
||||
IReadonlyEnemy<TAttr>,
|
||||
EnemyView<TAttr>
|
||||
IReadonlyEnemy<TEnemy>,
|
||||
EnemyView<TEnemy>
|
||||
> = new Map();
|
||||
|
||||
/** 当前已注册的光环转换器 */
|
||||
private readonly auraConverter: Set<IAuraConverter<TAttr, THero>> =
|
||||
private readonly auraConverter: Set<IAuraConverter<TEnemy, THero>> =
|
||||
new Set();
|
||||
/** 光环转换器是否启用 */
|
||||
private readonly converterStatus: Map<
|
||||
IAuraConverter<TAttr, THero>,
|
||||
IAuraConverter<TEnemy, THero>,
|
||||
boolean
|
||||
> = new Map();
|
||||
/** 所有已被转换的光环 */
|
||||
private readonly convertedAura: Map<ISpecial<any>, IAuraView<TAttr>> =
|
||||
private readonly convertedAura: Map<ISpecial<any>, IAuraView<TEnemy>> =
|
||||
new Map();
|
||||
|
||||
/** 普通查询效果注册,特殊属性 -> 此特殊属性的查询效果列表,按照优先级从高到低排序 */
|
||||
private readonly commonQueryMap: Map<
|
||||
number,
|
||||
IEnemyCommonQueryEffect<TAttr, THero>[]
|
||||
IEnemyCommonQueryEffect<TEnemy, THero>[]
|
||||
> = new Map();
|
||||
|
||||
/** 特殊查询效果注册,特殊属性 -> 此特殊属性的特殊查询效果列表,按照优先级从高到低排序 */
|
||||
private readonly specialQueryEffects: Map<
|
||||
number,
|
||||
IEnemySpecialQueryEffect<TAttr, THero>[]
|
||||
IEnemySpecialQueryEffect<TEnemy, THero>[]
|
||||
> = 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 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: MapLocIndexer = new MapLocIndexer();
|
||||
readonly indexer: ILocationIndexer = new MapLocIndexer();
|
||||
|
||||
/** 当前是否需要全量刷新 */
|
||||
private needUpdate: boolean = true;
|
||||
@ -91,6 +96,8 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
width: number = 0;
|
||||
height: number = 0;
|
||||
|
||||
constructor(readonly dataState: IStateBase<TEnemy, THero>) {}
|
||||
|
||||
resize(width: number, height: number): void {
|
||||
this.clear();
|
||||
this.width = width;
|
||||
@ -99,20 +106,20 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void {
|
||||
registerAuraConverter(converter: IAuraConverter<TEnemy, THero>): void {
|
||||
this.auraConverter.add(converter);
|
||||
this.converterStatus.set(converter, true);
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void {
|
||||
unregisterAuraConverter(converter: IAuraConverter<TEnemy, THero>): void {
|
||||
this.auraConverter.delete(converter);
|
||||
this.converterStatus.delete(converter);
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
setAuraConverterEnabled(
|
||||
converter: IAuraConverter<TAttr, THero>,
|
||||
converter: IAuraConverter<TEnemy, THero>,
|
||||
enabled: boolean
|
||||
): void {
|
||||
if (!this.auraConverter.has(converter)) return;
|
||||
@ -122,7 +129,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
|
||||
registerCommonQueryEffect(
|
||||
code: number,
|
||||
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||
effect: IEnemyCommonQueryEffect<TEnemy, THero>
|
||||
): void {
|
||||
const array = this.commonQueryMap.getOrInsert(code, []);
|
||||
array.push(effect);
|
||||
@ -132,7 +139,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
|
||||
unregisterCommonQueryEffect(
|
||||
code: number,
|
||||
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||
effect: IEnemyCommonQueryEffect<TEnemy, THero>
|
||||
): void {
|
||||
const array = this.commonQueryMap.get(code);
|
||||
if (!array) return;
|
||||
@ -143,7 +150,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
}
|
||||
|
||||
registerSpecialQueryEffect(
|
||||
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||
effect: IEnemySpecialQueryEffect<TEnemy, THero>
|
||||
): void {
|
||||
const list = this.specialQueryEffects.getOrInsert(effect.priority, []);
|
||||
list.push(effect);
|
||||
@ -151,7 +158,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
}
|
||||
|
||||
unregisterSpecialQueryEffect(
|
||||
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||
effect: IEnemySpecialQueryEffect<TEnemy, THero>
|
||||
): void {
|
||||
const list = this.specialQueryEffects.get(effect.priority);
|
||||
if (!list) return;
|
||||
@ -165,13 +172,13 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void {
|
||||
registerFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void {
|
||||
this.finalEffects.push(effect);
|
||||
this.finalEffects.sort((a, b) => b.priority - a.priority);
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void {
|
||||
unregisterFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void {
|
||||
const index = this.finalEffects.indexOf(effect);
|
||||
if (index !== -1) {
|
||||
this.finalEffects.splice(index, 1);
|
||||
@ -196,37 +203,39 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* @param locator 怪物位置
|
||||
*/
|
||||
private createHandler(
|
||||
enemy: IEnemy<TAttr>,
|
||||
enemy: IEnemy<TEnemy>,
|
||||
locator: ITileLocator
|
||||
): IEnemyHandler<TAttr, THero> {
|
||||
return { enemy, locator, hero: this.bindedHero! };
|
||||
): IEnemyHandler<TEnemy, THero> {
|
||||
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);
|
||||
if (index === undefined) return null;
|
||||
return this.indexer.indexToLocator(index);
|
||||
}
|
||||
|
||||
getEnemyLocatorByView(
|
||||
view: IEnemyView<TAttr>
|
||||
view: IEnemyView<TEnemy>
|
||||
): Readonly<ITileLocator> | null {
|
||||
const index = this.locatorViewMap.get(view);
|
||||
if (index === undefined) return null;
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -258,11 +267,11 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
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);
|
||||
this.deleteEnemyAt(index);
|
||||
|
||||
const view = new EnemyView<TAttr>(enemy, this);
|
||||
const view = new EnemyView<TEnemy>(enemy, this);
|
||||
this.enemyMap.set(index, enemy);
|
||||
this.enemyViewMap.set(index, view);
|
||||
this.locatorEnemyMap.set(enemy, index);
|
||||
@ -292,7 +301,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
private *internalScanRange<T>(
|
||||
range: IRange<T>,
|
||||
param: T
|
||||
): Iterable<[ITileLocator, EnemyView<TAttr>]> {
|
||||
): Iterable<[ITileLocator, EnemyView<TEnemy>]> {
|
||||
range.bindHost(this);
|
||||
const keys = new Set(this.enemyViewMap.keys());
|
||||
const matched = range.autoDetect(keys, param);
|
||||
@ -309,46 +318,46 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
scanRange<T>(
|
||||
range: IRange<T>,
|
||||
param: T
|
||||
): Iterable<[ITileLocator, IEnemyView<TAttr>]> {
|
||||
): Iterable<[ITileLocator, IEnemyView<TEnemy>]> {
|
||||
return this.internalScanRange(range, param);
|
||||
}
|
||||
|
||||
*iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TAttr>]> {
|
||||
*iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TEnemy>]> {
|
||||
for (const [index, view] of this.enemyViewMap) {
|
||||
const locator = this.indexer.indexToLocator(index);
|
||||
yield [locator, view];
|
||||
}
|
||||
}
|
||||
|
||||
addAura(aura: IAuraView<TAttr>): void {
|
||||
addAura(aura: IAuraView<TEnemy>): void {
|
||||
this.globalAuraList.add(aura);
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
deleteAura(aura: IAuraView<TAttr>): void {
|
||||
deleteAura(aura: IAuraView<TEnemy>): void {
|
||||
this.globalAuraList.delete(aura);
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void {
|
||||
attachMapDamage(damage: IMapDamage<TEnemy, THero> | null): void {
|
||||
this.mapDamage = damage;
|
||||
if (damage) {
|
||||
damage.refreshAll();
|
||||
}
|
||||
}
|
||||
|
||||
getMapDamage(): IMapDamage<TAttr, THero> | null {
|
||||
getMapDamage(): IMapDamage<TEnemy, THero> | null {
|
||||
return this.mapDamage;
|
||||
}
|
||||
|
||||
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void {
|
||||
attachDamageSystem(system: IDamageSystem<TEnemy, THero> | null): void {
|
||||
this.damageSystem = system;
|
||||
if (system) {
|
||||
system.bindHeroStatus(this.bindedHero);
|
||||
}
|
||||
}
|
||||
|
||||
getDamageSystem(): IDamageSystem<TAttr, THero> | null {
|
||||
getDamageSystem(): IDamageSystem<TEnemy, THero> | null {
|
||||
return this.damageSystem;
|
||||
}
|
||||
|
||||
@ -360,9 +369,9 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
*/
|
||||
private convertSpecial(
|
||||
special: ISpecial<any>,
|
||||
handler: IReadonlyEnemyHandler<TAttr, THero>
|
||||
): IEnemyAuraView<TAttr, any, any> | null {
|
||||
let matched: IAuraConverter<TAttr, THero> | null = null;
|
||||
handler: IReadonlyEnemyHandler<TEnemy, THero>
|
||||
): IEnemyAuraView<TEnemy, any, any> | null {
|
||||
let matched: IAuraConverter<TEnemy, THero> | null = null;
|
||||
for (const converter of this.auraConverter) {
|
||||
if (!this.converterStatus.get(converter)) continue;
|
||||
if (converter.shouldConvert(special, handler)) {
|
||||
@ -382,7 +391,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* 将光环按优先级插入到有序表中
|
||||
* @param aura 光环视图
|
||||
*/
|
||||
private insertIntoSortedAura(aura: IAuraView<TAttr>): void {
|
||||
private insertIntoSortedAura(aura: IAuraView<TEnemy>): void {
|
||||
const set = this.sortedAura.getOrInsertComputed(
|
||||
aura.priority,
|
||||
() => new Set()
|
||||
@ -394,7 +403,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* 从优先级表中移除一个光环
|
||||
* @param aura 光环视图
|
||||
*/
|
||||
private removeFromSortedAura(aura: IAuraView<TAttr>): void {
|
||||
private removeFromSortedAura(aura: IAuraView<TEnemy>): void {
|
||||
const set = this.sortedAura.get(aura.priority);
|
||||
if (set) {
|
||||
set.delete(aura);
|
||||
@ -412,12 +421,12 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* @param currentPriority 当前处理的优先级
|
||||
*/
|
||||
private processSpecialModifier(
|
||||
modifier: IEnemySpecialModifier<TAttr>,
|
||||
handler: IEnemyHandler<TAttr, THero>,
|
||||
modifier: IEnemySpecialModifier<TEnemy>,
|
||||
handler: IEnemyHandler<TEnemy, THero>,
|
||||
currentPriority: number
|
||||
): Set<IAuraView<TAttr>> {
|
||||
): Set<IAuraView<TEnemy>> {
|
||||
const enemy = handler.enemy;
|
||||
const affectedAuras = new Set<IAuraView<TAttr>>();
|
||||
const affectedAuras = new Set<IAuraView<TEnemy>>();
|
||||
const toAdd = modifier.add(handler);
|
||||
const toDelete = modifier.delete(handler);
|
||||
|
||||
@ -489,7 +498,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* @param currentPriority 当前处理的优先级
|
||||
*/
|
||||
private processSpecialQuery(
|
||||
effect: IEnemySpecialQueryEffect<TAttr, THero>,
|
||||
effect: IEnemySpecialQueryEffect<TEnemy, THero>,
|
||||
currentPriority: number
|
||||
): void {
|
||||
const modifier = effect.for(this);
|
||||
@ -521,7 +530,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* @param currentPriority 当前处理的优先级
|
||||
*/
|
||||
private processAuraSpecial(
|
||||
aura: IAuraView<TAttr>,
|
||||
aura: IAuraView<TEnemy>,
|
||||
currentPriority: number
|
||||
): void {
|
||||
const param = aura.getRangeParam();
|
||||
@ -696,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;
|
||||
this.dirtyEnemy.add(view);
|
||||
if (this.damageSystem) {
|
||||
@ -711,8 +720,8 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* @param locator 怪物位置
|
||||
*/
|
||||
private refreshSpecialModifier(
|
||||
modifier: IEnemySpecialModifier<TAttr>,
|
||||
handler: IEnemyHandler<TAttr, THero>
|
||||
modifier: IEnemySpecialModifier<TEnemy>,
|
||||
handler: IEnemyHandler<TEnemy, THero>
|
||||
): void {
|
||||
const enemy = handler.enemy;
|
||||
const toAdd = modifier.add(handler);
|
||||
@ -758,7 +767,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
* 刷新单个怪物视图的计算结果
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
private refreshEnemy(view: EnemyView<TAttr>): void {
|
||||
private refreshEnemy(view: EnemyView<TEnemy>): void {
|
||||
const locator = this.getEnemyLocatorByView(view);
|
||||
if (!locator) return;
|
||||
|
||||
@ -853,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.needTotallyRefresh.has(view)) {
|
||||
this.needUpdate = true;
|
||||
@ -863,11 +872,11 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||
return;
|
||||
}
|
||||
|
||||
this.refreshEnemy(view as EnemyView<TAttr>);
|
||||
this.refreshEnemy(view as EnemyView<TEnemy>);
|
||||
|
||||
for (const requestedView of this.requestedCommonContext) {
|
||||
if (requestedView === view) continue;
|
||||
this.refreshEnemy(requestedView as EnemyView<TAttr>);
|
||||
this.refreshEnemy(requestedView as EnemyView<TEnemy>);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { clamp } from 'lodash-es';
|
||||
import { ITileLocator, logger } from '@motajs/common';
|
||||
import {
|
||||
CriticalableHeroStatus,
|
||||
@ -8,11 +9,11 @@ import {
|
||||
IEnemyCritical,
|
||||
IEnemyDamageInfo,
|
||||
IReadonlyEnemyHandler,
|
||||
IEnemyView,
|
||||
IReadonlyEnemy
|
||||
IEnemyView
|
||||
} from './types';
|
||||
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { IReadonlyEnemy } from '../enemy';
|
||||
import { IStateBase } from '../types';
|
||||
|
||||
interface ICriticalSearchResult {
|
||||
/** 此临界点的属性值 */
|
||||
@ -21,20 +22,23 @@ interface ICriticalSearchResult {
|
||||
readonly info: IEnemyDamageInfo;
|
||||
}
|
||||
|
||||
export class DamageContext<TAttr, THero> implements IDamageContext<
|
||||
TAttr,
|
||||
export class DamageContext<TEnemy, THero> implements IDamageContext<
|
||||
TEnemy,
|
||||
THero
|
||||
> {
|
||||
/** 当前正在使用的计算器 */
|
||||
protected calculator: IDamageCalculator<TAttr, THero> | null;
|
||||
protected calculator: IDamageCalculator<TEnemy, THero> | null;
|
||||
/** 当前勇士属性 */
|
||||
protected heroStatus: IReadonlyHeroAttribute<THero> | null;
|
||||
|
||||
readonly dataState: IStateBase<TEnemy, THero>;
|
||||
|
||||
constructor(
|
||||
readonly context: IEnemyContext<TAttr, THero>,
|
||||
calculator: IDamageCalculator<TAttr, THero> | null = null,
|
||||
readonly context: IEnemyContext<TEnemy, THero>,
|
||||
calculator: IDamageCalculator<TEnemy, THero> | null = null,
|
||||
heroStatus: IReadonlyHeroAttribute<THero> | null = null
|
||||
) {
|
||||
this.dataState = context.dataState;
|
||||
this.calculator = calculator;
|
||||
this.heroStatus = heroStatus;
|
||||
}
|
||||
@ -46,14 +50,14 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
|
||||
* @param hero 勇士属性对象
|
||||
*/
|
||||
private createReadonlyHandler(
|
||||
enemy: IReadonlyEnemy<TAttr>,
|
||||
enemy: IReadonlyEnemy<TEnemy>,
|
||||
locator: ITileLocator,
|
||||
hero: IReadonlyHeroAttribute<THero>
|
||||
): IReadonlyEnemyHandler<TAttr, THero> {
|
||||
return { enemy, locator, hero };
|
||||
): IReadonlyEnemyHandler<TEnemy, THero> {
|
||||
return { enemy, locator, hero, data: this.dataState };
|
||||
}
|
||||
|
||||
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null {
|
||||
getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null {
|
||||
if (!this.heroStatus) {
|
||||
logger.warn(107);
|
||||
return null;
|
||||
@ -73,7 +77,7 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
|
||||
}
|
||||
|
||||
getDamageInfoByComputed(
|
||||
enemy: IReadonlyEnemy<TAttr>
|
||||
enemy: IReadonlyEnemy<TEnemy>
|
||||
): IEnemyDamageInfo | null {
|
||||
if (!this.heroStatus) {
|
||||
logger.warn(107);
|
||||
@ -96,7 +100,7 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
|
||||
}
|
||||
|
||||
*calculateCritical(
|
||||
view: IEnemyView<TAttr>,
|
||||
view: IEnemyView<TEnemy>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
precision: number = 12
|
||||
): Generator<IEnemyCritical, void, void> {
|
||||
@ -168,7 +172,7 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
|
||||
* @param maxIterations 最大迭代数量
|
||||
*/
|
||||
private findNextCritical(
|
||||
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||
handler: IReadonlyEnemyHandler<TEnemy, THero>,
|
||||
hero: IHeroAttribute<THero>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
currentValue: number,
|
||||
@ -206,24 +210,24 @@ export class DamageContext<TAttr, THero> implements IDamageContext<
|
||||
}
|
||||
}
|
||||
|
||||
export class DamageSystem<TAttr, THero>
|
||||
extends DamageContext<TAttr, THero>
|
||||
implements IDamageSystem<TAttr, THero>
|
||||
export class DamageSystem<TEnemy, THero>
|
||||
extends DamageContext<TEnemy, THero>
|
||||
implements IDamageSystem<TEnemy, THero>
|
||||
{
|
||||
/** 怪物伤害缓存 */
|
||||
private readonly cache: Map<IEnemyView<TAttr>, IEnemyDamageInfo> =
|
||||
private readonly cache: Map<IEnemyView<TEnemy>, IEnemyDamageInfo> =
|
||||
new Map();
|
||||
|
||||
constructor(context: IEnemyContext<TAttr, THero>) {
|
||||
constructor(context: IEnemyContext<TEnemy, THero>) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void {
|
||||
useCalculator(calculator: IDamageCalculator<TEnemy, THero>): void {
|
||||
this.calculator = calculator;
|
||||
this.markAllDirty();
|
||||
}
|
||||
|
||||
getCalculator(): IDamageCalculator<TAttr, THero> | null {
|
||||
getCalculator(): IDamageCalculator<TEnemy, THero> | null {
|
||||
return this.calculator;
|
||||
}
|
||||
|
||||
@ -232,7 +236,7 @@ export class DamageSystem<TAttr, THero>
|
||||
this.markAllDirty();
|
||||
}
|
||||
|
||||
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null {
|
||||
getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null {
|
||||
const cached = this.cache.get(enemy);
|
||||
if (cached) {
|
||||
return cached;
|
||||
@ -246,7 +250,7 @@ export class DamageSystem<TAttr, THero>
|
||||
}
|
||||
|
||||
getDamageInfoByComputed(
|
||||
enemy: IReadonlyEnemy<TAttr>
|
||||
enemy: IReadonlyEnemy<TEnemy>
|
||||
): IEnemyDamageInfo | null {
|
||||
const view = this.context.getViewByComputed(enemy);
|
||||
if (view) {
|
||||
@ -264,11 +268,11 @@ export class DamageSystem<TAttr, THero>
|
||||
return info;
|
||||
}
|
||||
|
||||
markDirty(enemy: IEnemyView<TAttr>): void {
|
||||
markDirty(enemy: IEnemyView<TEnemy>): void {
|
||||
this.cache.delete(enemy);
|
||||
}
|
||||
|
||||
deleteEnemy(enemy: IEnemyView<TAttr>): void {
|
||||
deleteEnemy(enemy: IEnemyView<TEnemy>): void {
|
||||
this.cache.delete(enemy);
|
||||
}
|
||||
|
||||
@ -276,7 +280,7 @@ export class DamageSystem<TAttr, THero>
|
||||
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);
|
||||
}
|
||||
}
|
||||
42
packages-user/data-base/src/combat/enemy.ts
Normal file
42
packages-user/data-base/src/combat/enemy.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { IEnemy, IReadonlyEnemy } from '../enemy';
|
||||
import { IEnemyView, IEnemyContext } from './types';
|
||||
|
||||
export class EnemyView<TAttr> implements IEnemyView<TAttr> {
|
||||
/** 计算后怪物 */
|
||||
private readonly computedEnemy: IEnemy<TAttr>;
|
||||
|
||||
constructor(
|
||||
readonly baseEnemy: IEnemy<TAttr>,
|
||||
readonly context: IEnemyContext<TAttr, unknown>
|
||||
) {
|
||||
this.computedEnemy = baseEnemy.clone();
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.computedEnemy.copyFrom(this.baseEnemy);
|
||||
}
|
||||
|
||||
getBaseEnemy(): IReadonlyEnemy<TAttr> {
|
||||
return this.baseEnemy;
|
||||
}
|
||||
|
||||
getComputedEnemy(): IReadonlyEnemy<TAttr> {
|
||||
this.context.requestRefresh(this);
|
||||
return this.computedEnemy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取计算中怪物对象,这个接口不对外暴露,仅在系统内部的 EnemyContext 中使用。
|
||||
*/
|
||||
getComputingEnemy(): IEnemy<TAttr> {
|
||||
return this.computedEnemy;
|
||||
}
|
||||
|
||||
getModifiableEnemy(): IEnemy<TAttr> {
|
||||
return this.baseEnemy;
|
||||
}
|
||||
|
||||
markDirty(): void {
|
||||
this.context.markDirty(this);
|
||||
}
|
||||
}
|
||||
5
packages-user/data-base/src/combat/index.ts
Normal file
5
packages-user/data-base/src/combat/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from './context';
|
||||
export * from './damage';
|
||||
export * from './enemy';
|
||||
export * from './mapDamage';
|
||||
export * from './types';
|
||||
@ -7,9 +7,10 @@ import {
|
||||
IMapDamageConverter,
|
||||
IMapDamageInfo,
|
||||
IMapDamageReducer,
|
||||
IMapDamageView,
|
||||
IMapLocIndexer
|
||||
IMapDamageView
|
||||
} from './types';
|
||||
import { ILocationHelper } from '../common/indexer';
|
||||
import { IStateBase } from '../types';
|
||||
|
||||
interface IPointInfo {
|
||||
/** 该点所有的地图伤害 */
|
||||
@ -34,9 +35,9 @@ interface IDamageStore<TAttr> {
|
||||
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;
|
||||
|
||||
@ -45,14 +46,14 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||
/** 有来源地图伤害,坐标 -> 点伤害信息 */
|
||||
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();
|
||||
/** 地图伤害信息 -> 其信息对象 */
|
||||
private readonly damageStore: Map<IMapDamageInfo, IDamageStore<TAttr>> =
|
||||
private readonly damageStore: Map<IMapDamageInfo, IDamageStore<TEnemy>> =
|
||||
new Map();
|
||||
/** 怪物视图 -> 其影响对象 */
|
||||
private readonly enemyStore: Map<
|
||||
IEnemyView<TAttr>,
|
||||
IEnemyView<TEnemy>,
|
||||
Set<IMapDamageView<any>>
|
||||
> = new Map();
|
||||
/** 需要延迟刷新的坐标索引 */
|
||||
@ -61,13 +62,16 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||
private readonly reducedCache: Map<number, IMapDamageInfo> = new Map();
|
||||
|
||||
/** 坐标索引对象 */
|
||||
private readonly indexer: IMapLocIndexer;
|
||||
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.dataState = context.dataState;
|
||||
}
|
||||
|
||||
useConverter(converter: IMapDamageConverter<TAttr, THero>): void {
|
||||
useConverter(converter: IMapDamageConverter<TEnemy, THero>): void {
|
||||
this.converter = converter;
|
||||
this.refreshAll();
|
||||
}
|
||||
@ -78,15 +82,16 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||
* @param locator 怪物位置
|
||||
*/
|
||||
private createReadonlyHandler(
|
||||
view: IEnemyView<TAttr>,
|
||||
view: IEnemyView<TEnemy>,
|
||||
locator: ITileLocator
|
||||
): IReadonlyEnemyHandler<TAttr, THero> | null {
|
||||
): IReadonlyEnemyHandler<TEnemy, THero> | null {
|
||||
const hero = this.context.getBindedHero();
|
||||
if (!hero) return null;
|
||||
return {
|
||||
enemy: view.getComputedEnemy(),
|
||||
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));
|
||||
}
|
||||
|
||||
markEnemyDirty(view: IEnemyView<TAttr>): void {
|
||||
markEnemyDirty(view: IEnemyView<TEnemy>): void {
|
||||
const store = this.enemyStore.get(view);
|
||||
const locator = this.context.getEnemyLocatorByView(view);
|
||||
if (!store) {
|
||||
@ -141,7 +146,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||
this.refreshEnemyAndClearCache(view, locator);
|
||||
}
|
||||
|
||||
deleteEnemy(view: IEnemyView<TAttr>): void {
|
||||
deleteEnemy(view: IEnemyView<TEnemy>): void {
|
||||
const store = this.enemyStore.get(view);
|
||||
if (!store) return;
|
||||
const collection = new Set<number>();
|
||||
@ -231,7 +236,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||
* 移除指定怪物所产生的地图伤害
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
private removeEnemyAffecting(view: IEnemyView<TAttr>) {
|
||||
private removeEnemyAffecting(view: IEnemyView<TEnemy>) {
|
||||
const views = this.enemyStore.get(view);
|
||||
if (!views) return;
|
||||
views.forEach(viewItem => {
|
||||
@ -253,7 +258,7 @@ export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||
* 刷新指定位置的怪物地图伤害,并执行刷新缓存的操作
|
||||
*/
|
||||
private refreshEnemyAndClearCache(
|
||||
view: IEnemyView<TAttr>,
|
||||
view: IEnemyView<TEnemy>,
|
||||
locator: ITileLocator
|
||||
) {
|
||||
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);
|
||||
if (!this.converter) return;
|
||||
const handler = this.createReadonlyHandler(view, locator);
|
||||
703
packages-user/data-base/src/combat/types.ts
Normal file
703
packages-user/data-base/src/combat/types.ts
Normal file
@ -0,0 +1,703 @@
|
||||
import { ITileLocator, IRange } from '@motajs/common';
|
||||
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
|
||||
import { IReadonlyHeroAttribute, IHeroAttribute } from '../hero';
|
||||
import { ILocationHelper } from '../common/indexer';
|
||||
import { IStateBase } from '../types';
|
||||
|
||||
//#region 辅助接口
|
||||
|
||||
export interface IEnemyHandler<TEnemy, THero> {
|
||||
/** 怪物属性信息 */
|
||||
readonly enemy: IEnemy<TEnemy>;
|
||||
/** 怪物定位符 */
|
||||
readonly locator: ITileLocator;
|
||||
/** 勇士属性信息 */
|
||||
readonly hero: IReadonlyHeroAttribute<THero>;
|
||||
/** 当前全局状态对象 */
|
||||
readonly data: IStateBase<TEnemy, THero>;
|
||||
}
|
||||
|
||||
export interface IReadonlyEnemyHandler<TEnemy, THero> {
|
||||
/** 怪物属性信息 */
|
||||
readonly enemy: IReadonlyEnemy<TEnemy>;
|
||||
/** 怪物定位符 */
|
||||
readonly locator: ITileLocator;
|
||||
/** 勇士属性信息 */
|
||||
readonly hero: IReadonlyHeroAttribute<THero>;
|
||||
/** 当前全局状态对象 */
|
||||
readonly data: IStateBase<TEnemy, THero>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 怪物对象
|
||||
|
||||
export interface IEnemyView<TEnemy> {
|
||||
/** 怪物视图所属的上下文 */
|
||||
readonly context: IEnemyContext<TEnemy, unknown>;
|
||||
|
||||
/**
|
||||
* 重置此怪物视图的状态,将计算后怪物对象恢复至初始状态
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* 获取基本怪物对象
|
||||
*/
|
||||
getBaseEnemy(): IReadonlyEnemy<TEnemy>;
|
||||
|
||||
/**
|
||||
* 获取计算后的怪物对象,返回的怪物对象同引用
|
||||
*/
|
||||
getComputedEnemy(): IReadonlyEnemy<TEnemy>;
|
||||
|
||||
/**
|
||||
* 获取可修改的怪物对象。如果修改此方法获取的怪物对象,那么怪物的真实信息是不会刷新的,
|
||||
* 需要手动调用 markDirty 方法来刷新。
|
||||
*/
|
||||
getModifiableEnemy(): IEnemy<TEnemy>;
|
||||
|
||||
/**
|
||||
* 将此怪物标记为脏,需要更新
|
||||
*/
|
||||
markDirty(): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 光环与查询
|
||||
|
||||
export interface IEnemySpecialModifier<TEnemy> {
|
||||
/**
|
||||
* 获取要添加到指定怪物身上的特殊属性
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
add(handler: IReadonlyEnemyHandler<TEnemy, unknown>): ISpecial<any>[];
|
||||
|
||||
/**
|
||||
* 获取制定怪物身上要删除的特殊属性
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
delete(handler: IReadonlyEnemyHandler<TEnemy, unknown>): ISpecial<any>[];
|
||||
|
||||
/**
|
||||
* 修改一个怪物的特殊属性,如果真正进行了修改则返回 true,否则返回 false
|
||||
* @param handler 信息对象
|
||||
* @param special 要修改的怪物特殊属性
|
||||
*/
|
||||
modify(
|
||||
handler: IEnemyHandler<TEnemy, unknown>,
|
||||
special: ISpecial<any>
|
||||
): boolean;
|
||||
}
|
||||
|
||||
export interface IAuraView<TEnemy, TRange = any> {
|
||||
/** 此光环视图的优先级 */
|
||||
readonly priority: number;
|
||||
/** 此光环视图的影响范围 */
|
||||
readonly range: IRange<TRange>;
|
||||
|
||||
/** 这个光环视图是否有可能修改怪物的基本属性 */
|
||||
readonly couldApplyBase: boolean;
|
||||
/** 这个光环视图是否有可能修改怪物的特殊属性 */
|
||||
readonly couldApplySpecial: boolean;
|
||||
|
||||
/**
|
||||
* 获取范围扫描参数
|
||||
*/
|
||||
getRangeParam(): TRange;
|
||||
|
||||
/**
|
||||
* 对指定怪物对象施加修饰器
|
||||
* @param handler 信息对象
|
||||
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
||||
*/
|
||||
apply(
|
||||
handler: IEnemyHandler<TEnemy, unknown>,
|
||||
baseEnemy: IReadonlyEnemy<TEnemy>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 对指定怪物对象添加特殊属性修饰器
|
||||
* @param handler 信息对象
|
||||
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
||||
*/
|
||||
applySpecial(
|
||||
handler: IEnemyHandler<TEnemy, unknown>,
|
||||
baseEnemy: IReadonlyEnemy<TEnemy>
|
||||
): IEnemySpecialModifier<TEnemy> | null;
|
||||
}
|
||||
|
||||
export interface IEnemyAuraView<TEnemy, TRange, TSpecial> extends IAuraView<
|
||||
TEnemy,
|
||||
TRange
|
||||
> {
|
||||
/** 此光环视图所属的怪物 */
|
||||
readonly enemy: IReadonlyEnemy<TEnemy>;
|
||||
/** 此光环视图所属的特殊属性 */
|
||||
readonly special: ISpecial<TSpecial>;
|
||||
/** 此光环视图所属怪物的定位符 */
|
||||
readonly locator: ITileLocator;
|
||||
}
|
||||
|
||||
export interface IAuraConverter<TEnemy, THero> {
|
||||
/**
|
||||
* 判断一个特殊属性是否应该被当前光环转换器执行转换
|
||||
* @param special 要转换的特殊属性
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
shouldConvert(
|
||||
special: ISpecial<any>,
|
||||
handler: IReadonlyEnemyHandler<TEnemy, THero>
|
||||
): boolean;
|
||||
|
||||
/**
|
||||
* 将一个特殊属性转换为光环视图
|
||||
*/
|
||||
convert(
|
||||
special: ISpecial<any>,
|
||||
handler: IReadonlyEnemyHandler<TEnemy, THero>,
|
||||
context: IEnemyContext<TEnemy, THero>
|
||||
): IEnemyAuraView<TEnemy, any, any>;
|
||||
}
|
||||
|
||||
export interface IEnemySpecialQueryModifier<
|
||||
TEnemy,
|
||||
THero
|
||||
> extends IEnemySpecialModifier<TEnemy> {
|
||||
/**
|
||||
* 判断一个怪物是否应该查询外部状态
|
||||
*/
|
||||
shouldQuery(handler: IReadonlyEnemyHandler<TEnemy, THero>): boolean;
|
||||
}
|
||||
|
||||
export interface IEnemySpecialQueryEffect<TEnemy, THero> {
|
||||
/** 效果优先级,与光环属性共用 */
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* 根据传入的怪物上下文,获取对应的怪物特殊属性修饰器
|
||||
*/
|
||||
for(
|
||||
ctx: IEnemyContext<TEnemy, THero>
|
||||
): IEnemySpecialQueryModifier<TEnemy, THero>;
|
||||
}
|
||||
|
||||
export interface IEnemyCommonQueryEffect<TEnemy, THero> {
|
||||
/** 优先级,越高的越先执行 */
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* 对怪物的某个特殊属性施加常规查询效果
|
||||
*/
|
||||
apply(
|
||||
handler: IEnemyHandler<TEnemy, THero>,
|
||||
special: ISpecial<any>,
|
||||
query: () => IEnemyContext<TEnemy, THero>
|
||||
): void;
|
||||
}
|
||||
|
||||
export interface IEnemyFinalEffect<TEnemy, THero> {
|
||||
/** 效果优先级,越高会越先被执行 */
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* 向怪物施加最终修饰效果
|
||||
*/
|
||||
apply(handler: IEnemyHandler<TEnemy, THero>): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 地图伤害
|
||||
|
||||
export interface IMapDamageInfoExtra {
|
||||
/** 捕捉怪物信息 */
|
||||
catch: Set<ITileLocator>;
|
||||
/** 阻击怪物信息 */
|
||||
repulse: Set<ITileLocator>;
|
||||
}
|
||||
|
||||
export interface IMapDamageInfo {
|
||||
/** 伤害值 */
|
||||
damage: number;
|
||||
/** 伤害类型 */
|
||||
type: number;
|
||||
/** 地图伤害额外信息 */
|
||||
extra: IMapDamageInfoExtra;
|
||||
}
|
||||
|
||||
export interface IMapDamageView<T = any> {
|
||||
/**
|
||||
* 获取地图伤害影响范围
|
||||
*/
|
||||
getRange(): IRange<T>;
|
||||
|
||||
/**
|
||||
* 获取范围参数
|
||||
*/
|
||||
getRangeParam(): T;
|
||||
|
||||
/**
|
||||
* 获取指定位置的地图伤害,会对坐标进行判断
|
||||
* @param locator 伤害位置
|
||||
*/
|
||||
getDamageAt(locator: ITileLocator): Readonly<IMapDamageInfo> | null;
|
||||
|
||||
/**
|
||||
* 获取指定位置的地图伤害,会对坐标进行判断
|
||||
* @param locator 伤害位置
|
||||
*/
|
||||
getDamageWithoutCheck(
|
||||
locator: ITileLocator
|
||||
): Readonly<IMapDamageInfo> | null;
|
||||
}
|
||||
|
||||
export interface IMapDamageConverter<TEnemy, THero> {
|
||||
/**
|
||||
* 转换地图伤害视图
|
||||
*/
|
||||
convert(
|
||||
handler: IReadonlyEnemyHandler<TEnemy, THero>,
|
||||
context: IEnemyContext<TEnemy, THero>
|
||||
): IMapDamageView<any>[];
|
||||
}
|
||||
|
||||
export interface IMapDamageReducer {
|
||||
/**
|
||||
* 对伤害信息进行合并
|
||||
*/
|
||||
reduce(
|
||||
info: Iterable<Readonly<IMapDamageInfo>>,
|
||||
locator: ITileLocator
|
||||
): Readonly<IMapDamageInfo>;
|
||||
}
|
||||
|
||||
export interface IMapDamage<TEnemy, THero> {
|
||||
/** 当前绑定的怪物上下文 */
|
||||
readonly context: IEnemyContext<TEnemy, THero>;
|
||||
/** 地图伤害系统绑定的全局状态对象 */
|
||||
readonly dataState: IStateBase<TEnemy, THero>;
|
||||
|
||||
/**
|
||||
* 设置地图伤害转换器,并基于当前上下文重建所有地图伤害视图
|
||||
* @param converter 地图伤害转换器
|
||||
*/
|
||||
useConverter(converter: IMapDamageConverter<TEnemy, THero>): void;
|
||||
|
||||
/**
|
||||
* 设置地图伤害合并器
|
||||
* @param reducer 地图伤害合并器
|
||||
*/
|
||||
useReducer(reducer: IMapDamageReducer): void;
|
||||
|
||||
/**
|
||||
* 在指定位置添加一条无来源地图伤害
|
||||
* @param locator 地图定位符
|
||||
* @param info 地图伤害信息
|
||||
*/
|
||||
addMapDamage(locator: ITileLocator, info: IMapDamageInfo): void;
|
||||
|
||||
/**
|
||||
* 在指定位置删除一条无来源地图伤害
|
||||
* @param locator 地图定位符
|
||||
* @param info 地图伤害信息
|
||||
*/
|
||||
deleteMapDamage(locator: ITileLocator, info: IMapDamageInfo): void;
|
||||
|
||||
/**
|
||||
* 将指定位置标记为脏,后续访问时会重新计算该点的有来源伤害
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
markDirty(locator: ITileLocator): void;
|
||||
|
||||
/**
|
||||
* 将指定怪物对应的地图伤害标记为脏并刷新
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
markEnemyDirty(view: IEnemyView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 基于当前上下文重新刷新全部有来源地图伤害
|
||||
*/
|
||||
refreshAll(): void;
|
||||
|
||||
/**
|
||||
* 删除指定怪物带来的全部地图伤害来源
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
deleteEnemy(view: IEnemyView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 获取指定位置合并后的地图伤害
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
getReducedDamage(locator: ITileLocator): Readonly<IMapDamageInfo> | null;
|
||||
|
||||
/**
|
||||
* 获取指定位置未合并的地图伤害列表
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
getSeparatedDamage(
|
||||
locator: ITileLocator
|
||||
): Iterable<Readonly<IMapDamageInfo>>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 伤害系统
|
||||
|
||||
export interface IEnemyDamageInfo {
|
||||
/** 战斗伤害值 */
|
||||
readonly damage: number;
|
||||
/** 战斗回合数 */
|
||||
readonly turn: number;
|
||||
}
|
||||
|
||||
export interface IEnemyCritical {
|
||||
/** 此临界点中指定勇士属性的值 */
|
||||
readonly nextValue: number;
|
||||
/** 当前勇士指定属性的值 */
|
||||
readonly baseValue: number;
|
||||
/** 此临界点中指定勇士数值的值与当前值的差,即 `nextValue - baseValue` */
|
||||
readonly nextDiff: number;
|
||||
/** 当前状态下怪物的伤害信息 */
|
||||
readonly baseInfo: IEnemyDamageInfo;
|
||||
/** 此临界点下怪物的伤害信息 */
|
||||
readonly info: IEnemyDamageInfo;
|
||||
/** 此临界点的伤害值与当前伤害值的差 */
|
||||
readonly damageDiff: number;
|
||||
}
|
||||
|
||||
export type CriticalableHeroStatus<THero> = keyof {
|
||||
[P in keyof THero as THero[P] extends number ? P : never]: number;
|
||||
};
|
||||
|
||||
export interface IDamageCalculator<TEnemy, THero> {
|
||||
/**
|
||||
* 计算战斗伤害信息
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
calculate(handler: IReadonlyEnemyHandler<TEnemy, THero>): IEnemyDamageInfo;
|
||||
|
||||
/**
|
||||
* 获取临界计算的上界
|
||||
* @param handler 信息对象
|
||||
* @param attribute 勇士的临界属性
|
||||
*/
|
||||
getCriticalLimit(
|
||||
handler: IReadonlyEnemyHandler<TEnemy, THero>,
|
||||
attribute: CriticalableHeroStatus<THero>
|
||||
): number;
|
||||
}
|
||||
|
||||
export interface IDamageContext<TEnemy, THero> {
|
||||
/** 伤害上下文所属的全局状态对象 */
|
||||
readonly dataState: IStateBase<TEnemy, THero>;
|
||||
|
||||
/**
|
||||
* 获取战斗伤害信息
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null;
|
||||
|
||||
/**
|
||||
* 根据怪物对象获取战斗伤害信息
|
||||
* @param enemy 怪物对象
|
||||
*/
|
||||
getDamageInfoByComputed(
|
||||
enemy: IReadonlyEnemy<TEnemy>
|
||||
): IEnemyDamageInfo | null;
|
||||
|
||||
/**
|
||||
* 计算怪物在指定勇士属性下的临界
|
||||
* @param enemy 怪物视图
|
||||
* @param attribute 计算临界的目标勇士属性,比如计算攻击临界、自定义属性的临界等等
|
||||
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可,默认是 12
|
||||
*/
|
||||
calculateCritical(
|
||||
enemy: IEnemyView<TEnemy>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
precision?: number
|
||||
): Generator<IEnemyCritical, void, void>;
|
||||
}
|
||||
|
||||
export interface IDamageSystem<TEnemy, THero> extends IDamageContext<
|
||||
TEnemy,
|
||||
THero
|
||||
> {
|
||||
/** 伤害系统所属的上下文 */
|
||||
readonly context: IEnemyContext<TEnemy, THero>;
|
||||
|
||||
/**
|
||||
* 设置当前伤害计算系统使用的伤害计算器
|
||||
* @param calculator 伤害计算器
|
||||
*/
|
||||
useCalculator(calculator: IDamageCalculator<TEnemy, THero>): void;
|
||||
|
||||
/**
|
||||
* 获取当前使用的伤害计算器
|
||||
*/
|
||||
getCalculator(): IDamageCalculator<TEnemy, THero> | null;
|
||||
|
||||
/**
|
||||
* 绑定勇士信息
|
||||
* @param hero 勇士信息
|
||||
*/
|
||||
bindHeroStatus(hero: IReadonlyHeroAttribute<THero> | null): void;
|
||||
|
||||
/**
|
||||
* 将指定的怪物标记为脏
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
markDirty(enemy: IEnemyView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 删除指定的怪物
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
deleteEnemy(enemy: IEnemyView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 将所有怪物标记为脏
|
||||
*/
|
||||
markAllDirty(): void;
|
||||
|
||||
/**
|
||||
* 修改勇士属性,然后返回修改后勇士属性所组成的计算对象,不影响当前伤害系统的状态
|
||||
* @param modify 勇士修改函数
|
||||
*/
|
||||
with(hero: IHeroAttribute<THero>): IDamageContext<TEnemy, THero>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 上下文
|
||||
|
||||
export interface IEnemyContext<TEnemy, THero> {
|
||||
/** 怪物上下文宽度 */
|
||||
readonly width: number;
|
||||
/** 怪物上下文高度 */
|
||||
readonly height: number;
|
||||
/** 此上下文使用的索引对象 */
|
||||
readonly indexer: ILocationHelper;
|
||||
/** 当前怪物上下文绑定的全局状态对象 */
|
||||
readonly dataState: IStateBase<TEnemy, THero>;
|
||||
|
||||
/**
|
||||
* 调整上下文尺寸,并清空当前上下文中的所有怪物与状态
|
||||
* @param width 地图宽度
|
||||
* @param height 地图高度
|
||||
*/
|
||||
resize(width: number, height: number): void;
|
||||
|
||||
/**
|
||||
* 注册一个光环转换器
|
||||
* @param converter 光环转换器
|
||||
*/
|
||||
registerAuraConverter(converter: IAuraConverter<TEnemy, THero>): void;
|
||||
|
||||
/**
|
||||
* 注销一个光环转换器
|
||||
* @param converter 光环转换器
|
||||
*/
|
||||
unregisterAuraConverter(converter: IAuraConverter<TEnemy, THero>): void;
|
||||
|
||||
/**
|
||||
* 设置光环转换器的启用状态
|
||||
* @param converter 光环转换器
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
setAuraConverterEnabled(
|
||||
converter: IAuraConverter<TEnemy, THero>,
|
||||
enabled: boolean
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注册一个特殊属性查询效果
|
||||
* @param effect 特殊属性查询效果
|
||||
*/
|
||||
registerSpecialQueryEffect(
|
||||
effect: IEnemySpecialQueryEffect<TEnemy, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注销一个特殊属性查询效果
|
||||
* @param effect 特殊属性查询效果
|
||||
*/
|
||||
unregisterSpecialQueryEffect(
|
||||
effect: IEnemySpecialQueryEffect<TEnemy, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 为指定特殊属性代码注册常规查询效果
|
||||
* @param code 特殊属性代码
|
||||
* @param effect 常规查询效果
|
||||
*/
|
||||
registerCommonQueryEffect(
|
||||
code: number,
|
||||
effect: IEnemyCommonQueryEffect<TEnemy, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注销指定特殊属性代码上的常规查询效果
|
||||
* @param code 特殊属性代码
|
||||
* @param effect 常规查询效果
|
||||
*/
|
||||
unregisterCommonQueryEffect(
|
||||
code: number,
|
||||
effect: IEnemyCommonQueryEffect<TEnemy, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注册一个最终效果
|
||||
* @param effect 最终效果
|
||||
*/
|
||||
registerFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void;
|
||||
|
||||
/**
|
||||
* 注销一个最终效果
|
||||
* @param effect 最终效果
|
||||
*/
|
||||
unregisterFinalEffect(effect: IEnemyFinalEffect<TEnemy, THero>): void;
|
||||
|
||||
/**
|
||||
* 绑定勇士对象
|
||||
* @param hero 勇士属性对象
|
||||
*/
|
||||
bindHero(hero: IReadonlyHeroAttribute<THero> | null): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的勇士属性对象
|
||||
*/
|
||||
getBindedHero(): IReadonlyHeroAttribute<THero> | null;
|
||||
|
||||
/**
|
||||
* 获取指定怪物对象当前所在位置
|
||||
* @param enemy 怪物对象
|
||||
*/
|
||||
getEnemyLocator(enemy: IEnemy<TEnemy>): Readonly<ITileLocator> | null;
|
||||
|
||||
/**
|
||||
* 获取指定怪物视图当前所在位置
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
getEnemyLocatorByView(
|
||||
view: IEnemyView<TEnemy>
|
||||
): Readonly<ITileLocator> | null;
|
||||
|
||||
/**
|
||||
* 根据定位符获取怪物视图
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
getEnemyByLocator(locator: ITileLocator): IEnemyView<TEnemy> | null;
|
||||
|
||||
/**
|
||||
* 根据坐标获取怪物视图
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
getEnemyByLoc(x: number, y: number): IEnemyView<TEnemy> | null;
|
||||
|
||||
/**
|
||||
* 根据计算后怪物对象反查怪物视图
|
||||
* @param enemy 计算后怪物对象
|
||||
*/
|
||||
getViewByComputed(enemy: IReadonlyEnemy<TEnemy>): IEnemyView<TEnemy> | null;
|
||||
|
||||
/**
|
||||
* 在指定位置放置一个怪物对象
|
||||
* @param locator 地图定位符
|
||||
* @param enemy 怪物对象
|
||||
*/
|
||||
setEnemyAt(locator: ITileLocator, enemy: IEnemy<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 删除指定位置的怪物
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
deleteEnemy(locator: ITileLocator): void;
|
||||
|
||||
/**
|
||||
* 扫描指定范围内的怪物视图
|
||||
* @param range 范围对象
|
||||
* @param param 范围参数
|
||||
*/
|
||||
scanRange<T>(
|
||||
range: IRange<T>,
|
||||
param: T
|
||||
): Iterable<[ITileLocator, IEnemyView<TEnemy>]>;
|
||||
|
||||
/**
|
||||
* 迭代上下文中的全部怪物
|
||||
*/
|
||||
iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TEnemy>]>;
|
||||
|
||||
/**
|
||||
* 添加一个全局光环视图
|
||||
* @param aura 光环视图
|
||||
*/
|
||||
addAura(aura: IAuraView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 删除一个全局光环视图
|
||||
* @param aura 光环视图
|
||||
*/
|
||||
deleteAura(aura: IAuraView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 绑定地图伤害管理器
|
||||
* @param damage 地图伤害管理器
|
||||
*/
|
||||
attachMapDamage(damage: IMapDamage<TEnemy, THero> | null): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的地图伤害管理器
|
||||
*/
|
||||
getMapDamage(): IMapDamage<TEnemy, THero> | null;
|
||||
|
||||
/**
|
||||
* 绑定伤害计算系统
|
||||
* @param system 伤害系统
|
||||
*/
|
||||
attachDamageSystem(system: IDamageSystem<TEnemy, unknown> | null): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的伤害计算系统
|
||||
*/
|
||||
getDamageSystem(): IDamageSystem<TEnemy, THero> | null;
|
||||
|
||||
/**
|
||||
* 重建当前上下文中的全部怪物计算结果
|
||||
*
|
||||
* 1. 对所有光环及特殊查询进行构建操作,这一步中会决定每个怪物所拥有的特殊属性,后续不会变动
|
||||
* 2. 执行所有的普通光环效果,修改怪物的基础属性
|
||||
* 3. 执行常规查询效果,允许查询上下文状态并修改怪物自身的基础属性
|
||||
* 4. 执行最终效果,不允许查询上下文状态,仅允许修改怪物自身的基础属性
|
||||
*/
|
||||
buildup(): void;
|
||||
|
||||
/**
|
||||
* 将指定怪物视图标记为脏
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
markDirty(view: IEnemyView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 申请刷新指定怪物视图
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
requestRefresh(view: IEnemyView<TEnemy>): void;
|
||||
|
||||
/**
|
||||
* 清空当前上下文中的所有对象与运行状态
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* 销毁当前上下文
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
282
packages-user/data-base/src/common/faceManager.ts
Normal file
282
packages-user/data-base/src/common/faceManager.ts
Normal file
@ -0,0 +1,282 @@
|
||||
import { FaceDirection } from './types';
|
||||
|
||||
//#region 接口与枚举
|
||||
|
||||
export const enum InternalFaceGroup {
|
||||
/** 四方向(上下左右) */
|
||||
Dir4,
|
||||
/** 八方向(上下左右+斜向) */
|
||||
Dir8
|
||||
}
|
||||
|
||||
export interface IFaceDescriptor {
|
||||
/** 横坐标增量 */
|
||||
readonly x: number;
|
||||
/** 纵坐标增量 */
|
||||
readonly y: number;
|
||||
}
|
||||
|
||||
export interface IFaceHandler<T extends number> {
|
||||
/**
|
||||
* 将任意朝向值降级为本组支持的方向。
|
||||
* 对于无法合理降级的方向(包括 `Unknown`),返回 `FaceDirection.Unknown`
|
||||
* @param dir 任意朝向值
|
||||
*/
|
||||
degrade(dir: number): T;
|
||||
|
||||
/**
|
||||
* 获取指定方向的单步坐标偏移量,输入先经过 `degrade`
|
||||
* @param dir 朝向
|
||||
*/
|
||||
movement(dir: number): IFaceDescriptor;
|
||||
|
||||
/**
|
||||
* 获取指定方向走 `count` 步的坐标偏移量,等价于 `movement * count`。
|
||||
* `count` 允许为负数,表示反向位移,输入先经过 `degrade`
|
||||
* @param dir 朝向
|
||||
* @param count 步数,允许为负
|
||||
*/
|
||||
move(dir: number, count: number): IFaceDescriptor;
|
||||
|
||||
/**
|
||||
* 获取本组内的反方向,输入先经过 `degrade`,`Unknown` 返回 `Unknown`
|
||||
* @param dir 朝向
|
||||
*/
|
||||
opposite(dir: number): T;
|
||||
|
||||
/**
|
||||
* 在本组方向集合内顺时针(默认)或逆时针旋转一步,输入先经过 `degrade`,
|
||||
* `Unknown` 返回 `Unknown`
|
||||
* @param dir 朝向
|
||||
* @param anticlockwise 是否逆时针,默认顺时针
|
||||
*/
|
||||
next(dir: number, anticlockwise?: boolean): T;
|
||||
|
||||
/**
|
||||
* 迭代本组支持的所有朝向,包含 `Unknown`
|
||||
*/
|
||||
mapDirection(): Iterable<T>;
|
||||
|
||||
/**
|
||||
* 迭代本组所有朝向及其对应的坐标描述器,包含 `Unknown`(对应 `{ x: 0, y: 0 }`)
|
||||
*/
|
||||
mapMovement(): Iterable<[T, IFaceDescriptor]>;
|
||||
}
|
||||
|
||||
export interface IFaceManager {
|
||||
/**
|
||||
* 以数字 key 注册一个 handler
|
||||
* @param group 数字 key
|
||||
* @param handler 朝向处理器
|
||||
*/
|
||||
register(group: number, handler: IFaceHandler<number>): void;
|
||||
|
||||
/**
|
||||
* 以字符串 id 注册一个 handler
|
||||
* @param id 字符串 id
|
||||
* @param handler 朝向处理器
|
||||
*/
|
||||
registerById(id: string, handler: IFaceHandler<number>): void;
|
||||
|
||||
/**
|
||||
* 按数字 key 查找 handler,未找到返回 `null`
|
||||
* @param group 数字 key
|
||||
*/
|
||||
get<T extends number>(group: number): IFaceHandler<T> | null;
|
||||
|
||||
/**
|
||||
* 按字符串 id 查找 handler,未找到返回 `null`
|
||||
* @param id 字符串 id
|
||||
*/
|
||||
getById<T extends number>(id: string): IFaceHandler<T> | null;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 内置 Handler
|
||||
|
||||
const ZERO_DESCRIPTOR: IFaceDescriptor = { x: 0, y: 0 };
|
||||
|
||||
const DIR8_MOVEMENTS: ReadonlyMap<FaceDirection, IFaceDescriptor> = new Map([
|
||||
[FaceDirection.Unknown, ZERO_DESCRIPTOR],
|
||||
[FaceDirection.Left, { x: -1, y: 0 }],
|
||||
[FaceDirection.Up, { x: 0, y: -1 }],
|
||||
[FaceDirection.Right, { x: 1, y: 0 }],
|
||||
[FaceDirection.Down, { x: 0, y: 1 }],
|
||||
[FaceDirection.LeftUp, { x: -1, y: -1 }],
|
||||
[FaceDirection.RightUp, { x: 1, y: -1 }],
|
||||
[FaceDirection.LeftDown, { x: -1, y: 1 }],
|
||||
[FaceDirection.RightDown, { x: 1, y: 1 }]
|
||||
]);
|
||||
|
||||
/** 顺时针旋转顺序(不含 Unknown) */
|
||||
const DIR8_CW: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Up, FaceDirection.RightUp],
|
||||
[FaceDirection.RightUp, FaceDirection.Right],
|
||||
[FaceDirection.Right, FaceDirection.RightDown],
|
||||
[FaceDirection.RightDown, FaceDirection.Down],
|
||||
[FaceDirection.Down, FaceDirection.LeftDown],
|
||||
[FaceDirection.LeftDown, FaceDirection.Left],
|
||||
[FaceDirection.Left, FaceDirection.LeftUp],
|
||||
[FaceDirection.LeftUp, FaceDirection.Up]
|
||||
]);
|
||||
|
||||
/** 逆时针旋转顺序(不含 Unknown) */
|
||||
const DIR8_CCW: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Up, FaceDirection.LeftUp],
|
||||
[FaceDirection.LeftUp, FaceDirection.Left],
|
||||
[FaceDirection.Left, FaceDirection.LeftDown],
|
||||
[FaceDirection.LeftDown, FaceDirection.Down],
|
||||
[FaceDirection.Down, FaceDirection.RightDown],
|
||||
[FaceDirection.RightDown, FaceDirection.Right],
|
||||
[FaceDirection.Right, FaceDirection.RightUp],
|
||||
[FaceDirection.RightUp, FaceDirection.Up]
|
||||
]);
|
||||
|
||||
const DIR8_OPPOSITE: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Up, FaceDirection.Down],
|
||||
[FaceDirection.Down, FaceDirection.Up],
|
||||
[FaceDirection.Left, FaceDirection.Right],
|
||||
[FaceDirection.Right, FaceDirection.Left],
|
||||
[FaceDirection.LeftUp, FaceDirection.RightDown],
|
||||
[FaceDirection.RightDown, FaceDirection.LeftUp],
|
||||
[FaceDirection.RightUp, FaceDirection.LeftDown],
|
||||
[FaceDirection.LeftDown, FaceDirection.RightUp]
|
||||
]);
|
||||
|
||||
export class Dir8FaceHandler implements IFaceHandler<FaceDirection> {
|
||||
degrade(dir: number): FaceDirection {
|
||||
return dir as FaceDirection;
|
||||
}
|
||||
|
||||
movement(dir: number): IFaceDescriptor {
|
||||
return DIR8_MOVEMENTS.get(this.degrade(dir)) ?? ZERO_DESCRIPTOR;
|
||||
}
|
||||
|
||||
move(dir: number, count: number): IFaceDescriptor {
|
||||
const { x, y } = this.movement(dir);
|
||||
return { x: x * count, y: y * count };
|
||||
}
|
||||
|
||||
opposite(dir: number): FaceDirection {
|
||||
const degraded = this.degrade(dir);
|
||||
return DIR8_OPPOSITE.get(degraded) ?? FaceDirection.Unknown;
|
||||
}
|
||||
|
||||
next(dir: number, anticlockwise: boolean = false): FaceDirection {
|
||||
const degraded = this.degrade(dir);
|
||||
if (degraded === FaceDirection.Unknown) return FaceDirection.Unknown;
|
||||
const map = anticlockwise ? DIR8_CCW : DIR8_CW;
|
||||
return map.get(degraded) ?? FaceDirection.Unknown;
|
||||
}
|
||||
|
||||
mapDirection(): Iterable<FaceDirection> {
|
||||
return DIR8_MOVEMENTS.keys();
|
||||
}
|
||||
|
||||
mapMovement(): Iterable<[FaceDirection, IFaceDescriptor]> {
|
||||
return DIR8_MOVEMENTS.entries();
|
||||
}
|
||||
}
|
||||
|
||||
const DIR4_DEGRADE: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Left, FaceDirection.Left],
|
||||
[FaceDirection.Up, FaceDirection.Up],
|
||||
[FaceDirection.Right, FaceDirection.Right],
|
||||
[FaceDirection.Down, FaceDirection.Down],
|
||||
[FaceDirection.LeftUp, FaceDirection.Left],
|
||||
[FaceDirection.LeftDown, FaceDirection.Left],
|
||||
[FaceDirection.RightUp, FaceDirection.Right],
|
||||
[FaceDirection.RightDown, FaceDirection.Right]
|
||||
]);
|
||||
|
||||
const DIR4_MOVEMENTS: ReadonlyMap<FaceDirection, IFaceDescriptor> = new Map([
|
||||
[FaceDirection.Unknown, ZERO_DESCRIPTOR],
|
||||
[FaceDirection.Left, { x: -1, y: 0 }],
|
||||
[FaceDirection.Up, { x: 0, y: -1 }],
|
||||
[FaceDirection.Right, { x: 1, y: 0 }],
|
||||
[FaceDirection.Down, { x: 0, y: 1 }]
|
||||
]);
|
||||
|
||||
const DIR4_CW: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Up, FaceDirection.Right],
|
||||
[FaceDirection.Right, FaceDirection.Down],
|
||||
[FaceDirection.Down, FaceDirection.Left],
|
||||
[FaceDirection.Left, FaceDirection.Up]
|
||||
]);
|
||||
|
||||
const DIR4_CCW: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Up, FaceDirection.Left],
|
||||
[FaceDirection.Left, FaceDirection.Down],
|
||||
[FaceDirection.Down, FaceDirection.Right],
|
||||
[FaceDirection.Right, FaceDirection.Up]
|
||||
]);
|
||||
|
||||
const DIR4_OPPOSITE: ReadonlyMap<FaceDirection, FaceDirection> = new Map([
|
||||
[FaceDirection.Up, FaceDirection.Down],
|
||||
[FaceDirection.Down, FaceDirection.Up],
|
||||
[FaceDirection.Left, FaceDirection.Right],
|
||||
[FaceDirection.Right, FaceDirection.Left]
|
||||
]);
|
||||
|
||||
export class Dir4FaceHandler implements IFaceHandler<FaceDirection> {
|
||||
degrade(dir: number): FaceDirection {
|
||||
return DIR4_DEGRADE.get(dir as FaceDirection) ?? FaceDirection.Unknown;
|
||||
}
|
||||
|
||||
movement(dir: number): IFaceDescriptor {
|
||||
return DIR4_MOVEMENTS.get(this.degrade(dir)) ?? ZERO_DESCRIPTOR;
|
||||
}
|
||||
|
||||
move(dir: number, count: number): IFaceDescriptor {
|
||||
const { x, y } = this.movement(dir);
|
||||
return { x: x * count, y: y * count };
|
||||
}
|
||||
|
||||
opposite(dir: number): FaceDirection {
|
||||
const degraded = this.degrade(dir);
|
||||
return DIR4_OPPOSITE.get(degraded) ?? FaceDirection.Unknown;
|
||||
}
|
||||
|
||||
next(dir: number, anticlockwise: boolean = false): FaceDirection {
|
||||
const degraded = this.degrade(dir);
|
||||
if (degraded === FaceDirection.Unknown) return FaceDirection.Unknown;
|
||||
const map = anticlockwise ? DIR4_CCW : DIR4_CW;
|
||||
return map.get(degraded) ?? FaceDirection.Unknown;
|
||||
}
|
||||
|
||||
mapDirection(): Iterable<FaceDirection> {
|
||||
return DIR4_MOVEMENTS.keys();
|
||||
}
|
||||
|
||||
mapMovement(): Iterable<[FaceDirection, IFaceDescriptor]> {
|
||||
return DIR4_MOVEMENTS.entries();
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region FaceManager
|
||||
|
||||
export class FaceManager implements IFaceManager {
|
||||
private readonly byGroup: Map<number, IFaceHandler<number>> = new Map();
|
||||
private readonly byId: Map<string, IFaceHandler<number>> = new Map();
|
||||
|
||||
register(group: number, handler: IFaceHandler<number>): void {
|
||||
this.byGroup.set(group, handler);
|
||||
}
|
||||
|
||||
registerById(id: string, handler: IFaceHandler<number>): void {
|
||||
this.byId.set(id, handler);
|
||||
}
|
||||
|
||||
get<T extends number>(group: number): IFaceHandler<T> | null {
|
||||
return (this.byGroup.get(group) as IFaceHandler<T>) ?? null;
|
||||
}
|
||||
|
||||
getById<T extends number>(id: string): IFaceHandler<T> | null {
|
||||
return (this.byId.get(id) as IFaceHandler<T>) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@ -1,4 +1,5 @@
|
||||
export * from './face';
|
||||
export * from './faceManager';
|
||||
export * from './mover';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
||||
61
packages-user/data-base/src/common/indexer.ts
Normal file
61
packages-user/data-base/src/common/indexer.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { ITileLocator } from '@motajs/common';
|
||||
|
||||
//#region 接口定义
|
||||
|
||||
export interface ILocationHelper {
|
||||
/**
|
||||
* 坐标 -> 索引
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
locToIndex(x: number, y: number): number;
|
||||
|
||||
/**
|
||||
* 定位符 -> 索引
|
||||
* @param locator 定位符
|
||||
*/
|
||||
locaterToIndex(locator: ITileLocator): number;
|
||||
|
||||
/**
|
||||
* 索引 -> 定位符
|
||||
* @param index 索引
|
||||
*/
|
||||
indexToLocator(index: number): ITileLocator;
|
||||
}
|
||||
|
||||
export interface ILocationIndexer extends ILocationHelper {
|
||||
/**
|
||||
* 设置地图宽度
|
||||
* @param width 地图宽度
|
||||
*/
|
||||
setWidth(width: number): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 默认实现
|
||||
|
||||
export class MapLocIndexer implements ILocationIndexer {
|
||||
private width: number = 0;
|
||||
|
||||
setWidth(width: number): void {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
locToIndex(x: number, y: number): number {
|
||||
return y * this.width + x;
|
||||
}
|
||||
|
||||
locaterToIndex(locator: ITileLocator): number {
|
||||
return locator.y * this.width + locator.x;
|
||||
}
|
||||
|
||||
indexToLocator(index: number): ITileLocator {
|
||||
return {
|
||||
x: index % this.width,
|
||||
y: Math.floor(index / this.width)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@ -1,13 +1,6 @@
|
||||
import { logger } from '@motajs/common';
|
||||
import {
|
||||
IEnemy,
|
||||
IEnemyContext,
|
||||
IEnemySaveState,
|
||||
IReadonlyEnemy,
|
||||
ISpecial,
|
||||
IEnemyView
|
||||
} from './types';
|
||||
import { SaveCompression } from '../common/types';
|
||||
import { SaveCompression } from '../common';
|
||||
import { IEnemy, IEnemySaveState, IReadonlyEnemy, ISpecial } from './types';
|
||||
|
||||
export class Enemy<TAttr> implements IEnemy<TAttr> {
|
||||
/** 怪物身上的特殊属性列表 */
|
||||
@ -113,43 +106,3 @@ export class Enemy<TAttr> implements IEnemy<TAttr> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class EnemyView<TAttr> implements IEnemyView<TAttr> {
|
||||
/** 计算后怪物 */
|
||||
private readonly computedEnemy: IEnemy<TAttr>;
|
||||
|
||||
constructor(
|
||||
readonly baseEnemy: IEnemy<TAttr>,
|
||||
readonly context: IEnemyContext<TAttr, unknown>
|
||||
) {
|
||||
this.computedEnemy = baseEnemy.clone();
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.computedEnemy.copyFrom(this.baseEnemy);
|
||||
}
|
||||
|
||||
getBaseEnemy(): IReadonlyEnemy<TAttr> {
|
||||
return this.baseEnemy;
|
||||
}
|
||||
|
||||
getComputedEnemy(): IReadonlyEnemy<TAttr> {
|
||||
this.context.requestRefresh(this);
|
||||
return this.computedEnemy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取计算中怪物对象,这个接口不对外暴露,仅在系统内部的 EnemyContext 中使用。
|
||||
*/
|
||||
getComputingEnemy(): IEnemy<TAttr> {
|
||||
return this.computedEnemy;
|
||||
}
|
||||
|
||||
getModifiableEnemy(): IEnemy<TAttr> {
|
||||
return this.baseEnemy;
|
||||
}
|
||||
|
||||
markDirty(): void {
|
||||
this.context.markDirty(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
export * from './enemy';
|
||||
export * from './context';
|
||||
export * from './damage';
|
||||
export * from './mapDamage';
|
||||
export * from './manager';
|
||||
export * from './special';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { SaveCompression } from '../common/types';
|
||||
import { SaveCompression } from '../common';
|
||||
import { ISpecial, SpecialCreation } from './types';
|
||||
|
||||
// TODO: 颜色参数
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import { IRange, ITileLocator } from '@motajs/common';
|
||||
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
|
||||
import { ISaveableContent } from '../common/types';
|
||||
import { ISaveableContent } from '../common';
|
||||
|
||||
//#region 怪物基础
|
||||
|
||||
@ -288,716 +286,3 @@ export interface IEnemyManager<TAttr> extends ISaveableContent<
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 辅助接口
|
||||
|
||||
export interface IMapLocHelper {
|
||||
/**
|
||||
* 坐标 -> 索引
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
locToIndex(x: number, y: number): number;
|
||||
|
||||
/**
|
||||
* 定位符 -> 索引
|
||||
* @param locator 定位符
|
||||
*/
|
||||
locaterToIndex(locator: ITileLocator): number;
|
||||
|
||||
/**
|
||||
* 索引 -> 定位符
|
||||
* @param index 索引
|
||||
*/
|
||||
indexToLocator(index: number): ITileLocator;
|
||||
}
|
||||
|
||||
export interface IMapLocIndexer extends IMapLocHelper {
|
||||
/**
|
||||
* 设置地图宽度
|
||||
* @param width 地图宽度
|
||||
*/
|
||||
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, unknown>;
|
||||
|
||||
/**
|
||||
* 重置此怪物视图的状态,将计算后怪物对象恢复至初始状态
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* 获取基本怪物对象
|
||||
*/
|
||||
getBaseEnemy(): IReadonlyEnemy<TAttr>;
|
||||
|
||||
/**
|
||||
* 获取计算后的怪物对象,返回的怪物对象同引用
|
||||
*/
|
||||
getComputedEnemy(): IReadonlyEnemy<TAttr>;
|
||||
|
||||
/**
|
||||
* 获取可修改的怪物对象。如果修改此方法获取的怪物对象,那么怪物的真实信息是不会刷新的,
|
||||
* 需要手动调用 markDirty 方法来刷新。
|
||||
*/
|
||||
getModifiableEnemy(): IEnemy<TAttr>;
|
||||
|
||||
/**
|
||||
* 将此怪物标记为脏,需要更新
|
||||
*/
|
||||
markDirty(): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 光环与查询
|
||||
|
||||
export interface IEnemySpecialModifier<TAttr> {
|
||||
/**
|
||||
* 获取要添加到指定怪物身上的特殊属性
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
add(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[];
|
||||
|
||||
/**
|
||||
* 获取制定怪物身上要删除的特殊属性
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
delete(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[];
|
||||
|
||||
/**
|
||||
* 修改一个怪物的特殊属性,如果真正进行了修改则返回 true,否则返回 false
|
||||
* @param handler 信息对象
|
||||
* @param special 要修改的怪物特殊属性
|
||||
*/
|
||||
modify(
|
||||
handler: IEnemyHandler<TAttr, unknown>,
|
||||
special: ISpecial<any>
|
||||
): boolean;
|
||||
}
|
||||
|
||||
export interface IAuraView<TAttr, T = any> {
|
||||
/** 此光环视图的优先级 */
|
||||
readonly priority: number;
|
||||
/** 此光环视图的影响范围 */
|
||||
readonly range: IRange<T>;
|
||||
|
||||
/** 这个光环视图是否有可能修改怪物的基本属性 */
|
||||
readonly couldApplyBase: boolean;
|
||||
/** 这个光环视图是否有可能修改怪物的特殊属性 */
|
||||
readonly couldApplySpecial: boolean;
|
||||
|
||||
/**
|
||||
* 获取范围扫描参数
|
||||
*/
|
||||
getRangeParam(): T;
|
||||
|
||||
/**
|
||||
* 对指定怪物对象施加修饰器
|
||||
* @param handler 信息对象
|
||||
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
||||
*/
|
||||
apply(
|
||||
handler: IEnemyHandler<TAttr, unknown>,
|
||||
baseEnemy: IReadonlyEnemy<TAttr>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 对指定怪物对象添加特殊属性修饰器
|
||||
* @param handler 信息对象
|
||||
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
||||
*/
|
||||
applySpecial(
|
||||
handler: IEnemyHandler<TAttr, unknown>,
|
||||
baseEnemy: IReadonlyEnemy<TAttr>
|
||||
): IEnemySpecialModifier<TAttr> | null;
|
||||
}
|
||||
|
||||
export interface IEnemyAuraView<TAttr, R, S> extends IAuraView<TAttr, R> {
|
||||
/** 此光环视图所属的怪物 */
|
||||
readonly enemy: IReadonlyEnemy<TAttr>;
|
||||
/** 此光环视图所属的特殊属性 */
|
||||
readonly special: ISpecial<S>;
|
||||
/** 此光环视图所属怪物的定位符 */
|
||||
readonly locator: ITileLocator;
|
||||
}
|
||||
|
||||
export interface IAuraConverter<TAttr, THero> {
|
||||
/**
|
||||
* 判断一个特殊属性是否应该被当前光环转换器执行转换
|
||||
* @param special 要转换的特殊属性
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
shouldConvert(
|
||||
special: ISpecial<any>,
|
||||
handler: IReadonlyEnemyHandler<TAttr, THero>
|
||||
): boolean;
|
||||
|
||||
/**
|
||||
* 将一个特殊属性转换为光环视图
|
||||
*/
|
||||
convert(
|
||||
special: ISpecial<any>,
|
||||
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||
context: IEnemyContext<TAttr, THero>
|
||||
): IEnemyAuraView<TAttr, any, any>;
|
||||
}
|
||||
|
||||
export interface IEnemySpecialQueryModifier<
|
||||
TAttr,
|
||||
THero
|
||||
> extends IEnemySpecialModifier<TAttr> {
|
||||
/**
|
||||
* 判断一个怪物是否应该查询外部状态
|
||||
*/
|
||||
shouldQuery(handler: IReadonlyEnemyHandler<TAttr, THero>): boolean;
|
||||
}
|
||||
|
||||
export interface IEnemySpecialQueryEffect<TAttr, THero> {
|
||||
/** 效果优先级,与光环属性共用 */
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* 根据传入的怪物上下文,获取对应的怪物特殊属性修饰器
|
||||
*/
|
||||
for(
|
||||
ctx: IEnemyContext<TAttr, THero>
|
||||
): IEnemySpecialQueryModifier<TAttr, THero>;
|
||||
}
|
||||
|
||||
export interface IEnemyCommonQueryEffect<TAttr, THero> {
|
||||
/** 优先级,越高的越先执行 */
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* 对怪物的某个特殊属性施加常规查询效果
|
||||
*/
|
||||
apply(
|
||||
handler: IEnemyHandler<TAttr, THero>,
|
||||
special: ISpecial<any>,
|
||||
query: () => IEnemyContext<TAttr, THero>
|
||||
): void;
|
||||
}
|
||||
|
||||
export interface IEnemyFinalEffect<TAttr, THero> {
|
||||
/** 效果优先级,越高会越先被执行 */
|
||||
readonly priority: number;
|
||||
|
||||
/**
|
||||
* 向怪物施加最终修饰效果
|
||||
*/
|
||||
apply(handler: IEnemyHandler<TAttr, THero>): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 地图伤害
|
||||
|
||||
export interface IMapDamageInfoExtra {
|
||||
/** 捕捉怪物信息 */
|
||||
catch: Set<ITileLocator>;
|
||||
/** 阻击怪物信息 */
|
||||
repulse: Set<ITileLocator>;
|
||||
}
|
||||
|
||||
export interface IMapDamageInfo {
|
||||
/** 伤害值 */
|
||||
damage: number;
|
||||
/** 伤害类型 */
|
||||
type: number;
|
||||
/** 地图伤害额外信息 */
|
||||
extra: IMapDamageInfoExtra;
|
||||
}
|
||||
|
||||
export interface IMapDamageView<T = any> {
|
||||
/**
|
||||
* 获取地图伤害影响范围
|
||||
*/
|
||||
getRange(): IRange<T>;
|
||||
|
||||
/**
|
||||
* 获取范围参数
|
||||
*/
|
||||
getRangeParam(): T;
|
||||
|
||||
/**
|
||||
* 获取指定位置的地图伤害,会对坐标进行判断
|
||||
* @param locator 伤害位置
|
||||
*/
|
||||
getDamageAt(locator: ITileLocator): Readonly<IMapDamageInfo> | null;
|
||||
|
||||
/**
|
||||
* 获取指定位置的地图伤害,会对坐标进行判断
|
||||
* @param locator 伤害位置
|
||||
*/
|
||||
getDamageWithoutCheck(
|
||||
locator: ITileLocator
|
||||
): Readonly<IMapDamageInfo> | null;
|
||||
}
|
||||
|
||||
export interface IMapDamageConverter<TAttr, THero> {
|
||||
/**
|
||||
* 转换地图伤害视图
|
||||
*/
|
||||
convert(
|
||||
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||
context: IEnemyContext<TAttr, THero>
|
||||
): IMapDamageView<any>[];
|
||||
}
|
||||
|
||||
export interface IMapDamageReducer {
|
||||
/**
|
||||
* 对伤害信息进行合并
|
||||
*/
|
||||
reduce(
|
||||
info: Iterable<Readonly<IMapDamageInfo>>,
|
||||
locator: ITileLocator
|
||||
): Readonly<IMapDamageInfo>;
|
||||
}
|
||||
|
||||
export interface IMapDamage<TAttr, THero> {
|
||||
/** 当前绑定的怪物上下文 */
|
||||
readonly context: IEnemyContext<TAttr, THero>;
|
||||
|
||||
/**
|
||||
* 设置地图伤害转换器,并基于当前上下文重建所有地图伤害视图
|
||||
* @param converter 地图伤害转换器
|
||||
*/
|
||||
useConverter(converter: IMapDamageConverter<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 设置地图伤害合并器
|
||||
* @param reducer 地图伤害合并器
|
||||
*/
|
||||
useReducer(reducer: IMapDamageReducer): void;
|
||||
|
||||
/**
|
||||
* 在指定位置添加一条无来源地图伤害
|
||||
* @param locator 地图定位符
|
||||
* @param info 地图伤害信息
|
||||
*/
|
||||
addMapDamage(locator: ITileLocator, info: IMapDamageInfo): void;
|
||||
|
||||
/**
|
||||
* 在指定位置删除一条无来源地图伤害
|
||||
* @param locator 地图定位符
|
||||
* @param info 地图伤害信息
|
||||
*/
|
||||
deleteMapDamage(locator: ITileLocator, info: IMapDamageInfo): void;
|
||||
|
||||
/**
|
||||
* 将指定位置标记为脏,后续访问时会重新计算该点的有来源伤害
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
markDirty(locator: ITileLocator): void;
|
||||
|
||||
/**
|
||||
* 将指定怪物对应的地图伤害标记为脏并刷新
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
markEnemyDirty(view: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 基于当前上下文重新刷新全部有来源地图伤害
|
||||
*/
|
||||
refreshAll(): void;
|
||||
|
||||
/**
|
||||
* 删除指定怪物带来的全部地图伤害来源
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
deleteEnemy(view: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 获取指定位置合并后的地图伤害
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
getReducedDamage(locator: ITileLocator): Readonly<IMapDamageInfo> | null;
|
||||
|
||||
/**
|
||||
* 获取指定位置未合并的地图伤害列表
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
getSeparatedDamage(
|
||||
locator: ITileLocator
|
||||
): Iterable<Readonly<IMapDamageInfo>>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 伤害系统
|
||||
|
||||
export interface IEnemyDamageInfo {
|
||||
/** 战斗伤害值 */
|
||||
readonly damage: number;
|
||||
/** 战斗回合数 */
|
||||
readonly turn: number;
|
||||
}
|
||||
|
||||
export interface IEnemyCritical {
|
||||
/** 此临界点中指定勇士属性的值 */
|
||||
readonly nextValue: number;
|
||||
/** 当前勇士指定属性的值 */
|
||||
readonly baseValue: number;
|
||||
/** 此临界点中指定勇士数值的值与当前值的差,即 `nextValue - baseValue` */
|
||||
readonly nextDiff: number;
|
||||
/** 当前状态下怪物的伤害信息 */
|
||||
readonly baseInfo: IEnemyDamageInfo;
|
||||
/** 此临界点下怪物的伤害信息 */
|
||||
readonly info: IEnemyDamageInfo;
|
||||
/** 此临界点的伤害值与当前伤害值的差 */
|
||||
readonly damageDiff: number;
|
||||
}
|
||||
|
||||
export type CriticalableHeroStatus<THero> = keyof {
|
||||
[P in keyof THero as THero[P] extends number ? P : never]: number;
|
||||
};
|
||||
|
||||
export interface IDamageCalculator<TAttr, THero> {
|
||||
/**
|
||||
* 计算战斗伤害信息
|
||||
* @param handler 信息对象
|
||||
*/
|
||||
calculate(handler: IReadonlyEnemyHandler<TAttr, THero>): IEnemyDamageInfo;
|
||||
|
||||
/**
|
||||
* 获取临界计算的上界
|
||||
* @param handler 信息对象
|
||||
* @param attribute 勇士的临界属性
|
||||
*/
|
||||
getCriticalLimit(
|
||||
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||
attribute: CriticalableHeroStatus<THero>
|
||||
): number;
|
||||
}
|
||||
|
||||
export interface IDamageContext<TAttr, THero> {
|
||||
/**
|
||||
* 获取战斗伤害信息
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null;
|
||||
|
||||
/**
|
||||
* 根据怪物对象获取战斗伤害信息
|
||||
* @param enemy 怪物对象
|
||||
*/
|
||||
getDamageInfoByComputed(
|
||||
enemy: IReadonlyEnemy<TAttr>
|
||||
): IEnemyDamageInfo | null;
|
||||
|
||||
/**
|
||||
* 计算怪物在指定勇士属性下的临界
|
||||
* @param enemy 怪物视图
|
||||
* @param attribute 计算临界的目标勇士属性,比如计算攻击临界、自定义属性的临界等等
|
||||
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可,默认是 12
|
||||
*/
|
||||
calculateCritical(
|
||||
enemy: IEnemyView<TAttr>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
precision?: number
|
||||
): Generator<IEnemyCritical, void, void>;
|
||||
}
|
||||
|
||||
export interface IDamageSystem<TAttr, THero> extends IDamageContext<
|
||||
TAttr,
|
||||
THero
|
||||
> {
|
||||
/** 伤害系统所属的上下文 */
|
||||
readonly context: IEnemyContext<TAttr, THero>;
|
||||
|
||||
/**
|
||||
* 设置当前伤害计算系统使用的伤害计算器
|
||||
* @param calculator 伤害计算器
|
||||
*/
|
||||
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 获取当前使用的伤害计算器
|
||||
*/
|
||||
getCalculator(): IDamageCalculator<TAttr, THero> | null;
|
||||
|
||||
/**
|
||||
* 绑定勇士信息
|
||||
* @param hero 勇士信息
|
||||
*/
|
||||
bindHeroStatus(hero: IReadonlyHeroAttribute<THero> | null): void;
|
||||
|
||||
/**
|
||||
* 将指定的怪物标记为脏
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
markDirty(enemy: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 删除指定的怪物
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
deleteEnemy(enemy: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 将所有怪物标记为脏
|
||||
*/
|
||||
markAllDirty(): void;
|
||||
|
||||
/**
|
||||
* 修改勇士属性,然后返回修改后勇士属性所组成的计算对象,不影响当前伤害系统的状态
|
||||
* @param modify 勇士修改函数
|
||||
*/
|
||||
with(hero: IHeroAttribute<THero>): IDamageContext<TAttr, THero>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 上下文
|
||||
|
||||
export interface IEnemyContext<TAttr, THero> {
|
||||
/** 怪物上下文宽度 */
|
||||
readonly width: number;
|
||||
/** 怪物上下文高度 */
|
||||
readonly height: number;
|
||||
/** 此上下文使用的索引对象 */
|
||||
readonly indexer: IMapLocIndexer;
|
||||
|
||||
/**
|
||||
* 调整上下文尺寸,并清空当前上下文中的所有怪物与状态
|
||||
* @param width 地图宽度
|
||||
* @param height 地图高度
|
||||
*/
|
||||
resize(width: number, height: number): void;
|
||||
|
||||
/**
|
||||
* 注册一个光环转换器
|
||||
* @param converter 光环转换器
|
||||
*/
|
||||
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 注销一个光环转换器
|
||||
* @param converter 光环转换器
|
||||
*/
|
||||
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 设置光环转换器的启用状态
|
||||
* @param converter 光环转换器
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
setAuraConverterEnabled(
|
||||
converter: IAuraConverter<TAttr, THero>,
|
||||
enabled: boolean
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注册一个特殊属性查询效果
|
||||
* @param effect 特殊属性查询效果
|
||||
*/
|
||||
registerSpecialQueryEffect(
|
||||
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注销一个特殊属性查询效果
|
||||
* @param effect 特殊属性查询效果
|
||||
*/
|
||||
unregisterSpecialQueryEffect(
|
||||
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 为指定特殊属性代码注册常规查询效果
|
||||
* @param code 特殊属性代码
|
||||
* @param effect 常规查询效果
|
||||
*/
|
||||
registerCommonQueryEffect(
|
||||
code: number,
|
||||
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注销指定特殊属性代码上的常规查询效果
|
||||
* @param code 特殊属性代码
|
||||
* @param effect 常规查询效果
|
||||
*/
|
||||
unregisterCommonQueryEffect(
|
||||
code: number,
|
||||
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 注册一个最终效果
|
||||
* @param effect 最终效果
|
||||
*/
|
||||
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 注销一个最终效果
|
||||
* @param effect 最终效果
|
||||
*/
|
||||
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 绑定勇士对象
|
||||
* @param hero 勇士属性对象
|
||||
*/
|
||||
bindHero(hero: IReadonlyHeroAttribute<THero> | null): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的勇士属性对象
|
||||
*/
|
||||
getBindedHero(): IReadonlyHeroAttribute<THero> | null;
|
||||
|
||||
/**
|
||||
* 获取指定怪物对象当前所在位置
|
||||
* @param enemy 怪物对象
|
||||
*/
|
||||
getEnemyLocator(enemy: IEnemy<TAttr>): Readonly<ITileLocator> | null;
|
||||
|
||||
/**
|
||||
* 获取指定怪物视图当前所在位置
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
getEnemyLocatorByView(
|
||||
view: IEnemyView<TAttr>
|
||||
): Readonly<ITileLocator> | null;
|
||||
|
||||
/**
|
||||
* 根据定位符获取怪物视图
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
getEnemyByLocator(locator: ITileLocator): IEnemyView<TAttr> | null;
|
||||
|
||||
/**
|
||||
* 根据坐标获取怪物视图
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
getEnemyByLoc(x: number, y: number): IEnemyView<TAttr> | null;
|
||||
|
||||
/**
|
||||
* 根据计算后怪物对象反查怪物视图
|
||||
* @param enemy 计算后怪物对象
|
||||
*/
|
||||
getViewByComputed(enemy: IReadonlyEnemy<TAttr>): IEnemyView<TAttr> | null;
|
||||
|
||||
/**
|
||||
* 在指定位置放置一个怪物对象
|
||||
* @param locator 地图定位符
|
||||
* @param enemy 怪物对象
|
||||
*/
|
||||
setEnemyAt(locator: ITileLocator, enemy: IEnemy<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 删除指定位置的怪物
|
||||
* @param locator 地图定位符
|
||||
*/
|
||||
deleteEnemy(locator: ITileLocator): void;
|
||||
|
||||
/**
|
||||
* 扫描指定范围内的怪物视图
|
||||
* @param range 范围对象
|
||||
* @param param 范围参数
|
||||
*/
|
||||
scanRange<T>(
|
||||
range: IRange<T>,
|
||||
param: T
|
||||
): Iterable<[ITileLocator, IEnemyView<TAttr>]>;
|
||||
|
||||
/**
|
||||
* 迭代上下文中的全部怪物
|
||||
*/
|
||||
iterateEnemy(): Iterable<[ITileLocator, IEnemyView<TAttr>]>;
|
||||
|
||||
/**
|
||||
* 添加一个全局光环视图
|
||||
* @param aura 光环视图
|
||||
*/
|
||||
addAura(aura: IAuraView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 删除一个全局光环视图
|
||||
* @param aura 光环视图
|
||||
*/
|
||||
deleteAura(aura: IAuraView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 绑定地图伤害管理器
|
||||
* @param damage 地图伤害管理器
|
||||
*/
|
||||
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的地图伤害管理器
|
||||
*/
|
||||
getMapDamage(): IMapDamage<TAttr, THero> | null;
|
||||
|
||||
/**
|
||||
* 绑定伤害计算系统
|
||||
* @param system 伤害系统
|
||||
*/
|
||||
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的伤害计算系统
|
||||
*/
|
||||
getDamageSystem(): IDamageSystem<TAttr, THero> | null;
|
||||
|
||||
/**
|
||||
* 重建当前上下文中的全部怪物计算结果
|
||||
*
|
||||
* 1. 对所有光环及特殊查询进行构建操作,这一步中会决定每个怪物所拥有的特殊属性,后续不会变动
|
||||
* 2. 执行所有的普通光环效果,修改怪物的基础属性
|
||||
* 3. 执行常规查询效果,允许查询上下文状态并修改怪物自身的基础属性
|
||||
* 4. 执行最终效果,不允许查询上下文状态,仅允许修改怪物自身的基础属性
|
||||
*/
|
||||
buildup(): void;
|
||||
|
||||
/**
|
||||
* 将指定怪物视图标记为脏
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
markDirty(view: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 申请刷新指定怪物视图
|
||||
* @param view 怪物视图
|
||||
*/
|
||||
requestRefresh(view: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 清空当前上下文中的所有对象与运行状态
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* 销毁当前上下文
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import { ITileLocator } from '@motajs/common';
|
||||
import { IMapLocIndexer } from './types';
|
||||
|
||||
export class MapLocIndexer implements IMapLocIndexer {
|
||||
private width: number = 0;
|
||||
|
||||
setWidth(width: number): void {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
locToIndex(x: number, y: number): number {
|
||||
return y * this.width + x;
|
||||
}
|
||||
|
||||
locaterToIndex(locator: ITileLocator): number {
|
||||
return locator.y * this.width + locator.x;
|
||||
}
|
||||
|
||||
indexToLocator(index: number): ITileLocator {
|
||||
return {
|
||||
x: index % this.width,
|
||||
y: Math.floor(index / this.width)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
export * from './combat';
|
||||
export * from './common';
|
||||
export * from './enemy';
|
||||
export * from './flag';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { IHeroFollower, IHeroState } from './hero';
|
||||
import { IEnemyManager } from './enemy';
|
||||
import { IFlagSystem } from './flag';
|
||||
import { IRoleFaceBinder, ISaveableContent } from './common';
|
||||
import { IFaceManager, IRoleFaceBinder, ISaveableContent } from './common';
|
||||
import { IMapStore } from './map';
|
||||
|
||||
export interface IStateSaveData {
|
||||
@ -12,6 +12,8 @@ export interface IStateSaveData {
|
||||
export interface IStateBase<TEnemy, THero> {
|
||||
/** 朝向绑定 */
|
||||
readonly roleFace: IRoleFaceBinder;
|
||||
/** 朝向管理 */
|
||||
readonly faceManager: IFaceManager;
|
||||
/** id 到图块数字的映射 */
|
||||
readonly idNumberMap: Map<string, number>;
|
||||
/** 图块数字到 id 的映射 */
|
||||
|
||||
@ -22,7 +22,12 @@ import {
|
||||
SaveCompression,
|
||||
IReadonlyEnemy,
|
||||
IMapStore,
|
||||
MapStore
|
||||
MapStore,
|
||||
IFaceManager,
|
||||
FaceManager,
|
||||
InternalFaceGroup,
|
||||
Dir4FaceHandler,
|
||||
Dir8FaceHandler
|
||||
} from '@user/data-base';
|
||||
import {
|
||||
CommonAuraConverter,
|
||||
@ -55,6 +60,7 @@ import { ISaveSystem, SaveSystem } from './save';
|
||||
export class CoreState implements ICoreState {
|
||||
// 全局内容
|
||||
readonly roleFace: IRoleFaceBinder;
|
||||
readonly faceManager: IFaceManager;
|
||||
readonly idNumberMap: Map<string, number>;
|
||||
readonly numberIdMap: Map<number, string>;
|
||||
|
||||
@ -82,7 +88,6 @@ export class CoreState implements ICoreState {
|
||||
|
||||
constructor() {
|
||||
this.maps = new MapStore();
|
||||
this.roleFace = new RoleFaceBinder();
|
||||
this.idNumberMap = new Map();
|
||||
this.numberIdMap = new Map();
|
||||
|
||||
@ -151,6 +156,16 @@ export class CoreState implements ICoreState {
|
||||
|
||||
//#region 其他初始化
|
||||
|
||||
// 朝向
|
||||
this.roleFace = new RoleFaceBinder();
|
||||
this.faceManager = new FaceManager();
|
||||
const dir4 = new Dir4FaceHandler();
|
||||
const dir8 = new Dir8FaceHandler();
|
||||
this.faceManager.register(InternalFaceGroup.Dir4, dir4);
|
||||
this.faceManager.registerById('dir4', dir4);
|
||||
this.faceManager.register(InternalFaceGroup.Dir8, dir8);
|
||||
this.faceManager.registerById('dir8', dir8);
|
||||
|
||||
this.flags = new FlagSystem();
|
||||
|
||||
// 加载先使用兼容层实现
|
||||
|
||||
Loading…
Reference in New Issue
Block a user