Как добавить Spin Dash
Автор гида: vladikcomperОсновано на гиде «SCHG How-to:Add Spin Dash to Sonic 1/Part 1»
Spin Dash — самая известная способность Соника, которая дебютировала в Sonic 2. В Первом Сонике этой способности не было, но ходят слухи, что подобие Spin Dash все-таки было, так как в РОМе найдены неиспользованные спрайты Соника, занимающегося Спин Дэшем. Однако правда до сих пор неизвестна, а спрайты могли использоваться для других целей.
Движок Sonic the Hedgehog 2 во многом сходен с движком Sonic 1, так что можно портировать Спин Дэш из Sonic 2, немного адаптировав его код для работы на другом движке. Все эти адаптации уже сделаны для Вас в этом гиде, вы даже можете проверить сходства предоставленных кодов, откопав суброутину Спин Дэша в коде Второго Соника.
Вставка основы Спин Дэша
Найдите в исходном коде лейбел Obj01_MdNormal и добавьте прямо после него строчку:
bsr.w Sonic_SpinDash ; ++ branch to Spin Dash
Теперь каждый раз, когда ежик стоит, будет запускаться суброутина Спин Дэша, которая в свою очередь будет проверять, можно ли выполнять Спин Дэш.
Настало время вставлять саму суброутину Sonic_SpinDash. Вставьте ее после суброутины Sonic_JumpHeight. Найдите конец суброутины, «; End of function Sonic_JumpHeight» и вставьте после него код:
; --------------------------------------------------------------------------- ; Subroutine allowing Sonic to do Spin Dash ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| Sonic_SpinDash: tst.b $39(a0) ; is Spin Dash flag set? bne.s SpinDash_Process ; if yes, branch cmpi.b #8,$1C(a0) ; is animation Duck? bne.s Sonic_SpinDash_Rts ; if not, branch move.b ($FFFFF603).w,d0 andi.b #$70,d0 ; is A/B/C pressed? beq.w Sonic_SpinDash_Rts ; if not, branch move.b #2,$1C(a0) ; use Spin Dashing animation move.w #$BE,d0 jsr (PlaySound_Special).l addq.l #4,sp ; avoid Sonic_Jump call move.b #1,$39(a0) ; set Spin Dash flag move.w #0,$3A(a0) bsr.w Sonic_LevelBound bsr.w Sonic_AnglePos Sonic_SpinDash_Rts: rts ; --------------------------------------------------------------------------- SpinDash_Process: move.b ($FFFFF602).w,d0 btst #1,d0 ; is Down pressed? bne.w SpinDash_Charge ; if yes, branch SpinDash_Release: move.b #$E,$16(a0) ; decrease Sonic's height move.b #7,$17(a0) move.b #2,$1C(a0) ; use rolling animation addq.w #5,$C(a0) ; add 5 to Y coordinate move.b #0,$39(a0) ; clear Spin Dash flag moveq #0,d0 move.b $3A(a0),d0 add.w d0,d0 move.w DashSpeeds(pc,d0.w),$14(a0) ; get Spin Dash speed btst #0,$22(a0) ; is Sonic facing right? beq.s SpinDash_ReleaseSound ; if yes, branch neg.w $14(a0) ; negate inertia SpinDash_ReleaseSound: bset #2,$22(a0) move.w #$BC,d0 jsr (PlaySound_Special).l bra.s loc_1AD78 ; --------------------------------------------------------------------------- DashSpeeds: dc.w $800 ; 0 dc.w $880 ; 1 dc.w $900 ; 2 dc.w $980 ; 3 dc.w $A00 ; 4 dc.w $A80 ; 5 dc.w $B00 ; 6 dc.w $B80 ; 7 dc.w $C00 ; 8 ; --------------------------------------------------------------------------- SpinDash_Charge: tst.w $3A(a0) ; test charge counter beq.s loc_1AD48 ; if it's zero, branch move.w $3A(a0),d0 ; move it to d0 lsr.w #5,d0 ; divide by 32 sub.w d0,$3A(a0) bcc.s loc_1AD48 move.w #0,$3A(a0) loc_1AD48: move.b ($FFFFF603).w,d0 andi.b #$70,d0 ; is A/B/C pressed? beq.w loc_1AD78 ; if not, branch ;move.w #$1F00,$1C(a0) move.w #$BE,d0 jsr (PlaySound_Special).l addi.w #$200,$3A(a0) cmpi.w #$800,$3A(a0) bcs.s loc_1AD78 move.w #$800,$3A(a0) loc_1AD78: addq.l #4,sp ; avoid Sonic_Jump call cmpi.w #$60,($FFFFEED8).w beq.s loc_1AD8C bcc.s loc_1AD88 addq.w #4,($FFFFEED8).w loc_1AD88: subq.w #2,($FFFFEED8).w loc_1AD8C: bsr.w Sonic_LevelBound bsr.w Sonic_AnglePos move.w #$60,($FFFFF73E).w ; reset looking up/down rts ; End of subroutine Sonic_SpinDash
Отлично! Теперь у нас есть подобие Спин Дэша, с виду похожее на Спин Дэш из Sonic CD.

Исправление багов
Камера не всегда может успевать прокручиваться вниз за Соником, особенно при прохождении двойных S-образных труб в GHZ. К тому же у камеры в Первом Сонике есть врожденный баг. Он заключается в том, что если Соник зайдет ниже своей высоты за нижнюю границу камеры, игра посчитает что он свалился в пропасть и убьет его. Чтобы исправить этот баг, идите к суброутине Boundary_Bottom и замените ее на это:
Boundary_Bottom: move.w ($FFFFF726).w,d0 move.w ($FFFFF72E).w,d1 cmp.w d0,d1 ; screen still scrolling down? blt.s Boundary_Bottom_locret ; if so, don't kill Sonic cmpi.w #$501,($FFFFFE10).w ; is level SBZ2 ? bne.w KillSonic ; if not, kill Sonic cmpi.w #$2000,($FFFFD008).w bcs.w KillSonic clr.b ($FFFFFE30).w ; clear lamppost counter move.w #1,($FFFFFE02).w ; restart the level move.w #$103,($FFFFFE10).w ; set level to SBZ3 (LZ4) Boundary_Bottom_locret: rts
Еще один заметный баг, который вскроется после исправления Spike Bug (смотрите гид «Как исправить Spike Bug») — отказ Соника прекращать Спин Дэш, после того как он накололся на иголках (Spikes). Для исправления бага, идите к лейбелу Hurt_ChkSpikes и вставьте прямо за ним строчку:
move.b #0,$39(a0) ; clear Spin Dash flag
Следующий баг, который предстоит исправить, заключается в том, что враг может спокойно повредить Соника, когда он совершает разгон на месте. Чтобы его исправить, идите к суброутине Touch_Enemy и перед строчкой «cmpi.b #2,$1C(a0) ; is Sonic rolling?» вставьте проверку на Спин Дэш:
cmpi.b #$1F,$1C(a0) ; is Sonic Spin Dashing? beq.w loc_1AF40 ; if yes, branch
Постройте РОМ и убедитесь, что эти баги исправлены.
Вставка анимации Спин Дэша
Чтобы Спин Дэш смотрелся как надо, сделать ему нормальную анимацию, чем мы сейчас и займемся.
Для начала, нужно портировать арт Спин Дэша из Sonic 2. Это я уже за Вас сделал, скачайте этот архив: spindash.zip. Достаньте из него spindash.bin и поместите этот файл в папку artunc.
Теперь в исходном коде игры найдите лейбел Art_Sonic и после строчки «incbin artunc\sonic.bin» вставьте:
incbin artunc\spindash.bin ; Spin Dash art
Арт Соника, особенный, так как каждый раз динамически подгружается нужный спрайту набор тайлов. Новый арт требуется вписать в список динамически загружаемых тайлов для дайльнешей работы с ним.
Откройте файл _inc/Sonic dynamic pattern load cues.asm и вставьте эти строки перед лейбелом SonPLC_Blank:
dc.w SonPLC_SpinDash1-SonicDynPLC ;58 dc.w SonPLC_SpinDash2-SonicDynPLC ;59 dc.w SonPLC_SpinDash3-SonicDynPLC ;5A dc.w SonPLC_SpinDash4-SonicDynPLC ;5B dc.w SonPLC_SpinDash5-SonicDynPLC ;5C dc.w SonPLC_SpinDash6-SonicDynPLC ;5D
Эти номерки справа от строк стоят для удобства. Они обозначают номер кадра, который полезно знать для создания покадровой анимации. Мы только что сделали ссылки на лейбелы, в которых содержится информация о подгружаемых тайлах, теперь нужно вставить сами лейбелы.
Почти в самом конце файла, перед строкой «even» вставьте нужные лейбелы:
SonPLC_SpinDash1: dc.b 1, $F5, $10 ; 01 F 510 SonPLC_SpinDash2: dc.b 1, $F5, $20 ; 01 F 520 SonPLC_SpinDash3: dc.b 1, $F5, $30 ; 01 F 530 SonPLC_SpinDash4: dc.b 1, $F5, $40 ; 01 F 540 SonPLC_SpinDash5: dc.b 1, $F5, $50 ; 01 F 550 SonPLC_SpinDash6: dc.b 1, $F5, $60 ; 01 F 560
В комментариях справа от строк дано более удобное представление информации. Данные о подгружаемых тайлах имеют формат NN(STTT), где N - количество следуемых за ним данных о тайлах STTT, S - количество погружаемых тайлов (от 1 до 16), T - номер тайла, с которого начинается подгрузка.
Вся эта динамическая подгрузка нужна для экономии VRAM (видеопамяти). Ее всего 64 Кб, если загрузить туда весь Сониковский арт, а он весит 40 Кб, то не хватит места на остальное.
Но вернемся к делу. Теперь нам нужно слепить спрайты из блоков 8х8, предоставленных в арте.
Откройте файл _maps/Sonic.asm и над строчкой «byte_21292: dc.b 0» вставьте:
dc.w byte_spdh1-Map_Sonic, byte_spdh2-Map_Sonic dc.w byte_spdh3-Map_Sonic, byte_spdh4-Map_Sonic dc.w byte_spdh5-Map_Sonic, byte_spdh6-Map_Sonic
Теперь надо прописать вышеперечисленные лейбелы. Опять, прописывать будем почти в конце файла, перед строкой «even»:
byte_spdh1: dc.b 1 ; Spin Dash 1 dc.b $F8, $F, 0, 0, $F0 byte_spdh2: dc.b 1 ; Spin Dash 2 dc.b $F8, $F, 0, 0, $F0 byte_spdh3: dc.b 1 ; Spin Dash 3 dc.b $F8, $F, 0, 0, $F0 byte_spdh4: dc.b 1 ; Spin Dash 4 dc.b $F8, $F, 0, 0, $F0 byte_spdh5: dc.b 1 ; Spin Dash 5 dc.b $F8, $F, 0, 0, $F0 byte_spdh6: dc.b 1 ; Spin Dash 6 dc.b $F8, $F, 0, 0, $F0
Спрайты готовы. То, что мы добавили, называется Спрайтовыми Маппингами (Sprite Mappings), об их устройстве вы можете посмотреть в справочниках.
Последнее, что осталось сделать — скрепить все спрайты анимацией, которая будет воспроизводится. Откройте _anim/Sonic.asm и перед строкой «SonAni_Walk: dc.b $FF, 8, 9, $A, $B, 6, 7, $FF» вставьте:
dc.w SonAni_SpinDash-SonicAniData ;1F
Справа записан порядковый номер анимации для справки. Теперь перед строкой «even» в конце файла вставьте:
SonAni_SpinDash: dc.b 0, $58, $59, $58, $5A, $58, $5B, $58, $5C, $58, $5D, $FF
В этой строке перечислены по порядку номера кадров, включенные в анимацию.
И нам осталось почти ничего, всего лишь отредактировать код Спин Дэша, чтобы он использовал новую анимацию. Идите к суброутине Sonic_SpinDash и замените «move.b #2,$1C(a0)» на:
move.b #$1F,$1C(a0) ; changed from #2
Теперь идите к лейбелу loc_1AD48 и раскомментируйте строчку «move.w #$1F00,$1C(a0)»
Постройте РОМ, все должно заработать.