mirror of
https://github.com/motajs/template.git
synced 2026-05-02 12:23:13 +08:00
refactor: 怪物系统与勇士属性系统联动
This commit is contained in:
parent
92ff489811
commit
eaa725553b
@ -16,7 +16,7 @@ export async function createMainExtension() {
|
|||||||
mainMapRenderer.useAsset(materials.trackedAsset);
|
mainMapRenderer.useAsset(materials.trackedAsset);
|
||||||
const layer = state.layer.getLayerByAlias('event');
|
const layer = state.layer.getLayerByAlias('event');
|
||||||
if (layer) {
|
if (layer) {
|
||||||
mainMapExtension.addHero(state.hero, layer);
|
mainMapExtension.addHero(state.hero.mover, layer);
|
||||||
mainMapExtension.addDoor(layer);
|
mainMapExtension.addDoor(layer);
|
||||||
}
|
}
|
||||||
mainMapExtension.addText();
|
mainMapExtension.addText();
|
||||||
|
|||||||
@ -8,17 +8,20 @@ import {
|
|||||||
IEnemyCommonQueryEffect,
|
IEnemyCommonQueryEffect,
|
||||||
IEnemyContext,
|
IEnemyContext,
|
||||||
IEnemyFinalEffect,
|
IEnemyFinalEffect,
|
||||||
|
IEnemyHandler,
|
||||||
IEnemySpecialModifier,
|
IEnemySpecialModifier,
|
||||||
IEnemySpecialQueryEffect,
|
IEnemySpecialQueryEffect,
|
||||||
IEnemyView,
|
IEnemyView,
|
||||||
IMapDamage,
|
IMapDamage,
|
||||||
IReadonlyEnemy,
|
IReadonlyEnemy,
|
||||||
|
IReadonlyEnemyHandler,
|
||||||
ISpecial
|
ISpecial
|
||||||
} from './types';
|
} from './types';
|
||||||
import { EnemyView } from './enemy';
|
import { EnemyView } from './enemy';
|
||||||
import { MapLocIndexer } from './utils';
|
import { MapLocIndexer } from './utils';
|
||||||
|
import { IReadonlyHeroAttribute } from '../hero';
|
||||||
|
|
||||||
export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
export class EnemyContext<TAttr, THero> implements IEnemyContext<TAttr, THero> {
|
||||||
private readonly enemyViewMap: Map<number, EnemyView<TAttr>> = new Map();
|
private readonly enemyViewMap: Map<number, EnemyView<TAttr>> = new Map();
|
||||||
private readonly enemyMap: Map<number, IEnemy<TAttr>> = new Map();
|
private readonly enemyMap: Map<number, IEnemy<TAttr>> = new Map();
|
||||||
private readonly locatorViewMap: Map<IEnemyView<TAttr>, number> = new Map();
|
private readonly locatorViewMap: Map<IEnemyView<TAttr>, number> = new Map();
|
||||||
@ -28,23 +31,26 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
EnemyView<TAttr>
|
EnemyView<TAttr>
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
|
||||||
private readonly auraConverter: Set<IAuraConverter<TAttr>> = new Set();
|
private readonly auraConverter: Set<IAuraConverter<TAttr, THero>> =
|
||||||
private readonly converterStatus: Map<IAuraConverter<TAttr>, boolean> =
|
new Set();
|
||||||
new Map();
|
private readonly converterStatus: Map<
|
||||||
|
IAuraConverter<TAttr, THero>,
|
||||||
|
boolean
|
||||||
|
> = new Map();
|
||||||
private readonly convertedAura: Map<ISpecial<any>, IAuraView<TAttr>> =
|
private readonly convertedAura: Map<ISpecial<any>, IAuraView<TAttr>> =
|
||||||
new Map();
|
new Map();
|
||||||
|
|
||||||
private readonly commonQueryMap: Map<
|
private readonly commonQueryMap: Map<
|
||||||
number,
|
number,
|
||||||
IEnemyCommonQueryEffect<TAttr>[]
|
IEnemyCommonQueryEffect<TAttr, THero>[]
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
|
||||||
private readonly specialQueryEffects: Map<
|
private readonly specialQueryEffects: Map<
|
||||||
number,
|
number,
|
||||||
IEnemySpecialQueryEffect<TAttr>[]
|
IEnemySpecialQueryEffect<TAttr, THero>[]
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
|
||||||
private readonly finalEffects: IEnemyFinalEffect<TAttr>[] = [];
|
private readonly finalEffects: IEnemyFinalEffect<TAttr, THero>[] = [];
|
||||||
private readonly globalAuraList: Set<IAuraView<TAttr>> = new Set();
|
private readonly globalAuraList: Set<IAuraView<TAttr>> = new Set();
|
||||||
private readonly sortedAura: Map<number, Set<IAuraView<TAttr>>> = new Map();
|
private readonly sortedAura: Map<number, Set<IAuraView<TAttr>>> = new Map();
|
||||||
|
|
||||||
@ -52,10 +58,17 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
private readonly requestedCommonContext: Set<IEnemyView<TAttr>> = new Set();
|
private readonly requestedCommonContext: Set<IEnemyView<TAttr>> = new Set();
|
||||||
private readonly dirtyEnemy: Set<IEnemyView<TAttr>> = new Set();
|
private readonly dirtyEnemy: Set<IEnemyView<TAttr>> = new Set();
|
||||||
|
|
||||||
private mapDamage: IMapDamage<TAttr> | null = null;
|
/** 当前绑定的勇士属性对象 */
|
||||||
private damageSystem: IDamageSystem<TAttr, unknown> | null = null;
|
private bindedHero: IReadonlyHeroAttribute<THero> | null = null;
|
||||||
|
/** 地图伤害对象 */
|
||||||
|
private mapDamage: IMapDamage<TAttr, THero> | null = null;
|
||||||
|
/** 伤害系统对象 */
|
||||||
|
private damageSystem: IDamageSystem<TAttr, THero> | null = null;
|
||||||
|
|
||||||
|
/** 索引工具 */
|
||||||
readonly indexer: MapLocIndexer = new MapLocIndexer();
|
readonly indexer: MapLocIndexer = new MapLocIndexer();
|
||||||
|
|
||||||
|
/** 当前是否需要全量刷新 */
|
||||||
private needUpdate: boolean = true;
|
private needUpdate: boolean = true;
|
||||||
|
|
||||||
built: boolean = false;
|
built: boolean = false;
|
||||||
@ -70,20 +83,20 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAuraConverter(converter: IAuraConverter<TAttr>): void {
|
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void {
|
||||||
this.auraConverter.add(converter);
|
this.auraConverter.add(converter);
|
||||||
this.converterStatus.set(converter, true);
|
this.converterStatus.set(converter, true);
|
||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterAuraConverter(converter: IAuraConverter<TAttr>): void {
|
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void {
|
||||||
this.auraConverter.delete(converter);
|
this.auraConverter.delete(converter);
|
||||||
this.converterStatus.delete(converter);
|
this.converterStatus.delete(converter);
|
||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAuraConverterEnabled(
|
setAuraConverterEnabled(
|
||||||
converter: IAuraConverter<TAttr>,
|
converter: IAuraConverter<TAttr, THero>,
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
): void {
|
): void {
|
||||||
if (!this.auraConverter.has(converter)) return;
|
if (!this.auraConverter.has(converter)) return;
|
||||||
@ -93,7 +106,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
|
|
||||||
registerCommonQueryEffect(
|
registerCommonQueryEffect(
|
||||||
code: number,
|
code: number,
|
||||||
effect: IEnemyCommonQueryEffect<TAttr>
|
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||||
): void {
|
): void {
|
||||||
const array = this.commonQueryMap.getOrInsert(code, []);
|
const array = this.commonQueryMap.getOrInsert(code, []);
|
||||||
array.push(effect);
|
array.push(effect);
|
||||||
@ -103,7 +116,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
|
|
||||||
unregisterCommonQueryEffect(
|
unregisterCommonQueryEffect(
|
||||||
code: number,
|
code: number,
|
||||||
effect: IEnemyCommonQueryEffect<TAttr>
|
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||||
): void {
|
): void {
|
||||||
const array = this.commonQueryMap.get(code);
|
const array = this.commonQueryMap.get(code);
|
||||||
if (!array) return;
|
if (!array) return;
|
||||||
@ -113,14 +126,16 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSpecialQueryEffect(effect: IEnemySpecialQueryEffect<TAttr>): void {
|
registerSpecialQueryEffect(
|
||||||
|
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||||
|
): void {
|
||||||
const list = this.specialQueryEffects.getOrInsert(effect.priority, []);
|
const list = this.specialQueryEffects.getOrInsert(effect.priority, []);
|
||||||
list.push(effect);
|
list.push(effect);
|
||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterSpecialQueryEffect(
|
unregisterSpecialQueryEffect(
|
||||||
effect: IEnemySpecialQueryEffect<TAttr>
|
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||||
): void {
|
): void {
|
||||||
const list = this.specialQueryEffects.get(effect.priority);
|
const list = this.specialQueryEffects.get(effect.priority);
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
@ -134,13 +149,13 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerFinalEffect(effect: IEnemyFinalEffect<TAttr>): void {
|
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void {
|
||||||
this.finalEffects.push(effect);
|
this.finalEffects.push(effect);
|
||||||
this.finalEffects.sort((a, b) => b.priority - a.priority);
|
this.finalEffects.sort((a, b) => b.priority - a.priority);
|
||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr>): void {
|
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void {
|
||||||
const index = this.finalEffects.indexOf(effect);
|
const index = this.finalEffects.indexOf(effect);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.finalEffects.splice(index, 1);
|
this.finalEffects.splice(index, 1);
|
||||||
@ -148,6 +163,29 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindHero(hero: IReadonlyHeroAttribute<THero> | null): void {
|
||||||
|
this.bindedHero = hero;
|
||||||
|
this.needUpdate = true;
|
||||||
|
this.damageSystem?.bindHeroStatus(hero);
|
||||||
|
this.mapDamage?.refreshAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
getBindedHero(): IReadonlyHeroAttribute<THero> | null {
|
||||||
|
return this.bindedHero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建可修改信息对象
|
||||||
|
* @param enemy 怪物对象
|
||||||
|
* @param locator 怪物位置
|
||||||
|
*/
|
||||||
|
private createHandler(
|
||||||
|
enemy: IEnemy<TAttr>,
|
||||||
|
locator: ITileLocator
|
||||||
|
): IEnemyHandler<TAttr, THero> {
|
||||||
|
return { enemy, locator, hero: this.bindedHero! };
|
||||||
|
}
|
||||||
|
|
||||||
getEnemyLocator(enemy: IEnemy<TAttr>): Readonly<ITileLocator> | null {
|
getEnemyLocator(enemy: IEnemy<TAttr>): Readonly<ITileLocator> | null {
|
||||||
const index = this.locatorEnemyMap.get(enemy);
|
const index = this.locatorEnemyMap.get(enemy);
|
||||||
if (index === undefined) return null;
|
if (index === undefined) return null;
|
||||||
@ -177,7 +215,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除指定索引位置的怪物以及与之关联的所有运行时状态。
|
* 删除指定索引位置的怪物以及与之关联的所有运行时状态
|
||||||
* @param index 地图索引
|
* @param index 地图索引
|
||||||
*/
|
*/
|
||||||
private deleteEnemyAt(index: number) {
|
private deleteEnemyAt(index: number) {
|
||||||
@ -231,14 +269,14 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在指定范围内筛选出当前上下文中的怪物视图。
|
* 在指定范围内筛选出当前上下文中的怪物视图
|
||||||
* @param range 范围对象
|
* @param range 范围对象
|
||||||
* @param param 范围参数
|
* @param param 范围参数
|
||||||
*/
|
*/
|
||||||
private *internalScanRange<T>(
|
private *internalScanRange<T>(
|
||||||
range: IRange<T>,
|
range: IRange<T>,
|
||||||
param: T
|
param: T
|
||||||
): Iterable<EnemyView<TAttr>> {
|
): Iterable<[ITileLocator, EnemyView<TAttr>]> {
|
||||||
range.bindHost(this);
|
range.bindHost(this);
|
||||||
const keys = new Set(this.enemyViewMap.keys());
|
const keys = new Set(this.enemyViewMap.keys());
|
||||||
const matched = range.autoDetect(keys, param);
|
const matched = range.autoDetect(keys, param);
|
||||||
@ -246,12 +284,16 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
for (const index of matched) {
|
for (const index of matched) {
|
||||||
const view = viewMap.get(index);
|
const view = viewMap.get(index);
|
||||||
if (view) {
|
if (view) {
|
||||||
yield view;
|
const locator = this.indexer.indexToLocator(index);
|
||||||
|
yield [locator, view];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scanRange<T>(range: IRange<T>, param: T): Iterable<IEnemyView<TAttr>> {
|
scanRange<T>(
|
||||||
|
range: IRange<T>,
|
||||||
|
param: T
|
||||||
|
): Iterable<[ITileLocator, IEnemyView<TAttr>]> {
|
||||||
return this.internalScanRange(range, param);
|
return this.internalScanRange(range, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,44 +314,42 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachMapDamage(damage: IMapDamage<TAttr> | null): void {
|
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void {
|
||||||
this.mapDamage = damage;
|
this.mapDamage = damage;
|
||||||
if (damage) {
|
if (damage) {
|
||||||
damage.refreshAll();
|
damage.refreshAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getMapDamage(): IMapDamage<TAttr> | null {
|
getMapDamage(): IMapDamage<TAttr, THero> | null {
|
||||||
return this.mapDamage;
|
return this.mapDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void {
|
attachDamageSystem(system: IDamageSystem<TAttr, unknown> | null): void {
|
||||||
this.damageSystem = system;
|
this.damageSystem = system;
|
||||||
if (system) {
|
if (system) {
|
||||||
system.markAllDirty();
|
system.bindHeroStatus(this.bindedHero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDamageSystem<THero>(): IDamageSystem<TAttr, THero> | null {
|
getDamageSystem(): IDamageSystem<TAttr, THero> | null {
|
||||||
return this.damageSystem as IDamageSystem<TAttr, THero> | null;
|
return this.damageSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将怪物身上的特殊属性尝试转换为光环视图。
|
* 将怪物身上的特殊属性尝试转换为光环视图
|
||||||
* @param special 特殊属性
|
* @param special 特殊属性
|
||||||
* @param enemy 怪物对象
|
* @param enemy 怪物对象
|
||||||
* @param locator 怪物位置
|
* @param locator 怪物位置
|
||||||
*/
|
*/
|
||||||
private convertSpecial(
|
private convertSpecial(
|
||||||
special: ISpecial<any>,
|
special: ISpecial<any>,
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IReadonlyEnemyHandler<TAttr, THero>
|
||||||
locator: ITileLocator
|
|
||||||
): IEnemyAuraView<TAttr, any, any> | null {
|
): IEnemyAuraView<TAttr, any, any> | null {
|
||||||
let matched: IAuraConverter<TAttr> | null = null;
|
let matched: IAuraConverter<TAttr, THero> | null = null;
|
||||||
|
|
||||||
for (const converter of this.auraConverter) {
|
for (const converter of this.auraConverter) {
|
||||||
if (!this.converterStatus.get(converter)) continue;
|
if (!this.converterStatus.get(converter)) continue;
|
||||||
if (converter.shouldConvert(special, enemy, locator)) {
|
if (converter.shouldConvert(special, handler)) {
|
||||||
if (matched) {
|
if (matched) {
|
||||||
logger.warn(97, special.code.toString());
|
logger.warn(97, special.code.toString());
|
||||||
return null;
|
return null;
|
||||||
@ -319,11 +359,11 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!matched) return null;
|
if (!matched) return null;
|
||||||
return matched.convert(special, enemy, locator, this);
|
return matched.convert(special, handler, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将光环按优先级插入到有序表中。
|
* 将光环按优先级插入到有序表中
|
||||||
* @param aura 光环视图
|
* @param aura 光环视图
|
||||||
*/
|
*/
|
||||||
private insertIntoSortedAura(aura: IAuraView<TAttr>): void {
|
private insertIntoSortedAura(aura: IAuraView<TAttr>): void {
|
||||||
@ -335,7 +375,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从优先级表中移除一个光环。
|
* 从优先级表中移除一个光环
|
||||||
* @param aura 光环视图
|
* @param aura 光环视图
|
||||||
*/
|
*/
|
||||||
private removeFromSortedAura(aura: IAuraView<TAttr>): void {
|
private removeFromSortedAura(aura: IAuraView<TAttr>): void {
|
||||||
@ -349,7 +389,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行特殊属性修饰器,并返回因此受到影响的光环集合。
|
* 执行特殊属性修饰器,并返回因此受到影响的光环集合
|
||||||
* @param modifier 特殊属性修饰器
|
* @param modifier 特殊属性修饰器
|
||||||
* @param enemy 目标怪物
|
* @param enemy 目标怪物
|
||||||
* @param locator 怪物位置
|
* @param locator 怪物位置
|
||||||
@ -357,14 +397,13 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
*/
|
*/
|
||||||
private processSpecialModifier(
|
private processSpecialModifier(
|
||||||
modifier: IEnemySpecialModifier<TAttr>,
|
modifier: IEnemySpecialModifier<TAttr>,
|
||||||
enemy: IEnemy<TAttr>,
|
handler: IEnemyHandler<TAttr, THero>,
|
||||||
locator: ITileLocator,
|
|
||||||
currentPriority: number
|
currentPriority: number
|
||||||
): Set<IAuraView<TAttr>> {
|
): Set<IAuraView<TAttr>> {
|
||||||
const toAdd = modifier.add(enemy, locator);
|
const enemy = handler.enemy;
|
||||||
const toDelete = modifier.delete(enemy, locator);
|
|
||||||
|
|
||||||
const affectedAuras = new Set<IAuraView<TAttr>>();
|
const affectedAuras = new Set<IAuraView<TAttr>>();
|
||||||
|
const toAdd = modifier.add(handler);
|
||||||
|
const toDelete = modifier.delete(handler);
|
||||||
|
|
||||||
if (toAdd.length > 0 && toDelete.length > 0) {
|
if (toAdd.length > 0 && toDelete.length > 0) {
|
||||||
logger.warn(100);
|
logger.warn(100);
|
||||||
@ -372,9 +411,9 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const adding of toAdd) {
|
for (const adding of toAdd) {
|
||||||
const aura = this.convertSpecial(adding, enemy, locator);
|
const aura = this.convertSpecial(adding, handler);
|
||||||
if (aura) {
|
if (aura) {
|
||||||
// 新生成的光环只能影响之后的阶段,不能反过来影响当前优先级链。
|
// 新生成的光环只能影响之后的阶段,不能反过来影响当前优先级链
|
||||||
if (import.meta.env.DEV && aura.priority > currentPriority) {
|
if (import.meta.env.DEV && aura.priority > currentPriority) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
99,
|
99,
|
||||||
@ -410,7 +449,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const special of enemy.iterateSpecials()) {
|
for (const special of enemy.iterateSpecials()) {
|
||||||
const success = modifier.modify(enemy, special, locator);
|
const success = modifier.modify(handler, special);
|
||||||
if (!success) continue;
|
if (!success) continue;
|
||||||
const aura = this.convertedAura.get(special);
|
const aura = this.convertedAura.get(special);
|
||||||
if (!aura) continue;
|
if (!aura) continue;
|
||||||
@ -429,12 +468,12 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行单个特殊查询效果。
|
* 执行单个特殊查询效果
|
||||||
* @param effect 特殊查询效果
|
* @param effect 特殊查询效果
|
||||||
* @param currentPriority 当前处理的优先级
|
* @param currentPriority 当前处理的优先级
|
||||||
*/
|
*/
|
||||||
private processSpecialQuery(
|
private processSpecialQuery(
|
||||||
effect: IEnemySpecialQueryEffect<TAttr>,
|
effect: IEnemySpecialQueryEffect<TAttr, THero>,
|
||||||
currentPriority: number
|
currentPriority: number
|
||||||
): void {
|
): void {
|
||||||
const modifier = effect.for(this);
|
const modifier = effect.for(this);
|
||||||
@ -442,13 +481,13 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
for (const [index, view] of this.enemyViewMap) {
|
for (const [index, view] of this.enemyViewMap) {
|
||||||
const locator = this.indexer.indexToLocator(index);
|
const locator = this.indexer.indexToLocator(index);
|
||||||
const enemy = view.getComputingEnemy();
|
const enemy = view.getComputingEnemy();
|
||||||
|
const handler = this.createHandler(enemy, locator);
|
||||||
|
|
||||||
if (!modifier.shouldQuery(enemy, locator)) continue;
|
if (!modifier.shouldQuery(handler)) continue;
|
||||||
|
|
||||||
const affectedAuras = this.processSpecialModifier(
|
const affectedAuras = this.processSpecialModifier(
|
||||||
modifier,
|
modifier,
|
||||||
enemy,
|
handler,
|
||||||
locator,
|
|
||||||
currentPriority
|
currentPriority
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -461,7 +500,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行光环带来的特殊属性修饰效果。
|
* 执行光环带来的特殊属性修饰效果
|
||||||
* @param aura 光环视图
|
* @param aura 光环视图
|
||||||
* @param currentPriority 当前处理的优先级
|
* @param currentPriority 当前处理的优先级
|
||||||
*/
|
*/
|
||||||
@ -470,30 +509,22 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
currentPriority: number
|
currentPriority: number
|
||||||
): void {
|
): void {
|
||||||
const param = aura.getRangeParam();
|
const param = aura.getRangeParam();
|
||||||
|
const iter = this.internalScanRange(aura.range, param);
|
||||||
|
|
||||||
for (const enemyView of this.internalScanRange(aura.range, param)) {
|
for (const [locator, enemyView] of iter) {
|
||||||
const locator = this.getEnemyLocatorByView(enemyView);
|
|
||||||
if (!locator) continue;
|
|
||||||
|
|
||||||
const enemy = enemyView.getComputingEnemy();
|
const enemy = enemyView.getComputingEnemy();
|
||||||
const base = enemyView.getBaseEnemy();
|
const base = enemyView.getBaseEnemy();
|
||||||
const modifier = aura.applySpecial(enemy, base, locator);
|
const handler = this.createHandler(enemy, locator);
|
||||||
|
const modifier = aura.applySpecial(handler, base);
|
||||||
if (!modifier) continue;
|
if (!modifier) continue;
|
||||||
|
|
||||||
this.processSpecialModifier(
|
this.processSpecialModifier(modifier, handler, currentPriority);
|
||||||
modifier,
|
|
||||||
enemy,
|
|
||||||
locator,
|
|
||||||
currentPriority
|
|
||||||
);
|
|
||||||
|
|
||||||
this.needTotallyRefresh.add(enemyView);
|
this.needTotallyRefresh.add(enemyView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建所有由特殊属性衍生出的光环与特殊查询结果。
|
* 构建所有由特殊属性衍生出的光环与特殊查询结果
|
||||||
*/
|
*/
|
||||||
private buildupSpecials(): void {
|
private buildupSpecials(): void {
|
||||||
for (const aura of this.globalAuraList) {
|
for (const aura of this.globalAuraList) {
|
||||||
@ -503,9 +534,10 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
for (const [index, view] of this.enemyViewMap) {
|
for (const [index, view] of this.enemyViewMap) {
|
||||||
const enemy = view.getComputingEnemy();
|
const enemy = view.getComputingEnemy();
|
||||||
const locator = this.indexer.indexToLocator(index);
|
const locator = this.indexer.indexToLocator(index);
|
||||||
|
const handler = this.createHandler(enemy, locator);
|
||||||
|
|
||||||
for (const special of enemy.iterateSpecials()) {
|
for (const special of enemy.iterateSpecials()) {
|
||||||
const aura = this.convertSpecial(special, enemy, locator);
|
const aura = this.convertSpecial(special, handler);
|
||||||
if (!aura) continue;
|
if (!aura) continue;
|
||||||
this.convertedAura.set(special, aura);
|
this.convertedAura.set(special, aura);
|
||||||
this.insertIntoSortedAura(aura);
|
this.insertIntoSortedAura(aura);
|
||||||
@ -554,7 +586,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按优先级执行所有基础属性光环效果。
|
* 按优先级执行所有基础属性光环效果
|
||||||
*/
|
*/
|
||||||
private buildupBase(): void {
|
private buildupBase(): void {
|
||||||
const priorities = [...this.sortedAura.keys()].sort((a, b) => b - a);
|
const priorities = [...this.sortedAura.keys()].sort((a, b) => b - a);
|
||||||
@ -563,23 +595,25 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
if (!auras) continue;
|
if (!auras) continue;
|
||||||
for (const aura of auras) {
|
for (const aura of auras) {
|
||||||
const param = aura.getRangeParam();
|
const param = aura.getRangeParam();
|
||||||
for (const view of this.internalScanRange(aura.range, param)) {
|
const iter = this.internalScanRange(aura.range, param);
|
||||||
|
for (const [locator, view] of iter) {
|
||||||
const enemy = view.getComputingEnemy();
|
const enemy = view.getComputingEnemy();
|
||||||
const base = view.getBaseEnemy();
|
const base = view.getBaseEnemy();
|
||||||
const locator = this.getEnemyLocatorByView(view)!;
|
const handler = this.createHandler(enemy, locator);
|
||||||
aura.apply(enemy, base, locator);
|
aura.apply(handler, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行常规查询效果,并记录哪些怪物查询了上下文。
|
* 执行常规查询效果,并记录哪些怪物查询了上下文
|
||||||
*/
|
*/
|
||||||
private buildupQuery(): void {
|
private buildupQuery(): void {
|
||||||
for (const [index, view] of this.enemyViewMap) {
|
for (const [index, view] of this.enemyViewMap) {
|
||||||
const enemy = view.getComputingEnemy();
|
const enemy = view.getComputingEnemy();
|
||||||
const locator = this.indexer.indexToLocator(index);
|
const locator = this.indexer.indexToLocator(index);
|
||||||
|
const handler = this.createHandler(enemy, locator);
|
||||||
let queried = false;
|
let queried = false;
|
||||||
const query = () => {
|
const query = () => {
|
||||||
queried = true;
|
queried = true;
|
||||||
@ -589,7 +623,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
const effects = this.commonQueryMap.get(special.code);
|
const effects = this.commonQueryMap.get(special.code);
|
||||||
if (!effects) continue;
|
if (!effects) continue;
|
||||||
for (const effect of effects) {
|
for (const effect of effects) {
|
||||||
effect.apply(enemy, special, query, locator);
|
effect.apply(handler, special, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (queried) {
|
if (queried) {
|
||||||
@ -599,20 +633,25 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行最终效果阶段。
|
* 执行最终效果阶段
|
||||||
*/
|
*/
|
||||||
private buildupFinal(): void {
|
private buildupFinal(): void {
|
||||||
for (const [index, view] of this.enemyViewMap) {
|
for (const [index, view] of this.enemyViewMap) {
|
||||||
const enemy = view.getComputingEnemy();
|
const enemy = view.getComputingEnemy();
|
||||||
const locator = this.indexer.indexToLocator(index);
|
const locator = this.indexer.indexToLocator(index);
|
||||||
|
const handler = this.createHandler(enemy, locator);
|
||||||
for (const effect of this.finalEffects) {
|
for (const effect of this.finalEffects) {
|
||||||
effect.apply(enemy, locator);
|
effect.apply(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildup(): void {
|
buildup(): void {
|
||||||
if (!this.needUpdate) return;
|
if (!this.needUpdate) return;
|
||||||
|
if (!this.bindedHero) {
|
||||||
|
logger.warn(110);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.needUpdate = false;
|
this.needUpdate = false;
|
||||||
this.sortedAura.clear();
|
this.sortedAura.clear();
|
||||||
this.convertedAura.clear();
|
this.convertedAura.clear();
|
||||||
@ -650,18 +689,18 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在局部刷新期间执行特殊属性修饰器,但不重建光环拓扑。
|
* 在局部刷新期间执行特殊属性修饰器,但不重建光环拓扑
|
||||||
* @param modifier 特殊属性修饰器
|
* @param modifier 特殊属性修饰器
|
||||||
* @param enemy 目标怪物
|
* @param enemy 目标怪物
|
||||||
* @param locator 怪物位置
|
* @param locator 怪物位置
|
||||||
*/
|
*/
|
||||||
private refreshSpecialModifier(
|
private refreshSpecialModifier(
|
||||||
modifier: IEnemySpecialModifier<TAttr>,
|
modifier: IEnemySpecialModifier<TAttr>,
|
||||||
enemy: IEnemy<TAttr>,
|
handler: IEnemyHandler<TAttr, THero>
|
||||||
locator: ITileLocator
|
|
||||||
): void {
|
): void {
|
||||||
const toAdd = modifier.add(enemy, locator);
|
const enemy = handler.enemy;
|
||||||
const toDelete = modifier.delete(enemy, locator);
|
const toAdd = modifier.add(handler);
|
||||||
|
const toDelete = modifier.delete(handler);
|
||||||
|
|
||||||
if (toAdd.length > 0 && toDelete.length > 0) {
|
if (toAdd.length > 0 && toDelete.length > 0) {
|
||||||
logger.warn(100);
|
logger.warn(100);
|
||||||
@ -671,7 +710,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
for (const adding of toAdd) {
|
for (const adding of toAdd) {
|
||||||
enemy.addSpecial(adding);
|
enemy.addSpecial(adding);
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
const aura = this.convertSpecial(adding, enemy, locator);
|
const aura = this.convertSpecial(adding, handler);
|
||||||
if (aura) {
|
if (aura) {
|
||||||
logger.warn(101, adding.code.toString());
|
logger.warn(101, adding.code.toString());
|
||||||
}
|
}
|
||||||
@ -681,7 +720,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
for (const deleting of toDelete) {
|
for (const deleting of toDelete) {
|
||||||
enemy.deleteSpecial(deleting);
|
enemy.deleteSpecial(deleting);
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
const aura = this.convertSpecial(deleting, enemy, locator);
|
const aura = this.convertSpecial(deleting, handler);
|
||||||
if (aura) {
|
if (aura) {
|
||||||
logger.warn(101, deleting.code.toString());
|
logger.warn(101, deleting.code.toString());
|
||||||
}
|
}
|
||||||
@ -689,7 +728,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const special of enemy.iterateSpecials()) {
|
for (const special of enemy.iterateSpecials()) {
|
||||||
const success = modifier.modify(enemy, special, locator);
|
const success = modifier.modify(handler, special);
|
||||||
if (import.meta.env.DEV && success) {
|
if (import.meta.env.DEV && success) {
|
||||||
const aura = this.convertedAura.get(special);
|
const aura = this.convertedAura.get(special);
|
||||||
if (aura) {
|
if (aura) {
|
||||||
@ -700,7 +739,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新单个怪物视图的计算结果。
|
* 刷新单个怪物视图的计算结果
|
||||||
* @param view 怪物视图
|
* @param view 怪物视图
|
||||||
*/
|
*/
|
||||||
private refreshEnemy(view: EnemyView<TAttr>): void {
|
private refreshEnemy(view: EnemyView<TAttr>): void {
|
||||||
@ -710,6 +749,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
view.reset();
|
view.reset();
|
||||||
const enemy = view.getComputingEnemy();
|
const enemy = view.getComputingEnemy();
|
||||||
const base = view.getBaseEnemy();
|
const base = view.getBaseEnemy();
|
||||||
|
const handler = this.createHandler(enemy, locator);
|
||||||
|
|
||||||
const specialPriorities = new Set<number>();
|
const specialPriorities = new Set<number>();
|
||||||
for (const priority of this.sortedAura.keys()) {
|
for (const priority of this.sortedAura.keys()) {
|
||||||
@ -725,30 +765,28 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
|
|
||||||
for (const priority of orderedSpecialPriorities) {
|
for (const priority of orderedSpecialPriorities) {
|
||||||
const auras = this.sortedAura.get(priority);
|
const auras = this.sortedAura.get(priority);
|
||||||
|
const effects = this.specialQueryEffects.get(priority);
|
||||||
|
|
||||||
if (auras) {
|
if (auras) {
|
||||||
for (const aura of auras) {
|
for (const aura of auras) {
|
||||||
if (!aura.couldApplySpecial) continue;
|
if (!aura.couldApplySpecial) continue;
|
||||||
const param = aura.getRangeParam();
|
const param = aura.getRangeParam();
|
||||||
aura.range.bindHost(this);
|
aura.range.bindHost(this);
|
||||||
// 局部刷新只重新判断“这个怪物是否被该光环命中”。
|
// 局部刷新只重新判断“这个怪物是否被该光环命中”
|
||||||
const inRange = aura.range.inRange(
|
if (!aura.range.inRange(locator.x, locator.y, param)) {
|
||||||
locator.x,
|
continue;
|
||||||
locator.y,
|
}
|
||||||
param
|
const modifier = aura.applySpecial(handler, base);
|
||||||
);
|
|
||||||
if (!inRange) continue;
|
|
||||||
const modifier = aura.applySpecial(enemy, base, locator);
|
|
||||||
if (!modifier) continue;
|
if (!modifier) continue;
|
||||||
this.refreshSpecialModifier(modifier, enemy, locator);
|
this.refreshSpecialModifier(modifier, handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const effects = this.specialQueryEffects.get(priority);
|
|
||||||
if (effects) {
|
if (effects) {
|
||||||
for (const effect of effects) {
|
for (const effect of effects) {
|
||||||
const modifier = effect.for(this);
|
const modifier = effect.for(this);
|
||||||
if (!modifier.shouldQuery(enemy, locator)) continue;
|
if (!modifier.shouldQuery(handler)) continue;
|
||||||
this.refreshSpecialModifier(modifier, enemy, locator);
|
this.refreshSpecialModifier(modifier, handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -762,10 +800,8 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
for (const aura of auras) {
|
for (const aura of auras) {
|
||||||
const param = aura.getRangeParam();
|
const param = aura.getRangeParam();
|
||||||
aura.range.bindHost(this);
|
aura.range.bindHost(this);
|
||||||
if (!aura.range.inRange(locator.x, locator.y, param)) {
|
if (!aura.range.inRange(locator.x, locator.y, param)) continue;
|
||||||
continue;
|
aura.apply(handler, base);
|
||||||
}
|
|
||||||
aura.apply(enemy, base, locator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,7 +815,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
const effects = this.commonQueryMap.get(special.code);
|
const effects = this.commonQueryMap.get(special.code);
|
||||||
if (!effects) continue;
|
if (!effects) continue;
|
||||||
for (const effect of effects) {
|
for (const effect of effects) {
|
||||||
effect.apply(enemy, special, query, locator);
|
effect.apply(handler, special, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (queried) {
|
if (queried) {
|
||||||
@ -787,7 +823,7 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const effect of this.finalEffects) {
|
for (const effect of this.finalEffects) {
|
||||||
effect.apply(enemy, locator);
|
effect.apply(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dirtyEnemy.delete(view);
|
this.dirtyEnemy.delete(view);
|
||||||
@ -846,5 +882,6 @@ export class EnemyContext<TAttr> implements IEnemyContext<TAttr> {
|
|||||||
this.commonQueryMap.clear();
|
this.commonQueryMap.clear();
|
||||||
this.specialQueryEffects.clear();
|
this.specialQueryEffects.clear();
|
||||||
this.finalEffects.length = 0;
|
this.finalEffects.length = 0;
|
||||||
|
this.bindedHero = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { logger } from '@motajs/common';
|
import { ITileLocator, logger } from '@motajs/common';
|
||||||
import {
|
import {
|
||||||
CriticalableHeroStatus,
|
CriticalableHeroStatus,
|
||||||
IDamageCalculator,
|
IDamageCalculator,
|
||||||
@ -6,9 +6,11 @@ import {
|
|||||||
IEnemyContext,
|
IEnemyContext,
|
||||||
IEnemyCritical,
|
IEnemyCritical,
|
||||||
IEnemyDamageInfo,
|
IEnemyDamageInfo,
|
||||||
|
IReadonlyEnemyHandler,
|
||||||
IEnemyView,
|
IEnemyView,
|
||||||
IReadonlyEnemy
|
IReadonlyEnemy
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
|
||||||
|
|
||||||
interface ICriticalSearchResult {
|
interface ICriticalSearchResult {
|
||||||
/** 此临界点的属性值 */
|
/** 此临界点的属性值 */
|
||||||
@ -21,12 +23,12 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
/** 当前正在使用的计算器 */
|
/** 当前正在使用的计算器 */
|
||||||
private calculator: IDamageCalculator<TAttr, THero> | null = null;
|
private calculator: IDamageCalculator<TAttr, THero> | null = null;
|
||||||
/** 当前勇士属性 */
|
/** 当前勇士属性 */
|
||||||
private heroStatus: Readonly<THero> | null = null;
|
private heroStatus: IReadonlyHeroAttribute<THero> | null = null;
|
||||||
/** 怪物伤害缓存 */
|
/** 怪物伤害缓存 */
|
||||||
private readonly cache: Map<IEnemyView<TAttr>, IEnemyDamageInfo> =
|
private readonly cache: Map<IEnemyView<TAttr>, IEnemyDamageInfo> =
|
||||||
new Map();
|
new Map();
|
||||||
|
|
||||||
constructor(readonly context: IEnemyContext<TAttr>) {}
|
constructor(readonly context: IEnemyContext<TAttr, THero>) {}
|
||||||
|
|
||||||
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void {
|
useCalculator(calculator: IDamageCalculator<TAttr, THero>): void {
|
||||||
this.calculator = calculator;
|
this.calculator = calculator;
|
||||||
@ -37,35 +39,23 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
return this.calculator;
|
return this.calculator;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindHeroStatus(hero: Readonly<THero>): void {
|
bindHeroStatus(hero: IReadonlyHeroAttribute<THero>): void {
|
||||||
this.heroStatus = hero;
|
this.heroStatus = hero;
|
||||||
this.markAllDirty();
|
this.markAllDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 深拷贝勇士属性
|
* 创建只读信息对象
|
||||||
|
* @param enemy 怪物对象
|
||||||
|
* @param locator 怪物位置
|
||||||
|
* @param hero 勇士属性对象
|
||||||
*/
|
*/
|
||||||
private cloneHeroStatus(): THero | null {
|
private createReadonlyHandler(
|
||||||
if (!this.heroStatus) return null;
|
|
||||||
else return structuredClone(this.heroStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在修改勇士属性的情况下计算怪物伤害
|
|
||||||
* @param enemy 怪物属性
|
|
||||||
* @param attribute 修改的属性键名
|
|
||||||
* @param value 修改为的属性值
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
private calculateDamageWithModified(
|
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
enemy: IReadonlyEnemy<TAttr>,
|
||||||
attribute: CriticalableHeroStatus<THero>,
|
locator: ITileLocator,
|
||||||
value: number
|
hero: IReadonlyHeroAttribute<THero>
|
||||||
): IEnemyDamageInfo {
|
): IReadonlyEnemyHandler<TAttr, THero> {
|
||||||
const hero = this.cloneHeroStatus()!;
|
return { enemy, locator, hero };
|
||||||
// @ts-expect-error 之后会进行修复
|
|
||||||
hero[attribute] = value;
|
|
||||||
return this.calculator!.calculate(hero, enemy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null {
|
getDamageInfo(enemy: IEnemyView<TAttr>): IEnemyDamageInfo | null {
|
||||||
@ -77,15 +67,20 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
logger.warn(106);
|
logger.warn(106);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const hero = this.cloneHeroStatus()!;
|
const hero = this.heroStatus;
|
||||||
|
const locator = this.context.getEnemyLocatorByView(enemy);
|
||||||
|
if (!hero || !locator) return null;
|
||||||
|
|
||||||
const cached = this.cache.get(enemy);
|
const cached = this.cache.get(enemy);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
const info = this.calculator.calculate(hero, enemy.getComputedEnemy());
|
const computed = enemy.getComputedEnemy();
|
||||||
|
const handler = this.createReadonlyHandler(computed, locator, hero);
|
||||||
|
const info = this.calculator.calculate(handler);
|
||||||
this.cache.set(enemy, info);
|
this.cache.set(enemy, info);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +99,7 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
*calculateCritical(
|
*calculateCritical(
|
||||||
view: IEnemyView<TAttr>,
|
view: IEnemyView<TAttr>,
|
||||||
attribute: CriticalableHeroStatus<THero>,
|
attribute: CriticalableHeroStatus<THero>,
|
||||||
precision: number
|
precision: number = 12
|
||||||
): Generator<IEnemyCritical, void, void> {
|
): Generator<IEnemyCritical, void, void> {
|
||||||
if (!this.heroStatus) {
|
if (!this.heroStatus) {
|
||||||
logger.warn(107);
|
logger.warn(107);
|
||||||
@ -115,15 +110,19 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentInfo = this.getDamageInfo(view);
|
const locator = this.context.getEnemyLocatorByView(view);
|
||||||
if (!currentInfo) return;
|
if (!locator) return;
|
||||||
|
|
||||||
const enemy = view.getComputedEnemy();
|
const enemy = view.getComputedEnemy();
|
||||||
const hero = this.cloneHeroStatus()!;
|
const hero = this.heroStatus.getModifiableClone();
|
||||||
const currentValue = hero[attribute] as number;
|
const handler = this.createReadonlyHandler(enemy, locator, hero);
|
||||||
|
|
||||||
|
const currentInfo = this.calculator.calculate(handler);
|
||||||
|
if (!currentInfo) return;
|
||||||
|
|
||||||
|
const currentValue = hero.getBaseAttribute(attribute) as number;
|
||||||
const upperLimit = Math.floor(
|
const upperLimit = Math.floor(
|
||||||
this.calculator.getCriticalLimit(hero, enemy, attribute)
|
this.calculator.getCriticalLimit(handler, attribute)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentValue >= upperLimit) return;
|
if (currentValue >= upperLimit) return;
|
||||||
@ -134,7 +133,8 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
|
|
||||||
while (baseValue < upperLimit) {
|
while (baseValue < upperLimit) {
|
||||||
const next = this.findNextCritical(
|
const next = this.findNextCritical(
|
||||||
enemy,
|
handler,
|
||||||
|
hero,
|
||||||
attribute,
|
attribute,
|
||||||
baseValue,
|
baseValue,
|
||||||
upperLimit,
|
upperLimit,
|
||||||
@ -159,7 +159,8 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算下一个临界点
|
* 计算下一个临界点
|
||||||
* @param enemy 怪物对象
|
* @param handler 信息对象,其中的 `hero` 成员与 `hero` 参数同引用
|
||||||
|
* @param hero 可修改勇士属性对象,与 `handler` 中的 `hero` 成员同引用
|
||||||
* @param attribute 勇士属性名
|
* @param attribute 勇士属性名
|
||||||
* @param currentValue 当前勇士属性值
|
* @param currentValue 当前勇士属性值
|
||||||
* @param upperLimit 二分上界
|
* @param upperLimit 二分上界
|
||||||
@ -167,7 +168,8 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
* @param maxIterations 最大迭代数量
|
* @param maxIterations 最大迭代数量
|
||||||
*/
|
*/
|
||||||
private findNextCritical(
|
private findNextCritical(
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||||
|
hero: IHeroAttribute<THero>,
|
||||||
attribute: CriticalableHeroStatus<THero>,
|
attribute: CriticalableHeroStatus<THero>,
|
||||||
currentValue: number,
|
currentValue: number,
|
||||||
upperLimit: number,
|
upperLimit: number,
|
||||||
@ -176,36 +178,30 @@ export class DamageSystem<TAttr, THero> implements IDamageSystem<TAttr, THero> {
|
|||||||
): ICriticalSearchResult | null {
|
): ICriticalSearchResult | null {
|
||||||
let left = currentValue;
|
let left = currentValue;
|
||||||
let right = upperLimit;
|
let right = upperLimit;
|
||||||
let rightInfo = this.calculateDamageWithModified(
|
|
||||||
enemy,
|
|
||||||
attribute,
|
|
||||||
right
|
|
||||||
);
|
|
||||||
|
|
||||||
if (rightInfo.damage >= referenceDamage) return null;
|
hero.setBaseAttribute(attribute, right as THero[typeof attribute]);
|
||||||
|
|
||||||
|
let targetInfo = this.calculator!.calculate(handler);
|
||||||
|
if (targetInfo.damage >= referenceDamage) return null;
|
||||||
|
|
||||||
let iter = 0;
|
let iter = 0;
|
||||||
while (iter < maxIterations) {
|
while (iter++ < maxIterations) {
|
||||||
const middle = Math.floor((left + right) / 2);
|
const middle = Math.floor((left + right) / 2);
|
||||||
const middleInfo = this.calculateDamageWithModified(
|
hero.setBaseAttribute(attribute, middle as THero[typeof attribute]);
|
||||||
enemy,
|
const middleInfo = this.calculator!.calculate(handler);
|
||||||
attribute,
|
|
||||||
middle
|
|
||||||
);
|
|
||||||
if (middleInfo.damage < referenceDamage) {
|
if (middleInfo.damage < referenceDamage) {
|
||||||
right = middle;
|
right = middle;
|
||||||
rightInfo = middleInfo;
|
|
||||||
} else {
|
} else {
|
||||||
left = middle;
|
left = middle;
|
||||||
|
targetInfo = middleInfo;
|
||||||
}
|
}
|
||||||
if (right - left <= 1) break;
|
if (right - left <= 1) break;
|
||||||
|
|
||||||
iter++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: right,
|
value: right,
|
||||||
info: rightInfo
|
info: targetInfo
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,7 +93,7 @@ export class EnemyView<TAttr> implements IEnemyView<TAttr> {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly baseEnemy: IEnemy<TAttr>,
|
readonly baseEnemy: IEnemy<TAttr>,
|
||||||
readonly context: IEnemyContext<TAttr>
|
readonly context: IEnemyContext<TAttr, unknown>
|
||||||
) {
|
) {
|
||||||
this.computedEnemy = baseEnemy.clone();
|
this.computedEnemy = baseEnemy.clone();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { logger, ITileLocator } from '@motajs/common';
|
import { logger, ITileLocator } from '@motajs/common';
|
||||||
import {
|
import {
|
||||||
IEnemyContext,
|
IEnemyContext,
|
||||||
|
IReadonlyEnemyHandler,
|
||||||
IEnemyView,
|
IEnemyView,
|
||||||
IMapDamage,
|
IMapDamage,
|
||||||
IMapDamageConverter,
|
IMapDamageConverter,
|
||||||
@ -33,9 +34,9 @@ interface IDamageStore<TAttr> {
|
|||||||
readonly index: number;
|
readonly index: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MapDamage<TAttr> implements IMapDamage<TAttr> {
|
export class MapDamage<TAttr, THero> implements IMapDamage<TAttr, THero> {
|
||||||
/** 当前使用的地图伤害转换器 */
|
/** 当前使用的地图伤害转换器 */
|
||||||
private converter: IMapDamageConverter<TAttr> | null = null;
|
private converter: IMapDamageConverter<TAttr, THero> | null = null;
|
||||||
/** 当前使用的地图伤害合并器 */
|
/** 当前使用的地图伤害合并器 */
|
||||||
private reducer: IMapDamageReducer | null = null;
|
private reducer: IMapDamageReducer | null = null;
|
||||||
|
|
||||||
@ -62,15 +63,33 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
|
|||||||
/** 坐标索引对象 */
|
/** 坐标索引对象 */
|
||||||
private readonly indexer: IMapLocIndexer;
|
private readonly indexer: IMapLocIndexer;
|
||||||
|
|
||||||
constructor(readonly context: IEnemyContext<TAttr>) {
|
constructor(readonly context: IEnemyContext<TAttr, THero>) {
|
||||||
this.indexer = context.indexer;
|
this.indexer = context.indexer;
|
||||||
}
|
}
|
||||||
|
|
||||||
useConverter(converter: IMapDamageConverter<TAttr>): void {
|
useConverter(converter: IMapDamageConverter<TAttr, THero>): void {
|
||||||
this.converter = converter;
|
this.converter = converter;
|
||||||
this.refreshAll();
|
this.refreshAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建只读信息对象
|
||||||
|
* @param view 怪物视图
|
||||||
|
* @param locator 怪物位置
|
||||||
|
*/
|
||||||
|
private createReadonlyHandler(
|
||||||
|
view: IEnemyView<TAttr>,
|
||||||
|
locator: ITileLocator
|
||||||
|
): IReadonlyEnemyHandler<TAttr, THero> | null {
|
||||||
|
const hero = this.context.getBindedHero();
|
||||||
|
if (!hero) return null;
|
||||||
|
return {
|
||||||
|
enemy: view.getComputedEnemy(),
|
||||||
|
locator,
|
||||||
|
hero
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
useReducer(reducer: IMapDamageReducer): void {
|
useReducer(reducer: IMapDamageReducer): void {
|
||||||
this.reducer = reducer;
|
this.reducer = reducer;
|
||||||
this.reducedCache.clear();
|
this.reducedCache.clear();
|
||||||
@ -174,6 +193,7 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
|
|||||||
const sourced = this.sourcedDamage.get(index);
|
const sourced = this.sourcedDamage.get(index);
|
||||||
if (sourceless) {
|
if (sourceless) {
|
||||||
if (sourced) {
|
if (sourced) {
|
||||||
|
// 大集合 union 小集合会更快,一般有来源伤害更多,所以 source union sourceless
|
||||||
return sourced.damages.union(sourceless.damages);
|
return sourced.damages.union(sourceless.damages);
|
||||||
} else {
|
} else {
|
||||||
return sourceless.damages;
|
return sourceless.damages;
|
||||||
@ -237,9 +257,11 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
|
|||||||
locator: ITileLocator
|
locator: ITileLocator
|
||||||
) {
|
) {
|
||||||
this.removeEnemyAffecting(view);
|
this.removeEnemyAffecting(view);
|
||||||
const enemy = view.getComputedEnemy();
|
if (!this.converter) return;
|
||||||
const views = this.converter!.convert(enemy, locator, this.context);
|
const handler = this.createReadonlyHandler(view, locator);
|
||||||
const set = new Set(views);
|
if (!handler) return;
|
||||||
|
const views = this.converter.convert(handler, this.context);
|
||||||
|
const set = new Set<IMapDamageView<any>>(views);
|
||||||
if (set.size === 0) return;
|
if (set.size === 0) return;
|
||||||
this.enemyStore.set(view, set);
|
this.enemyStore.set(view, set);
|
||||||
const collection = new Set<number>();
|
const collection = new Set<number>();
|
||||||
@ -275,9 +297,11 @@ export class MapDamage<TAttr> implements IMapDamage<TAttr> {
|
|||||||
*/
|
*/
|
||||||
private refreshEnemy(view: IEnemyView<TAttr>, locator: ITileLocator) {
|
private refreshEnemy(view: IEnemyView<TAttr>, locator: ITileLocator) {
|
||||||
this.removeEnemyAffecting(view);
|
this.removeEnemyAffecting(view);
|
||||||
const enemy = view.getComputedEnemy();
|
if (!this.converter) return;
|
||||||
const views = this.converter!.convert(enemy, locator, this.context);
|
const handler = this.createReadonlyHandler(view, locator);
|
||||||
const set = new Set(views);
|
if (!handler) return;
|
||||||
|
const views = this.converter.convert(handler, this.context);
|
||||||
|
const set = new Set<IMapDamageView<any>>(views);
|
||||||
if (set.size === 0) return;
|
if (set.size === 0) return;
|
||||||
this.enemyStore.set(view, set);
|
this.enemyStore.set(view, set);
|
||||||
set.forEach(viewItem => {
|
set.forEach(viewItem => {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { IRange, ITileLocator } from '@motajs/common';
|
import { IRange, ITileLocator } from '@motajs/common';
|
||||||
|
import { IReadonlyHeroAttribute } from '../hero';
|
||||||
|
|
||||||
//#region 怪物基础
|
//#region 怪物基础
|
||||||
|
|
||||||
@ -246,13 +247,31 @@ export interface IMapLocIndexer extends IMapLocHelper {
|
|||||||
setWidth(width: number): void;
|
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
|
//#endregion
|
||||||
|
|
||||||
//#region 怪物对象
|
//#region 怪物对象
|
||||||
|
|
||||||
export interface IEnemyView<TAttr> {
|
export interface IEnemyView<TAttr> {
|
||||||
/** 怪物视图所属的上下文 */
|
/** 怪物视图所属的上下文 */
|
||||||
readonly context: IEnemyContext<TAttr>;
|
readonly context: IEnemyContext<TAttr, unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置此怪物视图的状态,将计算后怪物对象恢复至初始状态
|
* 重置此怪物视图的状态,将计算后怪物对象恢复至初始状态
|
||||||
@ -288,31 +307,24 @@ export interface IEnemyView<TAttr> {
|
|||||||
export interface IEnemySpecialModifier<TAttr> {
|
export interface IEnemySpecialModifier<TAttr> {
|
||||||
/**
|
/**
|
||||||
* 获取要添加到指定怪物身上的特殊属性
|
* 获取要添加到指定怪物身上的特殊属性
|
||||||
* @param enemy 怪物对象
|
* @param handler 信息对象
|
||||||
* @param locator 怪物定位符
|
|
||||||
*/
|
*/
|
||||||
add(enemy: IReadonlyEnemy<TAttr>, locator: ITileLocator): ISpecial<any>[];
|
add(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取制定怪物身上要删除的特殊属性
|
* 获取制定怪物身上要删除的特殊属性
|
||||||
* @param enemy 怪物对象
|
* @param handler 信息对象
|
||||||
* @param locator 怪物定位符
|
|
||||||
*/
|
*/
|
||||||
delete(
|
delete(handler: IReadonlyEnemyHandler<TAttr, unknown>): ISpecial<any>[];
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
|
||||||
locator: ITileLocator
|
|
||||||
): ISpecial<any>[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改一个怪物的特殊属性,如果真正进行了修改则返回 true,否则返回 false
|
* 修改一个怪物的特殊属性,如果真正进行了修改则返回 true,否则返回 false
|
||||||
* @param enemy 怪物对象
|
* @param handler 信息对象
|
||||||
* @param special 要修改的怪物特殊属性
|
* @param special 要修改的怪物特殊属性
|
||||||
* @param locator 怪物定位符
|
|
||||||
*/
|
*/
|
||||||
modify(
|
modify(
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IEnemyHandler<TAttr, unknown>,
|
||||||
special: ISpecial<any>,
|
special: ISpecial<any>
|
||||||
locator: ITileLocator
|
|
||||||
): boolean;
|
): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,26 +346,22 @@ export interface IAuraView<TAttr, T = any> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 对指定怪物对象施加修饰器
|
* 对指定怪物对象施加修饰器
|
||||||
* @param enemy 怪物对象
|
* @param handler 信息对象
|
||||||
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
||||||
* @param locator 怪物定位符
|
|
||||||
*/
|
*/
|
||||||
apply(
|
apply(
|
||||||
enemy: IEnemy<TAttr>,
|
handler: IEnemyHandler<TAttr, unknown>,
|
||||||
baseEnemy: IReadonlyEnemy<TAttr>,
|
baseEnemy: IReadonlyEnemy<TAttr>
|
||||||
locator: ITileLocator
|
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对指定怪物对象添加特殊属性修饰器
|
* 对指定怪物对象添加特殊属性修饰器
|
||||||
* @param enemy 怪物对象
|
* @param handler 信息对象
|
||||||
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
* @param baseEnemy 原始怪物对象,即未进行任何修改的怪物对象
|
||||||
* @param locator 怪物定位符
|
|
||||||
*/
|
*/
|
||||||
applySpecial(
|
applySpecial(
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IEnemyHandler<TAttr, unknown>,
|
||||||
baseEnemy: IReadonlyEnemy<TAttr>,
|
baseEnemy: IReadonlyEnemy<TAttr>
|
||||||
locator: ITileLocator
|
|
||||||
): IEnemySpecialModifier<TAttr> | null;
|
): IEnemySpecialModifier<TAttr> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,14 +374,15 @@ export interface IEnemyAuraView<TAttr, R, S> extends IAuraView<TAttr, R> {
|
|||||||
readonly locator: ITileLocator;
|
readonly locator: ITileLocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAuraConverter<TAttr> {
|
export interface IAuraConverter<TAttr, THero> {
|
||||||
/**
|
/**
|
||||||
* 判断一个特殊属性是否应该被当前光环转换器执行转换
|
* 判断一个特殊属性是否应该被当前光环转换器执行转换
|
||||||
|
* @param special 要转换的特殊属性
|
||||||
|
* @param handler 信息对象
|
||||||
*/
|
*/
|
||||||
shouldConvert(
|
shouldConvert(
|
||||||
special: ISpecial<any>,
|
special: ISpecial<any>,
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IReadonlyEnemyHandler<TAttr, THero>
|
||||||
locator: ITileLocator
|
|
||||||
): boolean;
|
): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -381,32 +390,34 @@ export interface IAuraConverter<TAttr> {
|
|||||||
*/
|
*/
|
||||||
convert(
|
convert(
|
||||||
special: ISpecial<any>,
|
special: ISpecial<any>,
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||||
locator: ITileLocator,
|
context: IEnemyContext<TAttr, THero>
|
||||||
context: IEnemyContext<TAttr>
|
|
||||||
): IEnemyAuraView<TAttr, any, any>;
|
): IEnemyAuraView<TAttr, any, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEnemySpecialQueryModifier<
|
export interface IEnemySpecialQueryModifier<
|
||||||
TAttr
|
TAttr,
|
||||||
|
THero
|
||||||
> extends IEnemySpecialModifier<TAttr> {
|
> extends IEnemySpecialModifier<TAttr> {
|
||||||
/**
|
/**
|
||||||
* 判断一个怪物是否应该查询外部状态
|
* 判断一个怪物是否应该查询外部状态
|
||||||
*/
|
*/
|
||||||
shouldQuery(enemy: IReadonlyEnemy<TAttr>, locator: ITileLocator): boolean;
|
shouldQuery(handler: IReadonlyEnemyHandler<TAttr, THero>): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEnemySpecialQueryEffect<TAttr> {
|
export interface IEnemySpecialQueryEffect<TAttr, THero> {
|
||||||
/** 效果优先级,与光环属性共用 */
|
/** 效果优先级,与光环属性共用 */
|
||||||
readonly priority: number;
|
readonly priority: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据传入的怪物上下文,获取对应的怪物特殊属性修饰器
|
* 根据传入的怪物上下文,获取对应的怪物特殊属性修饰器
|
||||||
*/
|
*/
|
||||||
for(ctx: IEnemyContext<TAttr>): IEnemySpecialQueryModifier<TAttr>;
|
for(
|
||||||
|
ctx: IEnemyContext<TAttr, THero>
|
||||||
|
): IEnemySpecialQueryModifier<TAttr, THero>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEnemyCommonQueryEffect<TAttr> {
|
export interface IEnemyCommonQueryEffect<TAttr, THero> {
|
||||||
/** 优先级,越高的越先执行 */
|
/** 优先级,越高的越先执行 */
|
||||||
readonly priority: number;
|
readonly priority: number;
|
||||||
|
|
||||||
@ -414,21 +425,20 @@ export interface IEnemyCommonQueryEffect<TAttr> {
|
|||||||
* 对怪物的某个特殊属性施加常规查询效果
|
* 对怪物的某个特殊属性施加常规查询效果
|
||||||
*/
|
*/
|
||||||
apply(
|
apply(
|
||||||
enemy: IEnemy<TAttr>,
|
handler: IEnemyHandler<TAttr, THero>,
|
||||||
special: ISpecial<any>,
|
special: ISpecial<any>,
|
||||||
query: () => IEnemyContext<TAttr>,
|
query: () => IEnemyContext<TAttr, THero>
|
||||||
locator: ITileLocator
|
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEnemyFinalEffect<TAttr> {
|
export interface IEnemyFinalEffect<TAttr, THero> {
|
||||||
/** 效果优先级,越高会越先被执行 */
|
/** 效果优先级,越高会越先被执行 */
|
||||||
readonly priority: number;
|
readonly priority: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向怪物施加最终修饰效果
|
* 向怪物施加最终修饰效果
|
||||||
*/
|
*/
|
||||||
apply(enemy: IEnemy<TAttr>, locator: ITileLocator): void;
|
apply(handler: IEnemyHandler<TAttr, THero>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -477,14 +487,13 @@ export interface IMapDamageView<T = any> {
|
|||||||
): Readonly<IMapDamageInfo> | null;
|
): Readonly<IMapDamageInfo> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapDamageConverter<TAttr> {
|
export interface IMapDamageConverter<TAttr, THero> {
|
||||||
/**
|
/**
|
||||||
* 转换地图伤害视图
|
* 转换地图伤害视图
|
||||||
*/
|
*/
|
||||||
convert(
|
convert(
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||||
locator: ITileLocator,
|
context: IEnemyContext<TAttr, THero>
|
||||||
context: IEnemyContext<TAttr>
|
|
||||||
): IMapDamageView<any>[];
|
): IMapDamageView<any>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,15 +507,15 @@ export interface IMapDamageReducer {
|
|||||||
): Readonly<IMapDamageInfo>;
|
): Readonly<IMapDamageInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapDamage<TAttr> {
|
export interface IMapDamage<TAttr, THero> {
|
||||||
/** 当前绑定的怪物上下文 */
|
/** 当前绑定的怪物上下文 */
|
||||||
readonly context: IEnemyContext<TAttr>;
|
readonly context: IEnemyContext<TAttr, THero>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置地图伤害转换器,并基于当前上下文重建所有地图伤害视图
|
* 设置地图伤害转换器,并基于当前上下文重建所有地图伤害视图
|
||||||
* @param converter 地图伤害转换器
|
* @param converter 地图伤害转换器
|
||||||
*/
|
*/
|
||||||
useConverter(converter: IMapDamageConverter<TAttr>): void;
|
useConverter(converter: IMapDamageConverter<TAttr, THero>): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置地图伤害合并器
|
* 设置地图伤害合并器
|
||||||
@ -593,36 +602,30 @@ export interface IEnemyCritical {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CriticalableHeroStatus<THero> = keyof {
|
export type CriticalableHeroStatus<THero> = keyof {
|
||||||
[P in keyof THero as THero[P] extends number ? P : never]: unknown;
|
[P in keyof THero as THero[P] extends number ? P : never]: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IDamageCalculator<TAttr, THero> {
|
export interface IDamageCalculator<TAttr, THero> {
|
||||||
/**
|
/**
|
||||||
* 计算战斗伤害信息
|
* 计算战斗伤害信息
|
||||||
* @param hero 勇士信息
|
* @param handler 信息对象
|
||||||
* @param enemy 怪物信息
|
|
||||||
*/
|
*/
|
||||||
calculate(
|
calculate(handler: IReadonlyEnemyHandler<TAttr, THero>): IEnemyDamageInfo;
|
||||||
hero: Readonly<THero>,
|
|
||||||
enemy: IReadonlyEnemy<TAttr>
|
|
||||||
): IEnemyDamageInfo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取临界计算的上界
|
* 获取临界计算的上界
|
||||||
* @param hero 勇士信息
|
* @param handler 信息对象
|
||||||
* @param enemy 怪物信息
|
|
||||||
* @param attribute 勇士的临界属性
|
* @param attribute 勇士的临界属性
|
||||||
*/
|
*/
|
||||||
getCriticalLimit(
|
getCriticalLimit(
|
||||||
hero: Readonly<THero>,
|
handler: IReadonlyEnemyHandler<TAttr, THero>,
|
||||||
enemy: IReadonlyEnemy<TAttr>,
|
|
||||||
attribute: CriticalableHeroStatus<THero>
|
attribute: CriticalableHeroStatus<THero>
|
||||||
): number;
|
): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDamageSystem<TAttr, THero> {
|
export interface IDamageSystem<TAttr, THero> {
|
||||||
/** 伤害系统所属的上下文 */
|
/** 伤害系统所属的上下文 */
|
||||||
readonly context: IEnemyContext<TAttr>;
|
readonly context: IEnemyContext<TAttr, THero>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置当前伤害计算系统使用的伤害计算器
|
* 设置当前伤害计算系统使用的伤害计算器
|
||||||
@ -639,7 +642,7 @@ export interface IDamageSystem<TAttr, THero> {
|
|||||||
* 绑定勇士信息
|
* 绑定勇士信息
|
||||||
* @param hero 勇士信息
|
* @param hero 勇士信息
|
||||||
*/
|
*/
|
||||||
bindHeroStatus(hero: Readonly<THero>): void;
|
bindHeroStatus(hero: IReadonlyHeroAttribute<THero> | null): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取战斗伤害信息
|
* 获取战斗伤害信息
|
||||||
@ -668,12 +671,12 @@ export interface IDamageSystem<TAttr, THero> {
|
|||||||
* 计算怪物在指定勇士属性下的临界
|
* 计算怪物在指定勇士属性下的临界
|
||||||
* @param enemy 怪物视图
|
* @param enemy 怪物视图
|
||||||
* @param attribute 计算临界的目标勇士属性,比如计算攻击临界、自定义属性的临界等等
|
* @param attribute 计算临界的目标勇士属性,比如计算攻击临界、自定义属性的临界等等
|
||||||
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可
|
* @param precision 临界计算精度,表示会进行多少次二分计算,一般填写 `12-16` 之间的数即可,默认是 12
|
||||||
*/
|
*/
|
||||||
calculateCritical(
|
calculateCritical(
|
||||||
enemy: IEnemyView<TAttr>,
|
enemy: IEnemyView<TAttr>,
|
||||||
attribute: CriticalableHeroStatus<THero>,
|
attribute: CriticalableHeroStatus<THero>,
|
||||||
precision: number
|
precision?: number
|
||||||
): Generator<IEnemyCritical, void, void>;
|
): Generator<IEnemyCritical, void, void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +684,7 @@ export interface IDamageSystem<TAttr, THero> {
|
|||||||
|
|
||||||
//#region 上下文
|
//#region 上下文
|
||||||
|
|
||||||
export interface IEnemyContext<TAttr> {
|
export interface IEnemyContext<TAttr, THero> {
|
||||||
/** 怪物上下文宽度 */
|
/** 怪物上下文宽度 */
|
||||||
readonly width: number;
|
readonly width: number;
|
||||||
/** 怪物上下文高度 */
|
/** 怪物上下文高度 */
|
||||||
@ -700,13 +703,13 @@ export interface IEnemyContext<TAttr> {
|
|||||||
* 注册一个光环转换器
|
* 注册一个光环转换器
|
||||||
* @param converter 光环转换器
|
* @param converter 光环转换器
|
||||||
*/
|
*/
|
||||||
registerAuraConverter(converter: IAuraConverter<TAttr>): void;
|
registerAuraConverter(converter: IAuraConverter<TAttr, THero>): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注销一个光环转换器
|
* 注销一个光环转换器
|
||||||
* @param converter 光环转换器
|
* @param converter 光环转换器
|
||||||
*/
|
*/
|
||||||
unregisterAuraConverter(converter: IAuraConverter<TAttr>): void;
|
unregisterAuraConverter(converter: IAuraConverter<TAttr, THero>): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置光环转换器的启用状态
|
* 设置光环转换器的启用状态
|
||||||
@ -714,7 +717,7 @@ export interface IEnemyContext<TAttr> {
|
|||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
setAuraConverterEnabled(
|
setAuraConverterEnabled(
|
||||||
converter: IAuraConverter<TAttr>,
|
converter: IAuraConverter<TAttr, THero>,
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
@ -722,13 +725,17 @@ export interface IEnemyContext<TAttr> {
|
|||||||
* 注册一个特殊属性查询效果
|
* 注册一个特殊属性查询效果
|
||||||
* @param effect 特殊属性查询效果
|
* @param effect 特殊属性查询效果
|
||||||
*/
|
*/
|
||||||
registerSpecialQueryEffect(effect: IEnemySpecialQueryEffect<TAttr>): void;
|
registerSpecialQueryEffect(
|
||||||
|
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||||
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注销一个特殊属性查询效果
|
* 注销一个特殊属性查询效果
|
||||||
* @param effect 特殊属性查询效果
|
* @param effect 特殊属性查询效果
|
||||||
*/
|
*/
|
||||||
unregisterSpecialQueryEffect(effect: IEnemySpecialQueryEffect<TAttr>): void;
|
unregisterSpecialQueryEffect(
|
||||||
|
effect: IEnemySpecialQueryEffect<TAttr, THero>
|
||||||
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为指定特殊属性代码注册常规查询效果
|
* 为指定特殊属性代码注册常规查询效果
|
||||||
@ -737,7 +744,7 @@ export interface IEnemyContext<TAttr> {
|
|||||||
*/
|
*/
|
||||||
registerCommonQueryEffect(
|
registerCommonQueryEffect(
|
||||||
code: number,
|
code: number,
|
||||||
effect: IEnemyCommonQueryEffect<TAttr>
|
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -747,20 +754,31 @@ export interface IEnemyContext<TAttr> {
|
|||||||
*/
|
*/
|
||||||
unregisterCommonQueryEffect(
|
unregisterCommonQueryEffect(
|
||||||
code: number,
|
code: number,
|
||||||
effect: IEnemyCommonQueryEffect<TAttr>
|
effect: IEnemyCommonQueryEffect<TAttr, THero>
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册一个最终效果
|
* 注册一个最终效果
|
||||||
* @param effect 最终效果
|
* @param effect 最终效果
|
||||||
*/
|
*/
|
||||||
registerFinalEffect(effect: IEnemyFinalEffect<TAttr>): void;
|
registerFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注销一个最终效果
|
* 注销一个最终效果
|
||||||
* @param effect 最终效果
|
* @param effect 最终效果
|
||||||
*/
|
*/
|
||||||
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr>): void;
|
unregisterFinalEffect(effect: IEnemyFinalEffect<TAttr, THero>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定勇士对象
|
||||||
|
* @param hero 勇士属性对象
|
||||||
|
*/
|
||||||
|
bindHero(hero: IReadonlyHeroAttribute<THero> | null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前绑定的勇士属性对象
|
||||||
|
*/
|
||||||
|
getBindedHero(): IReadonlyHeroAttribute<THero> | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定怪物对象当前所在位置
|
* 获取指定怪物对象当前所在位置
|
||||||
@ -813,7 +831,10 @@ export interface IEnemyContext<TAttr> {
|
|||||||
* @param range 范围对象
|
* @param range 范围对象
|
||||||
* @param param 范围参数
|
* @param param 范围参数
|
||||||
*/
|
*/
|
||||||
scanRange<T>(range: IRange<T>, param: T): Iterable<IEnemyView<TAttr>>;
|
scanRange<T>(
|
||||||
|
range: IRange<T>,
|
||||||
|
param: T
|
||||||
|
): Iterable<[ITileLocator, IEnemyView<TAttr>]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 迭代上下文中的全部怪物
|
* 迭代上下文中的全部怪物
|
||||||
@ -836,12 +857,12 @@ export interface IEnemyContext<TAttr> {
|
|||||||
* 绑定地图伤害管理器
|
* 绑定地图伤害管理器
|
||||||
* @param damage 地图伤害管理器
|
* @param damage 地图伤害管理器
|
||||||
*/
|
*/
|
||||||
attachMapDamage(damage: IMapDamage<TAttr> | null): void;
|
attachMapDamage(damage: IMapDamage<TAttr, THero> | null): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前绑定的地图伤害管理器
|
* 获取当前绑定的地图伤害管理器
|
||||||
*/
|
*/
|
||||||
getMapDamage(): IMapDamage<TAttr> | null;
|
getMapDamage(): IMapDamage<TAttr, THero> | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定伤害计算系统
|
* 绑定伤害计算系统
|
||||||
@ -852,7 +873,7 @@ export interface IEnemyContext<TAttr> {
|
|||||||
/**
|
/**
|
||||||
* 获取当前绑定的伤害计算系统
|
* 获取当前绑定的伤害计算系统
|
||||||
*/
|
*/
|
||||||
getDamageSystem<THero>(): IDamageSystem<TAttr, THero> | null;
|
getDamageSystem(): IDamageSystem<TAttr, THero> | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重建当前上下文中的全部怪物计算结果
|
* 重建当前上下文中的全部怪物计算结果
|
||||||
|
|||||||
@ -149,4 +149,8 @@ export class HeroAttribute<THero> implements IHeroAttribute<THero> {
|
|||||||
}
|
}
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getModifiableClone(): IHeroAttribute<THero> {
|
||||||
|
return this.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,6 @@ export class HeroState<THero> implements IHeroState<THero> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getIsolatedAttribute(): IHeroAttribute<THero> {
|
getIsolatedAttribute(): IHeroAttribute<THero> {
|
||||||
return this.attribute.clone();
|
return this.attribute.getModifiableClone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,7 +71,12 @@ export interface IReadonlyHeroAttribute<THero> {
|
|||||||
* 深拷贝此勇士属性对象
|
* 深拷贝此勇士属性对象
|
||||||
* @param cloneModifier 是否同时复制修饰器,默认复制
|
* @param cloneModifier 是否同时复制修饰器,默认复制
|
||||||
*/
|
*/
|
||||||
clone(cloneModifier?: boolean): IHeroAttribute<THero>;
|
clone(cloneModifier?: boolean): IReadonlyHeroAttribute<THero>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取此勇士属性对象的可修改副本
|
||||||
|
*/
|
||||||
|
getModifiableClone(): IHeroAttribute<THero>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
|
export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
|
||||||
@ -101,6 +106,12 @@ export interface IHeroAttribute<THero> extends IReadonlyHeroAttribute<THero> {
|
|||||||
name: K,
|
name: K,
|
||||||
modifier: IHeroModifier<THero[K], unknown>
|
modifier: IHeroModifier<THero[K], unknown>
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深拷贝此勇士属性对象
|
||||||
|
* @param cloneModifier 是否同时复制修饰器,默认复制
|
||||||
|
*/
|
||||||
|
clone(cloneModifier?: boolean): IHeroAttribute<THero>;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
HeroState,
|
HeroState,
|
||||||
IHeroState
|
IHeroState
|
||||||
} from '@user/data-base';
|
} from '@user/data-base';
|
||||||
import { IEnemyAttributes } from './enemy/types';
|
import { IEnemyAttr } from './enemy/types';
|
||||||
import {
|
import {
|
||||||
CommonAuraConverter,
|
CommonAuraConverter,
|
||||||
EnemyLegacyBridge,
|
EnemyLegacyBridge,
|
||||||
@ -24,7 +24,7 @@ import {
|
|||||||
registerSpecials
|
registerSpecials
|
||||||
} from './enemy';
|
} from './enemy';
|
||||||
import { HERO_DEFAULT_ATTRIBUTE, TILE_HEIGHT, TILE_WIDTH } from './shared';
|
import { HERO_DEFAULT_ATTRIBUTE, TILE_HEIGHT, TILE_WIDTH } from './shared';
|
||||||
import { IHeroAttributeObject } from './hero';
|
import { IHeroAttr } from './hero';
|
||||||
|
|
||||||
export class CoreState implements ICoreState {
|
export class CoreState implements ICoreState {
|
||||||
readonly layer: ILayerState;
|
readonly layer: ILayerState;
|
||||||
@ -32,10 +32,10 @@ export class CoreState implements ICoreState {
|
|||||||
readonly idNumberMap: Map<string, number>;
|
readonly idNumberMap: Map<string, number>;
|
||||||
readonly numberIdMap: Map<number, string>;
|
readonly numberIdMap: Map<number, string>;
|
||||||
|
|
||||||
readonly hero: IHeroState<IHeroAttributeObject>;
|
readonly hero: IHeroState<IHeroAttr>;
|
||||||
|
|
||||||
readonly enemyManager: IEnemyManager<IEnemyAttributes>;
|
readonly enemyManager: IEnemyManager<IEnemyAttr>;
|
||||||
readonly enemyContext: IEnemyContext<IEnemyAttributes>;
|
readonly enemyContext: IEnemyContext<IEnemyAttr, IHeroAttr>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.layer = new LayerState();
|
this.layer = new LayerState();
|
||||||
@ -43,6 +43,15 @@ export class CoreState implements ICoreState {
|
|||||||
this.idNumberMap = new Map();
|
this.idNumberMap = new Map();
|
||||||
this.numberIdMap = new Map();
|
this.numberIdMap = new Map();
|
||||||
|
|
||||||
|
//#region 勇士初始化
|
||||||
|
|
||||||
|
const heroMover = new HeroMover();
|
||||||
|
const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE);
|
||||||
|
const heroState = new HeroState(heroMover, heroAttribute);
|
||||||
|
this.hero = heroState;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
//#region 怪物初始化
|
//#region 怪物初始化
|
||||||
|
|
||||||
// 怪物管理器初始化
|
// 怪物管理器初始化
|
||||||
@ -56,7 +65,7 @@ export class CoreState implements ICoreState {
|
|||||||
registerSpecials(enemyManager);
|
registerSpecials(enemyManager);
|
||||||
this.enemyManager = enemyManager;
|
this.enemyManager = enemyManager;
|
||||||
// 怪物上下文初始化
|
// 怪物上下文初始化
|
||||||
const enemyContext = new EnemyContext<IEnemyAttributes>();
|
const enemyContext = new EnemyContext<IEnemyAttr, IHeroAttr>();
|
||||||
const damageSystem = new DamageSystem(enemyContext);
|
const damageSystem = new DamageSystem(enemyContext);
|
||||||
const mapDamage = new MapDamage(enemyContext);
|
const mapDamage = new MapDamage(enemyContext);
|
||||||
mapDamage.useConverter(new MainMapDamageConverter());
|
mapDamage.useConverter(new MainMapDamageConverter());
|
||||||
@ -67,18 +76,10 @@ export class CoreState implements ICoreState {
|
|||||||
enemyContext.registerAuraConverter(new GuardAuraConverter());
|
enemyContext.registerAuraConverter(new GuardAuraConverter());
|
||||||
enemyContext.registerFinalEffect(new MainEnemyFinalEffect());
|
enemyContext.registerFinalEffect(new MainEnemyFinalEffect());
|
||||||
enemyContext.resize(TILE_WIDTH, TILE_HEIGHT);
|
enemyContext.resize(TILE_WIDTH, TILE_HEIGHT);
|
||||||
|
enemyContext.bindHero(heroAttribute);
|
||||||
this.enemyContext = enemyContext;
|
this.enemyContext = enemyContext;
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 勇士初始化
|
|
||||||
|
|
||||||
const heroMover = new HeroMover();
|
|
||||||
const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE);
|
|
||||||
const heroState = new HeroState(heroMover, heroAttribute);
|
|
||||||
this.hero = heroState;
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveState(): IStateSaveData {
|
saveState(): IStateSaveData {
|
||||||
|
|||||||
@ -9,16 +9,18 @@ import {
|
|||||||
} from '@motajs/common';
|
} from '@motajs/common';
|
||||||
import {
|
import {
|
||||||
IAuraConverter,
|
IAuraConverter,
|
||||||
|
IEnemyHandler,
|
||||||
IEnemyAuraView,
|
IEnemyAuraView,
|
||||||
IEnemyContext,
|
IEnemyContext,
|
||||||
IEnemySpecialModifier,
|
IEnemySpecialModifier,
|
||||||
IEnemyView,
|
IEnemyView,
|
||||||
|
IReadonlyEnemyHandler,
|
||||||
IReadonlyEnemy,
|
IReadonlyEnemy,
|
||||||
ISpecial,
|
ISpecial
|
||||||
IEnemy
|
|
||||||
} from '@user/data-base';
|
} from '@user/data-base';
|
||||||
import { IHaloValue } from './special';
|
import { IHaloValue } from './special';
|
||||||
import { IEnemyAttributes } from './types';
|
import { IEnemyAttr } from './types';
|
||||||
|
import { IHeroAttr } from '../hero';
|
||||||
|
|
||||||
const FULL_RANGE = new FullRange();
|
const FULL_RANGE = new FullRange();
|
||||||
const RECT_RANGE = new RectRange();
|
const RECT_RANGE = new RectRange();
|
||||||
@ -26,22 +28,24 @@ const MANHATTAN_RANGE = new ManhattanRange();
|
|||||||
|
|
||||||
//#region 25-光环
|
//#region 25-光环
|
||||||
|
|
||||||
export class CommonAuraConverter implements IAuraConverter<IEnemyAttributes> {
|
export class CommonAuraConverter implements IAuraConverter<
|
||||||
|
IEnemyAttr,
|
||||||
|
IHeroAttr
|
||||||
|
> {
|
||||||
shouldConvert(special: ISpecial<any>): boolean {
|
shouldConvert(special: ISpecial<any>): boolean {
|
||||||
return special.code === 25;
|
return special.code === 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
special: ISpecial<IHaloValue>,
|
special: ISpecial<IHaloValue>,
|
||||||
enemy: IReadonlyEnemy<IEnemyAttributes>,
|
handler: IReadonlyEnemyHandler<IEnemyAttr, IHeroAttr>
|
||||||
locator: ITileLocator
|
|
||||||
): CommonAura {
|
): CommonAura {
|
||||||
return new CommonAura(enemy, special, locator);
|
return new CommonAura(handler.enemy, special, handler.locator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommonAura implements IEnemyAuraView<
|
export class CommonAura implements IEnemyAuraView<
|
||||||
IEnemyAttributes,
|
IEnemyAttr,
|
||||||
IRectRangeParam | IManhattanRangeParam | void,
|
IRectRangeParam | IManhattanRangeParam | void,
|
||||||
IHaloValue
|
IHaloValue
|
||||||
> {
|
> {
|
||||||
@ -51,7 +55,7 @@ export class CommonAura implements IEnemyAuraView<
|
|||||||
readonly range: IRange<IRectRangeParam | IManhattanRangeParam | void>;
|
readonly range: IRange<IRectRangeParam | IManhattanRangeParam | void>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly enemy: IReadonlyEnemy<IEnemyAttributes>,
|
readonly enemy: IReadonlyEnemy<IEnemyAttr>,
|
||||||
readonly special: ISpecial<IHaloValue>,
|
readonly special: ISpecial<IHaloValue>,
|
||||||
readonly locator: ITileLocator
|
readonly locator: ITileLocator
|
||||||
) {
|
) {
|
||||||
@ -89,9 +93,10 @@ export class CommonAura implements IEnemyAuraView<
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply(
|
apply(
|
||||||
enemy: IEnemy<IEnemyAttributes>,
|
handler: IEnemyHandler<IEnemyAttr, unknown>,
|
||||||
baseEnemy: IReadonlyEnemy<IEnemyAttributes>
|
baseEnemy: IReadonlyEnemy<IEnemyAttr>
|
||||||
): void {
|
): void {
|
||||||
|
const { enemy } = handler;
|
||||||
const { hpBuff, atkBuff, defBuff } = this.special.value;
|
const { hpBuff, atkBuff, defBuff } = this.special.value;
|
||||||
|
|
||||||
if (hpBuff !== 0) {
|
if (hpBuff !== 0) {
|
||||||
@ -110,7 +115,7 @@ export class CommonAura implements IEnemyAuraView<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applySpecial(): IEnemySpecialModifier<IEnemyAttributes> | null {
|
applySpecial(): IEnemySpecialModifier<IEnemyAttr> | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,23 +124,25 @@ export class CommonAura implements IEnemyAuraView<
|
|||||||
|
|
||||||
//#region 26-支援
|
//#region 26-支援
|
||||||
|
|
||||||
export class GuardAuraConverter implements IAuraConverter<IEnemyAttributes> {
|
export class GuardAuraConverter implements IAuraConverter<
|
||||||
|
IEnemyAttr,
|
||||||
|
IHeroAttr
|
||||||
|
> {
|
||||||
shouldConvert(special: ISpecial<any>): boolean {
|
shouldConvert(special: ISpecial<any>): boolean {
|
||||||
return special.code === 26;
|
return special.code === 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert(
|
convert(
|
||||||
special: ISpecial<void>,
|
special: ISpecial<void>,
|
||||||
enemy: IReadonlyEnemy<IEnemyAttributes>,
|
handler: IReadonlyEnemyHandler<IEnemyAttr, IHeroAttr>,
|
||||||
locator: ITileLocator,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>
|
||||||
context: IEnemyContext<IEnemyAttributes>
|
|
||||||
): GuardAura {
|
): GuardAura {
|
||||||
return new GuardAura(context, enemy, special, locator);
|
return new GuardAura(context, handler.enemy, special, handler.locator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GuardAura implements IEnemyAuraView<
|
export class GuardAura implements IEnemyAuraView<
|
||||||
IEnemyAttributes,
|
IEnemyAttr,
|
||||||
IRectRangeParam,
|
IRectRangeParam,
|
||||||
void
|
void
|
||||||
> {
|
> {
|
||||||
@ -144,11 +151,11 @@ export class GuardAura implements IEnemyAuraView<
|
|||||||
readonly couldApplySpecial: boolean = false;
|
readonly couldApplySpecial: boolean = false;
|
||||||
readonly range: IRange<IRectRangeParam> = RECT_RANGE;
|
readonly range: IRange<IRectRangeParam> = RECT_RANGE;
|
||||||
|
|
||||||
private readonly sourceView: IEnemyView<IEnemyAttributes> | null;
|
private readonly sourceView: IEnemyView<IEnemyAttr> | null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: IEnemyContext<IEnemyAttributes>,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>,
|
||||||
readonly enemy: IReadonlyEnemy<IEnemyAttributes>,
|
readonly enemy: IReadonlyEnemy<IEnemyAttr>,
|
||||||
readonly special: ISpecial<void>,
|
readonly special: ISpecial<void>,
|
||||||
readonly locator: ITileLocator
|
readonly locator: ITileLocator
|
||||||
) {
|
) {
|
||||||
@ -164,19 +171,16 @@ export class GuardAura implements IEnemyAuraView<
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(
|
apply(handler: IEnemyHandler<IEnemyAttr, IHeroAttr>): void {
|
||||||
enemy: IEnemy<IEnemyAttributes>,
|
|
||||||
_baseEnemy: IReadonlyEnemy<IEnemyAttributes>,
|
|
||||||
locator: ITileLocator
|
|
||||||
): void {
|
|
||||||
if (!this.sourceView) return;
|
if (!this.sourceView) return;
|
||||||
|
const { enemy, locator } = handler;
|
||||||
if (locator.x === this.locator.x && locator.y === this.locator.y) {
|
if (locator.x === this.locator.x && locator.y === this.locator.y) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enemy.getAttribute('guard').add(this.sourceView);
|
enemy.getAttribute('guard').add(this.sourceView);
|
||||||
}
|
}
|
||||||
|
|
||||||
applySpecial(): IEnemySpecialModifier<IEnemyAttributes> | null {
|
applySpecial(): IEnemySpecialModifier<IEnemyAttr> | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,28 @@
|
|||||||
import { IEnemy, IEnemyFinalEffect } from '@user/data-base';
|
import { IEnemyFinalEffect, IEnemyHandler } from '@user/data-base';
|
||||||
import { IEnemyAttributes } from './types';
|
import { IEnemyAttr } from './types';
|
||||||
import { ITileLocator } from '@motajs/common';
|
import { IHeroAttr } from '../hero';
|
||||||
|
|
||||||
const HERO_STATUS_PLACEHOLDER = {
|
export class MainEnemyFinalEffect implements IEnemyFinalEffect<
|
||||||
atk: 0,
|
IEnemyAttr,
|
||||||
def: 0
|
IHeroAttr
|
||||||
} as const;
|
> {
|
||||||
|
|
||||||
export class MainEnemyFinalEffect implements IEnemyFinalEffect<IEnemyAttributes> {
|
|
||||||
readonly priority: number = 0;
|
readonly priority: number = 0;
|
||||||
|
|
||||||
apply(enemy: IEnemy<IEnemyAttributes>, _locator: ITileLocator): void {
|
apply(handler: IEnemyHandler<IEnemyAttr, IHeroAttr>): void {
|
||||||
|
const enemy = handler.enemy;
|
||||||
|
const heroAtk = handler.hero.getFinalAttribute('atk');
|
||||||
|
const heroDef = handler.hero.getFinalAttribute('def');
|
||||||
|
|
||||||
// 3-坚固
|
// 3-坚固
|
||||||
if (enemy.hasSpecial(3)) {
|
if (enemy.hasSpecial(3)) {
|
||||||
const target = Math.max(
|
const target = Math.max(enemy.getAttribute('def'), heroAtk - 1);
|
||||||
enemy.getAttribute('def'),
|
|
||||||
HERO_STATUS_PLACEHOLDER.atk - 1
|
|
||||||
);
|
|
||||||
enemy.setAttribute('def', target);
|
enemy.setAttribute('def', target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10-模仿
|
// 10-模仿
|
||||||
if (enemy.hasSpecial(10)) {
|
if (enemy.hasSpecial(10)) {
|
||||||
enemy.setAttribute('atk', HERO_STATUS_PLACEHOLDER.atk);
|
enemy.setAttribute('atk', heroAtk);
|
||||||
enemy.setAttribute('def', HERO_STATUS_PLACEHOLDER.def);
|
enemy.setAttribute('def', heroDef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { IEnemyLegacyBridge } from '@user/data-base';
|
import { IEnemyLegacyBridge } from '@user/data-base';
|
||||||
import { IEnemyAttributes } from './types';
|
import { IEnemyAttr } from './types';
|
||||||
|
|
||||||
export class EnemyLegacyBridge implements IEnemyLegacyBridge<IEnemyAttributes> {
|
export class EnemyLegacyBridge implements IEnemyLegacyBridge<IEnemyAttr> {
|
||||||
fromLegacyEnemy(
|
fromLegacyEnemy(
|
||||||
enemy: Enemy,
|
enemy: Enemy,
|
||||||
defaultAttr: Partial<IEnemyAttributes>
|
defaultAttr: Partial<IEnemyAttr>
|
||||||
): IEnemyAttributes {
|
): IEnemyAttr {
|
||||||
return {
|
return {
|
||||||
hp: enemy.hp ?? defaultAttr.hp ?? 0,
|
hp: enemy.hp ?? defaultAttr.hp ?? 0,
|
||||||
atk: enemy.atk ?? defaultAttr.atk ?? 0,
|
atk: enemy.atk ?? defaultAttr.atk ?? 0,
|
||||||
|
|||||||
@ -11,17 +11,20 @@ import {
|
|||||||
RectRange,
|
RectRange,
|
||||||
ITileLocator
|
ITileLocator
|
||||||
} from '@motajs/common';
|
} from '@motajs/common';
|
||||||
import { IReadonlyEnemy, ISpecial } from '@user/data-base';
|
|
||||||
import {
|
import {
|
||||||
IEnemyContext,
|
IEnemyContext,
|
||||||
IMapDamageConverter,
|
IMapDamageConverter,
|
||||||
IMapDamageInfo,
|
IMapDamageInfo,
|
||||||
IMapDamageInfoExtra,
|
IMapDamageInfoExtra,
|
||||||
IMapDamageReducer,
|
IMapDamageReducer,
|
||||||
IMapDamageView
|
IReadonlyEnemyHandler,
|
||||||
|
ISpecial,
|
||||||
|
IMapDamageView,
|
||||||
|
IReadonlyHeroAttribute
|
||||||
} from '@user/data-base';
|
} from '@user/data-base';
|
||||||
import { IZoneValue } from './special';
|
import { IZoneValue } from './special';
|
||||||
import { IEnemyAttributes, MapDamageType } from './types';
|
import { IEnemyAttr, MapDamageType } from './types';
|
||||||
|
import { IHeroAttr } from '../hero';
|
||||||
|
|
||||||
const RECT_RANGE = new RectRange();
|
const RECT_RANGE = new RectRange();
|
||||||
const MANHATTAN_RANGE = new ManhattanRange();
|
const MANHATTAN_RANGE = new ManhattanRange();
|
||||||
@ -33,7 +36,9 @@ const DIR4 = [...DIRECTION_MAPPER.map(InternalDirectionGroup.Dir4)];
|
|||||||
//#region 地图伤害
|
//#region 地图伤害
|
||||||
|
|
||||||
abstract class BaseMapDamageView<T> implements IMapDamageView<T> {
|
abstract class BaseMapDamageView<T> implements IMapDamageView<T> {
|
||||||
constructor(protected readonly context: IEnemyContext<IEnemyAttributes>) {}
|
constructor(
|
||||||
|
protected readonly context: IEnemyContext<IEnemyAttr, IHeroAttr>
|
||||||
|
) {}
|
||||||
|
|
||||||
abstract getRange(): IRange<T>;
|
abstract getRange(): IRange<T>;
|
||||||
|
|
||||||
@ -80,7 +85,7 @@ export class ZoneDamageView extends BaseMapDamageView<
|
|||||||
IRectRangeParam | IManhattanRangeParam
|
IRectRangeParam | IManhattanRangeParam
|
||||||
> {
|
> {
|
||||||
constructor(
|
constructor(
|
||||||
context: IEnemyContext<IEnemyAttributes>,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>,
|
||||||
private readonly locator: Readonly<ITileLocator>,
|
private readonly locator: Readonly<ITileLocator>,
|
||||||
private readonly special: Readonly<ISpecial<IZoneValue>>
|
private readonly special: Readonly<ISpecial<IZoneValue>>
|
||||||
) {
|
) {
|
||||||
@ -108,16 +113,14 @@ export class ZoneDamageView extends BaseMapDamageView<
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getDamageWithoutCheck(
|
getDamageWithoutCheck(): Readonly<IMapDamageInfo> | null {
|
||||||
_locator: ITileLocator
|
|
||||||
): Readonly<IMapDamageInfo> | null {
|
|
||||||
return this.createInfo(this.special.value.zone, MapDamageType.Zone);
|
return this.createInfo(this.special.value.zone, MapDamageType.Zone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RepulseDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
export class RepulseDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
||||||
constructor(
|
constructor(
|
||||||
context: IEnemyContext<IEnemyAttributes>,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>,
|
||||||
private readonly locator: Readonly<ITileLocator>,
|
private readonly locator: Readonly<ITileLocator>,
|
||||||
private readonly special: Readonly<ISpecial<number>>
|
private readonly special: Readonly<ISpecial<number>>
|
||||||
) {
|
) {
|
||||||
@ -151,7 +154,7 @@ export class RepulseDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
|||||||
|
|
||||||
export class LaserDamageView extends BaseMapDamageView<IRayRangeParam> {
|
export class LaserDamageView extends BaseMapDamageView<IRayRangeParam> {
|
||||||
constructor(
|
constructor(
|
||||||
context: IEnemyContext<IEnemyAttributes>,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>,
|
||||||
private readonly locator: Readonly<ITileLocator>,
|
private readonly locator: Readonly<ITileLocator>,
|
||||||
private readonly special: Readonly<ISpecial<number>>,
|
private readonly special: Readonly<ISpecial<number>>,
|
||||||
private readonly dir: IDirectionDescriptor[] = DIR4
|
private readonly dir: IDirectionDescriptor[] = DIR4
|
||||||
@ -171,23 +174,16 @@ export class LaserDamageView extends BaseMapDamageView<IRayRangeParam> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getDamageWithoutCheck(
|
getDamageWithoutCheck(): Readonly<IMapDamageInfo> | null {
|
||||||
locator: ITileLocator
|
|
||||||
): Readonly<IMapDamageInfo> | null {
|
|
||||||
if (locator.x === this.locator.x && locator.y === this.locator.y) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.createInfo(this.special.value, MapDamageType.Layer);
|
return this.createInfo(this.special.value, MapDamageType.Layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BetweenDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
export class BetweenDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
||||||
private static readonly DAMAGE = 1;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: IEnemyContext<IEnemyAttributes>,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>,
|
||||||
private readonly locator: Readonly<ITileLocator>
|
private readonly locator: Readonly<ITileLocator>,
|
||||||
|
private readonly hero: IReadonlyHeroAttribute<IHeroAttr>
|
||||||
) {
|
) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
@ -232,13 +228,14 @@ export class BetweenDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.createInfo(BetweenDamageView.DAMAGE, MapDamageType.Between);
|
const damage = this.hero.getFinalAttribute('hp');
|
||||||
|
return this.createInfo(damage, MapDamageType.Between);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AmbushDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
export class AmbushDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
||||||
constructor(
|
constructor(
|
||||||
context: IEnemyContext<IEnemyAttributes>,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>,
|
||||||
private readonly locator: Readonly<ITileLocator>
|
private readonly locator: Readonly<ITileLocator>
|
||||||
) {
|
) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -256,13 +253,7 @@ export class AmbushDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getDamageWithoutCheck(
|
getDamageWithoutCheck(): Readonly<IMapDamageInfo> | null {
|
||||||
locator: ITileLocator
|
|
||||||
): Readonly<IMapDamageInfo> | null {
|
|
||||||
if (locator.x === this.locator.x && locator.y === this.locator.y) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.createInfo(0, MapDamageType.Unknown, {
|
return this.createInfo(0, MapDamageType.Unknown, {
|
||||||
catch: new Set([this.locator])
|
catch: new Set([this.locator])
|
||||||
});
|
});
|
||||||
@ -273,13 +264,16 @@ export class AmbushDamageView extends BaseMapDamageView<IManhattanRangeParam> {
|
|||||||
|
|
||||||
//#region 转换器
|
//#region 转换器
|
||||||
|
|
||||||
export class MainMapDamageConverter implements IMapDamageConverter<IEnemyAttributes> {
|
export class MainMapDamageConverter implements IMapDamageConverter<
|
||||||
|
IEnemyAttr,
|
||||||
|
IHeroAttr
|
||||||
|
> {
|
||||||
convert(
|
convert(
|
||||||
enemy: IReadonlyEnemy<IEnemyAttributes>,
|
handler: IReadonlyEnemyHandler<IEnemyAttr, IHeroAttr>,
|
||||||
locator: ITileLocator,
|
context: IEnemyContext<IEnemyAttr, IHeroAttr>
|
||||||
context: IEnemyContext<IEnemyAttributes>
|
|
||||||
): IMapDamageView<any>[] {
|
): IMapDamageView<any>[] {
|
||||||
const views: IMapDamageView<any>[] = [];
|
const views: IMapDamageView<any>[] = [];
|
||||||
|
const { enemy, locator } = handler;
|
||||||
|
|
||||||
const zone = enemy.getSpecial<IZoneValue>(15);
|
const zone = enemy.getSpecial<IZoneValue>(15);
|
||||||
if (zone) {
|
if (zone) {
|
||||||
@ -287,7 +281,7 @@ export class MainMapDamageConverter implements IMapDamageConverter<IEnemyAttribu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enemy.hasSpecial(16)) {
|
if (enemy.hasSpecial(16)) {
|
||||||
views.push(new BetweenDamageView(context, locator));
|
views.push(new BetweenDamageView(context, locator, handler.hero));
|
||||||
}
|
}
|
||||||
|
|
||||||
const repulse = enemy.getSpecial<number>(18);
|
const repulse = enemy.getSpecial<number>(18);
|
||||||
@ -313,10 +307,7 @@ export class MainMapDamageConverter implements IMapDamageConverter<IEnemyAttribu
|
|||||||
//#region 合并器
|
//#region 合并器
|
||||||
|
|
||||||
export class MainMapDamageReducer implements IMapDamageReducer {
|
export class MainMapDamageReducer implements IMapDamageReducer {
|
||||||
reduce(
|
reduce(info: Iterable<Readonly<IMapDamageInfo>>): Readonly<IMapDamageInfo> {
|
||||||
info: Iterable<Readonly<IMapDamageInfo>>,
|
|
||||||
_locator: ITileLocator
|
|
||||||
): Readonly<IMapDamageInfo> {
|
|
||||||
let damage = 0;
|
let damage = 0;
|
||||||
let type = MapDamageType.Unknown;
|
let type = MapDamageType.Unknown;
|
||||||
let maxDamage = -Infinity;
|
let maxDamage = -Infinity;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
IEnemyManager
|
IEnemyManager
|
||||||
} from '@user/data-base';
|
} from '@user/data-base';
|
||||||
import { getHeroStatusOn } from '../legacy/hero';
|
import { getHeroStatusOn } from '../legacy/hero';
|
||||||
import { IEnemyAttributes } from './types';
|
import { IEnemyAttr } from './types';
|
||||||
|
|
||||||
//#region 复合属性值类型
|
//#region 复合属性值类型
|
||||||
|
|
||||||
@ -48,9 +48,7 @@ export interface IHaloValue {
|
|||||||
* 8. 重生属性:还在脚本编辑的 changingFloor
|
* 8. 重生属性:还在脚本编辑的 changingFloor
|
||||||
* 9. 阻击 | 捕捉 的每步效果:packages-user/legacy-plugin-data/src/enemy/checkblock.ts
|
* 9. 阻击 | 捕捉 的每步效果:packages-user/legacy-plugin-data/src/enemy/checkblock.ts
|
||||||
*/
|
*/
|
||||||
export function registerSpecials(
|
export function registerSpecials(manager: IEnemyManager<IEnemyAttr>): void {
|
||||||
manager: IEnemyManager<IEnemyAttributes>
|
|
||||||
): void {
|
|
||||||
manager.setAttributeDefaults('guard', new Set());
|
manager.setAttributeDefaults('guard', new Set());
|
||||||
|
|
||||||
// 0 - 空
|
// 0 - 空
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { IEnemyView } from '@user/data-base';
|
import { IEnemyView } from '@user/data-base';
|
||||||
|
|
||||||
export interface IEnemyAttributes {
|
export interface IEnemyAttr {
|
||||||
/** 怪物生命值 */
|
/** 怪物生命值 */
|
||||||
hp: number;
|
hp: number;
|
||||||
/** 怪物攻击力 */
|
/** 怪物攻击力 */
|
||||||
@ -14,7 +14,7 @@ export interface IEnemyAttributes {
|
|||||||
/** 怪物加点量 */
|
/** 怪物加点量 */
|
||||||
point: number;
|
point: number;
|
||||||
/** 支援来源怪物视图列表 */
|
/** 支援来源怪物视图列表 */
|
||||||
guard: Set<IEnemyView<IEnemyAttributes>>;
|
guard: Set<IEnemyView<IEnemyAttr>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum MapDamageType {
|
export const enum MapDamageType {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
//#region 勇士属性
|
//#region 勇士属性
|
||||||
|
|
||||||
export interface IHeroAttributeObject {
|
export interface IHeroAttr {
|
||||||
/** 勇士名称 */
|
/** 勇士名称 */
|
||||||
name: string;
|
name: string;
|
||||||
/** 勇士生命值 */
|
/** 勇士生命值 */
|
||||||
|
|||||||
@ -288,7 +288,7 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
protected async onMoveStart(controller: IMoveController): Promise<void> {
|
protected async onMoveStart(controller: IMoveController): Promise<void> {
|
||||||
this.beforeMoveSpeed = this.moveSpeed;
|
this.beforeMoveSpeed = this.moveSpeed;
|
||||||
if (!core.isReplaying() || core.status.replay.speed <= 12) {
|
if (!core.isReplaying() || core.status.replay.speed <= 12) {
|
||||||
state.hero.startMove();
|
state.hero.mover.startMove();
|
||||||
}
|
}
|
||||||
// 这里要检查前面那一格能不能走,不能走则不触发平滑视角,以避免撞墙上视角卡住
|
// 这里要检查前面那一格能不能走,不能走则不触发平滑视角,以避免撞墙上视角卡住
|
||||||
if (!this.ignoreTerrain) {
|
if (!this.ignoreTerrain) {
|
||||||
@ -310,7 +310,7 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
protected async onMoveEnd(controller: IMoveController): Promise<void> {
|
protected async onMoveEnd(controller: IMoveController): Promise<void> {
|
||||||
this.moveSpeed = this.beforeMoveSpeed;
|
this.moveSpeed = this.beforeMoveSpeed;
|
||||||
this.onSetMoveSpeed(this.moveSpeed, controller);
|
this.onSetMoveSpeed(this.moveSpeed, controller);
|
||||||
await state.hero.endMove();
|
await state.hero.mover.endMove();
|
||||||
// viewport.sync('endMove');
|
// viewport.sync('endMove');
|
||||||
core.clearContinueAutomaticRoute();
|
core.clearContinueAutomaticRoute();
|
||||||
core.stopAutomaticRoute();
|
core.stopAutomaticRoute();
|
||||||
@ -442,19 +442,22 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
const replaying = core.isReplaying();
|
const replaying = core.isReplaying();
|
||||||
if (replaying) {
|
if (replaying) {
|
||||||
if (core.status.replay.speed > 12) {
|
if (core.status.replay.speed > 12) {
|
||||||
await state.hero.endMove();
|
await state.hero.mover.endMove();
|
||||||
await sleep(speed);
|
await sleep(speed);
|
||||||
state.hero.setPosition(x, y);
|
state.hero.mover.setPosition(x, y);
|
||||||
} else {
|
} else {
|
||||||
state.hero.startMove();
|
state.hero.mover.startMove();
|
||||||
await state.hero.move(
|
await state.hero.mover.move(
|
||||||
fromDirectionString(moveDir),
|
fromDirectionString(moveDir),
|
||||||
this.moveSpeed / core.status.replay.speed
|
this.moveSpeed / core.status.replay.speed
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.hero.startMove();
|
state.hero.mover.startMove();
|
||||||
await state.hero.move(fromDirectionString(moveDir), this.moveSpeed);
|
await state.hero.mover.move(
|
||||||
|
fromDirectionString(moveDir),
|
||||||
|
this.moveSpeed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { IHeroAttributeObject } from './hero';
|
import { IHeroAttr } from './hero';
|
||||||
|
|
||||||
/** 每个地图的默认宽度 */
|
/** 每个地图的默认宽度 */
|
||||||
export const TILE_WIDTH = 13;
|
export const TILE_WIDTH = 13;
|
||||||
@ -11,7 +11,7 @@ export const TILE_HEIGHT = 13;
|
|||||||
export const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png';
|
export const DEFAULT_HERO_IMAGE: ImageIds = 'hero.png';
|
||||||
|
|
||||||
/** 勇士的初始属性,数值填多少目前都无所谓,因为最终会从旧样板读取,但是必须得填 */
|
/** 勇士的初始属性,数值填多少目前都无所谓,因为最终会从旧样板读取,但是必须得填 */
|
||||||
export const HERO_DEFAULT_ATTRIBUTE: IHeroAttributeObject = {
|
export const HERO_DEFAULT_ATTRIBUTE: IHeroAttr = {
|
||||||
name: '',
|
name: '',
|
||||||
hp: 1,
|
hp: 1,
|
||||||
hpmax: 0,
|
hpmax: 0,
|
||||||
|
|||||||
@ -6,12 +6,12 @@ import {
|
|||||||
IHeroFollower,
|
IHeroFollower,
|
||||||
IHeroState
|
IHeroState
|
||||||
} from '@user/data-base';
|
} from '@user/data-base';
|
||||||
import { IEnemyAttributes } from './enemy/types';
|
import { IEnemyAttr } from './enemy/types';
|
||||||
import { IHeroAttributeObject } from './hero';
|
import { IHeroAttr } from './hero';
|
||||||
|
|
||||||
export interface IGameDataState {
|
export interface IGameDataState {
|
||||||
/** 怪物管理器 */
|
/** 怪物管理器 */
|
||||||
readonly enemyManager: IEnemyManager<IEnemyAttributes>;
|
readonly enemyManager: IEnemyManager<IEnemyAttr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStateSaveData {
|
export interface IStateSaveData {
|
||||||
@ -23,7 +23,7 @@ export interface ICoreState {
|
|||||||
/** 地图状态 */
|
/** 地图状态 */
|
||||||
readonly layer: ILayerState;
|
readonly layer: ILayerState;
|
||||||
/** 勇士状态 */
|
/** 勇士状态 */
|
||||||
readonly hero: IHeroState<IHeroAttributeObject>;
|
readonly hero: IHeroState<IHeroAttr>;
|
||||||
/** 朝向绑定 */
|
/** 朝向绑定 */
|
||||||
readonly roleFace: IRoleFaceBinder;
|
readonly roleFace: IRoleFaceBinder;
|
||||||
/** id 到图块数字的映射 */
|
/** id 到图块数字的映射 */
|
||||||
@ -32,9 +32,9 @@ export interface ICoreState {
|
|||||||
readonly numberIdMap: Map<number, string>;
|
readonly numberIdMap: Map<number, string>;
|
||||||
|
|
||||||
/** 怪物管理器 */
|
/** 怪物管理器 */
|
||||||
readonly enemyManager: IEnemyManager<IEnemyAttributes>;
|
readonly enemyManager: IEnemyManager<IEnemyAttr>;
|
||||||
/** 怪物上下文 */
|
/** 怪物上下文 */
|
||||||
readonly enemyContext: IEnemyContext<IEnemyAttributes>;
|
readonly enemyContext: IEnemyContext<IEnemyAttr, IHeroAttr>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存状态
|
* 保存状态
|
||||||
|
|||||||
@ -153,13 +153,13 @@ export function initFallback() {
|
|||||||
core.status.hero.loc[name] = value;
|
core.status.hero.loc[name] = value;
|
||||||
if (name === 'direction') {
|
if (name === 'direction') {
|
||||||
const dir = fromDirectionString(value as Dir);
|
const dir = fromDirectionString(value as Dir);
|
||||||
state.hero.turn(dir);
|
state.hero.mover.turn(dir);
|
||||||
setHeroDirection(value as Dir);
|
setHeroDirection(value as Dir);
|
||||||
} else if (name === 'x') {
|
} else if (name === 'x') {
|
||||||
// 为了防止逆天样板出问题
|
// 为了防止逆天样板出问题
|
||||||
core.bigmap.posX = value as number;
|
core.bigmap.posX = value as number;
|
||||||
if (!noGather) {
|
if (!noGather) {
|
||||||
state.hero.setPosition(
|
state.hero.mover.setPosition(
|
||||||
value as number,
|
value as number,
|
||||||
core.status.hero.loc.y
|
core.status.hero.loc.y
|
||||||
);
|
);
|
||||||
@ -168,7 +168,7 @@ export function initFallback() {
|
|||||||
// 为了防止逆天样板出问题
|
// 为了防止逆天样板出问题
|
||||||
core.bigmap.posY = value as number;
|
core.bigmap.posY = value as number;
|
||||||
if (!noGather) {
|
if (!noGather) {
|
||||||
state.hero.setPosition(
|
state.hero.mover.setPosition(
|
||||||
core.status.hero.loc.x,
|
core.status.hero.loc.x,
|
||||||
value as number
|
value as number
|
||||||
);
|
);
|
||||||
@ -218,7 +218,7 @@ export function initFallback() {
|
|||||||
|
|
||||||
patch2.add('setHeroIcon', function (name: ImageIds) {
|
patch2.add('setHeroIcon', function (name: ImageIds) {
|
||||||
core.status.hero.image = name;
|
core.status.hero.image = name;
|
||||||
state.hero.setImage(name);
|
state.hero.mover.setImage(name);
|
||||||
});
|
});
|
||||||
|
|
||||||
patch.add('isMoving', function () {
|
patch.add('isMoving', function () {
|
||||||
@ -564,7 +564,7 @@ export function initFallback() {
|
|||||||
time /= core.status.replay.speed;
|
time /= core.status.replay.speed;
|
||||||
if (core.status.replay.speed === 24) time = 1;
|
if (core.status.replay.speed === 24) time = 1;
|
||||||
|
|
||||||
await state.hero.jumpHero(ex, ey, time);
|
await state.hero.mover.jumpHero(ex, ey, time);
|
||||||
|
|
||||||
if (!locked) core.unlockControl();
|
if (!locked) core.unlockControl();
|
||||||
core.setHeroLoc('x', ex);
|
core.setHeroLoc('x', ex);
|
||||||
|
|||||||
@ -165,6 +165,7 @@
|
|||||||
"107": "Hero status is not bound, damage calculation is unavailable.",
|
"107": "Hero status is not bound, damage calculation is unavailable.",
|
||||||
"108": "Hero modifier '$1' has already been added once. Ignore repeated add for attribute '$2'.",
|
"108": "Hero modifier '$1' has already been added once. Ignore repeated add for attribute '$2'.",
|
||||||
"109": "Expected a different object reference returned, but got a same reference at modifier '$1' for property '$2'.",
|
"109": "Expected a different object reference returned, but got a same reference at modifier '$1' for property '$2'.",
|
||||||
|
"110": "Expected a hero attribute binding before executing any enemy context calculation.",
|
||||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency."
|
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,19 +104,20 @@ export class ManhattanRange extends BaseRange<IManhattanRangeParam> {
|
|||||||
protected estimatePointCount(
|
protected estimatePointCount(
|
||||||
param: Readonly<IManhattanRangeParam>
|
param: Readonly<IManhattanRangeParam>
|
||||||
): number {
|
): number {
|
||||||
const radius = Math.max(0, param.radius);
|
const radius = Math.abs(param.radius);
|
||||||
return 1 + 2 * radius * (radius + 1);
|
return 1 + 2 * radius * (radius + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
*iterateLoc(param: Readonly<IManhattanRangeParam>): Iterable<number> {
|
*iterateLoc(param: Readonly<IManhattanRangeParam>): Iterable<number> {
|
||||||
const { width, height } = this.host;
|
const { width, height } = this.host;
|
||||||
for (let dy = -param.radius; dy <= param.radius; dy++) {
|
const radius = Math.abs(param.radius);
|
||||||
|
for (let dy = -radius; dy <= radius; dy++) {
|
||||||
const y = param.cy + dy;
|
const y = param.cy + dy;
|
||||||
if (y < 0 || y >= height) {
|
if (y < 0 || y >= height) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const span = param.radius - Math.abs(dy);
|
const span = radius - Math.abs(dy);
|
||||||
const startX = Math.max(0, param.cx - span);
|
const startX = Math.max(0, param.cx - span);
|
||||||
const endX = Math.min(width - 1, param.cx + span);
|
const endX = Math.min(width - 1, param.cx + span);
|
||||||
for (let x = startX; x <= endX; x++) {
|
for (let x = startX; x <= endX; x++) {
|
||||||
@ -130,10 +131,10 @@ export class ManhattanRange extends BaseRange<IManhattanRangeParam> {
|
|||||||
y: number,
|
y: number,
|
||||||
param: Readonly<IManhattanRangeParam>
|
param: Readonly<IManhattanRangeParam>
|
||||||
): boolean {
|
): boolean {
|
||||||
return (
|
const radius = Math.abs(param.radius);
|
||||||
this.inBound(x, y) &&
|
const dx = Math.abs(x - param.cx);
|
||||||
Math.abs(x - param.cx) + Math.abs(y - param.cy) <= param.radius
|
const dy = Math.abs(y - param.cy);
|
||||||
);
|
return this.inBound(x, y) && dx + dy <= radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +142,7 @@ export class RayRange extends BaseRange<IRayRangeParam> {
|
|||||||
protected estimatePointCount(param: Readonly<IRayRangeParam>): number {
|
protected estimatePointCount(param: Readonly<IRayRangeParam>): number {
|
||||||
const { width, height } = this.host;
|
const { width, height } = this.host;
|
||||||
// 考虑到这种范围的 `inRange` 判断要更加耗时,因此将返回值略微降低,更倾向于使用 `scan` 方式
|
// 考虑到这种范围的 `inRange` 判断要更加耗时,因此将返回值略微降低,更倾向于使用 `scan` 方式
|
||||||
|
// 正常情况下应该 / 2
|
||||||
return ((width + height) * param.dir.length) / 3;
|
return ((width + height) * param.dir.length) / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user