refactor: рабочая база на three.js + меню/правила уровней + доки
Полный рефакторинг проекта в стабильную расширяемую базу рогалика. Почему: после разнесения single-file на модули потерялся вызов генерации карты (RoomMap не генерировал комнаты) → игра падала на старте; баг скрывался тем, что Bun-бандлер не проверяет типы. Архитектура: - Логика игры (src/core/) полностью отделена от рендера: без three.js и DOM, тестируется без браузера. - Рендер мира на three.js с ортокамерой (2D-вид); HUD/миникарта — 2D-канвас поверх. - Фиксированный игровой цикл 60 Гц + интерполяция (раньше скорость зависела от частоты кадров). - Seeded-RNG, ввод через абстрактные «намерения» (InputState). Возможности: - Стартовое меню с выбором уровня (Esc → меню). - Конфигуратор уровней: LevelRules + 5 пресетов (размер карты, плотность/сила врагов, HP, фиксированный seed). - Тема внешнего вида (render/theme.ts) — задел под кастомные ассеты. Качество: - 27 юнит-тестов ядра (генерация, симметрия дверей, коллизии, спавн, правила). - Два круга adversarial-ревью; исправлено 6 реальных багов (кнокбэк сквозь стены → софт-лок; незакрываемая сокровищница; фикс-сид после рестарта; перенос ввода между забегами; нет source maps; неточности в доках). - Документация: README, docs/ARCHITECTURE.md, CLAUDE.md, docs/HOWTO.md. - dist/ исключён из гита; bun.lock зафиксирован. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
import { isBlocked, collidesWall } from '../src/core/systems/collision';
|
||||
import { Room } from '../src/core/world/Room';
|
||||
import { OX, OY, TILE, COLS, ROWS } from '../src/config';
|
||||
|
||||
function cleanRoom(): Room {
|
||||
const room = new Room(0, 0, 'normal');
|
||||
room.doors = { up: true, down: false, left: false, right: false };
|
||||
room.cleared = true;
|
||||
room.rebuildTiles();
|
||||
return room;
|
||||
}
|
||||
|
||||
describe('collision', () => {
|
||||
it('стены по краям блокируют движение', () => {
|
||||
const room = cleanRoom();
|
||||
expect(isBlocked(room, 0, 0)).toBe(true);
|
||||
expect(isBlocked(room, COLS - 1, ROWS - 1)).toBe(true);
|
||||
});
|
||||
|
||||
it('внутренний пол не блокирует', () => {
|
||||
const room = cleanRoom();
|
||||
expect(isBlocked(room, 5, 5)).toBe(false);
|
||||
});
|
||||
|
||||
it('за пределами комнаты — блок, но дверной проём открыт', () => {
|
||||
const room = cleanRoom();
|
||||
// Над комнатой (row < 0) обычно стена...
|
||||
expect(isBlocked(room, 0, -1)).toBe(true);
|
||||
// ...но в колонках двери up проём открыт.
|
||||
expect(isBlocked(room, 7, -1)).toBe(false);
|
||||
});
|
||||
|
||||
it('collidesWall ловит хитбокс на стене и пропускает на полу', () => {
|
||||
const room = cleanRoom();
|
||||
const insideFloor = { x: OX + 5 * TILE, y: OY + 5 * TILE, w: 26, h: 26 };
|
||||
expect(collidesWall(insideFloor, room)).toBe(false);
|
||||
|
||||
const onWall = { x: OX - 5, y: OY - 5, w: 26, h: 26 };
|
||||
expect(collidesWall(onWall, room)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user