SF21 /
Творчество /
Уроки /
Ромхакинг / Редактирование исходного кода: делаем отдельную мелодию для бонуса ускорения
Редактирование исходного кода:
Делаем отдельную мелодию для бонуса ускорения
Текст и листинги кодов: Jet
Последняя редакция от 09.07.09.
Внимание! Данное руководство предполагает, что у вас уже есть определённые навыки и/или познания в области редактирования исходных кодов, в частности, S1 Hivebrain Disassembly (исходный код игры Sonic the Hedgehog от Hivebrain). Знание английского языка также приветствуется.
Вступительные замечания
В первом уроке мы рассмотрим, как заменить стандартное ускорение музыки при взятии бонуса ускорения на воспроизведение отдельной мелодии. Вам понадобится полностью готовый к редактированию исходный код игры Sonic the Hedgehog с необходимыми инструментами и краткой инструкцией на русском яхыке, запакованные в единый архив
Sonic 1 (Split and Text by Hivebrain)(ASM68K)(Rus) 1.1. Мы будем работать с исходным кодом игры, так что никакие дополнительные утилиты, кроме тех, что есть в архиве, нам не понадобятся. Рекомендуем Вам не использовать для редактирования стандартный Блокнот Windows. Он работает лишь с кодировкой Win-1251, очень неудобен и зачастую портит исходники и скрипты. К тому же его система поиска оставляет желать лучшего. Лучше возьмите любой другой, желательно без лишних наворотов, но с поддержкой разных кодировок. Например, неплохой текстовый редактор AkelPad, вложенный в упомянутый выше архив.
Итак, поехали!
Найдите в файле sonic1.asm процедуру
Obj2E_ChkShoes (используйте поиск по файлу, нажав сочетание клавиш
Ctrl + F). Указанное сочетание может встречаться в нескольких местах, так что продолжайте поиск, пока не увидите:
Листинг 1
|
Obj2E_ChkShoes:
cmpi.b #3,d0 ; does monitor contain speed shoes?
bne.s Obj2E_ChkShield
move.b #1,($FFFFFE2E).w ; speed up the BG music
move.w #$4B0,($FFFFD034).w ; time limit for the power-up
move.w #$C00,($FFFFF760).w ; change Sonic's top speed
move.w #$18,($FFFFF762).w
move.w #$80,($FFFFF764).w
move.w #$E2,d0
jmp (PlaySound).l ; Speed up the music
|
Первые две инструкции отвечают за проверку содержимого монитора (например, жизнь, непобедимость, ускорение). Их не трогаем. Далее идёт строка
move.b #1,($FFFFFE2E).w – она неправильно откомментирована создателем исходника, на самом деле это флаг (указатель) этого Power-up’a (бонуса).
Свойства самого бонуса (следующие четыре строки) оставляем как есть. Наконец, последние две инструкции и вызывают ускорение музыки, они то нам и нужны! Разберем их поподробнее. Строка, приведенная в
листинге 1.1, помещает значение
E2 в регистр
d0 и этим задаёт номер мелодии, которую нужно проиграть, в данном случае
E2 (
E2 – это процедура ускорения музыки, хотя и вызывается она как мелодия; в меню
Sound Test она конечно же не видна).
А инструкция, указанная в
листинге 1.2, вызывает процедуру воспроизведения музыки
PlaySound, которая проигрывает музыку по номеру, заданному в предыдущей строке.
Листинг 1.1
|
move.w #$E2,d0
|
Листинг 1.2
|
jmp (PlaySound).l ; Speed up the music
|
Напрашивается следующий вывод: если нам надо проиграть мелодию, мы должны написать вот это:
Листинг 2
|
move.w X,d0 ; отправка номера мелодии
jmp (PlaySound).l ; вызов процедуры проигрывания музыки
|
Вместо значка "Х" в
листинге 2 должен стоять идентификатор мелодии; если этот идентификатор однозначный и содержит
только цифру, пишем
#[знак] (пример:
#2), если содержит букву или двузначный –
#$[знаки] (например,
#$A6,
#$C). После знака «точка с запятой» может указываться любой комментарий – такие текстовые блоки, в отличие от остального кода, программой игнорируются и служат исключительно для удобства разработчика (так удобнее и быстрее ориентироваться в коде, читая оставленные ранее подсказки).
Теперь, когда мы разобрались, заменим
E2 на любую свою мелодию. Возьмем музыку
91; тогда у нас должно получиться примерно следующее:
Листинг 2.1
|
move.w #$91,d0 ; комментарий: было E2, стало – 91
jmp (PlaySound).l ; играть мелодию 91!
|
Сохраним файл
sonic1.asm. Теперь нужно собрать (скомпилировать) ром (о том, как это сделать, читайте файл
README.txt, вложенный в скачанный вами архив с разбивкой рома (в том же архиве лежал только что отредактированный вами файл
sonic1.asm)). Запустим новоиспеченный хак на эмуляторе...
Непредвиденная ошибка
Получилось! Теперь играет новая мелодия! Но что это?! О нет, действие бонуса кончилось, Соник бегает как обычно, но музыка не прекратилась! Надо исправлять. Ищем процедуру
Obj01_ChkShoes:
Листинг 3
|
Obj01_ChkShoes:
tst.b ($FFFFFE2E).w ; does Sonic have speed shoes?
beq.s Obj01_ExitChk ; if not, branch
tst.w $34(a0) ; check time remaining
beq.s Obj01_ExitChk
subq.w #1,$34(a0) ; subtract 1 from time
bne.s Obj01_ExitChk
move.w #$600,($FFFFF760).w ; restore Sonic's speed
move.w #$C,($FFFFF762).w ; restore Sonic's acceleration
move.w #$80,($FFFFF764).w ; restore Sonic's deceleration
move.b #0,($FFFFFE2E).w ; cancel speed shoes
move.w #$E3,d0
jmp (PlaySound).l ; run music at normal speed
|
Эта процедура работает в качестве таймера и возвращает исходные свойства ежа по истечении времени (то есть убирает действие бонуса ускорения). Нас интересуют только последние две строки, возвращающие музыку к нормальной скорости. Тут простая замена идентификатора не поможет – ведь по окончании действия бонуса нам нужно вернуть исходную мелодию этапа, а она в каждой зоне разная! До нашей предыдущей замены мелодия этапа просто ускорялась (идентификатор
E2, помните?), а указанный здесь идентификатор
E3 возвращает ускоренной музыке нормальный темп. Но мы заменили ускорение темпа на совсем другую мелодию. Что же делать?
Ответ на вопрос содержится в самом исходном коде. Ведь подобный принцип замены мелодии (а не ускорения уже играющей) используется в бонусе неуязвимости! Идем чуть выше, к
Obj01_ChkInvin:
Листинг 4
|
Obj01_ChkInvin:
tst.b ($FFFFFE2D).w ; does Sonic have invincibility?
beq.s Obj01_ChkShoes ; if not, branch
tst.w $32(a0) ; check time remaining for invinciblity
beq.s Obj01_ChkShoes ; if no time remains, branch
subq.w #1,$32(a0) ; subtract 1 from time
bne.s Obj01_ChkShoes
tst.b ($FFFFF7AA).w
bne.s Obj01_RmvInvin
cmpi.w #$C,($FFFFFE14).w
bcs.s Obj01_RmvInvin
moveq #0,d0
move.b ($FFFFFE10).w,d0
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
bne.s Obj01_PlayMusic
moveq #5,d0 ; play SBZ music
|
Здесь нужную музыку возвращают последние пять строчек. Копируем их и вставляем в конец процедуры
Obj01_ChkShoes вместо последних двух. Результат:
Листинг 5
|
Obj01_ChkShoes:
tst.b ($FFFFFE2E).w ; does Sonic have speed shoes?
beq.s Obj01_ExitChk ; if not, branch
tst.w $34(a0) ; check time remaining
beq.s Obj01_ExitChk
subq.w #1,$34(a0) ; subtract 1 from time
bne.s Obj01_ExitChk
move.w #$600,($FFFFF760).w ; restore Sonic's speed
move.w #$C,($FFFFF762).w ; restore Sonic's acceleration
move.w #$80,($FFFFF764).w ; restore Sonic's deceleration
move.b #0,($FFFFFE2E).w ; cancel speed shoes
moveq #0,d0
move.b ($FFFFFE10).w,d0
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
bne.s Obj01_PlayMusic
moveq #5,d0 ; play SBZ music
|
Опять сохраняем файл, компилируем ром и тестируем его на эмуляторе. Работает! Однако есть некоторые баги (ошибки в программе). О том, что это за баги и как их исправить, мы поговорим чуть ниже.
Ловим баги!
М-мда. Из-за того, что теперь в игре два бонуса с одинаковой системой изменения музыки, могут вылезти неприятные глюки. Посмотрим, как они работают одновременно. В Spring Yard Zone Act 3 есть место, где лежат два монитора, один с непобедимостью, другой с ускорением – как раз то, что нужно (этот тайник находится на огромном двойном трамплине справа наверху):
Разбейте сначала монитор с неуязвимостью, затем через 3-4 секунды – монитор c ускорением и ждите. Через определенное время музыка скорости прекратится и начнется мелодия текущего уровня (потому что кончилась неуязвимость и программа заменила мелодию), хотя Соник еще будет бегать быстро (как раз эти самые 3-4 секунды). Через этот же промежуток времени музыка этапа перезапустится (на этот раз по той причине, что закончился бонус ускорения и по его окончании запустилась мелодия этапа). Естественно, тот же эффект можно наблюдать, если точно так же разбить эти мониторы, но в противоположном порядке.
Разумеется, можно скомпоновать уровень так, чтоб два бонуса не могли работать одновременно – просто расположить их подальше друг от друга. Но так мы теряем свободу в создании своего хака, да и чем мы хуже иностранных ромхакеров, а? Давайте уже все делать на совесть! Ведь настоящему ромхакеру (пусть и начинающему) нужно уметь решать возникающие проблемы. Компромиссов быть не может.
Итак, в двух местах используется один и тот же кусок кода; так почему бы не оптимизировать его? В процедуре
Obj01_ChkInvin вырежьте последние пять строк кода, а в
Obj01_ChkShoes их просто удалите. Вставьте ранее вырезанный текст после
Obj01_ChkShoes и напишите перед ним
Obj01_PlayMusic2:. Это название вашей новой процедуры по возвращению музыки! Вырежьте процедуру
Obj01_PlayMusic и вставьте ее между
Obj01_PlayMusic2 и
Obj01_ExitChk. Это позволит избежать досадных ошибок при компиляции хака. Теперь самое интересное: программирование условий. Это очень просто.
Листинг 6
|
cmpi.b #1,($FFFFFE2E).w ; ботинки есть? (1)
bne.s Obj01_PlayMusic2 ; если нету, вернуть музыку текущего уровня (2)
|
Код, приведенный в
листинге 6, означает, что если выполняется условие, описанное в строке (1), процессор выполнит инструкцию (2). В противном случае (если условие не выполняется) строка (2) пропускается и выполняется код после нее. Этот код необходимо вставить в конце
Obj01_ChkInvin. Для
Obj01_ChkShoes инструкции такие:
Листинг 7
|
cmpi.b #1,($FFFFFE2D).w ; непобедимость есть?
bne.s Obj01_PlayMusic2 ; если нет, отключить музыку
bra.s Obj01_ChkInvin ; если есть, продолжить проверки
|
Их тоже вставьте в конец соответствующей процедуры. Кстати, процедуру
Obj01_RmvInvin можете стереть, она не нужна. Замените каждую из строк
bne.s Obj01_RmvInvin и
bcs.s Obj01_RmvInvin на
move.b #0,($FFFFFE2D).w ; cancel invincibility. Итоговый вариант кода приведен в
листинге 8.
Листинг 8
|
Obj01_ChkInvin:
tst.b ($FFFFFE2D).w ; does Sonic have invincibility?
beq.s Obj01_ChkShoes ; if not, branch
tst.w $32(a0) ; check time remaining for invinciblity
beq.s Obj01_ChkShoes ; if no time remains, branch
subq.w #1,$32(a0) ; subtract 1 from time
bne.s Obj01_ChkShoes
tst.b ($FFFFF7AA).w
move.b #0,($FFFFFE2D).w ; cancel invincibility
cmpi.w #$C,($FFFFFE14).w
move.b #0,($FFFFFE2D).w ; cancel invincibility
cmpi.b #1,($FFFFFE2E).w ; ботинки есть?
bne.s Obj01_PlayMusic2 ; если нету, вернуть музыку текущего уровня
Obj01_ChkShoes:
tst.b ($FFFFFE2E).w ; does Sonic have speed shoes?
beq.s Obj01_ExitChk ; if not, branch
tst.w $34(a0) ; check time remaining
beq.s Obj01_ExitChk
subq.w #1,$34(a0) ; subtract 1 from time
bne.s Obj01_ExitChk
move.w #$600,($FFFFF760).w ; restore Sonic's speed
move.w #$C,($FFFFF762).w ; restore Sonic's acceleration
move.w #$80,($FFFFF764).w ; restore Sonic's deceleration
move.b #0,($FFFFFE2E).w ; cancel speed shoes
cmpi.b #1,($FFFFFE2D).w ; непобедимость есть?
bne.s Obj01_PlayMusic2 ; если нету, заткнуть музыку
bra.s Obj01_ChkInvin ; если есть, продолжить проверки
Obj01_PlayMusic2:
moveq #0,d0
move.b ($FFFFFE10).w,d0
cmpi.w #$103,($FFFFFE10).w ; check if level is SBZ3
bne.s Obj01_PlayMusic
moveq #5,d0 ; play SBZ music
jmp Obj01_ChkInvin
Obj01_PlayMusic:
lea (MusicList2).l,a1
move.b (a1,d0.w),d0
jsr (PlaySound).l ; play normal music
Obj01_ExitChk:
rts
|
Вроде мелочь, а сколько пришлось возиться! Все! Компилируем, проверяем, радуемся, экспериментируем и ждем следующих мануалов по редактированию исходных кодов сониковских ромов!
© Jet, 2009.
SF21 /
Творчество /
Уроки /
Ромхакинг / Редактирование исходного кода: делаем отдельную мелодию для бонуса ускорения