refactor: 战斗系统文件结构

This commit is contained in:
unanmed 2026-05-12 17:11:33 +08:00
parent 8ea0c12024
commit 0da19e1697
13 changed files with 863 additions and 805 deletions

View 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 路径无需改动 |

View File

@ -3,7 +3,6 @@ import {
IAuraConverter,
IAuraView,
IDamageSystem,
IEnemy,
IEnemyAuraView,
IEnemyCommonQueryEffect,
IEnemyContext,
@ -13,13 +12,12 @@ 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';
export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
/** 坐标索引 -> 怪物视图 */
@ -82,7 +80,7 @@ export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
private damageSystem: IDamageSystem<TAttr, THero> | null = null;
/** 索引工具 */
readonly indexer: MapLocIndexer = new MapLocIndexer();
readonly indexer: ILocationIndexer = new MapLocIndexer();
/** 当前是否需要全量刷新 */
private needUpdate: boolean = true;

View File

@ -1,3 +1,4 @@
import { clamp } from 'lodash-es';
import { ITileLocator, logger } from '@motajs/common';
import {
CriticalableHeroStatus,
@ -8,11 +9,10 @@ import {
IEnemyCritical,
IEnemyDamageInfo,
IReadonlyEnemyHandler,
IEnemyView,
IReadonlyEnemy
IEnemyView
} from './types';
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
import { clamp } from 'lodash-es';
import { IReadonlyEnemy } from '../enemy';
interface ICriticalSearchResult {
/** 此临界点的属性值 */

View 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);
}
}

View File

@ -0,0 +1,5 @@
export * from './context';
export * from './damage';
export * from './enemy';
export * from './mapDamage';
export * from './types';

View File

@ -7,9 +7,9 @@ import {
IMapDamageConverter,
IMapDamageInfo,
IMapDamageReducer,
IMapDamageView,
IMapLocIndexer
IMapDamageView
} from './types';
import { ILocationHelper } from '../common/indexer';
interface IPointInfo {
/** 该点所有的地图伤害 */
@ -61,7 +61,7 @@ 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>) {
this.indexer = context.indexer;

View File

@ -0,0 +1,688 @@
import { ITileLocator, IRange } from '@motajs/common';
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
import { IReadonlyHeroAttribute, IHeroAttribute } from '../hero';
import { ILocationHelper } from '../common/indexer';
//#region 辅助接口
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: ILocationHelper;
/**
* 调整上下文尺寸,并清空当前上下文中的所有怪物与状态
* @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

View 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

View File

@ -1,12 +1,5 @@
import { logger } from '@motajs/common';
import {
IEnemy,
IEnemyContext,
IEnemySaveState,
IReadonlyEnemy,
ISpecial,
IEnemyView
} from './types';
import { IEnemy, IEnemySaveState, IReadonlyEnemy, ISpecial } from './types';
import { SaveCompression } from '../common/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);
}
}

View File

@ -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';

View File

@ -1,5 +1,3 @@
import { IRange, ITileLocator } from '@motajs/common';
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
import { ISaveableContent } from '../common/types';
//#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

View File

@ -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)
};
}
}

View File

@ -1,3 +1,4 @@
export * from './combat';
export * from './common';
export * from './enemy';
export * from './flag';