Игра обрабатывает различный код каждый кадр. (Поскольку идет в 60fps) Игровой цикл выглядит примерно так: Повторить:
jsr Обработать код объектов
jsr Обработать взаимодействия объектов
jsr Обработать другие вещи
jsr Обработать карту уровня
jsr Обработать спрайты объектов
tst ждать следующего кадра
jmp повторить
Коды связанные с объектами состоят из цикла , выполняемого 15 раз (поскольку у нас 15 слотов для объектов).
Начало цикла для спрайтов находится тут:
ROM $3063F: BANK6:862F
Для счета текущего обрабатываемого объекта используются Y и $15 и $16 для хранения Y. То есть обработали один объект (выставили его спрайты), далее увеличиваем Y и $15 на 1, и так пока не достигнет $F. (завершаем цикл на 16-ом объекте так как у нас их всего 15-ть) Кроме того счетчик двух-направленный в зависимости от четности кадра. То есть в следующий раз счет пойдет не с первого по 15-ый объект, а с 15-ого по 1-ый. И на значении -1 завершится. Это сделано чтобы при лимите спрайтов обрабатывать объекты идущие в последних слотах, создает эффект мерцания спрайтов.
Баг: если лаг возникнет во время обработки этого цикла (то есть игра не успеет обработать весь нужный код за кадр), происходит запись неверного значения в память ячейку $15 (так как например $15 также используется во время опроса джойстиков)во время прерывания nmi и переключения кода в другое место рома (где и есть опрос джойстиков). После обработки его игра возвращается в исходное место и продолжит обрабатывать спрайты. В нормальных ситуациях когда игра всё оработала она сама ждет прерывания на следующий кадр. Это нормальная ситуация для всех игр, только тут ошибка состоит в том, что для счечтика взяли ячейку $15 которая используется и во время прерывания, и в результате лага значение $15 сбивается и цикл обработки спрайтов продолжится с неверным значением. В нормальных условиях значение должно быть от $00 до $0E. А после такого возврата после лага оно может быть любым хоть 20 хоть 66. И игра продолжит обработку спрайтов для несуществующих слотов объектов.
Представим массив(таблицу) чисел созданную из 5-ти ячеек:
скорость
1
1
0
1
0
ускорение
5
0
5
5
5
поворот
2
2
0
0
2
Например машина #1 имеет параметры - 1/5/2 ; #2 - 1/0/2; Какие значения будут у машины #6? Мы ведь не вписывали ее в таблицу. Но данные в роме(или в памяти) то идут друг за другом и игре всё равно есть это или нет. 1 1 0 1 0 5 0 5 5 5 2 2 0 0 2 Поэтому для машины #6 возьмется цифра 5 (6-ая по счёту), далее 2, а третья вообще из следом идущих данных.
Теперь вернемся к жабам. Если значение счетчика сбилось мы продолжим обрабатывать несуществующие объекты: Например для объекта #17 - obj type id ($3C1) совпадет c $3D1 (player2_anim_frame); 03FD Objects_Xpos_L с 40D player2_Ypos_H; то есть для оработки спрайта объекта если надо считать его XposL для #17 она считается из ячейки 40D (player2Y). Вообщем для обработки спрайтов следующих объектов используются совершенно левые значения образуемые из последующих ячеек памяти.
Сам код вывода обработки спрайтов объекта состоит не только из чтения из нужных ячеек объектов LDA (координаты например) и запись в буфер спрайтов, но также и запись STA в некоторые ячейки. Причем есть как глобальные участки кода(для любых объектов), так и специфические для определенных. В частности для объекта tall walker (ходули), есть записи в ячейки:
STA Objects_Z_floor,Y
STA Objects_Y_shad,Y
STA Objects_various_flags,Y
STA Objects_Y_shad,Y
Для других объектов тоже могут быть записи в ячейки, да и в общем коде.(не изучалось пока)
В ситуации если значение объект-счетчика сбилось на #$3D, то
RAM:0475 Objects_Z_floor 475+3D = 4B2
а 4B2 это RAM:04B2 player2_state, то еcть запись
STA Objects_Z_floor,Y, где Y =3d
запишет данные в ячейку 4b2 - в стейт флаги 2-ого объекта. А 5-ый бит стейта отвечает за наличие стика (палочки).
Теперь рассмотрим все условия именно этой комбинации:
Рассмотренные STA идут только для объекта 'ходуля' (есть и другие - но не проверялись); поэтому перед выполнением этого участка кода вначале идет опроса кода на тип объекта:
Узнаем тип объекта с номером #3D
RAM:03C1 Objects_TypeID: . + 3D = равняется ячейка 3FE =
RAM:03FE player2_Xpos_L:
То есть мы можем влиять на эту ячейку - меняя Xpos игрока мы и будем менять также ID объекта #3D. Нам нужно значение в этой ячейке = $11 или $12, для запуска кода спрайтов специфичного для Ходуль , где есть приведенные выше записи в в память STA. - В частности STA Objects_Z_floor,Y
BANK6:8659 STY tmp_var_16 ; slotID
BANK6:865B LDA Objects_TypeID,Y
BANK6:8672 CMP #$11
BANK6:8674 BEQ loc_6000_86A2
BANK6:8676 CMP #$12
BANK6:8678 BEQ loc_6000_86A2
Кроме того STA (запись A) само число A должно оказаться любым чтобы создался стик - но так чтобы 05-ый бит в этом числе был равен 1. Вообщем тут 50/50 но на A косвенно влияют например $21 и $8D:
BANK6:89F0 SBC byte_0_21
BANK6:89F2 SEC
BANK6:89F3 SBC #4
BANK6:89F5 SBC byte_0_8D
BANK6:89F7
BANK6:89F7 level4_stick_bug: ; if Y = 3D
BANK6:89F7 STA Objects_Z_floor,Y
Таким образом происходит запись неверного числа в атрибут игрока и у него появляется в руках стик (палочка от ходули).
Это не единственный возможный баг из-за счетчика, так как возможны записи и в другие ячейки , и в эту же при других условиях - надо смотреть код, да и Y дает очень большой диапозон. Ведь он может сбится и продолжить обрабатывать сотню несуществующих слотов объектов. Другое дело что не все комбинации могут быть легко выполнены, а некоторые даже теоретически невыполнимы. Да и надо придумывать что кроме атрибут можно было бы полезным изменить. Но для начала надо научится вызывать лаг чтобы сбивать счечтик (на само число можно повлиять кнопками и в зависимости от четности кадра надо ставить число больше $F либо меньше $7F). А потом изучить все возможные STA.
Например для STA 0475 Objects_Z_floor, для счечтика от F до $7F возможны записи в ячейки - 475+F - 475+7F, дипозон 484-4F4. Только для Y отличного от 3D, для запуска этого кода ID считываться уже будет не с RAM:03FE player2_Xpos_L , а с 3С1+F - 3C1+7F, диапозон 3D0-440, то есть при 3d0=12 и Y =F возможна запис в 484. при 3d1=12 и y=10 возможна запись в 485 и т.д.
Ну и другие комбинации с STA/ ID. Для тех же ходуль есть еще записи в память, добавляем Y>F и смотрим куда записи пойдут.
STA Objects_Y_shad,Y
STA Objects_various_flags,Y
А для объекта номер 6 BANK6:867E CMP #6 есть запись например в :
BANK6:87EE STA Objects_TargetID,Y