Newer
Older
rpg / apps / game / src / systems / combat / __tests__ / los.test.ts
import { describe, expect, it } from 'vitest';
import { hasLineOfSight, tileOpaque } from '../los';
import { TILES, buildMeadowsMap } from '../../../data/map';

/** Карта 10x10 с одной стеной в центре. */
function mapWithWall(): { width: number; height: number; tiles: number[]; tall: Record<number, unknown> } {
    const tiles = new Array(100).fill(TILES.GRASS);
    tiles[5 * 10 + 5] = TILES.HOUSE; // tall-объект
    return { width: 10, height: 10, tiles, tall: { [TILES.HOUSE]: { height: 1, ground: 0 } } };
}

describe('hasLineOfSight', () => {
    const opaque = tileOpaque({ data: mapWithWall() });

    it('свободная линия видна', () => {
        expect(hasLineOfSight({ x: 0.5, y: 0.5 }, { x: 4.5, y: 0.5 }, opaque)).toBe(true);
    });

    it('стена в середине перекрывает обзор', () => {
        expect(hasLineOfSight({ x: 0.5, y: 5.5 }, { x: 9.5, y: 5.5 }, opaque)).toBe(false);
    });

    it('линия мимо стены видит', () => {
        expect(hasLineOfSight({ x: 0.5, y: 0.5 }, { x: 9.5, y: 4.5 }, opaque)).toBe(true);
    });

    it('короткие отрезки и нулевая длина видимы', () => {
        expect(hasLineOfSight({ x: 3, y: 3 }, { x: 3.2, y: 3 }, opaque)).toBe(true);
        expect(hasLineOfSight({ x: 3, y: 3 }, { x: 3, y: 3 }, opaque)).toBe(true);
    });

    it('вне карты — непрозрачно', () => {
        expect(hasLineOfSight({ x: 9.5, y: 5.5 }, { x: 15.5, y: 5.5 }, opaque)).toBe(false);
    });
});

describe('tileOpaque на реальной карте', () => {
    it('дерево/дом непрозрачны, трава/тропа/вода прозрачны', () => {
        const map = { data: buildMeadowsMap() };
        const is = tileOpaque(map);
        expect(is(2, 12)).toBe(true); // дерево
        expect(is(1, 7)).toBe(true); // дерево
        expect(is(14, 14)).toBe(false); // трава у спавна
        expect(is(19, 7)).toBe(false); // вода прозрачна
    });
});