refactor(03-18): own event registrations as explicit classes

- Replace generic per-invocation registration factories with explicit
  SetBlock/MoveBlock/DeleteBlock, MoveHero/MoveHeroStep/TouchFront, and
  InsertEvents/InsertEvent registration classes
- Move eventTouchFront, invocation collection, and target resolution into
  the hero event layer while keeping awaited OnTouch behavior
- Keep map and control modules limited to their own registration ownership
This commit is contained in:
unanmed 2026-09-11 22:59:16 +08:00
parent febdcdc967
commit cb716f699e
3 changed files with 210 additions and 202 deletions

View File

@ -4,45 +4,16 @@ import {
IBlockEventParam,
IGameEventExecutor,
IGameEventInvocation,
IGameEventSystem,
BlockEventType
IGameEventSystem
} from '@user/data-system';
import {
FaceDirection,
EventTrigger,
IGameEventStore
} from '@user/data-common';
import {
IStateBase,
IReadonlyTileBase as IReadonlyMapTileBase
} from '@user/data-base';
import { getPossibleLayer } from './map';
import { IGameEventStore } from '@user/data-common';
import { IStateBase } from '@user/data-base';
import {
EventBuiltinName,
IInsertEventEventParam,
IInsertEventsEventParam,
ITouchFrontEventParam
IInsertEventsEventParam
} from './types';
type ControlEventBuiltinHandler<TParam> = (
param: TParam,
env: IBlockEventEnv
) => void | Promise<void>;
function createControlEventBuiltin<TParam>(
name: EventBuiltinName,
handler: ControlEventBuiltinHandler<TParam>
): BuiltInFunction {
return { name, func: handler as BuiltInFunction['func'] };
}
interface IEventSource {
readonly priority: number;
readonly id: string;
readonly type: BlockEventType;
readonly tile: IReadonlyMapTileBase | null;
}
interface IEventState extends IStateBase {
readonly eventSystem: IGameEventSystem;
}
@ -56,7 +27,7 @@ function hasEventSystem(state: IStateBase): state is IEventState {
}
/** 从环境获取事件执行器和事件存储器 */
function getEventRuntime(env: IBlockEventEnv): {
export function getEventRuntime(env: IBlockEventEnv): {
readonly executor: IGameEventExecutor;
readonly store: IGameEventStore;
} | null {
@ -69,103 +40,6 @@ function getEventRuntime(env: IBlockEventEnv): {
};
}
/** 按坐标收集事件来源并保留其触发环境 */
function collectInvocations(
env: IBlockEventEnv,
layer: NonNullable<IBlockEventEnv['layer']>,
x: number,
y: number,
trigger: EventTrigger
): IGameEventInvocation[] {
const pointSources: IEventSource[] = [];
const tileSources: IEventSource[] = [];
const point = layer.getPointEvent(x, y);
const location = layer.getLocationData(x, y);
if (point) {
for (const [priority, id] of point) {
pointSources.push({
priority,
id,
type: BlockEventType.PointEvent,
tile: null
});
}
}
if (location) {
if (location.static) {
for (const [priority, id] of location.static.tileEvent().get()) {
tileSources.push({
priority,
id,
type: BlockEventType.TileEvent,
tile: location.static
});
}
}
for (const tile of location.dynamics) {
for (const [priority, id] of tile.tileEvent().get()) {
tileSources.push({
priority,
id,
type: BlockEventType.TileEvent,
tile
});
}
}
}
pointSources.sort((a, b) => b.priority - a.priority);
tileSources.sort((a, b) => b.priority - a.priority);
const hero = env.state.hero.getLocation();
const invocations: IGameEventInvocation[] = [];
for (const source of [...pointSources, ...tileSources]) {
const sourceEnv: IBlockEventEnv = {
state: env.state,
type: source.type,
trigger,
heroLocator: hero,
heroFloor: env.heroFloor,
triggerLocator: { x, y },
tile: source.tile,
layer,
map: layer.map
};
invocations.push({ id: source.id, env: sourceEnv });
}
return invocations;
}
/** 触发勇士正面的 onTouch 事件 */
export async function eventTouchFront(
_param: ITouchFrontEventParam,
env: IBlockEventEnv
): Promise<void> {
if (!env.state.hero) return;
const layer = getPossibleLayer(env);
if (!layer) return;
const runtime = getEventRuntime(env);
if (!runtime) return;
const hero = env.state.hero.getLocation();
const mover = env.state.hero.location.mover;
const direction = mover.tile.getCurrentFaceDirection();
if (direction === FaceDirection.Unknown) return;
const movement = mover.faceHandler.movement(direction);
const x = hero.x + movement.x;
const y = hero.y + movement.y;
if (!layer.inMap(x, y)) return;
const invocations = collectInvocations(
env,
layer,
x,
y,
EventTrigger.OnTouch
);
if (invocations.length === 0) return;
await runtime.executor.execute<void>(invocations, { custom: {} });
}
/** 过滤存在的事件 id 并构造临时事件调用 */
function collectEventInvocations(
ids: readonly string[],
@ -205,21 +79,6 @@ export async function eventInsertEvents(
}
}
/** 创建事件控制事件的内建函数注册项 */
export function createControlEventBuiltinRegistrations(): ReadonlyArray<BuiltInFunction> {
return [
createControlEventBuiltin(EventBuiltinName.TouchFront, eventTouchFront),
createControlEventBuiltin(
EventBuiltinName.InsertEvents,
eventInsertEvents
),
createControlEventBuiltin(
EventBuiltinName.InsertEvent,
eventInsertEvent
)
];
}
/** 临时直接执行一段事件语句 */
export async function eventInsertEvent(
param: IInsertEventEventParam,
@ -239,3 +98,24 @@ export async function eventInsertEvent(
eventInsertDepth.delete(env);
}
}
export class InsertEventsEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.InsertEvents =
EventBuiltinName.InsertEvents;
readonly func: BuiltInFunction['func'] =
eventInsertEvents as BuiltInFunction['func'];
}
export class InsertEventEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.InsertEvent = EventBuiltinName.InsertEvent;
readonly func: BuiltInFunction['func'] =
eventInsertEvent as BuiltInFunction['func'];
}
/** 创建事件控制事件的内建函数注册项 */
export function createControlEventBuiltinRegistrations(): ReadonlyArray<BuiltInFunction> {
return [
new InsertEventsEventRegistration(),
new InsertEventEventRegistration()
];
}

View File

@ -1,7 +1,19 @@
import { BuiltInFunction } from 'anon-tokyo';
import { IBlockEventEnv } from '@user/data-system';
import { IHeroLocation, IHeroMover } from '@user/data-base';
import {
BlockEventType,
IBlockEventEnv,
IGameEventInvocation
} from '@user/data-system';
import {
IGameMap,
IHeroLocation,
IHeroMover,
IMapLayer,
IReadonlyTileBase as IReadonlyMapTileBase
} from '@user/data-base';
import {
EventTrigger,
FaceDirection,
IMoverController,
IObjectMovable,
IObjectMover,
@ -12,19 +24,30 @@ import {
import {
EventBuiltinName,
IMoveHeroEventParam,
IMoveHeroStepEventParam
IMoveHeroStepEventParam,
ITouchFrontEventParam
} from './types';
import { getEventRuntime } from './event';
type HeroEventBuiltinHandler<TParam> = (
param: TParam,
env: IBlockEventEnv
) => void | Promise<void>;
/** 通过环境参量获取可能的地图对象 */
export function getPossibleMap(env: IBlockEventEnv): IGameMap | null {
if (env.map) return env.map;
if (env.layer) return env.layer.map;
function createHeroEventBuiltin<TParam>(
name: EventBuiltinName,
handler: HeroEventBuiltinHandler<TParam>
): BuiltInFunction {
return { name, func: handler as BuiltInFunction['func'] };
const map = env.state.maps.getMap(env.heroFloor);
if (map) return map;
return null;
}
/** 通过环境参量获取可能的事件图层 */
export function getPossibleLayer(env: IBlockEventEnv): IMapLayer | null {
if (env.layer) return env.layer;
const map = getPossibleMap(env);
if (map?.eventLayer) return map.eventLayer;
return null;
}
/** 启动勇士移动并等待其完整结束 */
@ -109,10 +132,134 @@ export async function eventMoveHeroStep(
await controller.onEnd;
}
interface IEventSource {
readonly priority: number;
readonly id: string;
readonly type: BlockEventType;
readonly tile: IReadonlyMapTileBase | null;
}
/** 按坐标收集事件来源并保留其触发环境 */
function collectInvocations(
env: IBlockEventEnv,
layer: NonNullable<IBlockEventEnv['layer']>,
x: number,
y: number,
trigger: EventTrigger
): IGameEventInvocation[] {
const pointSources: IEventSource[] = [];
const tileSources: IEventSource[] = [];
const point = layer.getPointEvent(x, y);
const location = layer.getLocationData(x, y);
if (point) {
for (const [priority, id] of point) {
pointSources.push({
priority,
id,
type: BlockEventType.PointEvent,
tile: null
});
}
}
if (location) {
if (location.static) {
for (const [priority, id] of location.static.tileEvent().get()) {
tileSources.push({
priority,
id,
type: BlockEventType.TileEvent,
tile: location.static
});
}
}
for (const tile of location.dynamics) {
for (const [priority, id] of tile.tileEvent().get()) {
tileSources.push({
priority,
id,
type: BlockEventType.TileEvent,
tile
});
}
}
}
pointSources.sort((a, b) => b.priority - a.priority);
tileSources.sort((a, b) => b.priority - a.priority);
const hero = env.state.hero.getLocation();
const invocations: IGameEventInvocation[] = [];
for (const source of [...pointSources, ...tileSources]) {
const sourceEnv: IBlockEventEnv = {
state: env.state,
type: source.type,
trigger,
heroLocator: hero,
heroFloor: env.heroFloor,
triggerLocator: { x, y },
tile: source.tile,
layer,
map: layer.map
};
invocations.push({ id: source.id, env: sourceEnv });
}
return invocations;
}
/** 触发勇士正面的 onTouch 事件 */
export async function eventTouchFront(
_param: ITouchFrontEventParam,
env: IBlockEventEnv
): Promise<void> {
if (!env.state.hero) return;
const layer = getPossibleLayer(env);
if (!layer) return;
const runtime = getEventRuntime(env);
if (!runtime) return;
const hero = env.state.hero.getLocation();
const mover = env.state.hero.location.mover;
const direction = mover.tile.getCurrentFaceDirection();
if (direction === FaceDirection.Unknown) return;
const movement = mover.faceHandler.movement(direction);
const x = hero.x + movement.x;
const y = hero.y + movement.y;
if (!layer.inMap(x, y)) return;
const invocations = collectInvocations(
env,
layer,
x,
y,
EventTrigger.OnTouch
);
if (invocations.length === 0) return;
await runtime.executor.execute<void>(invocations, { custom: {} });
}
export class MoveHeroEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.MoveHero = EventBuiltinName.MoveHero;
readonly func: BuiltInFunction['func'] =
eventMoveHero as BuiltInFunction['func'];
}
export class MoveHeroStepEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.MoveHeroStep =
EventBuiltinName.MoveHeroStep;
readonly func: BuiltInFunction['func'] =
eventMoveHeroStep as BuiltInFunction['func'];
}
export class TouchFrontEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.TouchFront = EventBuiltinName.TouchFront;
readonly func: BuiltInFunction['func'] =
eventTouchFront as BuiltInFunction['func'];
}
/** 创建勇士控制事件的内建函数注册项 */
export function createHeroEventBuiltinRegistrations(): ReadonlyArray<BuiltInFunction> {
return [
createHeroEventBuiltin(EventBuiltinName.MoveHero, eventMoveHero),
createHeroEventBuiltin(EventBuiltinName.MoveHeroStep, eventMoveHeroStep)
new MoveHeroEventRegistration(),
new MoveHeroStepEventRegistration(),
new TouchFrontEventRegistration()
];
}

View File

@ -1,51 +1,14 @@
import { BuiltInFunction } from 'anon-tokyo';
import { IBlockEventEnv } from '@user/data-system';
import {
EventBuiltinName,
IDeleteBlockEventParam,
IMoveBlockEventParam,
ISetBlockEventParam
} from './types';
import { isNil } from 'lodash-es';
import { logger } from '@motajs/common';
import { IGameMap, IMapLayer } from '@user/data-base';
import { appendMoveSteps } from './hero';
import { EventBuiltinName } from './types';
type MapEventBuiltinHandler<TParam> = (
param: TParam,
env: IBlockEventEnv
) => void | Promise<void>;
function createMapEventBuiltin<TParam>(
name: EventBuiltinName,
handler: MapEventBuiltinHandler<TParam>
): BuiltInFunction {
return { name, func: handler as BuiltInFunction['func'] };
}
/**
*
* @param env
*/
export function getPossibleMap(env: IBlockEventEnv): IGameMap | null {
if (env.map) return env.map;
if (env.layer) return env.layer.map;
const map = env.state.maps.getMap(env.heroFloor);
if (map) return map;
return null;
}
/** 通过环境参量获取可能的事件图层 */
export function getPossibleLayer(env: IBlockEventEnv): IMapLayer | null {
if (env.layer) return env.layer;
const map = getPossibleMap(env);
if (map?.eventLayer) return map.eventLayer;
return null;
}
import { appendMoveSteps, getPossibleLayer } from './hero';
export function eventSetBlock(
param: ISetBlockEventParam,
@ -105,11 +68,29 @@ export async function eventDeleteBlock(
}
}
export class SetBlockEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.SetBlock = EventBuiltinName.SetBlock;
readonly func: BuiltInFunction['func'] =
eventSetBlock as BuiltInFunction['func'];
}
export class MoveBlockEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.MoveBlock = EventBuiltinName.MoveBlock;
readonly func: BuiltInFunction['func'] =
eventMoveBlock as BuiltInFunction['func'];
}
export class DeleteBlockEventRegistration implements BuiltInFunction {
readonly name: EventBuiltinName.DeleteBlock = EventBuiltinName.DeleteBlock;
readonly func: BuiltInFunction['func'] =
eventDeleteBlock as BuiltInFunction['func'];
}
/** 创建地图控制事件的内建函数注册项 */
export function createMapEventBuiltinRegistrations(): ReadonlyArray<BuiltInFunction> {
return [
createMapEventBuiltin(EventBuiltinName.SetBlock, eventSetBlock),
createMapEventBuiltin(EventBuiltinName.MoveBlock, eventMoveBlock),
createMapEventBuiltin(EventBuiltinName.DeleteBlock, eventDeleteBlock)
new SetBlockEventRegistration(),
new MoveBlockEventRegistration(),
new DeleteBlockEventRegistration()
];
}