Герои мата и меха

браузерный порт старой консольной игрушки

Игра

Где-то в начале 2000-х эта консольная игрушка для MS-DOS была популярна и на нашем факультете, хотя
мы и не матмехе. Кто-то принес на дискете. Игра представляет собой текстовый квест, в котором в котором
предлагается сдать сессию на матмехе СПбГУ.

Не так давно обнаружил ее порт на JS. Порт делался дизассемблированим
и причесыванием кода.

Проект, видимо, заброшен. Я немного поиграл, и нашел некоторые баги и спорные моменты.

Например, автор весьма оригинально сделал подход к асинхронности - каждый раз по нажатию на кнопку, вся логика
игры запускается заново, но без отрисовки на экране. Весь код хранится в html файле, для отрисовки используется
таблица. С последним мириться было нельзя, и я принялся за дело.

Сборка

Прикрутил webpack, сборку, запуск в dev-режиме. Можно будет прикрутить babel, настроить компиляцию
async/await для старых браузеров, но не нужно.

Асинхронность

Вместо сложной логики перезапуска игры по нажатию кнопки, проще писать async/await почти везде. Так что я обернул
большинство функций в асинхронные вызовы.

Сначала стал вешать обработчик события в момент вызова функции, и снимать после того, как кнопка нажата. Но оказалось,
что это чувствуется, и часть нажатий теряется. Так что сделал очередь на нажатые кнопки, а ReadKey проверяет эту очередь,
и, либо сразу резолвит промис, либо сохраняет его, если очередь пуста.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const keyBuffer = [];
const readKeyWaiters = [];

function onKeyDown(event) {
if (event.keyCode === 116 || event.keyCode === 16 || event.keyCode === 17 || event.keyCode === 18 || event.keyCode === 91) {
return;
}
if (readKeyWaiters.length) {
readKeyWaiters.shift()(event.keyCode);
} else {
keyBuffer.push(event.keyCode);
}
}

document.body.addEventListener('keydown', onKeyDown, false);

export function ReadKey() {
return new Promise((resolve, reject) => {
if (keyBuffer.length) {
resolve(keyBuffer.shift());
} else {
readKeyWaiters.push(resolve);
}
});
}

Терминал

Выпилил самописный терминал, и добавил xterm.js

Окрашивание текста проихвожу через вывод ANSI-последовательностей. Буду добиваться возможности запуска из node.js,
а не только из браузера.

Сложности

У xterm.js есть проблема определить текущее положение курсора.

В интернетах советуют использовать term.buffer.active.cursorY, но если перед вызовом мы пытаемся что-то вывести,
то это попадет в буфер, и курсор не сдвинется.

Вызов Delay(0) перед получением свойства сработал, но все это как-то некрасиво

1
2
await Delay(0);
return term.buffer.active.cursorY;