import type { TransitionDef } from './locations';
import type { ItemId } from './items';
/**
* Чистый выбор перехода: по тайлу и триггеру, с условиями и дисармом.
* Вынесен из сцены — тестируется в Vitest без браузера.
*/
/** Что резолверу нужно знать о герое (подставляется из GameState/Inventory). */
export interface TransitionCtx {
hasFlag(flag: string): boolean;
hasItem(id: ItemId): boolean;
}
/** Результат выбора: переход доступен / отказ (заперт) / ничего на этом тайле. */
export type TransitionPick =
| { ok: true; def: TransitionDef }
| { ok: false; lockedText: string }
| null;
/**
* Первый переход на тайле с этим триггером. step-переходы на disarmTile
* игнорируются (герой только что вошёл через него — иначе немедленный вылет).
*/
export function resolveTransition(
defs: TransitionDef[],
tile: { x: number; y: number },
trigger: 'step' | 'click',
ctx: TransitionCtx,
disarmTile?: { x: number; y: number } | null
): TransitionPick {
const def = defs.find(
(t) =>
t.tile.x === tile.x &&
t.tile.y === tile.y &&
(t.trigger ?? 'step') === trigger &&
!(trigger === 'step' && disarmTile && t.tile.x === disarmTile.x && t.tile.y === disarmTile.y)
);
if (!def) return null;
if (def.requiresFlag && !ctx.hasFlag(def.requiresFlag)) {
return { ok: false, lockedText: def.lockedText ?? 'Не пускает.' };
}
if (def.requiresItem && !ctx.hasItem(def.requiresItem)) {
return { ok: false, lockedText: def.lockedText ?? 'Нужен предмет.' };
}
return { ok: true, def };
}