mirror of
https://github.com/motajs/template.git
synced 2026-05-17 23:21:14 +08:00
Compare commits
No commits in common. "39e91b241f16dadb9da28a171d74bae51eda3bbd" and "4c0960fff7cf8d8bdd6e565d8ea946470422b0e4" have entirely different histories.
39e91b241f
...
4c0960fff7
@ -1,8 +1,8 @@
|
||||
import { IRange, ITileLocator, logger } from '@motajs/common';
|
||||
import { IRange, logger } from '@motajs/common';
|
||||
import { ITileLocator } from '@user/types';
|
||||
import {
|
||||
IAuraConverter,
|
||||
IAuraView,
|
||||
IDamageSystem,
|
||||
IEnemy,
|
||||
IEnemyAuraView,
|
||||
IEnemyCommonQueryEffect,
|
||||
@ -53,7 +53,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
private readonly dirtyEnemy: Set<IEnemyView<TAttr>> = new Set();
|
||||
|
||||
private mapDamage: IMapDamage<TAttr> | null = null;
|
||||
private damageSystem: IDamageSystem<TAttr, unknown> | null = null;
|
||||
readonly indexer: MapLocIndexer = new MapLocIndexer();
|
||||
|
||||
private needUpdate: boolean = true;
|
||||
@ -175,9 +174,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
if (this.mapDamage) {
|
||||
this.mapDamage.deleteEnemy(view);
|
||||
}
|
||||
if (this.damageSystem) {
|
||||
this.damageSystem.deleteEnemy(view);
|
||||
}
|
||||
|
||||
this.needTotallyRefresh.delete(view);
|
||||
this.dirtyEnemy.delete(view);
|
||||
@ -257,15 +253,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
return this.mapDamage;
|
||||
}
|
||||
|
||||
attachDamageSystem(system: IDamageSystem<TAttr, unknown>): void {
|
||||
this.damageSystem = system;
|
||||
system.markAllDirty();
|
||||
}
|
||||
|
||||
getDamageSystem<THero>(): IDamageSystem<TAttr, THero> | null {
|
||||
return this.damageSystem as IDamageSystem<TAttr, THero> | null;
|
||||
}
|
||||
|
||||
private convertSpecial(
|
||||
special: ISpecial<any>,
|
||||
enemy: IReadonlyEnemy<TAttr>,
|
||||
@ -558,10 +545,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
this.buildupFinal();
|
||||
}
|
||||
|
||||
if (this.damageSystem) {
|
||||
this.damageSystem.markAllDirty();
|
||||
}
|
||||
|
||||
if (this.mapDamage) {
|
||||
this.mapDamage.refreshAll();
|
||||
}
|
||||
@ -570,9 +553,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
markDirty(view: IEnemyView<TAttr>): void {
|
||||
if (!this.locatorViewMap.has(view)) return;
|
||||
this.dirtyEnemy.add(view);
|
||||
if (this.damageSystem) {
|
||||
this.damageSystem.markDirty(view);
|
||||
}
|
||||
}
|
||||
|
||||
private refreshSpecialModifier(
|
||||
@ -707,10 +687,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
|
||||
this.dirtyEnemy.delete(view);
|
||||
|
||||
if (this.damageSystem) {
|
||||
this.damageSystem.markDirty(view);
|
||||
}
|
||||
|
||||
if (this.mapDamage) {
|
||||
this.mapDamage.markEnemyDirty(view);
|
||||
}
|
||||
@ -745,9 +721,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
this.needTotallyRefresh.clear();
|
||||
this.requestedCommonContext.clear();
|
||||
this.dirtyEnemy.clear();
|
||||
if (this.damageSystem) {
|
||||
this.damageSystem.markAllDirty();
|
||||
}
|
||||
if (this.mapDamage) {
|
||||
this.mapDamage.refreshAll();
|
||||
}
|
||||
@ -756,7 +729,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
||||
destroy(): void {
|
||||
this.clear();
|
||||
this.attachMapDamage(null);
|
||||
this.damageSystem = null;
|
||||
this.auraConverter.clear();
|
||||
this.commonQueryMap.clear();
|
||||
this.specialQueryEffects.clear();
|
||||
|
||||
@ -1,211 +0,0 @@
|
||||
import { logger } from '@motajs/common';
|
||||
import {
|
||||
CriticalableHeroStatus,
|
||||
IDamageCalculator,
|
||||
IDamageSystem,
|
||||
IEnemyContext,
|
||||
IEnemyCritical,
|
||||
IEnemyDamageInfo,
|
||||
IEnemyView,
|
||||
IReadonlyEnemy
|
||||
} from './types';
|
||||
|
||||
interface ICriticalSearchResult {
|
||||
/** 此临界点的属性值 */
|
||||
readonly value: number;
|
||||
/** 此临界点的伤害信息 */
|
||||
readonly info: IEnemyDamageInfo;
|
||||
}
|
||||
|
||||
export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
||||
/** 当前正在使用的计算器 */
|
||||
private calculator: IDamageCalculator<TAttr, THero> | null = null;
|
||||
/** 当前勇士属性 */
|
||||
private heroStatus: Readonly<THero> | null = null;
|
||||
/** 怪物伤害缓存 */
|
||||
private readonly cache: Map<IEnemyView<TAttr>, IEnemyDamageInfo> =
|
||||
new Map();
|
||||
|
||||
constructor(readonly context: IEnemyContext<TAttr>) {}
|
||||
|
||||
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void {
|
||||
this.calculator = calculator;
|
||||
this.markAllDirty();
|
||||
}
|
||||
|
||||
getCalculator(): IDamageCalculator<TAttr, THero> | null {
|
||||
return this.calculator;
|
||||
}
|
||||
|
||||
bindHeroStatus(hero: Readonly<THero>): void {
|
||||
this.heroStatus = hero;
|
||||
this.markAllDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 深拷贝勇士属性
|
||||
*/
|
||||
private cloneHeroStatus(): THero | null {
|
||||
if (!this.heroStatus) return null;
|
||||
else return structuredClone(this.heroStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在修改勇士属性的情况下计算怪物伤害
|
||||
* @param enemy 怪物属性
|
||||
* @param attribute 修改的属性键名
|
||||
* @param value 修改为的属性值
|
||||
* @returns
|
||||
*/
|
||||
private calculateDamageWithModified(
|
||||
enemy: IReadonlyEnemy<TAttr>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
value: number
|
||||
): IEnemyDamageInfo {
|
||||
const hero = this.cloneHeroStatus()!;
|
||||
// @ts-expect-error 之后会进行修复
|
||||
hero[attribute] = value;
|
||||
return this.calculator!.calculate(hero, enemy);
|
||||
}
|
||||
|
||||
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null {
|
||||
if (!this.heroStatus) {
|
||||
logger.warn(107);
|
||||
return null;
|
||||
}
|
||||
if (!this.calculator) {
|
||||
logger.warn(106);
|
||||
return null;
|
||||
}
|
||||
const hero = this.cloneHeroStatus()!;
|
||||
|
||||
const cached = this.cache.get(enemy);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const info = this.calculator.calculate(hero, enemy.getComputedEnemy());
|
||||
this.cache.set(enemy, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
markDirty(enemy: IEnemyView<TAttr>): void {
|
||||
this.cache.delete(enemy);
|
||||
}
|
||||
|
||||
deleteEnemy(enemy: IEnemyView<TAttr>): void {
|
||||
this.cache.delete(enemy);
|
||||
}
|
||||
|
||||
markAllDirty(): void {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
*calculateCritical(
|
||||
view: IEnemyView<TAttr>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
precision: number
|
||||
): Generator<IEnemyCritical, void, void> {
|
||||
if (!this.heroStatus) {
|
||||
logger.warn(107);
|
||||
return;
|
||||
}
|
||||
if (!this.calculator) {
|
||||
logger.warn(106);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentInfo = this.getDamageInfo(view);
|
||||
if (!currentInfo) return;
|
||||
|
||||
const enemy = view.getComputedEnemy();
|
||||
const hero = this.cloneHeroStatus()!;
|
||||
const currentValue = hero[attribute] as number;
|
||||
|
||||
const upperLimit = Math.floor(
|
||||
this.calculator.getCriticalLimit(hero, enemy, attribute)
|
||||
);
|
||||
|
||||
if (currentValue >= upperLimit) return;
|
||||
|
||||
const maxIterations = Math.max(0, Math.floor(precision));
|
||||
let baseValue = currentValue;
|
||||
let baseInfo = currentInfo;
|
||||
|
||||
while (baseValue < upperLimit) {
|
||||
const next = this.findNextCritical(
|
||||
enemy,
|
||||
attribute,
|
||||
baseValue,
|
||||
upperLimit,
|
||||
baseInfo.damage,
|
||||
maxIterations
|
||||
);
|
||||
if (!next) return;
|
||||
|
||||
yield {
|
||||
nextValue: next.value,
|
||||
baseValue: currentValue,
|
||||
nextDiff: next.value - currentValue,
|
||||
baseInfo: currentInfo,
|
||||
info: next.info,
|
||||
damageDiff: next.info.damage - currentInfo.damage
|
||||
};
|
||||
|
||||
baseValue = next.value;
|
||||
baseInfo = next.info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算下一个临界点
|
||||
* @param enemy 怪物对象
|
||||
* @param attribute 勇士属性名
|
||||
* @param currentValue 当前勇士属性值
|
||||
* @param upperLimit 二分上界
|
||||
* @param referenceDamage 参考伤害值
|
||||
* @param maxIterations 最大迭代数量
|
||||
*/
|
||||
private findNextCritical(
|
||||
enemy: IReadonlyEnemy<TAttr>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
currentValue: number,
|
||||
upperLimit: number,
|
||||
referenceDamage: number,
|
||||
maxIterations: number
|
||||
): ICriticalSearchResult | null {
|
||||
let left = currentValue;
|
||||
let right = upperLimit;
|
||||
let rightInfo = this.calculateDamageWithModified(
|
||||
enemy,
|
||||
attribute,
|
||||
right
|
||||
);
|
||||
|
||||
if (rightInfo.damage >= referenceDamage) return null;
|
||||
|
||||
let iter = 0;
|
||||
while (iter < maxIterations) {
|
||||
const middle = Math.floor((left + right) / 2);
|
||||
const middleInfo = this.calculateDamageWithModified(
|
||||
enemy,
|
||||
attribute,
|
||||
middle
|
||||
);
|
||||
if (middleInfo.damage < referenceDamage) {
|
||||
right = middle;
|
||||
rightInfo = middleInfo;
|
||||
} else {
|
||||
left = middle;
|
||||
}
|
||||
if (right - left <= 1) break;
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
return {
|
||||
value: right,
|
||||
info: rightInfo
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -51,13 +51,6 @@ export class Enemy<TAttr> implements IEnemy<TAttr> {
|
||||
this.attributes[key] = value;
|
||||
}
|
||||
|
||||
addAttribute<K extends SelectKey<TAttr, number>>(
|
||||
key: K,
|
||||
value: number
|
||||
): void {
|
||||
(this.attributes[key] as number) += value;
|
||||
}
|
||||
|
||||
getAttribute<K extends keyof TAttr>(key: K): TAttr[K] {
|
||||
return this.attributes[key];
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
export * from './enemy';
|
||||
export * from './context';
|
||||
export * from './damage';
|
||||
export * from './mapDamage';
|
||||
export * from './manager';
|
||||
export * from './special';
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
import { logger } from '@motajs/common';
|
||||
import { Enemy as EnemyImpl } from './enemy';
|
||||
import {
|
||||
IEnemy,
|
||||
IEnemyManager,
|
||||
IEnemyLegacyBridge,
|
||||
SpecialCreation
|
||||
} from './types';
|
||||
import { IEnemy, IEnemyManager, SpecialCreation } from './types';
|
||||
|
||||
export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
|
||||
/** 特殊属性注册表,code -> 创建函数 */
|
||||
private readonly specialRegistry: Map<number, SpecialCreation<any, TAttr>> =
|
||||
new Map();
|
||||
/** 自定义怪物属性注册表,name -> 默认值 */
|
||||
private readonly attributeRegistry: Map<keyof TAttr, any> = new Map();
|
||||
private readonly attributeRegistry: Map<string, any> = new Map();
|
||||
/** 怪物模板表,code -> IEnemy */
|
||||
private readonly prefabByCode: Map<number, IEnemy<TAttr>> = new Map();
|
||||
/** 怪物模板表,id -> IEnemy */
|
||||
@ -20,16 +15,11 @@ export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
|
||||
/** 旧样板怪物 id 到 code 的映射,用于 fromLegacyEnemy 快速查找已有模板 */
|
||||
private readonly legacyIdToCode: Map<string, number> = new Map();
|
||||
|
||||
constructor(readonly bridge: IEnemyLegacyBridge<TAttr>) {}
|
||||
|
||||
registerSpecial(code: number, cons: SpecialCreation<any, TAttr>): void {
|
||||
this.specialRegistry.set(code, cons);
|
||||
}
|
||||
|
||||
setAttributeDefaults<K extends keyof TAttr>(
|
||||
name: K,
|
||||
defaultValue: TAttr[K]
|
||||
): void {
|
||||
registerAttribute(name: string, defaultValue: any): void {
|
||||
if (
|
||||
typeof defaultValue === 'function' ||
|
||||
typeof defaultValue === 'symbol' ||
|
||||
@ -60,13 +50,16 @@ export class EnemyManager<TAttr> implements IEnemyManager<TAttr> {
|
||||
* @param enemy 旧样板怪物对象
|
||||
*/
|
||||
private createAttributes(enemy: Enemy): TAttr {
|
||||
const attrs: Partial<TAttr> = {};
|
||||
const attrs: Record<string, any> = {};
|
||||
for (const [name, defaultValue] of this.attributeRegistry) {
|
||||
attrs[name] = structuredClone(defaultValue);
|
||||
}
|
||||
|
||||
Object.assign(attrs, this.bridge.fromLegacyEnemy(enemy, attrs));
|
||||
|
||||
attrs.hp = enemy.hp;
|
||||
attrs.atk = enemy.atk;
|
||||
attrs.def = enemy.def;
|
||||
attrs.money = enemy.money;
|
||||
attrs.exp = enemy.exp;
|
||||
attrs.point = enemy.point;
|
||||
return attrs as TAttr;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { logger, ITileLocator } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import { ITileLocator } from '@user/types';
|
||||
import {
|
||||
IEnemyContext,
|
||||
IEnemyView,
|
||||
@ -59,12 +60,10 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
|
||||
/** 合并后伤害缓存,索引 -> 合并结果 */
|
||||
private readonly reducedCache: Map<number, IMapDamageInfo> = new Map();
|
||||
|
||||
/** 坐标索引对象 */
|
||||
private readonly indexer: IMapLocIndexer;
|
||||
|
||||
constructor(readonly context: IEnemyContext<TAttr>) {
|
||||
this.indexer = context.indexer;
|
||||
}
|
||||
constructor(
|
||||
readonly context: IEnemyContext<TAttr>,
|
||||
readonly indexer: IMapLocIndexer
|
||||
) {}
|
||||
|
||||
useConverter(converter: IMapDamageConverter<TAttr>): void {
|
||||
this.converter = converter;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { IRange, ITileLocator } from '@motajs/common';
|
||||
|
||||
//#region 怪物基础
|
||||
import { IRange } from '@motajs/common';
|
||||
import { ITileLocator } from '@user/types';
|
||||
|
||||
export interface ISpecial<T = void> {
|
||||
/** 特殊属性代码 */
|
||||
@ -101,16 +100,6 @@ export interface IEnemy<TAttr> extends IReadonlyEnemy<TAttr> {
|
||||
*/
|
||||
setAttribute<K extends keyof TAttr>(key: K, value: TAttr[K]): void;
|
||||
|
||||
/**
|
||||
* 对一个数字类型的属性值进行增减操作
|
||||
* @param key 属性名称
|
||||
* @param value 要增加的属性值,可以是负值
|
||||
*/
|
||||
addAttribute<K extends SelectKey<TAttr, number>>(
|
||||
key: K,
|
||||
value: number
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 深拷贝此怪物对象
|
||||
*/
|
||||
@ -123,20 +112,8 @@ export interface IEnemy<TAttr> extends IReadonlyEnemy<TAttr> {
|
||||
copyFrom(enemy: IReadonlyEnemy<TAttr>): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 怪物管理器
|
||||
|
||||
export type SpecialCreation<T, TAttr> = (enemy: IEnemy<TAttr>) => ISpecial<T>;
|
||||
|
||||
export interface IEnemyLegacyBridge<TAttr> {
|
||||
/**
|
||||
* 从旧样板的怪物对象中获取其属性
|
||||
* @param enemy 旧样板怪物对象
|
||||
*/
|
||||
fromLegacyEnemy(enemy: Enemy, defaultValue: Partial<TAttr>): TAttr;
|
||||
}
|
||||
|
||||
export interface IEnemyManager<TAttr> {
|
||||
/**
|
||||
* 注册一个特殊属性
|
||||
@ -150,10 +127,7 @@ export interface IEnemyManager<TAttr> {
|
||||
* @param name 属性名称
|
||||
* @param defaultValue 属性默认值
|
||||
*/
|
||||
setAttributeDefaults<K extends keyof TAttr>(
|
||||
name: K,
|
||||
defaultValue: TAttr[K]
|
||||
): void;
|
||||
registerAttribute(name: string, defaultValue: any): void;
|
||||
|
||||
/**
|
||||
* 根据旧样板怪物对象生成一个新的怪物对象
|
||||
@ -213,8 +187,6 @@ export interface IEnemyManager<TAttr> {
|
||||
changePrefab(code: number | string, enemy: IEnemy<TAttr>): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 辅助接口
|
||||
|
||||
export interface IMapLocHelper {
|
||||
@ -568,117 +540,6 @@ export interface IMapDamage<TAttr> {
|
||||
|
||||
//#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]: unknown;
|
||||
};
|
||||
|
||||
export interface IDamageCalculator<TAttr, THero> {
|
||||
/**
|
||||
* 计算战斗伤害信息
|
||||
* @param hero 勇士信息
|
||||
* @param enemy 怪物信息
|
||||
*/
|
||||
calculate(
|
||||
hero: Readonly<THero>,
|
||||
enemy: IReadonlyEnemy<TAttr>
|
||||
): IEnemyDamageInfo;
|
||||
|
||||
/**
|
||||
* 获取临界计算的上界
|
||||
* @param hero 勇士信息
|
||||
* @param enemy 怪物信息
|
||||
* @param attribute 勇士的临界属性
|
||||
*/
|
||||
getCriticalLimit(
|
||||
hero: Readonly<THero>,
|
||||
enemy: IReadonlyEnemy<TAttr>,
|
||||
attribute: CriticalableHeroStatus<THero>
|
||||
): number;
|
||||
}
|
||||
|
||||
export interface IDamageSystem<TAttr, THero> {
|
||||
/** 伤害系统所属的上下文 */
|
||||
readonly context: IEnemyContext<TAttr>;
|
||||
|
||||
/**
|
||||
* 设置当前伤害计算系统使用的伤害计算器
|
||||
* @param calculator 伤害计算器
|
||||
*/
|
||||
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void;
|
||||
|
||||
/**
|
||||
* 获取当前使用的伤害计算器
|
||||
*/
|
||||
getCalculator(): IDamageCalculator<TAttr, THero> | null;
|
||||
|
||||
/**
|
||||
* 绑定勇士信息
|
||||
* @param hero 勇士信息
|
||||
*/
|
||||
bindHeroStatus(hero: Readonly<THero>): void;
|
||||
|
||||
/**
|
||||
* 获取战斗伤害信息
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null;
|
||||
|
||||
/**
|
||||
* 将指定的怪物标记为脏
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
markDirty(enemy: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 删除指定的怪物
|
||||
* @param enemy 怪物视图
|
||||
*/
|
||||
deleteEnemy(enemy: IEnemyView<TAttr>): void;
|
||||
|
||||
/**
|
||||
* 将所有怪物标记为脏
|
||||
*/
|
||||
markAllDirty(): void;
|
||||
|
||||
/**
|
||||
* 计算怪物在指定勇士属性下的临界
|
||||
* @param enemy 怪物视图
|
||||
* @param attribute 计算临界的目标勇士属性,比如计算攻击临界、自定义属性的临界等等
|
||||
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可
|
||||
*/
|
||||
calculateCritical(
|
||||
enemy: IEnemyView<TAttr>,
|
||||
attribute: CriticalableHeroStatus<THero>,
|
||||
precision: number
|
||||
): Generator<IEnemyCritical, void, void>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 上下文
|
||||
|
||||
export interface IEnemyContext<TAttr> {
|
||||
@ -686,8 +547,6 @@ export interface IEnemyContext<TAttr> {
|
||||
readonly width: number;
|
||||
/** 怪物上下文高度 */
|
||||
readonly height: number;
|
||||
/** 此上下文使用的索引对象 */
|
||||
readonly indexer: IMapLocIndexer;
|
||||
|
||||
/**
|
||||
* 调整上下文尺寸,并清空当前上下文中的所有怪物与状态
|
||||
@ -843,17 +702,6 @@ export interface IEnemyContext<TAttr> {
|
||||
*/
|
||||
getMapDamage(): IMapDamage<TAttr> | null;
|
||||
|
||||
/**
|
||||
* 绑定伤害计算系统
|
||||
* @param system 伤害系统
|
||||
*/
|
||||
attachDamageSystem(system: IDamageSystem<TAttr, unknown>): void;
|
||||
|
||||
/**
|
||||
* 获取当前绑定的伤害计算系统
|
||||
*/
|
||||
getDamageSystem<THero>(): IDamageSystem<TAttr, THero> | null;
|
||||
|
||||
/**
|
||||
* 重建当前上下文中的全部怪物计算结果
|
||||
*
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ITileLocator } from '@motajs/common';
|
||||
import { ITileLocator } from '@user/types';
|
||||
import { IMapLocIndexer } from './types';
|
||||
|
||||
export class MapLocIndexer implements IMapLocIndexer {
|
||||
|
||||
@ -6,4 +6,4 @@
|
||||
"@user/data-base": "workspace:*",
|
||||
"@user/data-utils": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,71 +1,24 @@
|
||||
import { ICoreState, IStateSaveData } from './types';
|
||||
import { ICoreState, IGameDataState, IStateSaveData } from './types';
|
||||
import { IHeroState, HeroState } from './hero';
|
||||
import { ILayerState, LayerState } from './map';
|
||||
import { IRoleFaceBinder, RoleFaceBinder } from './common';
|
||||
import {
|
||||
DamageSystem,
|
||||
EnemyContext,
|
||||
EnemyManager,
|
||||
IEnemyContext,
|
||||
IEnemyManager,
|
||||
MapDamage
|
||||
} from '@user/data-base';
|
||||
import { IEnemyAttributes } from './enemy/types';
|
||||
import {
|
||||
CommonAuraConverter,
|
||||
EnemyLegacyBridge,
|
||||
GuardAuraConverter,
|
||||
MainEnemyFinalEffect,
|
||||
MainMapDamageConverter,
|
||||
MainMapDamageReducer,
|
||||
registerSpecials
|
||||
} from './enemy';
|
||||
import { TILE_HEIGHT, TILE_WIDTH } from './shared';
|
||||
import { GameDataState } from './data';
|
||||
|
||||
export class CoreState implements ICoreState {
|
||||
readonly layer: ILayerState;
|
||||
readonly hero: IHeroState;
|
||||
readonly roleFace: IRoleFaceBinder;
|
||||
readonly data: IGameDataState;
|
||||
readonly idNumberMap: Map<string, number>;
|
||||
readonly numberIdMap: Map<number, string>;
|
||||
|
||||
readonly enemyManager: IEnemyManager<IEnemyAttributes>;
|
||||
readonly enemyContext: IEnemyContext<IEnemyAttributes>;
|
||||
|
||||
constructor() {
|
||||
this.layer = new LayerState();
|
||||
this.hero = new HeroState();
|
||||
this.roleFace = new RoleFaceBinder();
|
||||
this.idNumberMap = new Map();
|
||||
this.numberIdMap = new Map();
|
||||
|
||||
//#region 怪物初始化
|
||||
|
||||
// 怪物管理器初始化
|
||||
const enemyManager = new EnemyManager(new EnemyLegacyBridge());
|
||||
enemyManager.setAttributeDefaults('hp', 0);
|
||||
enemyManager.setAttributeDefaults('atk', 0);
|
||||
enemyManager.setAttributeDefaults('def', 0);
|
||||
enemyManager.setAttributeDefaults('exp', 0);
|
||||
enemyManager.setAttributeDefaults('money', 0);
|
||||
enemyManager.setAttributeDefaults('point', 0);
|
||||
registerSpecials(enemyManager);
|
||||
this.enemyManager = enemyManager;
|
||||
// 怪物上下文初始化
|
||||
const enemyContext = new EnemyContext<IEnemyAttributes>();
|
||||
const damageSystem = new DamageSystem(enemyContext);
|
||||
const mapDamage = new MapDamage(enemyContext);
|
||||
mapDamage.useConverter(new MainMapDamageConverter());
|
||||
mapDamage.useReducer(new MainMapDamageReducer());
|
||||
enemyContext.attachDamageSystem(damageSystem);
|
||||
enemyContext.attachMapDamage(mapDamage);
|
||||
enemyContext.registerAuraConverter(new CommonAuraConverter());
|
||||
enemyContext.registerAuraConverter(new GuardAuraConverter());
|
||||
enemyContext.registerFinalEffect(new MainEnemyFinalEffect());
|
||||
enemyContext.resize(TILE_WIDTH, TILE_HEIGHT);
|
||||
this.enemyContext = enemyContext;
|
||||
|
||||
//#endregion
|
||||
this.data = new GameDataState();
|
||||
}
|
||||
|
||||
saveState(): IStateSaveData {
|
||||
|
||||
13
packages-user/data-state/src/data.ts
Normal file
13
packages-user/data-state/src/data.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { EnemyManager, IEnemyManager } from '@user/data-base';
|
||||
import { IEnemyAttributes } from './enemy/types';
|
||||
import { IGameDataState } from './types';
|
||||
import { registerSpecials } from './enemy';
|
||||
|
||||
export class GameDataState implements IGameDataState {
|
||||
readonly enemyManager: IEnemyManager<IEnemyAttributes>;
|
||||
|
||||
constructor() {
|
||||
this.enemyManager = new EnemyManager<IEnemyAttributes>();
|
||||
registerSpecials(this.enemyManager);
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,6 @@ import {
|
||||
IManhattanRangeParam,
|
||||
IRange,
|
||||
IRectRangeParam,
|
||||
ITileLocator,
|
||||
ManhattanRange,
|
||||
RectRange
|
||||
} from '@motajs/common';
|
||||
@ -18,6 +17,7 @@ import {
|
||||
IEnemy
|
||||
} from '@user/data-base';
|
||||
import { IHaloValue } from './special';
|
||||
import { ITileLocator } from '@user/types';
|
||||
import { IEnemyAttributes } from './types';
|
||||
|
||||
const FULL_RANGE = new FullRange();
|
||||
@ -95,18 +95,27 @@ export class CommonAura implements IEnemyAuraView<
|
||||
const { hpBuff, atkBuff, defBuff } = this.special.value;
|
||||
|
||||
if (hpBuff !== 0) {
|
||||
const hpValue = (baseEnemy.getAttribute('hp') * hpBuff) / 100;
|
||||
enemy.addAttribute('hp', Math.floor(hpValue));
|
||||
enemy.setAttribute(
|
||||
'hp',
|
||||
enemy.getAttribute('hp') +
|
||||
Math.floor((baseEnemy.getAttribute('hp') * hpBuff) / 100)
|
||||
);
|
||||
}
|
||||
|
||||
if (atkBuff !== 0) {
|
||||
const atkValue = (baseEnemy.getAttribute('atk') * atkBuff) / 100;
|
||||
enemy.addAttribute('atk', Math.floor(atkValue));
|
||||
enemy.setAttribute(
|
||||
'atk',
|
||||
enemy.getAttribute('atk') +
|
||||
Math.floor((baseEnemy.getAttribute('atk') * atkBuff) / 100)
|
||||
);
|
||||
}
|
||||
|
||||
if (defBuff !== 0) {
|
||||
const defValue = (baseEnemy.getAttribute('def') * defBuff) / 100;
|
||||
enemy.addAttribute('def', Math.floor(defValue));
|
||||
enemy.setAttribute(
|
||||
'def',
|
||||
enemy.getAttribute('def') +
|
||||
Math.floor((baseEnemy.getAttribute('def') * defBuff) / 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
import { IEnemy, IEnemyFinalEffect } from '@user/data-base';
|
||||
import { IEnemyAttributes } from './types';
|
||||
import { ITileLocator } from '@motajs/common';
|
||||
|
||||
const HERO_STATUS_PLACEHOLDER = {
|
||||
atk: 0,
|
||||
def: 0
|
||||
} as const;
|
||||
|
||||
export class MainEnemyFinalEffect implements IEnemyFinalEffect<IEnemyAttributes> {
|
||||
readonly priority: number = 0;
|
||||
|
||||
apply(enemy: IEnemy<IEnemyAttributes>, _locator: ITileLocator): void {
|
||||
if (enemy.hasSpecial(3)) {
|
||||
enemy.setAttribute(
|
||||
'def',
|
||||
Math.max(
|
||||
enemy.getAttribute('def'),
|
||||
HERO_STATUS_PLACEHOLDER.atk - 1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (enemy.hasSpecial(10)) {
|
||||
enemy.setAttribute('atk', HERO_STATUS_PLACEHOLDER.atk);
|
||||
enemy.setAttribute('def', HERO_STATUS_PLACEHOLDER.def);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
export * from './aura';
|
||||
export * from './damage';
|
||||
export * from './final';
|
||||
export * from './legacy';
|
||||
export * from './mapDamage';
|
||||
export * from './special';
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { IEnemyLegacyBridge } from '@user/data-base';
|
||||
import { IEnemyAttributes } from './types';
|
||||
|
||||
export class EnemyLegacyBridge implements IEnemyLegacyBridge<IEnemyAttributes> {
|
||||
fromLegacyEnemy(
|
||||
enemy: Enemy,
|
||||
defaultAttr: Partial<IEnemyAttributes>
|
||||
): IEnemyAttributes {
|
||||
return {
|
||||
hp: enemy.hp ?? defaultAttr.hp ?? 0,
|
||||
atk: enemy.atk ?? defaultAttr.atk ?? 0,
|
||||
def: enemy.def ?? defaultAttr.def ?? 0,
|
||||
money: enemy.money ?? defaultAttr.money ?? 0,
|
||||
exp: enemy.exp ?? defaultAttr.exp ?? 0,
|
||||
point: enemy.point ?? defaultAttr.point ?? 0,
|
||||
guard: defaultAttr.guard ?? new Set()
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -8,9 +8,9 @@ import {
|
||||
IRectRangeParam,
|
||||
ManhattanRange,
|
||||
RayRange,
|
||||
RectRange,
|
||||
ITileLocator
|
||||
RectRange
|
||||
} from '@motajs/common';
|
||||
import { ITileLocator } from '@user/types';
|
||||
import { IReadonlyEnemy, ISpecial } from '@user/data-base';
|
||||
import {
|
||||
IEnemyContext,
|
||||
|
||||
@ -51,7 +51,7 @@ export interface IHaloValue {
|
||||
export function registerSpecials(
|
||||
manager: IEnemyManager<IEnemyAttributes>
|
||||
): void {
|
||||
manager.setAttributeDefaults('guard', new Set());
|
||||
manager.registerAttribute('guard', new Set());
|
||||
|
||||
// 0 - 空
|
||||
manager.registerSpecial(
|
||||
|
||||
@ -1,7 +1,2 @@
|
||||
/** 每个地图的默认宽度 */
|
||||
export const TILE_WIDTH = 13;
|
||||
/** 每个地图的默认高度 */
|
||||
export const TILE_HEIGHT = 13;
|
||||
|
||||
/** 默认的勇士图片 */
|
||||
export const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ILayerState } from './map';
|
||||
import { IHeroFollower, IHeroState } from './hero';
|
||||
import { IRoleFaceBinder } from './common';
|
||||
import { IEnemyContext, IEnemyManager } from '@user/data-base';
|
||||
import { IEnemyManager } from '@user/data-base';
|
||||
import { IEnemyAttributes } from './enemy/types';
|
||||
|
||||
export interface IGameDataState {
|
||||
@ -21,16 +21,13 @@ export interface ICoreState {
|
||||
readonly hero: IHeroState;
|
||||
/** 朝向绑定 */
|
||||
readonly roleFace: IRoleFaceBinder;
|
||||
/** 游戏数据状态 */
|
||||
readonly data: IGameDataState;
|
||||
/** id 到图块数字的映射 */
|
||||
readonly idNumberMap: Map<string, number>;
|
||||
/** 图块数字到 id 的映射 */
|
||||
readonly numberIdMap: Map<number, string>;
|
||||
|
||||
/** 怪物管理器 */
|
||||
readonly enemyManager: IEnemyManager<IEnemyAttributes>;
|
||||
/** 怪物上下文 */
|
||||
readonly enemyContext: IEnemyContext<IEnemyAttributes>;
|
||||
|
||||
/**
|
||||
* 保存状态
|
||||
*/
|
||||
|
||||
3
packages-user/types/package.json
Normal file
3
packages-user/types/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "@user/types"
|
||||
}
|
||||
87
packages-user/types/src/enemy.ts
Normal file
87
packages-user/types/src/enemy.ts
Normal file
@ -0,0 +1,87 @@
|
||||
// todo: 2.C
|
||||
|
||||
export interface IEnemyInfo {}
|
||||
|
||||
export interface IDamageInfo {}
|
||||
|
||||
export interface IDamageEnemy {
|
||||
/** 原始怪物信息 */
|
||||
readonly enemy: Enemy;
|
||||
/** 该怪物所属的怪物列表 */
|
||||
readonly collection: IEnemyCollection | null;
|
||||
/** 怪物横坐标 */
|
||||
readonly x: number | undefined;
|
||||
|
||||
/**
|
||||
* 获取怪物属性信息
|
||||
*/
|
||||
getEnemyInfo(): IEnemyInfo;
|
||||
|
||||
/**
|
||||
* 获取这个怪物的伤害信息
|
||||
*/
|
||||
getDamageInfo(): IDamageInfo;
|
||||
}
|
||||
|
||||
export interface IMapDamage {
|
||||
/** 伤害类型 */
|
||||
readonly type: string;
|
||||
/** 伤害值 */
|
||||
readonly damage: number;
|
||||
/** 伤害优先级 */
|
||||
readonly priority: number;
|
||||
}
|
||||
|
||||
export interface IMapDamageSummary {
|
||||
/** 该点的总伤害 */
|
||||
readonly totalDamage: number;
|
||||
/** 该点的伤害信息 */
|
||||
readonly damages: IMapDamage[];
|
||||
}
|
||||
|
||||
export interface IEnemyCollection {
|
||||
/** 怪物列表,索引为 x + width * y,值表示该点对应的怪物 */
|
||||
readonly list: Map<number, IDamageEnemy>;
|
||||
|
||||
/** 楼层 id */
|
||||
readonly floorId: FloorIds;
|
||||
/** 楼层宽度 */
|
||||
readonly width: number;
|
||||
/** 楼层高度 */
|
||||
readonly height: number;
|
||||
|
||||
/** 地图伤害 */
|
||||
readonly mapDamage: Map<number, IMapDamageSummary>;
|
||||
|
||||
/** 用于计算本怪物列表中怪物信息的勇士属性 */
|
||||
readonly hero: HeroStatus;
|
||||
|
||||
/**
|
||||
* 获取一点的怪物信息,不存在时返回 null
|
||||
* @param x 怪物横坐标
|
||||
* @param y 怪物纵坐标
|
||||
*/
|
||||
getEnemy(x: number, y: number): IDamageEnemy | null;
|
||||
|
||||
/**
|
||||
* 获取一点的地图伤害信息,每一点都包含地图伤害对象,传入地图外坐标时返回 null
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
getMapDamage(x: number, y: number): IMapDamageSummary | null;
|
||||
|
||||
/**
|
||||
* 重置此地图的怪物信息,并重新计算
|
||||
*/
|
||||
refresh(): void;
|
||||
|
||||
/**
|
||||
* 复制这个怪物列表,同时将复制后的列表中勇士属性设为指定值
|
||||
* @param status 新的勇士属性
|
||||
*/
|
||||
with(status: HeroStatus): IEnemyCollection;
|
||||
}
|
||||
|
||||
export interface IDamageSystem {
|
||||
readonly collections: Map<FloorIds, IEnemyCollection>;
|
||||
}
|
||||
2
packages-user/types/src/index.ts
Normal file
2
packages-user/types/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './enemy';
|
||||
export * from './spatial';
|
||||
6
packages-user/types/src/spatial.ts
Normal file
6
packages-user/types/src/spatial.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface ITileLocator {
|
||||
/** 图块所在横坐标 */
|
||||
x: number;
|
||||
/** 图块所在纵坐标 */
|
||||
y: number;
|
||||
}
|
||||
@ -161,8 +161,6 @@
|
||||
"103": "Map damage reducer is missing, reduced map damage is unavailable.",
|
||||
"104": "Enemy dirty marking failed since specific enemy is not in current context.",
|
||||
"105": "No specific map damage view stored, which seems like an internal bug of map damage system.",
|
||||
"106": "Damage calculator is missing, damage calculation is unavailable.",
|
||||
"107": "Hero status is not bound, damage calculation is unavailable.",
|
||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency."
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,13 +135,6 @@ export interface IRayRangeParam {
|
||||
|
||||
//#region 实用接口
|
||||
|
||||
export interface ITileLocator {
|
||||
/** 图块所在横坐标 */
|
||||
x: number;
|
||||
/** 图块所在纵坐标 */
|
||||
y: number;
|
||||
}
|
||||
|
||||
export const enum InternalDirectionGroup {
|
||||
/** 上下左右四方向 */
|
||||
Dir4,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user