import { describe, expect, it } from 'vitest';
import type { Vec2 } from '@rpg/engine';
import { resolveClick, type ClickProbe } from '../ClickRouting';
import type { NpcDef } from '../../data/npcs';
import type { InteractableDef } from '../../data/interactables';
import type { TransitionDef } from '../../data/locations';
const npc: NpcDef = {
id: 'elder',
name: 'Ирвин',
sprite: 'elder',
pos: { x: 5.5, y: 5.5 },
tile: { x: 5, y: 5 },
flagKey: 'met_elder',
dialogueFirst: 'd1',
dialogueRepeat: 'd2'
};
const chest: InteractableDef = {
id: 'chest',
kind: 'container',
pos: { x: 6.5, y: 5.5 },
tile: { x: 6, y: 5 },
responses: []
};
const well: TransitionDef = {
tile: { x: 7, y: 5 },
label: 'колодец',
target: { kind: 'area', area: 'meadows', entry: { x: 1, y: 1 } }
};
/** Проба клика: тайл и мир задаются тестом, остальное — заглушки. */
function probe(opts: {
clicked?: { x: number; y: number } | null;
world?: Vec2;
npcAt?: ClickProbe['npcAt'];
transitionPick?: ClickProbe['transitionPick'];
interactableAt?: ClickProbe['interactableAt'];
flowerAt?: ClickProbe['flowerAt'];
enemyAt?: ClickProbe['enemyAt'];
}): ClickProbe {
return {
world: opts.world ?? { x: 0, y: 0 },
clicked: opts.clicked ?? null,
npcAt: opts.npcAt ?? (() => null),
transitionPick: opts.transitionPick ?? null,
interactableAt: opts.interactableAt ?? (() => null),
flowerAt: opts.flowerAt ?? (() => false),
enemyAt: opts.enemyAt ?? (() => null)
};
}
describe('resolveClick — приоритеты клика по миру', () => {
it('NPC на тайле — talk (выше перехода и интерактива)', () => {
const a = resolveClick(
probe({
clicked: { x: 5, y: 5 },
npcAt: () => npc,
transitionPick: { ok: true, def: well },
interactableAt: () => chest
})
);
expect(a).toEqual({ kind: 'talk', def: npc });
});
it('клик-переход: открытый — transition', () => {
const a = resolveClick(probe({ clicked: { x: 7, y: 5 }, transitionPick: { ok: true, def: well } }));
expect(a).toEqual({ kind: 'transition', def: well });
});
it('клик-переход: запертый — locked с текстом (не движение)', () => {
const a = resolveClick(
probe({
clicked: { x: 7, y: 5 },
transitionPick: { ok: false, lockedText: 'Заперто.' }
})
);
expect(a).toEqual({ kind: 'locked', text: 'Заперто.' });
});
it('интерактивный объект на тайле — interact', () => {
const a = resolveClick(probe({ clicked: { x: 6, y: 5 }, interactableAt: () => chest }));
expect(a).toEqual({ kind: 'interact', def: chest });
});
it('сборный тайл (цветок) — flower', () => {
const a = resolveClick(
probe({ clicked: { x: 3, y: 3 }, flowerAt: (x, y) => x === 3 && y === 3 })
);
expect(a).toEqual({ kind: 'flower', x: 3, y: 3 });
});
it('враг под точкой мира — enemy (тайл может быть за картой)', () => {
const e = { id: 1 } as never;
const a = resolveClick(
probe({ clicked: null, world: { x: 10, y: 20 }, enemyAt: (w) => (w.x === 10 ? e : null) })
);
expect(a).toEqual({ kind: 'enemy', entity: e });
});
it('клик за картой без врага — move', () => {
expect(resolveClick(probe({ clicked: null }))).toEqual({ kind: 'move' });
expect(resolveClick(probe({ clicked: { x: 9, y: 9 } }))).toEqual({ kind: 'move' });
});
});