В новом релизе Pizza Legacy v0.1.0 появилась возможность выиграть игру, как в оригинале.

Когда я играл в Pizza Tycoon (1994 год) в детстве, то не особо задавался вопросом, как победить. Мне просто нравилось открывать рестораны и придумывать пиццы, а также приторговывать оружием для финансирования роста моей империи пиццы. Но выигрыш? Я никогда до него не доходил и, вероятно, даже не задумывался о нём.

Когда я принялся за проект воссоздания этой игры, то начал изучать файлы данных Pizza Tycoon, обнаружив графический файл ENDE.VGA и текстовый файл ENDE.E, сообщающий нам, что происходит в случае выигрыша:

ENDE.VGA; a man in a golden chair wearing a crown and a                 big smile, with a bikini clad woman on either side of him, one                 looking admiringly at him and the other winking at us.
ENDE.VGA (ende — это «конец» по-немецки).

Первая строка ENDE.E:

Вы достигли немыслимого! Вы — король всего западного рынка фастфуда! Вы — тот самый МАГНАТ ПИЦЦЫ.

Это даёт нам понять, что игру как-то можно выиграть, но не как именно.

Я просто забыл об этом вопросе, ведь мне предстояло реализовать столь многое до того, прежде чем задумываться о победе игрока, но однажды наткнулся на пост «Я "выиграл" в Pizza Tycoon? (Как это произошло?)». После нахождения этого поста мне стало любопытно, но не настолько любопытно, чтобы заниматься расследованием, поэтому я поискал условия конца игры в ассемблерном коде и попросил Claude проанализировать их. Он сказал следующее:

end_of_week_processing раз в неделю проверяет, есть ли у текущего игрока >= доля рынка 5% во ВСЕХ 10 городах.

Это соответствует моим представлениям: логично с точки зрения геймплея и текста, найденного в ENDE.E. Однако это не соответствовало написанному пользователем Reddit: он утверждал, что у него были рестораны только в Париже и Берлине; возможно, он опустил какие-то подробности или у него просто был повреждённый файл сохранения? Я написал ему, но не получил ответа.

Что-то не сходится

Позже, во время плейтестинга моего движка я смотрел видео Pizza Tycoon - Worldwide Pizza King - Episode Ten, в котором пользователь YouTube AppleSauce играл в Париже и внезапно началась заставка, сообщающая о победе.

Так как весь процесс, приведший его к победе, был записан, я мог чётко увидеть, что на самом деле у него не было ресторанов во всех городах, а значит, что-то явно было не так. Теперь я как раз приближался к необходимости реализации сценария победы в Pizza Legacy, поэтому вернулся к ассемблерному коду, чтобы глубже разобраться в происходящем.

Соответствующий код находится в функции, которую я назвал end_of_week_processing (последние 15 лет я постепенно документировал ассемблерный код исполняемого файла игры PT.EXE; имена выбраны мной или сгенерированы IDA). Эта функция вызывается один раз за игровую неделю для каждого живого игрока: она выполняет такие действия, как проверка на банкротство, еженедельные трансферы прибыли городов, в которых игрока ещё нет, и проверку условия победы.

A screenshot of the IDA reverse engineering tool running on              macOS, showing the assembly code (reproduced below) execution              paths, showing six blocks of code with arrows between them to show              how the execution moves through the code depending on various              conditions.

Проверка условия победы выполняется так:

loc_546CD:
        xor     bl, bl          ; индекс города = 0 (Париж)
        jmp     short loc_546F3 ; начало цикла

loc_546D1:
        movzx   eax, bh         ; bh = индекс текущего игрока
        imul    eax, 0x0A       ; игрок * 10 (10 городов)
        movzx   edx, bl         ; индекс города
        cmp     byte ptr marketshare_percentages[edx+eax], 5
        jb      short loc_546F8 ; доля рынка < 5%? выходим из цикла
        push    0x8C            ; константа GAME_OVER_YOU_WON
        call    schedule_fullscreen_status_update ; ВЫ ПОБЕДИЛИ
        add     esp, 4
        inc     bl

loc_546F3:
        cmp     bl, 0x0A        ; выполнение цикла, пока город < 10
        jb      short loc_546D1 ; переход к реализации цикла

loc_546F8:                      ; код после цикла
        movzx   edx, bh
        ...

На C это бы выглядело примерно так:

int number_of_cities = 10;
for (int city_id = 0; city_id < number_of_cities; city_id++) {
    if (marketshare_percentages[player_id][city_id] < 5) {
        break;
    }
    schedule_fullscreen_status_update(GAME_OVER_YOU_WON);
}

Проблема в том, что schedule_fullscreen_status_update(GAME_OVER_YOU_WON) исполняется внутри цикла, а не после успешного завершения. На самом деле условие не «доля рынка не менее 5% во всех городах». Достаточно получить долю рынка 5% в городе 0!

Тестируем самостоятельно

Эта новая информация объясняет и пост на Reddit, и видео на YouTube. Чтобы убедиться, я протестировал это сам. В моей собственной сохранёнке с долей рынка более 5% в шести городах ничего не происходило, пока я не открыл пару ресторанов в Париже, после чего я впервые в своей жизни победил в Pizza Tycoon.

Мне стало любопытно, есть ли такое же поведение в оригинальной Pizza Connection (на немецком языке).

Я бы мог попробовать проверить это и в немецком исполняемом файле, но я документировал ассемблерный код только англоязычного PT.EXE; немецкий PC.EXE собирали сильно другим компилятором и он имел совершенно другую структуру, так что поиск эквивалентного условия победы потребовал бы существенного труда по реверс-инжинирингу. Поэтому я решил просто добиться доли рынка 5% в Париже, играя в игру.

Чтобы быстро получить нужную долю рынка (я ведь занимался расследованием, а не хотел потратить вечер на игровой процесс), мне требовались деньги. Быстрее всего добиться этого торговлей оружием: покупаем по дешёвке, продаём задорого, надеемся не попасться.

Вместо того, чтобы трудиться самому, я немного сжульничал: купил бомбу, сохранил игру, потом купил ещё одну бомбу, снова сохранился, и повторил это ещё раз. Потом я написал на Python небольшой скрипт, который сравнивает три файла сохранений и выводит рядом все различающиеся значения. Потом я посмотрел все поля, которые изменились ровно на единицу в каждой последующей сохранёнке (я подумал, что искать один байт с количеством бомб будет проще, чем double с деньгами).

По смещению 0x2E0 в файле сохранения я нашёл значения 158, 159 и 160. Открыв файл сохранения в шестнадцатеричном редакторе, изменив значение и загрузив сохранение в игре, я убедился, что именно там хранится количество бомб со смещением 157: если байт равен 157, то у игрока есть 0 бомб, значение 158 соответствует 1 бомбе. Так как значение хранится в одном байте, значения ниже 157 приводят к циклическому переполнению, поэтому значение 156 даёт нам 255 бомб.

Я записал значение 141, чтобы добавить себе 240 бомб (потому что быстрее всего продавать их наборами по 20 штук), продал их все и без особой траты времени получил капитал, который нужен, чтобы начать расширяться.

Вскоре я получил больше 5% рынка в Париже, но игра не закончилась, а значит, баг был присущ только английской версии Pizza Tycoon.

Screenshot of the market share screen in the German Pizza                  Connection, showing player one (me) with a bar close to 1                  (10%), indicating I have more than 5% market share but the                  game is not over yet.
Несмотря на заголовок, на оси Y указаны значения от 0 до 100, а не от 0 до 10, то есть когда мы достигли 1, на самом деле это 10%.

Это сделано специально или случайно?

Я предполагаю, что исходная логика заключалась в том, чтобы игрок одновременно удерживал долю рынка не менее 5% во всех десяти городах. В этом случае структура цикла, выполняющая выход при неудачной проверке, имеет смысл; если бы разработчикам нужен был только первый город, они бы могли проверить только индекс 0, а не писать цикл, который никогда не доходит дальше первого элемента.

Так почему же игра говорит, что мы выиграли, когда добились нужного только в одном Париже? Англоязычная Pizza Tycoon не совсем аналогична немецкому оригиналу. Терри Грир, работавший в то время художником в MicroProse, вспоминает, что Pizza Connection была немецкой игрой, которую MicroProse хотела переименовать и продавать под своим брендом Tycoon. Где-то в процессе этого и изменилось условие победы: в немецком оригинале бага нет, он присутствует только в англоязычном релизе, которым занималась MicroProse.

Думаю, что кто-то хотел облегчить задачу и позволить выиграть, если пользователь добьётся 5% рынка в любом городе, но из-за этого в коде появился баг. В результате это стал конкретно Париж, просто потому что это город 0, первый, который проверяется в цикле. То, что это оказался Париж, кажется случайностью: MicroProse локализовала игру для англоязычного рынка, даже вставив в неё три города США (Балтимор, Нью-Йорк и Чикаго, заменившие Москву, Цюрих и Афины). Если бы разработчики хотели выбрать победный город вручную, то скорее всего это был бы один из американских городов, а не столица Франции.

Зачем вообще упрощать победу? Возможно, это было сделано из коммерческих соображений. Pizza Tycoon была не отдельным релизом MicroProse, а частью Tycoon Series, наряду с Transport Tycoon Криса Сойера и более ранней Railroad Tycoon Сида Мейера; все они продавались под логотипом MicroProse примерно в одно и то же время. В рекламе на всю страницу Computer Gaming World (выпуск 126 за январь 1995 года) все три коробки объединены надписью «Теперь твой ход» и позиционируются как игры для PC, позволяющие покупать, строить и управлять собственной империей со слоганом MicroProse «Anything is possible».

A MicroProse magazine advertisement: a red banner                      reading 'Now it's your turn.' above the box art for                      Transport Tycoon, Pizza Tycoon and Sid Meier's Railroad                      Tycoon (Classic), with the text 'Introducing the Tycoon                      Series from MicroProse. PC games that let you buy, build                      and control your own empire.'

В рамках локализации MicroProse изменила и текст в конце; на немецком игра оканчивается саркастическими титрами, в которых команда разработчиков благодарит, в том числе, разработчика компилятора за встраивание нужных багов в новые версии компилятора ровно в нужный момент, чтобы привести разработчиков в отчаяние. В конце титры предлагают начать всё сначала, ведь игра хороша.

В англоязычной версии от всего этого избавились: после коронации единственного и неповторимого МАГНАТА ПИЦЦЫ игра спрашивает, что пользователь собирается делать дальше и сама отвечает на этот вопрос:

ENDE.VGA; The same screenshot as before but with the                      following text on it: "But you, you are already                      having visions of branching out into... TRANSPORT!!!"
Но у вас уже есть мечта создания... ТРАНСПОРТНОЙ империи! Просто представьте — поезда, самолёты и автомобили, принадлежащие только вам.

Наверно, причина упрощения — всё вышеперечисленное. Игру рекламировали для другой, более казуальной аудитории, поэтому её нужно было пройти проще, а локализация текста в конце позволила добавить рекламу похожего продукта. Как бы то ни было, это привело к противоположным результатам, и Париж случайно стал единственным способом пройти игру.

Как это отражено в моей Pizza Legacy

Спустя тридцать лет игроки в Pizza Tycoon наконец-то могут выиграть так, как это задумывали разработчики: став настоящим магнатом, а не просто доминируя на французском рынке.

При включении опции устранения багов в Pizza Legacy игроку придётся получить долю больше 5% рынка во всех городах, а при отключенной опции сохраняется исходное поведение: для победы нужно достичь 5% рынка только в одном Париже. Думаю, в следующем релизе я добавлю переключатель победы между всеми городами и любым городом.

В Pizza Legacy уже есть довольно много модернизаций и исправлений; полный список можно найти в файле MODERNIZATIONS.md репозитория.

Недавно выпущенная Pizza Legacy v0.1.0 — это первый релиз, который наконец-то позволяет вам выиграть. Подробности о том, что было добавлено в этом релизе, можно прочитать в посте с объявлением релиза.

Комментарии (1)


  1. Zifix
    09.06.2026 13:55

    Как бы то ни было, это привело к противоположным результатам, и Париж случайно стал единственным способом пройти игру.

    Вроде нет, Париж стал читом, позволяющим пройти её быстрее, но захватив все города, как планировали разработчики изначально, тоже можно было победить.