Борьба за байты: что влезло и не влезло в Organic Matter

Много ли игры можно упихать в 37 килобайт? От чего пришлось отказаться при создании Organic Matter? Узнайте из этого выпуска дневников разработки! Спойлеры, слухи, скандалы… ну, вообще-то, в основном спойлеры.

qbXkv4z

Итак, работа над проектом понемногу шла к завершению, и тут после очередной доработки игра перестала запускаться. Я показал свои исходники Кесу ван Оссу (Kees van Oss), который уже помогал мне с кодом, и он ответил, что программа занимает слишком много места — перебор на целых 543 байта. Кес также любезно перечислил проблемные места, исправление которых могло помочь уложиться в рамки. Собственно, рамки как раз и стали одной из проблем: хотя текстов в игре было не так и много, вокруг каждого из них я сделал обрамление, чтобы они красивее смотрелись и лучше выделялись на фоне.

vBhz7N1

Рамочки без затей выводились символами кастомного шрифта. В результате получалось, что для оформления строчки длиной X нужно было потратить еще 2X+6 байт, не считая самой команды PRINT. Особенно отжирала память, конечно, речь заведующего космолабом, и Кес придумал для нее параметрические рамочки: число строчек и ширина для каждого блока болтовни считывались из оператора DATA, потом на основе этих параметров выводилась рамка, а потом в нее впечатывался текст. Для интересующихся — фрагмент кода:

   LET I = 0; счетчик блоков текста
    RESTORE
    REPEAT 8
      READ V; число строк
      READ W; число символов =
      LET M = 11; номер строки на экране, начинаем с 11-й
      AT M,1
      CHR 42: REPEAT W: CHR 61: ENDREPEAT: CHR 42; строка *====*
      REPEAT V
        ADD 1 TO M
        AT M,1        
        CHR 124: REPEAT W: CHR 32: ENDREPEAT: CHR 124; вертикальные линии и пробелы между ними
      ENDREPEAT

      ADD 1 TO M
      AT M,1        
      CHR 42: REPEAT W: CHR 61: ENDREPEAT: CHR 42; строка *====*
      AT 12,3
      IF I = 0
;        PRINT "i don’t know who you are,"; этот текст пришлось вырезать
;        AT 13,3 ;
       PRINT "i suspect you were sent"
        AT 13,3
       PRINT "by the earth government."
      ENDIF

      IF I = 1
       PRINT "we created this arboreal"
        AT 13,3
       PRINT "structure that powers our"
        AT 14,3
       PRINT "starlab."
;        AT 15,3 ;
;        PRINT "gives us food."; вырезано
      ENDIF
<…>

ENDREPEAT
DATA 2 25 3 27 2 28 4 28 2 24 3 29 2 25 1 19 ;

К сожалению, вынести параметрические рамочки в отдельную функцию было нельзя, т.к. MPAGD поддерживает подпрограммы только на уровне машинного кода, а особо лезть в ассемблер я не хотел (это, конечно, первая ошибка того, кто хочет написать путную программу для «Спектрума», но не забудем, что изначально я вообще собирался просто сделать фейковый трейлер, не особо заморачиваясь). Тем не менее, некоторый выигрыш по памяти это решение принесло, однако в дальнейшем пришлось сократить и осмысленную часть речи начальника космостанции — в частности, реплику о том, что мутанты буквально растут, как груши, на дереве УОМ. Впрочем, для пояснения этой идеи я отгрохал в нижнем правом углу станции мутантский инкубатор и школу с ползающими мелкими личинками.

Пришлось отставить разговорчики и другим обитателям станции. Вначале я хотел, чтобы все персонажи, для которых доступна опция TALK, говорили хоть что-нибудь, но борьба за байты заткнула рты всем квестово неважным мутантам (а также лишила нас удовольствия пообщаться с туалетным указателем).

SzjVYRU
Стоящий слева профессор должен был просить у героя сдать кровь для опытов, но это взаимодействие в итоге реализовано не было.

Была у меня также мысль, что память станции хранится в повсеместно растущих ягодках, и после того как вы слопаете их определенное количество, у вас случается видение, рассказывающее очередной кусок сюжета. Задумка классная, но, чтобы она нормально работала, нужно было, чтобы съеденные ягоды оставались съеденными — иначе игрок сможет сожрать сколько угодно ягод, не исследуя станцию, а просто повторно приходя в один и тот же экран. Однако постоянство съеденности требовало дополнительных изворотов в коде. Дело в том, что по умолчанию все изменения на экране обнуляются, если вы выйдете с него, и даже в специальном режиме adventure исключение делается только для кирпичей, поставленных функцией PUTBLOCK, а те, что собраны с помощью GETBLOCKS (как те же ягодки) или прокопаны посредством DIG, все равно отрастают обратно. В общем, с этим я не стал возиться, поэтому съесть ягоды можно, но это ни на что не влияет.

Кстати, поскольку состояние спрайтов также не сохраняется, мне пришлось добавлять (через PUTBLOCK) блок с брызгами зеленой крови для застреленных мутантов, чтобы при перезапуске экрана они сразу же сталкивались с этим блоком и снова переходили в убитое состояние. Для движущихся мутантов так изящно эту проблему было не решить, поэтому я для себя решил, что их ряды постоянно пополняются — летающие прилетают из других частей станции, а мелкие ползуны вырастают на дереве.

Летающим мутантам вообще не повезло: изначально у них были «левый» и «правый» спрайт, но потом пришлось обходиться одним видом анфас (его, кстати, нарисовал мой сын). Кроме того, предполагалось, что если герой проявляет агрессию и станция входит в локдаун, летуны будут гадить на него, вызывая эффект, аналогичный нырянию в канализацию. Этот код (к счастью?) тоже уже не влез в игру.

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

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

Во-вторых, из-за некоторой нескладности движка такое удаление может повлечь за собой неожиданные глюки. Так что лучше планировать все заранее, и мой любимый подход «придумаем всё по ходу разработки» тут, к сожалению, не очень эффективен.

В заключение расскажу, на какие жертвы НЕ пришлось идти. Я неоднократно порывался удалить одну или несколько комнат, и первыми кандидатами становились коридоры справа от пассажирского и грузового доков. Так, на каком-то этапе я решил убрать всю соответствующую вертикаль и, в частности, сжать первые два экрана в один:

q7bK1KX

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

KwJ7i0J

Но оба раза мой сын категорически сказал «нет» — и был абсолютно прав. Во-первых, эти экраны, пусть в них изначально ничего и не происходило, были важны для начального создания атмосферы, а во вторых они в MPAGD довольно хорошо пакуются, так что их удаление не больно-то помогло бы ужаться в рамки.

Кроме этого, я не удалял никакие из созданных блоков-тайлов, и наконец, из одного встроенного шрифта и одного кастомного мне удалось выжать целых три визуальных решения для текстов: «обычное» для  служебных текстов/речи героя, «футуристическое» для речи роботов (заглавные буквы кастомного шрифта) и «органическое» для речи мутантов (строчные).

xA9pl2f

В итоге по-настоящему жалею я только о том, что не удалось вставить в игру больше текстов. Станция получилась небольшая, но симпатичная, обитатели её — достаточно разнообразными, и я даже смог сделать в игре два разных финала!

В следующем выпуске дневников речь пойдет о том, ради чего все и затевалось — о создании трейлера Organic Matter.