feat: 触发器收集

This commit is contained in:
unanmed 2026-05-17 22:56:01 +08:00
parent 7428f41c93
commit 03dc5ea60b
6 changed files with 176 additions and 42 deletions

View File

@ -33,7 +33,11 @@ import {
DamageSystem,
EnemyContext,
IEnemyContext,
MapDamage
ITriggerCollector,
ITriggerRegistry,
MapDamage,
TriggerCollector,
TriggerRegistry
} from '@user/data-system';
import {
CommonAuraConverter,
@ -78,6 +82,8 @@ export class CoreState implements ICoreState {
// Layer 2 内容
readonly enemyContext: IEnemyContext<IEnemyAttr, IHeroAttr>;
readonly triggerRegistry: ITriggerRegistry;
readonly triggerCollector: ITriggerCollector;
// 用户层内容
readonly loadProgress: ILoadProgressTotal;
@ -95,26 +101,43 @@ export class CoreState implements ICoreState {
> = new Map();
constructor() {
//#region L0 初始化
// 朝向
this.roleFace = new RoleFaceBinder();
this.faceManager = new FaceManager();
const dir4 = new Dir4FaceHandler();
const dir8 = new Dir8FaceHandler();
this.faceManager.register(FaceGroup.Dir4, dir4);
this.faceManager.registerById('dir4', dir4);
this.faceManager.register(FaceGroup.Dir8, dir8);
this.faceManager.registerById('dir8', dir8);
// 图块
const tileStore = new TileStore<LegacyTileData>();
tileStore.attachLegacyConverter(new TileLegacyBridge());
this.tileStore = tileStore;
//#endregion
//#region L1 初始化
// Flag 系统
this.flags = new FlagSystem();
// 地图
this.maps = new MapStore(tileStore, this);
this.loadProgress = new LoadProgressTotal();
this.dataLoader = new MotaDataLoader(this.loadProgress);
//#region 勇士初始化
// 勇士
const heroMover = new HeroMover();
const heroAttribute = new HeroAttribute(HERO_DEFAULT_ATTRIBUTE);
const heroState = new HeroState(heroMover, heroAttribute);
this.hero = heroState;
//#endregion
this.loadProgress = new LoadProgressTotal();
this.dataLoader = new MotaDataLoader(this.loadProgress);
//#region 怪物初始化
// 怪物管理器初始化
// 怪物管理器
const comparer = new MainEnemyComparer();
const enemyManager = new EnemyManager(new EnemyLegacyBridge());
enemyManager.attachEnemyComparer(comparer);
@ -126,7 +149,12 @@ export class CoreState implements ICoreState {
enemyManager.setAttributeDefaults('point', 0);
registerSpecials(enemyManager);
this.enemyManager = enemyManager;
// 怪物上下文初始化
//#endregion
//#region L2 初始化
// 怪物上下文
const enemyContext = new EnemyContext<IEnemyAttr, IHeroAttr>(this);
const damageSystem = new DamageSystem(enemyContext);
const mapDamage = new MapDamage(enemyContext);
@ -142,10 +170,18 @@ export class CoreState implements ICoreState {
enemyContext.bindHero(heroAttribute);
this.enemyContext = enemyContext;
// 触发器注册与收集器
const triggerRegistry = new TriggerRegistry();
const triggerCollector = new TriggerCollector();
triggerCollector.attachRegistry(triggerRegistry);
this.triggerRegistry = triggerRegistry;
this.triggerCollector = triggerCollector;
//#endregion
//#region 存档系统
//#region 顶层初始化
// 存档系统
this.saveSystem = new SaveSystem();
// 配置存档系统,一般情况下不建议动,除非你知道你在干什么
this.saveSystem.config({
@ -155,29 +191,16 @@ export class CoreState implements ICoreState {
saveTimeTolerance: 100,
autosaveStackSize: 20
});
this.addSaveableContent('@system/hero', this.hero);
this.addSaveableContent('@system/flags', this.flags);
this.addSaveableContent('@system/maps', this.maps);
this.addSaveableContent('@system/enemy', this.enemyManager);
// 初始化存档数据库,不要动
loading.once('coreInit', () => {
this.saveSystem.init(`@game/${core.firstData.name}`);
});
//#endregion
//#region 其他初始化
// 朝向
this.roleFace = new RoleFaceBinder();
this.faceManager = new FaceManager();
const dir4 = new Dir4FaceHandler();
const dir8 = new Dir8FaceHandler();
this.faceManager.register(FaceGroup.Dir4, dir4);
this.faceManager.registerById('dir4', dir4);
this.faceManager.register(FaceGroup.Dir8, dir8);
this.faceManager.registerById('dir8', dir8);
this.flags = new FlagSystem();
// 加载先使用兼容层实现
// 加载初始化,先使用兼容层实现
loading.once('loaded', () => {
this.initTileStore(core.maps.blocksInfo);
this.initEnemyManager(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80);
@ -186,13 +209,6 @@ export class CoreState implements ICoreState {
core.floors as Record<FloorIds, ResolvedFloor>
);
});
this.addSaveableContent('@system/hero', this.hero);
this.addSaveableContent('@system/flags', this.flags);
this.addSaveableContent('@system/maps', this.maps);
this.addSaveableContent('@system/enemy', this.enemyManager);
//#endregion
}
/**

View File

@ -5,7 +5,7 @@ export class TriggerCollection implements ITriggerCollection {
private readonly triggerList: ITrigger[];
constructor(triggers: Iterable<ITrigger>) {
this.triggerList = Array.from(triggers);
this.triggerList = [...triggers];
}
count(): number {

View File

@ -0,0 +1,112 @@
import { IMapLayer } from '@user/data-base';
import {
ITrigger,
ITriggerCollection,
ITriggerCollector,
ITriggerRegistry
} from './types';
import { logger } from '@motajs/common';
import { TriggerCollection } from './collection';
export class TriggerCollector implements ITriggerCollector {
/** 当前收集器使用的注册对象 */
private registry: ITriggerRegistry | null = null;
collect(x: number, y: number, layer: IMapLayer): ITriggerCollection {
if (!this.registry) {
logger.warn(135);
return new TriggerCollection([]);
}
const staticType = layer.getTriggerType(x, y);
const staticTrigger = this.registry.create(staticType);
const dynamics = [...layer.dynamicLayer.getDynamicTilesAt(x, y)];
if (dynamics.length === 0) {
// 没有动态图块
if (staticTrigger) {
return new TriggerCollection([staticTrigger]);
} else {
return new TriggerCollection([]);
}
} else if (dynamics.length === 1) {
// 一个动态图块,只需要进行一次额外判断即可
const dynamic = dynamics[0];
const dynamicTrigger = this.registry.create(dynamic.triggerType);
// 直接穷举所有可能情况
if (!staticTrigger && !dynamicTrigger) {
return new TriggerCollection([]);
} else if (staticTrigger && !dynamicTrigger) {
return new TriggerCollection([staticTrigger]);
} else if (!staticTrigger && dynamicTrigger) {
return new TriggerCollection([dynamicTrigger]);
} else {
// 静态动态都有,则需要额外判断优先级,动态图层在前,因此包含等号
if (dynamicTrigger!.priority >= staticTrigger!.priority) {
const arr = [dynamicTrigger!, staticTrigger!];
return new TriggerCollection(arr);
} else {
const arr = [staticTrigger!, dynamicTrigger!];
return new TriggerCollection(arr);
}
}
} else {
// 动态图块大于两个,使用通用方案,记录重复触发器并抛出警告
const usedPriority = new Set<number>();
const duplicate = new Set<number>();
if (staticTrigger) {
// 有静态触发器
const lessTriggers: ITrigger[] = [];
const greaterTriggers: ITrigger[] = [];
// 先收集所有的触发器,并记录重复情况
for (const tile of layer.dynamicLayer.getDynamicTilesAt(x, y)) {
const trigger = this.registry.create(tile.triggerType);
if (trigger) {
if (usedPriority.has(trigger.priority)) {
duplicate.add(trigger.priority);
}
usedPriority.add(trigger.priority);
// 同优先级下动态在前,因此包含等号
if (trigger.priority >= staticTrigger.priority) {
greaterTriggers.push(trigger);
} else {
lessTriggers.push(trigger);
}
}
}
if (duplicate.size > 0) {
logger.warn(136, [...duplicate].join(','));
}
const arr = [
...greaterTriggers.sort((a, b) => b.priority - a.priority),
staticTrigger,
...lessTriggers.sort((a, b) => b.priority - a.priority)
];
return new TriggerCollection(arr);
} else {
// 没有静态触发器
const triggers: ITrigger[] = [];
for (const tile of layer.dynamicLayer.getDynamicTilesAt(x, y)) {
const trigger = this.registry.create(tile.triggerType);
if (trigger) {
if (usedPriority.has(trigger.priority)) {
duplicate.add(trigger.priority);
}
usedPriority.add(trigger.priority);
triggers.push(trigger);
}
}
if (duplicate.size > 0) {
logger.warn(136, [...duplicate].join(','));
}
return new TriggerCollection(
triggers.sort((a, b) => b.priority - a.priority)
);
}
}
}
attachRegistry(registry: ITriggerRegistry | null): void {
this.registry = registry;
}
}

View File

@ -1,3 +1,4 @@
export * from './types';
export * from './collection';
export * from './collector';
export * from './registry';
export * from './types';

View File

@ -1,9 +1,14 @@
import { IStateBase } from '@user/data-base';
import { IEnemyContext } from './combat';
import { ITriggerCollector, ITriggerRegistry } from './trigger';
export interface IStateSystem<TEnemy, THero> extends IStateBase<TEnemy, THero> {
/** 怪物上下文 */
readonly enemyContext: IEnemyContext<TEnemy, THero>;
/** 触发器注册 */
readonly triggerRegistry: ITriggerRegistry;
/** 触发器收集器 */
readonly triggerCollector: ITriggerCollector;
}
export interface IStateSystemExtended<TEnemy = unknown, THero = unknown> {

View File

@ -55,8 +55,7 @@
"53": "Expected serializable value set as enemy's default attribute.",
"54": "Legacy '$1' API has been removed, consider using new APIs: '$2'.",
"55": "Cannot load MapStore state: reference data (compareWith) has not been set.",
"56": "Cannot convert legacy tile data since no tile legacy converter is attached to TileStore.",
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency."
"56": "Cannot convert legacy tile data since no tile legacy converter is attached to TileStore."
},
"warn": {
"1": "Resource with type of 'none' is loaded.",
@ -193,6 +192,7 @@
"132": "Trigger registry entry of $1 '$2' already exists, new factory will override old factory.",
"133": "TileStore.addTile: tile number $1 already exists (id: '$2'), old tile data will be overridden.",
"134": "TileStore.addTile: tile id '$1' already maps to number $2, old tile data will be overridden.",
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency."
"135": "Expected a trigger registry attched before collect triggers.",
"136": "Unexpected duplicate trigger priority $1, which may cause trigger executed in an unexpected order."
}
}