Исправить баги в коде спиндэша Sonic 2 и добавить уровни скорости
Шаблон:Translateit In Sonic 2 and Sonic 3 & Knuckles, the spindash counter uses a word value for what's referenced in the spindash speed table as one byte and there's several instances where both word and byte values are used. Also, every time a button is pressed, the spindash counter value increases by 2 instead of 1 causing some spindash speeds to be unused. Due to the word and byte value confusion, the counter goes down rapidly to 0 making the top possible speed of the spindash unobtainable. This can cause some problems during game play, most notably how when charging a spindash with the buggy code on EHZ 2 at the start of the level and going as fast as possible with the code when discharged will cause Sonic to collide with the piranha badnik on the bridge near the start of the level and die. Fixing the code will increase the speed that Sonic can reach while spindashing and the revving down of the spindash will stop, allowing for less frantic button pressing to reach the top speed and the spindash will never have to be re-recharged while in the spindash position.
To fix the spindash code, find:
<asm> Sonic_CheckSpindash: tst.b spindash_flag(a0) bne.s Sonic_UpdateSpindash cmpi.b #8,anim(a0) bne.s return_1AC8C move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 beq.w return_1AC8C move.b #9,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addq.l #4,sp move.b #1,spindash_flag(a0) move.w #0,spindash_counter(a0) cmpi.b #$C,air_left(a0) ; if he's drowning, branch to not make dust bcs.s + move.b #2,(Sonic_Dust+anim).w + bsr.w Sonic_LevelBound bsr.w AnglePos </asm>
and replace it with:
<asm> Sonic_CheckSpindash: tst.b spindash_flag(a0) bne.s Sonic_UpdateSpindash cmpi.b #8,anim(a0) bne.s return_1AC8C move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 beq.w return_1AC8C move.b #9,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addq.l #4,sp move.b #1,spindash_flag(a0) move.b #0,spindash_counter(a0) cmpi.b #$C,air_left(a0) ; if he's drowning, branch to not make dust bcs.s + move.b #2,(Sonic_Dust+anim).w + bsr.w Sonic_LevelBound bsr.w AnglePos </asm>
now find:
<asm> Sonic_UpdateSpindash: move.b (Ctrl_1_Held_Logical).w,d0 btst #1,d0 bne.w Sonic_ChargingSpindash
; unleash the charged spindash and start rolling quickly: move.b #$E,y_radius(a0) move.b #7,x_radius(a0) move.b #2,anim(a0) addq.w #5,y_pos(a0) ; add the difference between Sonic's rolling and standing heights move.b #0,spindash_flag(a0) moveq #0,d0 move.b spindash_counter(a0),d0 add.w d0,d0 </asm>
and replace it with:
<asm> Sonic_UpdateSpindash: move.b (Ctrl_1_Held_Logical).w,d0 btst #1,d0 bne.w Sonic_ChargingSpindash
; unleash the charged spindash and start rolling quickly: move.b #$E,y_radius(a0) move.b #7,x_radius(a0) move.b #2,anim(a0) addq.w #5,y_pos(a0) ; add the difference between Sonic's rolling and standing heights move.b #0,spindash_flag(a0) moveq #0,d0 move.b spindash_counter(a0),d0 add.b d0,d0 </asm>
then find:
<asm> Sonic_ChargingSpindash: ; If still charging the dash... tst.w spindash_counter(a0) beq.s + move.w spindash_counter(a0),d0 lsr.w #5,d0 sub.w d0,spindash_counter(a0) bcc.s + move.w #0,spindash_counter(a0) + move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 beq.w Obj01_Spindash_ResetScr move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.w #$200,spindash_counter(a0) cmpi.w #$800,spindash_counter(a0) bcs.s Obj01_Spindash_ResetScr move.w #$800,spindash_counter(a0) </asm>
and replace it with:
<asm> Sonic_ChargingSpindash: ; If still charging the dash... tst.b spindash_counter(a0) beq.s + move.b spindash_counter(a0),d0 lsr.b #5,d0 sub.b d0,spindash_counter(a0) bcc.s + move.b #0,spindash_counter(a0) + move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 beq.w Obj01_Spindash_ResetScr move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.b #$1,spindash_counter(a0) cmpi.b #$8,spindash_counter(a0) bcs.s Obj01_Spindash_ResetScr move.b #$8,spindash_counter(a0) bra.s Obj01_Spindash_ResetScr move.b #$8,spindash_counter(a0) </asm>
next find:
<asm> CheckGameOver: move.b #1,($FFFFEEBE).w move.b #0,spindash_flag(a0) move.w (Camera_Max_Y_pos_now).w,d0 addi.w #$100,d0 cmp.w y_pos(a0),d0 bge.w return_1B31A move.b #8,routine(a0) ; => Obj01_Gone move.w #$3C,spindash_counter(a0) addq.b #1,(Update_HUD_lives).w ; update lives counter subq.b #1,(Life_count).w ; subtract 1 from number of lives bne.s Obj01_ResetLevel ; if it's not a game over, branch move.w #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 (game over text) move.b #$39,(Object_RAM+$C0).w ; load Obj39 (game over text) move.b #1,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w clr.b (Time_Over_flag).w </asm>
and change it to:
<asm> CheckGameOver: move.b #1,($FFFFEEBE).w move.b #0,spindash_flag(a0) move.w (Camera_Max_Y_pos_now).w,d0 addi.w #$100,d0 cmp.w y_pos(a0),d0 bge.w return_1B31A move.b #8,routine(a0) ; => Obj01_Gone move.b #$3C,spindash_counter(a0) addq.b #1,(Update_HUD_lives).w ; update lives counter subq.b #1,(Life_count).w ; subtract 1 from number of lives bne.s Obj01_ResetLevel ; if it's not a game over, branch move.b #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 (game over text) move.b #$39,(Object_RAM+$C0).w ; load Obj39 (game over text) move.b #1,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w clr.b (Time_Over_flag).w </asm>
now find:
<asm>
Obj01_ResetLevel: tst.b (Time_Over_flag).w beq.s Obj01_ResetLevel_Part2 move.w #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 move.b #$39,(Object_RAM+$C0).w ; load Obj39 move.b #2,(Object_RAM+$80+mapping_frame).w move.b #3,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w bra.s Obj01_Finished </asm>
and replace it with:
<asm> Obj01_ResetLevel: tst.b (Time_Over_flag).w beq.s Obj01_ResetLevel_Part2 move.b #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 move.b #$39,(Object_RAM+$C0).w ; load Obj39 move.b #2,(Object_RAM+$80+mapping_frame).w move.b #3,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w bra.s Obj01_Finished </asm>
then find:
<asm> Obj01_ResetLevel_Part2: tst.w (Two_player_mode).w beq.s return_1B31A move.b #0,($FFFFEEBE).w move.b #$A,routine(a0) ; => Obj01_Respawning move.w (Saved_x_pos).w,x_pos(a0) move.w (Saved_y_pos).w,y_pos(a0) move.w (Saved_art_tile).w,art_tile(a0) move.w (Saved_layer).w,layer(a0) clr.w (Ring_count).w clr.b (Extra_life_flags).w move.b #0,obj_control(a0) move.b #5,anim(a0) move.w #0,x_vel(a0) move.w #0,y_vel(a0) move.w #0,inertia(a0) move.b #2,status(a0) move.w #0,move_lock(a0) move.w #0,spindash_counter(a0) </asm>
and replace it with:
<asm> Obj01_ResetLevel_Part2: tst.w (Two_player_mode).w beq.s return_1B31A move.b #0,($FFFFEEBE).w move.b #$A,routine(a0) ; => Obj01_Respawning move.w (Saved_x_pos).w,x_pos(a0) move.w (Saved_y_pos).w,y_pos(a0) move.w (Saved_art_tile).w,art_tile(a0) move.w (Saved_layer).w,layer(a0) clr.w (Ring_count).w clr.b (Extra_life_flags).w move.b #0,obj_control(a0) move.b #5,anim(a0) move.w #0,x_vel(a0) move.w #0,y_vel(a0) move.w #0,inertia(a0) move.b #2,status(a0) move.b #0,move_lock(a0) move.b #0,spindash_counter(a0) </asm>
now find:
<asm> Obj01_Gone: tst.w spindash_counter(a0) beq.s + subq.w #1,spindash_counter(a0) bne.s + move.w #1,(Level_Inactive_flag).w + rts </asm>
and then change it to:
<asm> Obj01_Gone: tst.b spindash_counter(a0) beq.s + subq.b #1,spindash_counter(a0) bne.s + move.w #1,(Level_Inactive_flag).w + rts </asm>
Go to:
<asm> TailsCPU_Respawn: move.w #4,(Tails_CPU_routine).w ; => TailsCPU_Flying move.w x_pos(a1),d0 move.w d0,x_pos(a0) move.w d0,(Tails_CPU_target_x).w move.w y_pos(a1),d0 move.w d0,(Tails_CPU_target_y).w subi.w #$C0,d0 move.w d0,y_pos(a0) ori.w #$8000,art_tile(a0) move.b #0,spindash_flag(a0) move.w #0,spindash_counter(a0) </asm>
and change it to:
<asm> TailsCPU_Respawn: move.w #4,(Tails_CPU_routine).w ; => TailsCPU_Flying move.w x_pos(a1),d0 move.w d0,x_pos(a0) move.w d0,(Tails_CPU_target_x).w move.w y_pos(a1),d0 move.w d0,(Tails_CPU_target_y).w subi.w #$C0,d0 move.w d0,y_pos(a0) ori.w #$8000,art_tile(a0) move.b #0,spindash_flag(a0) move.b #0,spindash_counter(a0) </asm>
Now find:
<asm> TailsCPU_Normal: cmpi.b #6,(MainCharacter+routine).w ; is Sonic dead? bcs.s TailsCPU_Normal_SonicOK ; if not, branch ; Sonic's dead; fly down to his corpse move.w #4,(Tails_CPU_routine).w ; => TailsCPU_Flying move.b #0,spindash_flag(a0) move.w #0,spindash_counter(a0) move.b #$81,obj_control(a0) move.b #2,status(a0) move.b #$20,anim(a0) rts </asm>
then replace it with:
<asm> TailsCPU_Normal: cmpi.b #6,(MainCharacter+routine).w ; is Sonic dead? bcs.s TailsCPU_Normal_SonicOK ; if not, branch ; Sonic's dead; fly down to his corpse move.w #4,(Tails_CPU_routine).w ; => TailsCPU_Flying move.b #0,spindash_flag(a0) move.b #0,spindash_counter(a0) move.b #$81,obj_control(a0) move.b #2,status(a0) move.b #$20,anim(a0) rts </asm>
now find:
<asm> Tails_CheckSpindash: tst.b spindash_flag(a0) bne.s Tails_UpdateSpindash cmpi.b #8,anim(a0) bne.s return_1C75C move.b (Ctrl_2_Press_Logical).w,d0 andi.b #$70,d0 beq.w return_1C75C move.b #9,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addq.l #4,sp move.b #1,spindash_flag(a0) move.w #0,spindash_counter(a0) cmpi.b #$C,air_left(a0) ; if he's drowning, branch to not make dust bcs.s loc_1C754 move.b #2,(Tails_Dust+anim).w </asm>
then change it to:
<asm> Tails_CheckSpindash: tst.b spindash_flag(a0) bne.s Tails_UpdateSpindash cmpi.b #8,anim(a0) bne.s return_1C75C move.b (Ctrl_2_Press_Logical).w,d0 andi.b #$70,d0 beq.w return_1C75C move.b #9,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addq.l #4,sp move.b #1,spindash_flag(a0) move.b #0,spindash_counter(a0) cmpi.b #$C,air_left(a0) ; if he's drowning, branch to not make dust bcs.s loc_1C754 move.b #2,(Tails_Dust+anim).w </asm>
then find:
<asm> Tails_UpdateSpindash: move.b (Ctrl_2_Held_Logical).w,d0 btst #1,d0 bne.s Tails_ChargingSpindash
; unleash the charged spindash and start rolling quickly: move.b #$E,y_radius(a0) move.b #7,x_radius(a0) move.b #2,anim(a0) addq.w #1,y_pos(a0) ; add the difference between Tails' rolling and standing heights move.b #0,spindash_flag(a0) moveq #0,d0 move.b spindash_counter(a0),d0 add.w d0,d0 </asm>
and replace it with:
<asm> Tails_UpdateSpindash: move.b (Ctrl_2_Held_Logical).w,d0 btst #1,d0 bne.s Tails_ChargingSpindash
; unleash the charged spindash and start rolling quickly: move.b #$E,y_radius(a0) move.b #7,x_radius(a0) move.b #2,anim(a0) addq.w #1,y_pos(a0) ; add the difference between Tails' rolling and standing heights move.b #0,spindash_flag(a0) moveq #0,d0 move.b spindash_counter(a0),d0 add.w d0,d0 </asm>
now find:
<asm> Tails_ChargingSpindash: ; If still charging the dash... tst.w spindash_counter(a0) beq.s loc_1C7F8 move.w spindash_counter(a0),d0 lsr.w #5,d0 sub.w d0,spindash_counter(a0) bcc.s loc_1C7F8 move.w #0,spindash_counter(a0) </asm>
and replace it with:
<asm> Tails_ChargingSpindash: ; If still charging the dash... tst.b spindash_counter(a0) beq.s loc_1C7F8 move.b spindash_counter(a0),d0 lsr.b #5,d0 sub.b d0,spindash_counter(a0) bcc.s loc_1C7F8 move.b #0,spindash_counter(a0) </asm>
Now find:
<asm> loc_1C7F8: move.b (Ctrl_2_Press_Logical).w,d0 andi.b #$70,d0 beq.w loc_1C828 move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.w #$200,spindash_counter(a0) cmpi.w #$800,spindash_counter(a0) bcs.s loc_1C828 move.w #$800,spindash_counter(a0) </asm>
and replace it with:
<asm> loc_1C7F8: move.b (Ctrl_2_Press_Logical).w,d0 andi.b #$70,d0 beq.w loc_1C828 move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.b #$1,spindash_counter(a0) cmpi.b #$8,spindash_counter(a0) bcs.s loc_1C828 move.b #$8,spindash_counter(a0) bra.s loc_1C828 move.b #$8,spindash_counter(a0) </asm>
Now find:
<asm> Obj02_CheckGameOver_2Pmode: addq.b #1,(Update_HUD_lives_2P).w subq.b #1,(Life_count_2P).w bne.s Obj02_ResetLevel move.w #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 move.b #$39,(Object_RAM+$C0).w ; load Obj39 move.b #1,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w clr.b (Time_Over_flag_2P).w </asm>
and replace it with:
<asm> Obj02_CheckGameOver_2Pmode: addq.b #1,(Update_HUD_lives_2P).w subq.b #1,(Life_count_2P).w bne.s Obj02_ResetLevel move.b #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 move.b #$39,(Object_RAM+$C0).w ; load Obj39 move.b #1,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w clr.b (Time_Over_flag_2P).w </asm>
Now find:
<asm> Obj02_ResetLevel: tst.b (Time_Over_flag).w beq.s Obj02_ResetLevel_Part2 tst.b (Time_Over_flag_2P).w beq.s Obj02_ResetLevel_Part3 move.w #0,spindash_counter(a0) clr.b (Update_HUD_timer).w clr.b (Update_HUD_timer_2P).w move.b #8,routine(a0) rts
- ---------------------------------------------------------------------------
Obj02_ResetLevel_Part2: tst.b (Time_Over_flag_2P).w beq.s Obj02_ResetLevel_Part3 move.w #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 move.b #$39,(Object_RAM+$C0).w ; load Obj39 move.b #2,(Object_RAM+$80+mapping_frame).w move.b #3,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w bra.s Obj02_Finished </asm>
and replace it with:
<asm> Obj02_ResetLevel: tst.b (Time_Over_flag).w beq.s Obj02_ResetLevel_Part2 tst.b (Time_Over_flag_2P).w beq.s Obj02_ResetLevel_Part3 move.b #0,spindash_counter(a0) clr.b (Update_HUD_timer).w clr.b (Update_HUD_timer_2P).w move.b #8,routine(a0) rts
- ---------------------------------------------------------------------------
Obj02_ResetLevel_Part2: tst.b (Time_Over_flag_2P).w beq.s Obj02_ResetLevel_Part3 move.b #0,spindash_counter(a0) move.b #$39,(Object_RAM+$80).w ; load Obj39 move.b #$39,(Object_RAM+$C0).w ; load Obj39 move.b #2,(Object_RAM+$80+mapping_frame).w move.b #3,(Object_RAM+$C0+mapping_frame).w move.w a0,(Object_RAM+$80+parent).w bra.s Obj02_Finished </asm>
then find:
<asm> Obj02_Gone: tst.w spindash_counter(a0) beq.s + subq.w #1,spindash_counter(a0) bne.s + move.w #1,(Level_Inactive_flag).w + rts </asm>
and change it to:
<asm> Obj02_Gone: tst.b spindash_counter(a0) beq.s + subq.b #1,spindash_counter(a0) bne.s + move.w #1,(Level_Inactive_flag).w + rts </asm>
Doing this also helps to free up an OST Value. ($3B)
Now, if needed, you can add more spindash values by following these steps.
Replace:
<asm> Sonic_ChargingSpindash: ; If still charging the dash... tst.b spindash_counter(a0) beq.s + move.b spindash_counter(a0),d0 lsr.b #5,d0 sub.b d0,spindash_counter(a0) bcc.s + move.b #0,spindash_counter(a0) + move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 beq.w Obj01_Spindash_ResetScr move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.b #$1,spindash_counter(a0) cmpi.b #$8,spindash_counter(a0) bcs.s Obj01_Spindash_ResetScr move.b #$8,spindash_counter(a0) bra.s Obj01_Spindash_ResetScr move.b #$8,spindash_counter(a0) </asm>
and:
<asm> loc_1C7F8: move.b (Ctrl_2_Press_Logical).w,d0 andi.b #$70,d0 beq.w loc_1C828 move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.b #$1,spindash_counter(a0) cmpi.b #$8,spindash_counter(a0) bcs.s loc_1C828 move.b #$8,spindash_counter(a0) bra.s loc_1C828 move.b #$8,spindash_counter(a0) </asm>
with:
<asm> Sonic_ChargingSpindash: ; If still charging the dash... tst.b spindash_counter(a0) beq.s + move.b spindash_counter(a0),d0 lsr.b #5,d0 sub.b d0,spindash_counter(a0) bcc.s + move.b #0,spindash_counter(a0) + move.b (Ctrl_1_Press_Logical).w,d0 andi.b #$70,d0 beq.w Obj01_Spindash_ResetScr move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.b #$1,spindash_counter(a0) cmpi.b #$X,spindash_counter(a0) bcs.s Obj01_Spindash_ResetScr move.b #$X,spindash_counter(a0) bra.s Obj01_Spindash_ResetScr move.b #$X,spindash_counter(a0) </asm>
and:
<asm> loc_1C7F8: move.b (Ctrl_2_Press_Logical).w,d0 andi.b #$70,d0 beq.w loc_1C828 move.w #$900,anim(a0) move.w #$E0,d0 jsr (PlaySound).l addi.b #$1,spindash_counter(a0) cmpi.b #$X,spindash_counter(a0) bcs.s loc_1C828 move.b #$X,spindash_counter(a0) bra.s loc_1C828 move.b #$X,spindash_counter(a0) </asm>
then add the needed speeds to equal $X to Sonic_SpindashSpeeds, SpindashSpeedsSuper, and Tails_SpindashSpeeds.
Here's a ROM File with all the changes made in this guide. (информация)
[[Категория:Как сделать]]