Недавно я вспомнил о игре Star Wars Jedi Knight: Jedi Academy, на которую подростком потратил больше десяти тысяч часов. Изучая материалы о ней, я наткнулся на любопытную историю.

Оказывается в 2013 году, сразу после покупки Lucasfilm компанией Disney и закрытия LucasArts, разработчики Raven Software запаниковали, что их работа будет навсегда положена под сукно, поэтому они в спешке выложили весь исходный код Jedi Outcast и Jedi Academy онлайн.

Этот дамп был таким внезапным, что они даже не подчистили внутренние комментарии разработчиков. Весь код превратился в капсулу времени, прочитав которую, можно узнать о том, как вымотанные программисты сходят с ума, пытаясь реализовать физику светового меча на движке Quake 3.

Заглянув в основной файл боёв (bg_saber.c), можно увидеть, что вся система схваток на световых мечах представляет собой огромную спагетти-конструкцию switch из пяти тысяч строк.

Я прошерстил кодовую базу и нашёл потрясающие комментарии:

1. sv_savegame.cpp — разработчику пришлось написать имитацию цикла загрузки, чтобы игрок успевал прочитать всплывающее сообщение «Saving».

// Если мне ещё раз придётся писать такую белиберду, я просто брошусь под автобус. 
int startOfFunction = Sys_Milliseconds(); 
// ...спустя несколько десятков строк... 
// Первым дело отложенный скрипт закрывает всплывающее сообщение "Saving", 
// но нам нужно, чтобы оно отображалось хотя бы секунду, поэтому сидим здесь 
// в ждущем цикле. См. примечание об автобусе в начале функции.

2. AI_Jedi.cpp — пытаться заставить ботов пользоваться Силой и перемещаться по 3D-картам на CPU 2003 года явно было не самым лучшим решением.

{ //нахрен, просто будем прыгать
{ //нахрен, просто шарашим Силой

3. Отрезание конечностей (G2_bones.cpp) — для того, чтобы можно было отрезать конечности, в Jedi Academy использовалась собственная система скелетной анимации. Необходимость ручного переопределения углов шарниров привела к этому примечанию:

// зачем это нужно делать, одному дьяволу известно. но это нужно делать.

4. bg_pmove.cpp — кому-то пришлось слишком долго отучать модели персонажей соскальзывать по плоским поверхностям.

{ //мы на земле, не движемся и находимся на ровной поверхности, не надо добавлять сраную гравитацию с помощью clipvelocity!!!

5. NPC_reactions.cpp — реальная логика кода, когда игрок стоит, направив прицел прямо в лицо дружественного NPC.

//спрашиваем, какого хера он делает

6. Математика Quake (q_math.cpp) — этот знаменитый трюк с битовым хакингом позаимствован прямиком из движка Quake 3 Джона Кармака. Даже разработчики из Raven, читавшие его годы спустя, не смогли разобраться, как он работает.

i = 0x5f3759df - ( i >> 1 ); // это что за нахрен?

7. mhead.c — когда движок находит повреждённые заголовки данных файлов MP3 или WAV.

return 0; // чёрт знает, что это такое, но точно не наше...

8. Ошибка с фатальным вылетом (win_glimp.cpp) — что происходит, если графический рендерер полностью отказывается загружаться в Windows.

// сообщение об ошибке, к которой я сделаю откат, если навернётся что-то серьёзное

9. Ярость, направленная на Win32 (инструмент ModView) — те, кому доводилось бороться с Win32 API, оценят имя этой функции, которая заставляла UI обновлять панель заголовка документа.

void FuckingWellSetTheDocumentNameAndDontBloodyIgnoreMeYouCunt(LPCSTR psDocName) {
    if (gpLastOpenedModViewDoc) {
        // чтоб, сука, он точно делал то, что нужно...
        gpLastOpenedModViewDoc->SetTitle(psDocName);
    }
}

10. wp_saber.cpp — когда другой разработчик ломает главный файл заголовка, и тебе приходится вручную объявлять свои переменные через extern.

// Нужно вручную написать для них extern. Нельзя сделать #include qcommon.h из-за какого-то дятла

11. Ограничения скелетных мешей (g_client.cpp) — попытка изогнуть позвоночник персонажа игрока на основании отклонения мыши, не поломав при этом хитбокс.

//ФУХ-Х-Х... ломаем его полностью
// ...18 строк спустя... 
//ФУХ-Х-Х... позвоночник мотыляется, к хренам всё это

12. Исправление мультиплеер-коллизий в однопользовательской игре game (g_active.cpp) — самый быстрый способ устранить баг физики, при котором NPC убивались, залезая друг в друга моделями.

Так как движок Quake 3 создавался для мультиплеера, все части игры — это «клиенты»; Raven переделали движок под однопользовательскую компанию, поэтому если NPC сталкиваются друг с другом на слишком большой скорости, то они просто умирают. Вместо того, чтобы тратить недели на изменение алгоритмов поиска пути искусственного интеллекта, разработчики одним оператором if просто избавились от урона при коллизиях между NPC.

{//ой, в жопу всё это, клиенты больше не наносят друг другу урон, только если это не player

13. Логика состояния транспорта (g_vehicles.c) — обработка логики при столкновении летающих спидеров со стеной.

{//просто сваливаем из него

14. Урон от взрыва (g_mover.c) — вычисление урона игрокам, стоящим рядом с разрушаемым объектом карты.

{//просто разносим их к чертям

15. нахрен високосные года

16. Отказываемся от линейной алгебры (r_surface.cpp). ModView — это внутренний инструмент, который использовался для рендеринга моделей и анимаций 3D-персонажей. Очевидно, работа с матричными 3D-преобразованиями и математикой поверхностей вынула из кого-то душу.

// В дупу эту математическую хрень, она не работает
// #define real_nclip(x0,y0,x1,y1,x2,y2) ( (y1-y0)*(x2-x1) - (x1-x0)*(y2-y1) )

17. Ярость из-за «железа» начала 2000-х (textures.cpp). Жёстко прописываем исключение просто для того, чтобы графические карты AMD/ATI не вызывали вылет инструмента.

if (error && error != GL_STACK_OVERFLOW
    /* дебильные карты ATI почему-то иногда выдают эту ошибку */ )

18. Машина времени аудиобуфера (cl_mp3.org). Для экономии ОЗУ движок декодирует сжатое аудио линейными блоками, поэтому не может просто перемотать аудиобуфер назад. Разработчик решил, что если часы игрового движка внезапно подтормаживают и из-за этого он запрашивает аудиосэмпл из прошлого, то нужно сдаться и ничего не делать.

// чё?!?!?! Я назад в прошлое должен улететь или чего? Да ну нафиг

Очень странно осознавать, что на этом коде работает одна из самых качественных многопользовательских игр. Выражаю благодарность команде OpenJK за улучшение этого спагетти-кода и сохранение жизни игры.