Compare commits

..

2 Commits

Author SHA1 Message Date
AncTe
8e4efddd63
Merge 604c77f7c7 into 1a569804d8 2026-06-12 14:55:22 +00:00
604c77f7c7 refactor: 怪物接口拓展自 IStateBaseExtended 2026-06-12 22:55:08 +08:00
7 changed files with 170 additions and 45 deletions

View File

@ -97,7 +97,7 @@ export class MainDamageCalculator implements IDamageCalculator<
context: handler.context,
locator,
hero,
data: handler.data
state: handler.state
});
turn += extraInfo.turn;
damage += extraInfo.damage;

View File

@ -311,7 +311,7 @@ export class MainMapDamageConverter implements IMapDamageConverter<
const laser = enemy.getSpecial<number>(24);
if (laser) {
const face = handler.data.faceManager.get(FaceGroup.Dir4)!;
const face = handler.state.faceManager.get(FaceGroup.Dir4)!;
views.push(new LaserDamageView(context, locator, laser, face));
}

View File

@ -100,7 +100,7 @@ export class EnemyContext<TEnemy, THero> implements IEnemyContext<
width: number = 0;
height: number = 0;
constructor(readonly dataState: IStateBase) {}
constructor(readonly state: IStateBase) {}
resize(width: number, height: number): void {
this.clear();
@ -215,7 +215,7 @@ export class EnemyContext<TEnemy, THero> implements IEnemyContext<
context: this,
locator,
hero: this.bindedHero!,
data: this.dataState
state: this.state
};
}

View File

@ -9,7 +9,8 @@ import {
IEnemyCritical,
IEnemyDamageInfo,
IReadonlyEnemyHandler,
IEnemyView
IEnemyView,
IEnemyDamageInfoBase
} from './types';
import {
IHeroAttribute,
@ -22,7 +23,7 @@ interface ICriticalSearchResult {
/** 此临界点的属性值 */
readonly value: number;
/** 此临界点的伤害信息 */
readonly info: IEnemyDamageInfo;
readonly info: IEnemyDamageInfoBase;
}
export class DamageContext<TEnemy, THero> implements IDamageContext<
@ -34,14 +35,14 @@ export class DamageContext<TEnemy, THero> implements IDamageContext<
/** 当前勇士属性 */
protected heroStatus: IReadonlyHeroAttribute<THero> | null;
readonly dataState: IStateBase;
readonly state: IStateBase;
constructor(
readonly context: IEnemyContext<TEnemy, THero>,
calculator: IDamageCalculator<TEnemy, THero> | null = null,
heroStatus: IReadonlyHeroAttribute<THero> | null = null
) {
this.dataState = context.dataState;
this.state = context.state;
this.calculator = calculator;
this.heroStatus = heroStatus;
}
@ -62,11 +63,13 @@ export class DamageContext<TEnemy, THero> implements IDamageContext<
context: this.context,
locator,
hero,
data: this.dataState
state: this.state
};
}
getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null {
getDamageInfo(
enemy: IEnemyView<TEnemy>
): IEnemyDamageInfo<TEnemy, THero> | null {
if (!this.heroStatus) {
logger.warn(107);
return null;
@ -82,12 +85,15 @@ export class DamageContext<TEnemy, THero> implements IDamageContext<
const computed = enemy.getComputedEnemy();
const handler = this.createReadonlyHandler(computed, locator, hero);
return this.calculator.calculate(handler);
return {
handler,
...this.calculator.calculate(handler)
};
}
getDamageInfoByComputed(
enemy: IReadonlyEnemy<TEnemy>
): IEnemyDamageInfo | null {
): IEnemyDamageInfo<TEnemy, THero> | null {
if (!this.heroStatus) {
logger.warn(107);
return null;
@ -105,7 +111,10 @@ export class DamageContext<TEnemy, THero> implements IDamageContext<
const handler = this.createReadonlyHandler(enemy, locator, hero);
return this.calculator.calculate(handler);
return {
handler,
...this.calculator.calculate(handler)
};
}
*calculateCritical(
@ -224,8 +233,10 @@ export class DamageSystem<TEnemy, THero>
implements IDamageSystem<TEnemy, THero>
{
/** 怪物伤害缓存 */
private readonly cache: Map<IEnemyView<TEnemy>, IEnemyDamageInfo> =
new Map();
private readonly cache: Map<
IEnemyView<TEnemy>,
IEnemyDamageInfo<TEnemy, THero>
> = new Map();
constructor(context: IEnemyContext<TEnemy, THero>) {
super(context);
@ -245,7 +256,9 @@ export class DamageSystem<TEnemy, THero>
this.markAllDirty();
}
getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null {
getDamageInfo(
enemy: IEnemyView<TEnemy>
): IEnemyDamageInfo<TEnemy, THero> | null {
const cached = this.cache.get(enemy);
if (cached) {
return cached;
@ -260,7 +273,7 @@ export class DamageSystem<TEnemy, THero>
getDamageInfoByComputed(
enemy: IReadonlyEnemy<TEnemy>
): IEnemyDamageInfo | null {
): IEnemyDamageInfo<TEnemy, THero> | null {
const view = this.context.getViewByComputed(enemy);
if (view) {
const cached = this.cache.get(view);

View File

@ -64,11 +64,11 @@ export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
/** 坐标索引对象 */
private readonly indexer: ILocationHelper;
readonly dataState: IStateBase;
readonly state: IStateBase;
constructor(readonly context: IEnemyContext<TEnemy, THero>) {
this.indexer = context.indexer;
this.dataState = context.dataState;
this.state = context.state;
}
useConverter(converter: IMapDamageConverter<TEnemy, THero>): void {
@ -92,7 +92,7 @@ export class MapDamage<TEnemy, THero> implements IMapDamage<TEnemy, THero> {
context: this.context,
locator,
hero,
data: this.context.dataState
state: this.state
};
}

View File

@ -1,11 +1,12 @@
import { ITileLocator, IRange } from '@motajs/common';
import { ITileLocator, IRange, IHookable, IHookBase } from '@motajs/common';
import {
IEnemy,
IReadonlyEnemy,
ISpecial,
IReadonlyHeroAttribute,
IHeroAttribute,
IStateBase
IStateBase,
IStateBaseExtended
} from '@user/data-base';
import { ILocationHelper } from '@user/data-common';
@ -21,7 +22,7 @@ export interface IEnemyHandler<TEnemy, THero> {
/** 勇士属性信息 */
readonly hero: IReadonlyHeroAttribute<THero>;
/** 当前全局状态对象 */
readonly data: IStateBase;
readonly state: IStateBase;
}
export interface IReadonlyEnemyHandler<TEnemy, THero> {
@ -34,7 +35,7 @@ export interface IReadonlyEnemyHandler<TEnemy, THero> {
/** 勇士属性信息 */
readonly hero: IReadonlyHeroAttribute<THero>;
/** 当前全局状态对象 */
readonly data: IStateBase;
readonly state: IStateBase;
}
//#endregion
@ -282,11 +283,9 @@ export interface IMapDamageReducer {
): Readonly<IMapDamageInfo>;
}
export interface IMapDamage<TEnemy, THero> {
export interface IMapDamage<TEnemy, THero> extends IStateBaseExtended {
/** 当前绑定的怪物上下文 */
readonly context: IEnemyContext<TEnemy, THero>;
/** 地图伤害系统绑定的全局状态对象 */
readonly dataState: IStateBase;
/**
*
@ -356,13 +355,18 @@ export interface IMapDamage<TEnemy, THero> {
//#region 伤害系统
export interface IEnemyDamageInfo {
export interface IEnemyDamageInfoBase {
/** 战斗伤害值 */
readonly damage: number;
/** 战斗回合数 */
readonly turn: number;
}
export interface IEnemyDamageInfo<TEnemy, THero> extends IEnemyDamageInfoBase {
/** 信息对象 */
readonly handler: IReadonlyEnemyHandler<TEnemy, THero>;
}
export interface IEnemyCritical {
/** 此临界点中指定勇士属性的值 */
readonly nextValue: number;
@ -371,9 +375,9 @@ export interface IEnemyCritical {
/** 此临界点中指定勇士数值的值与当前值的差,即 `nextValue - baseValue` */
readonly nextDiff: number;
/** 当前状态下怪物的伤害信息 */
readonly baseInfo: IEnemyDamageInfo;
readonly baseInfo: IEnemyDamageInfoBase;
/** 此临界点下怪物的伤害信息 */
readonly info: IEnemyDamageInfo;
readonly info: IEnemyDamageInfoBase;
/** 此临界点的伤害值与当前伤害值的差 */
readonly damageDiff: number;
}
@ -387,7 +391,9 @@ export interface IDamageCalculator<TEnemy, THero> {
*
* @param handler
*/
calculate(handler: IReadonlyEnemyHandler<TEnemy, THero>): IEnemyDamageInfo;
calculate(
handler: IReadonlyEnemyHandler<TEnemy, THero>
): IEnemyDamageInfoBase;
/**
*
@ -400,15 +406,14 @@ export interface IDamageCalculator<TEnemy, THero> {
): number;
}
export interface IDamageContext<TEnemy, THero> {
/** 伤害上下文所属的全局状态对象 */
readonly dataState: IStateBase;
export interface IDamageContext<TEnemy, THero> extends IStateBaseExtended {
/**
*
* @param enemy
*/
getDamageInfo(enemy: IEnemyView<TEnemy>): IEnemyDamageInfo | null;
getDamageInfo(
enemy: IEnemyView<TEnemy>
): IEnemyDamageInfo<TEnemy, THero> | null;
/**
*
@ -416,7 +421,7 @@ export interface IDamageContext<TEnemy, THero> {
*/
getDamageInfoByComputed(
enemy: IReadonlyEnemy<TEnemy>
): IEnemyDamageInfo | null;
): IEnemyDamageInfo<TEnemy, THero> | null;
/**
*
@ -483,15 +488,16 @@ export interface IDamageSystem<TEnemy, THero> extends IDamageContext<
//#region 上下文
export interface IReadonlyEnemyContext<TEnemy, THero> {
export interface IReadonlyEnemyContext<
TEnemy,
THero
> extends IStateBaseExtended {
/** 怪物上下文宽度 */
readonly width: number;
/** 怪物上下文高度 */
readonly height: number;
/** 此上下文使用的索引对象 */
readonly indexer: ILocationHelper;
/** 当前怪物上下文绑定的全局状态对象 */
readonly dataState: IStateBase;
/**
*
@ -567,8 +573,6 @@ export interface IEnemyContext<TEnemy, THero> extends IReadonlyEnemyContext<
readonly height: number;
/** 此上下文使用的索引对象 */
readonly indexer: ILocationHelper;
/** 当前怪物上下文绑定的全局状态对象 */
readonly dataState: IStateBase;
/**
*
@ -724,3 +728,111 @@ export interface IEnemyContext<TEnemy, THero> extends IReadonlyEnemyContext<
}
//#endregion
//#region 战斗流程
export interface ICombatFlowHandler<TEnemy, THero> {
/** 战斗的怪物是否在地图上 */
readonly onMap: boolean;
/** 可修改勇士对象 */
readonly hero: IHeroAttribute<THero>;
/** 可修改怪物对象 */
readonly enemy: IEnemy<TEnemy>;
/** 怪物上下文 */
readonly context: IEnemyContext<TEnemy, THero>;
/** 怪物位置 */
readonly locator: ITileLocator;
/** 全局状态对象 */
readonly state: IStateBase;
}
export interface ICombatFlowHook<TEnemy, THero> extends IHookBase {
/**
*
* @param info
*/
onBeforeCombat?(info: IEnemyDamageInfo<TEnemy, THero>): Promise<void>;
/**
*
* @param info
*/
onAfterCombat?(info: IEnemyDamageInfo<TEnemy, THero>): Promise<void>;
}
export interface ICombatScript<TEnemy, THero> {
/** 此脚本的优先级 */
readonly priority: number;
/**
* `false`
* @param info
* @param handler
*/
before(
info: IEnemyDamageInfo<TEnemy, THero>,
handler: ICombatFlowHandler<TEnemy, THero>
): Promise<boolean>;
/**
*
* @param info
* @param handler
*/
after(
info: IEnemyDamageInfo<TEnemy, THero>,
handler: ICombatFlowHandler<TEnemy, THero>
): Promise<void>;
}
export interface ICombatFlow<TEnemy, THero>
extends IHookable<ICombatFlowHook<TEnemy, THero>>, IStateBaseExtended {
/** 勇士属性对象 */
readonly hero: IReadonlyHeroAttribute<THero> | null;
/** 怪物上下文对象 */
readonly context: IReadonlyEnemyContext<TEnemy, THero> | null;
/** 伤害上下文 */
readonly damage: IDamageContext<TEnemy, THero> | null;
/**
*
* @param hero
*/
bindHero(hero: IHeroAttribute<THero> | null): void;
/**
*
* @param context
*/
bindContext(context: IReadonlyEnemyContext<TEnemy, THero> | null): void;
/**
*
* @param damage
*/
bindDamage(damage: IDamageContext<TEnemy, THero> | null): void;
/**
*
* @param enemy
*/
battle(
enemy: IEnemyView<TEnemy>
): Promise<IEnemyDamageInfo<TEnemy, THero> | null>;
/**
*
* @param enemy
*/
battleComputed(
enemy: IReadonlyEnemy<TEnemy>
): Promise<IEnemyDamageInfo<TEnemy, THero> | null>;
/**
*
* @param script
*/
addCombatScript(script: ICombatScript<TEnemy, THero>): void;
}
//#endregion

View File

@ -34,7 +34,7 @@
## 示例文档
对于新增接口/彻底性地重构接口,大致按照以下格式编写其余需求按照自己的理解去写即可。如某部分需要详细描述可单独开设标题若某个接口内容较多也可以在文档中为其单独开一个章节进行讲述。我会使用引用块的形式在文档中提出建议或回答。Markdown 文档不需要刻意换行,我的编辑器有自动换行功能,正常写没有问题。
对于新增接口/彻底性地重构接口按照以下格式编写其余需求按照自己的理解去写即可。如某部分需要详细描述可单独开设标题若某个接口内容较多也可以在文档中为其单独开一个章节进行讲述。我会使用引用块的形式在文档中提出建议或回答。Markdown 文档不需要刻意换行,我的编辑器有自动换行功能,正常写没有问题。文档保证简洁,不要过于啰嗦,但必须包含所有的必要信息,控制在 100-250 行以内。
```md
# 需求综述
@ -43,7 +43,7 @@
# 接口设计与预期
这是相当重要的一章。需要按接口逐一列出其方法与成员,分析每个成员和方法的预期使用频率(高 / 中 / 低)并说明判断依据;对于中频和高频成员,还需列出至少一个典型使用场景。
这是相当重要的一章。需要按接口逐一列出其方法与成员,分析每个成员和方法的预期使用频率(高 / 中 / 低)并说明判断依据;对于中频和高频成员,还需列出至少一个典型使用场景。必须认真分析频率,想明白**用户**调用的频率,而不是**引擎**调用的频率,比如战斗函数在引擎中可能是中频,但是对用户来说,用户一般会使用系统默认行为来战斗,而**不会**自己调用,因此属于低频。
**命名长度原则**:频率越高,成员名应越短。高频成员以一个单词为宜,最多不超过两个单词;中频成员不超过三个单词;低频成员可以稍长,但不宜过长。
@ -68,7 +68,7 @@
## 可能风险
在任何时候,需要修改已有接口时,必须在文档中写明修改这一接口的风险。这里的已有接口指的是本次设计新增接口之外的接口,即所有已存在于代码中的接口。
在任何时候,需要修改已有接口时,必须在文档中写明修改这一接口的风险。这里的已有接口指的是本次设计新增接口之外的接口,即所有已存在于代码中的接口。本章节**只写**关于修改已有接口可能导致的风险,**不写任何关于实现的风险**,关于实现的风险应该写到最后的问题节,而不是这里。
# 实现思路