Add Spin Dash to Sonic 1/Part 1
From Sonic Retro
(Redirected from SCHG How-to:Add Spin Dash to Sonic 1 (GitHub))
(Original guide by DikinBaus)
(Based on the original guide by Lightning)
In this guide, we'll be porting the Spin Dash from Sonic 2 into Sonic 1. It is designed to work with the latest version of the Sonic 1 GitHub disassembly.
This tutorial was made possible thanks to the references, guides, and information found at Hacking CulT, The Sonic 2 Beta Page, Sonic Retro, and Sonicology. It was inspired by the guide at The Glowing Bridge that details porting the simpler Spin Dash from the Sonic 2 early beta, as well as Lightning's original guide that expands upon the former.
This newly-revamped guide is based off of the original version of the guide that was targeted for the legacy Hivebrain 2005 disassembly of Sonic 1. Thanks to Kramlat for making some additional updates to the original guide before this one. Additional updates to the guide have also been provided by JGMR, new minor fixes by DikinBaus.
Sonic and all related characters and graphics that appear on this page are property of SEGA and are used without permission for no profit.
Contents
Requirements
- Sonic 1 split disassembly
- Sonic 2 split disassembly (only necessary if you want to grab the spindash sprites yourself)
- A plain text editor of your choice
- A tile/mappings editor capable of editing Genesis format tiles
- A graphics editor of your choice
- A Sega Genesis emulator of your choice (or real hardware with a flashcart)
In making this tutorial, I used:
- Sonic Retro's GitHub Sonic 1 split disassembly (latest, AS version)
- Sonic Retro's GitHub Sonic 2 split disassembly (latest)
- Windows Notepad - simple text editor bundled with Microsoft Windows
- Notepad++ 7.9.5 - free and open-source text and source code editor
- Flex 2 1.3.3 - Universal Genesis sprite editor
- GIMP 2.10.38 - GNU raster graphics editor
- Kega Fusion 3.64 - Cross-platform Genesis emulator
Other disassemblies of Sonic 1 and 2 can be used, but you'll have to work out the differences on your own (it shouldn't be too hard to figure it out).
Porting the Spin Dash
Now that you've gotten the required tools and disassemblies of your choice, it's time to start porting over the Spin Dash into Sonic 1!
Adding new flags
Before we can start, we'll need to do some prerequisites before we can port over our code. For the purposes of this tutorial, I'll assume you do it that way too. Open Constants.asm and search for stick_to_convex:. There you will define a new RAM equate by adding the following lines underneath it:
spindash_flag: equ $39 ; spin dash flag
spindash_counter: equ $3A ; spin dash counter
This should preferably be put in between the stick_to_convex: and standonobject: variables. We could skip this, but then we'll have to replace all spindash_flag in the code for $39 and spindash_counter in the code for $3A, and that's not what we want.
Next, open Variables.asm and above v_shieldobj = v_objspace+object_size*6, add this in:
v_spindust = v_objspace+object_size*4 ; object variable space for the spin dash dust ($40 bytes)
The code should look like this:
v_spindust = v_objspace+object_size*4 ; object variable space for the spin dash dust ($40 bytes)
v_shieldobj = v_objspace+object_size*6 ; object variable space for the shield ($40 bytes)
v_starsobj1 = v_objspace+object_size*8 ; object variable space for the invincibility stars #1 ($40 bytes)
v_starsobj2 = v_objspace+object_size*9 ; object variable space for the invincibility stars #2 ($40 bytes)
v_starsobj3 = v_objspace+object_size*10 ; object variable space for the invincibility stars #3 ($40 bytes)
v_starsobj4 = v_objspace+object_size*11 ; object variable space for the invincibility stars #4 ($40 bytes)
Directly copying the object code
Open sonic.asm in your text editor, and search for SonicPlayer:. This should bring you to the Sonic object's code, which is what you're interested in editing right now. For reference, go to your Sonic 2 disassembly and open up s2.asm. Search for Obj01: (like you would in the legacy Hivebrain 2005 disassembly of Sonic 1), and it will once again take you to Sonic's code. Having been based on the same code from Sonic 1, it shows many similarities that you should be able to spot. Thanks to the similar code, our job in porting the Spin Dash should be pretty easy!
Find or scroll to the location Sonic_MdNormal: (line 6948) in Sonic 1's source, and Obj01_MdNormal: in Sonic 2's source. Below this label in each is a series of branches and jumps to subroutines that manage Sonic's actions in different situations. If you look carefully, you'll notice that there are nine of these branches in Sonic 2, but only eight in Sonic 1. That's because Sonic 2 has another branch to the code that handles the Spin Dash.
In the sonic.asm file of your Sonic 1 disassembly, make a copy of the line bsr.w Sonic_Jump and change it to bsr.w Sonic_SpinDash, as shown:
Sonic_MdNormal:
bsr.w Sonic_SpinDash ; <-- add this line!
bsr.w Sonic_Jump
...
Don't worry about the code below bsr.w Sonic_Jump, as we won't be touching them.
Now, to actually copy the Spin Dash subroutine. For this, we'll need to make a new ASM file. So create a new file within the _incObj folder and name it "Sonic SpinDash.asm". Now copy the code below and paste it into the Sonic SpinDash.asm file.
; ---------------------------------------------------------------------------
; Subroutine to check for starting to charge a spindash
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
; loc_1AC3E:
Sonic_SpinDash:
tst.b spindash_flag(a0)
bne.s Sonic_UpdateSpindash
cmpi.b #id_Duck,obAnim(a0)
bne.s return_1AC8C
move.b (v_jpadpress2).w,d0
andi.b #$70,d0
beq.w return_1AC8C
move.b #$1F,obAnim(a0)
move.w #$BE,d0
jsr (PlaySound_Special).l
addq.l #4,sp
move.b #1,spindash_flag(a0)
move.w #0,$3A(a0)
cmpi.b #$C,$28(a0) ; if he's drowning, branch to not make dust
blo.s loc_1AC84
move.b #2,($FFFFD11C).w
loc_1AC84:
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
return_1AC8C:
rts
; End of subroutine Sonic_CheckSpindash
; ---------------------------------------------------------------------------
; Subrouting to update an already-charging spindash
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
; loc_1AC8E:
Sonic_UpdateSpindash:
move.b #$1F,$1C(a0)
move.b (v_jpadhold2).w,d0
btst #1,d0
bne.w Sonic_ChargingSpindash
; unleash the charged spindash and start rolling quickly:
move.b #$E,$16(a0)
move.b #7,$17(a0)
move.b #2,obAnim(a0)
addq.w #5,obY(a0) ; add the difference between Sonic's rolling and standing heights
move.b #0,spindash_flag(a0)
moveq #0,d0
move.b $3A(a0),d0
add.w d0,d0
move.w SpindashSpeeds(pc,d0.w),obInertia(a0)
move.w obInertia(a0),d0
subi.w #$800,d0
add.w d0,d0
andi.w #$1F00,d0
neg.w d0
addi.w #$2000,d0
move.w d0,($FFFFEED0).w
btst #0,$22(a0)
beq.s loc_1ACF4
neg.w $14(a0)
loc_1ACF4:
bset #2,$22(a0)
move.b #0,($FFFFD11C).w
move.w #$BC,d0
jsr (PlaySound_Special).l
bra.s Obj01_Spindash_ResetScr
; ===========================================================================
; word_1AD0C:
SpindashSpeeds:
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
; ===========================================================================
; loc_1AD30:
Sonic_ChargingSpindash: ; If still charging the dash...
tst.w $3A(a0)
beq.s loc_1AD48
move.w $3A(a0),d0
lsr.w #5,d0
sub.w d0,$3A(a0)
bcc.s loc_1AD48
move.w #0,$3A(a0)
loc_1AD48:
move.b (v_jpadpress2).w,d0
andi.b #$70,d0 ; 'p'
beq.w Obj01_Spindash_ResetScr
move.w #$1F00,$1C(a0)
move.w #$BE,d0 ; 'à'
jsr (PlaySound_Special).l
addi.w #$200,$3A(a0)
cmpi.w #$800,$3A(a0)
blo.s Obj01_Spindash_ResetScr
move.w #$800,$3A(a0)
; loc_1AD78:
Obj01_Spindash_ResetScr:
addq.l #4,sp
cmpi.w #(224/2)-16,($FFFFEED8).w
beq.s loc_1AD8C
bhs.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,(v_lookshift).w
rts
; End of subroutine Sonic_UpdateSpindash
Now everything should be there. Let's test it! First, save your changes, and then build the ROM. Then, test it out in the emulator, and...
Bugs
Uh-oh. Things are looking pretty rough right now.
Okay, so as we've expected, there are some bugs. First off, Sonic's Spin Dashing animation isn't present in Sonic 1, of course, and right now it's using the animation from the spot that the Spin Dash's animation data replaced, we fix this in the "Adding new sprites" and "Adding animation". "Also, the sound when you charge a Spin Dash isn't the same from Sonic 2's. That's because the sound ID that is the Spin Dash sound in Sonic 2 has a different purpose in Sonic 1.
Second, if you try to charge a Spin Dash while having rings and a badnik starts attacking you, they can still hurt you in your Spin Dashing state. Sonic gets injured, when instead the enemy should be destroyed. If you get hit while charging, you'll still be in a Spin Dash-charging state when you land. If you let go of the down button while bouncing backwards, Sonic will release his Spin Dash right away when he hits the ground (depending on which way you got hit; forwards or backwards). Now, this might be awesome in itself and certainly should kill whatever badnik hurt you in the first place, but it isn't what should happen.
Third, move somewhere where the camera has room to move downward. If you hold the down button to duck, then start charging the Spin Dash, the camera stays low until you are no longer rolling/Spin Dashing.
Another problem that results from this is that if you Spin Dash before an area where your vertical position radically changes (for instance, the double-S-tube in GHZ1), the camera is too slow to catch up to Sonic in time.
Aside from all of those bugs, the physics of the Spin Dash are clearly in place, however. This is a good start!
Fixing bugs/errors
First of all, let's fix the camera issue. Go back to the Spin Dash code and replace instances of $FFFFEED8 with v_lookshift. Build and test. You should find that the camera does reset itself when you start to Spin Dash, but if you take the double-S-tube in GHZ1, you still barely outrun the screen vertically and get killed. So to fix this, go into the _incObj folder and open the file "Sonic LevelBound.asm". Find the code at the label .bottom:. Here's what you should see:
.bottom:
cmpi.w #(id_SBZ<<8)+1,(v_zone).w ; is level SBZ2 ?
bne.w KillSonic ; if not, kill Sonic
cmpi.w #$2000,(v_player+obX).w
blo.w KillSonic
clr.b (v_lastlamp).w ; clear lamppost counter
move.w #1,(f_restart).w ; restart the level
move.w #(id_LZ<<8)+3,(v_zone).w ; set level to SBZ3 (LZ4)
rts
First, add a label just before the rts at the end of the block of code, named .dontkill. Then, at the beginning of .bottom:, you're going to compare two values in memory. If (v_limitbtm1).w < (v_limitbtm2).w, then the screen is still scrolling down and you don't want to die. In this case, it should skip the part below that jumps to the routine KillSonic. You should add the following lines of code below .bottom::
move.w (v_limitbtm1).w,d0
move.w (v_limitbtm2).w,d1
cmp.w d0,d1 ; screen still scrolling down?
blt.s .dontkill ; if so, don't kill Sonic
You should end up with something like this:
.bottom:
move.w (v_limitbtm1).w,d0
move.w (v_limitbtm2).w,d1
cmp.w d0,d1 ; screen still scrolling down?
blt.s .dontkill ; if so, don't kill Sonic
cmpi.w #(id_SBZ<<8)+1,(v_zone).w ; is level SBZ2 ?
bne.w KillSonic ; if not, kill Sonic
cmpi.w #$2000,(v_player+obX).w
blo.w KillSonic
clr.b (v_lastlamp).w ; clear lamppost counter
move.w #1,(f_restart).w ; restart the level
move.w #(id_LZ<<8)+3,(v_zone).w ; set level to SBZ3 (LZ4)
rts
; ===========================================================================
.dontkill:
rts
Now, you'll fix the problem where Sonic is still in his Spin Dashing state after he gets injured. Open sub ReactToItem.asm located in _incObj and find the subroutine HurtSonic. This is the code that handles hurting Sonic, if you couldn't guess. You're going to have it clear the flag in memory that stores whether or not Sonic is Spin Dashing. Scroll to the label .isleft and add the line move.b #0,(a0) directly below it. You should end up with:
.isleft:
move.b #0,spindash_flag(a0) ; clear Spin Dash flag
move.w #0,obInertia(a0)
move.b #id_Hurt,obAnim(a0)
move.w #120,flashtime(a0) ; set temp invincible time to 2 seconds
Save, build, and test. See if you can try to outrun the camera in the S-tube in GHZ1 and try to see if you're not in a Spin Dashing state after getting hurt. If all goes well, those pesky bugs should be gone! As for the animation, we'll deal with that in the Adding new sprites section of the guide
Adding new sprites
As mentioned before, the Spin Dash that we have doesn't look like the one found in Sonic 2. Sonic doesn't have a dedicated Spin Dash sprite as it's not present in Sonic 1, so we need to implement the sprites for it. So for this section, you'll add the Spin Dash sprites from Sonic 2's sprites to Sonic 1's. To do this, we'll need a tile/sprite/mappings editor of our choice. We'll be using Flex 2 for this guide.
Open up sonic1.flex.json in your disassembly via Flex 2 and select the "Sonic" object under the "Common" folder. Once there, click on the "Load" button under Object and...
Uh-oh, looks like we have problems yet again. But, not to worry, we can try selecting "Sonic 2" under Game Format, and let’s see what we got:
Success! Our object now displays as intended!
Now create a new object by hitting the "Object" button, and it should appear above the "Common" folder. We could name our new object to anything we want. Select your newly-created object, and begin loading in your Sonic 2 files. Here's what you will need to find as far as Sonic 2 files go:
- Sonic's art tiles (found under "art\uncompressed" with the filename "Sonic's art.bin")
- Sonic's mappings (found under "mappings\sprite" with the filename "Sonic.asm")
- Sonic's DPLCs (found under "mappings\spriteDPLC" with the filename "Sonic.asm"; rename as "SonicDPLC.asm")
As for the palette, we'll continue to use Sonic's palette from our Sonic 1 disassembly. This way the sprites will still remain consistent in terms of the color palette, otherwise they will use the nearest matching color palette for our Spin Dash sprites if we import them with the Sonic 2 palette, and that’s not what we want.
Now we’ll create a new folder on our disassembly for the time being to put our Sonic 2 files in. We can name this folder to anything we want. Then, we’ll head over to our Sonic 2 disassembly, copy our provided files, and then paste them into our new folder. To save you some time, I have provided the Sonic 2 files here for download. Once you have downloaded the archive containing the files, extract them to the folder that you will be putting the Sonic 2 files in.
Now load those files into Flex 2, select "Sonic 2" under Game Format, and then press the "Load" button near Object. Here's what we should get:
Success! The basic setup is complete!
Now comes the long part, extracting the Spin Dash sprites. Head over to the "Mappings" tab and press the "Export Spritesheet" option (or press "E" (uppercase) on your keyboard) and save it. Once finished, open your exported sprite sheet in a raster graphics editor of your choice (I'll be using GIMP for this part of the guide). You should see a big list of all of the sprites that we just exported. Make sure to find where the Spin Dash sprites are located. Now you want to crop the image down to focus only on the Spin Dash sprites. Use the selection tool to select the Spin Dash sprites as shown:
Now find the option to crop the image. If you're using GIMP, go to "Image" => "Crop to Selection". You should be left with this:
Now save your image file as a new image by going to "File" => "Export As...", and then save your new image in the folder that you put the Sonic 2 files in. Make sure the file extension ".png" is defined, as we'll be saving in this format. You can name your new image to anything you want; I will be using "spindash.png" as an example. For your convenience, I have provided the sprite images that you'll be needing for this section here; download it and then extract it to the folder you put the Sonic 2 files in.
Then, head over to Flex 2 again, select the "Project" tab, click on the "Sonic" object under the "Common" folder, load it, and then head back to the "Mappings" tab. Click on the Import Spritesheet button (or pressing "s" (lowercase) on your keyboard) and select the Spin Dash sprite you have saved. You should see a screen that looks like this:
Leave all of the other options alone for now, as we won't be messing around with them in this guide. Click on Detect Sprites, and you should see all of the Spin Dash sprites being highlighted. Click on Import 6 Sprites. This will take you to the Import Sprite screen for each individual sprite, as shown below:
To save us some time in importing each and every single sprite, We'll click on Import All to import all of our Spin Dash sprites. We'll leave all of the other options alone for now. Once finished, we should see our Spin Dash sprites that we added once you scroll down to the bottom under the "Sprites" section.
Next, we'll adjust the positioning of them. To do that, we'll select each individual Spin Dash sprite one by one from our selection and then hitting "Ctrl+A" (or by clicking and dragging the mouse cursor) to select the whole sprite as shown:
Then, we'll start moving the sprites down by 9 pixels and to the right by 5 pixels. This will ensure that our newly-added Spin Dash sprites match up with the original Sonic 2 sprites in terms of the positioning. Repeat this step for every single Spin Dash sprite. There is a very handy indicator that shows you the positioning of the sprites when you have selected the whole sprite (or even individual sprite pieces), as shown here (highlighted in red):
Make sure to double-check the positioning of your newly-added sprites by selecting one sprite or another that has the same positioning as the one you've done several times. You can also scroll the horizontal scroll bar to easily see the position of the sprites. Go ahead and save your files by going to the "Project" tab, clicking on the "Sonic" object under the "Common" folder, and then hitting the "Save" button under Object. Make sure to switch the Game Format to "Sonic 1" before saving.
Now you're ready to build your ROM. BUT WAIT! Don't go for that "build.bat" option yet! We've only just scratched the surface!
Adding animations
We still need to set our animation for our new Spin Dash sprites that we've just imported. Go to Sonic.asm (or Sonic (without frame IDs).asm) in the _anim folder and locate ptr_Float4:. You'll see this:
ptr_Float4: dc.w SonAni_Float4-Ani_Sonic
Add this underneath:
ptr_Spindash: dc.w SonAni_Spindash-Ani_Sonic ;1F
The ;1F comment after it is again an index in the table. If you were to enumerate all of the lines in this table, starting at zero, this new line would be the $1Fth line. Notice that at index 2 is the rolling animation that we are currently using, and at index 9 is the warping one that it used before we fixed it!
Once that's done, locate SonAni_Float4:. You'll see this:
SonAni_Float4: dc.b 3, fr_Float1, afChange, id_Walk
even
Now, you will again add data just after the even at the end of the second table:
SonAni_Spindash: dc.b 0, fr_Spindash1, fr_Spindash2, fr_Spindash1, fr_Spindash3, fr_Spindash1, fr_Spindash4
dc.b fr_Spindash1, fr_Spindash5, fr_Spindash1, fr_Spindash6, afEnd
even
The first byte in this animation script tells the game to use the fastest speed in this animation. Each subsequent word until the afEnd tells the game which animation frame to use. The afEnd at the end tells the game to loop the entire animation. Basically, this script will have the game cycle through the 1st, 2nd, 1st, 3rd, ..., 1st, 6th, 1st, 2nd, ... frames.
Notice that you could have use numbers ($58, $59, etc...) as they are the indexes of your Spin Dash PLCs. In the _anim folder of your disassembly, there is an alternative animation file that does not use frame IDs, titled Sonic (without frame IDs).asm. This is the code that you should looking at when it has been done this way:
SonAni_SpinDash:
dc.b 0, $58, $59, $58, $5A, $58, $5B, $58, $5C, $58, $5D, afEnd
even
Paste this underneath the animation data for SonAni_Float4: within Sonic (without frame IDs).asm.
Now go to the very end of the file and add this line:
id_Spindash: equ (ptr_Spindash-Ani_Sonic)/2 ; $1F
Now to call the frame IDs for the Spin Dash animation. Go to Constants.asm and look for fr_WaterSlide: equ $57. Underneath it, insert these lines:
fr_Spindash1: equ $58
fr_Spindash2: equ $59
fr_Spindash3: equ $5A
fr_Spindash4: equ $5B
fr_Spindash5: equ $5C
fr_Spindash6: equ $5D
Now that you have added all the necessary information for using the new Spin Dash tiles in an animation, it's time for the last step in adding this animation: actually having the game use it! Open Sonic SpinDash.asm in your text editor (if you haven't already) and replace instances of id_Roll with id_Spindash, making sure to not touch the other id_Roll animation under Sonic_UpdateSpindash:.
Now let's save our progress, build the ROM, and test it out in our emulator to see how it goes:
Success! We now have a Spin Dash that looks and functions identical to Sonic 2! BUT WAIT! We still haven't forgotten about the Spin Dashing badnik bug! So even though we now have the new animation in place, the game’s hurt-checking routine doesn't recognize this new animation that we just set, so if a badnik touches Sonic while Spin Dashing, he will be hurt instead of actually killing the badnik.
Final bugfix
So to fix this prior bug once and for all, go back to sub ReactToItem under _incObj and look for React_Enemy:. Find this piece of code:
React_Enemy:
tst.b (v_invinc).w ; is Sonic invincible?
bne.s .donthurtsonic ; if yes, branch
cmpi.b #id_Roll,obAnim(a0) ; is Sonic rolling/jumping?
bne.w React_ChkHurt ; if not, branch
Just above the line cmpi.b #id_Roll,obAnim(a0) ; is Sonic rolling/jumping?, add these lines underneath:
cmpi.b #id_Spindash,obAnim(a0) ; is Sonic Spin Dashing?
beq.w .donthurtsonic ; if yes, branch
You should end up with something like this:
React_Enemy:
tst.b (v_invinc).w ; is Sonic invincible?
bne.s .donthurtsonic ; if yes, branch
cmpi.b #id_Spindash,obAnim(a0) ; is Sonic Spin Dashing?
beq.w .donthurtsonic ; if yes, branch
cmpi.b #id_Roll,obAnim(a0) ; is Sonic rolling/jumping?
bne.w React_ChkHurt ; if not, branch
This code adds a check to see if the animation is id_Spindash (#$1F), our new animation. If it is, it branches over the code that jumps to the routine to hurt Sonic. Notice that there is another comparison for if the animation id_Roll (#2) is present, which concealed this bug before, since we were using that animation.
Save, build, and test. Let's test our Spin Dash against a badnik:
Pop! Congratulations! We have now successfully imported the Spin Dash into Sonic 1! It's still not fully complete though, as it's missing a few bells and whistles. We have to add the Spin Dash smoke/dust object, as well as fixing a few more bugs. Luckily, Puto has written a wonderful guide continuing from this point that does exactly that.
Complete Spin Dash code
For your convenience, here's the full Spin Dash code after following this guide, complete with commenting:
; ---------------------------------------------------------------------------
; Subroutine to make Sonic perform a spindash
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
Sonic_SpinDash:
tst.b spindash_flag(a0) ; already Spin Dashing?
bne.s Sonic_UpdateSpindash ; if set, branch
cmpi.b #id_Duck,anim(a0) ; is anim duck?
bne.s return_1AC8C ; if not, return
move.b (v_jpadpress2).w,d0 ; read controller
andi.b #btnB|btnC|btnA,d0 ; pressing A/B/C ?
beq.w return_1AC8C ; if not, return
move.b #id_Spindash,anim(a0) ; set Spin Dash anim (9 in s2)
move.w #sfx_Roll,d0 ; spin sound ($E0 in s2)
jsr (PlaySound_Special).l ; play spin sound
addq.l #4,sp ; increment stack ptr
move.b #1,spindash_flag(a0) ; set Spin Dash flag
move.w #0,spindash_counter(a0) ; set charge count to 0
cmpi.b #12,air_left(a0) ; if he's drowning, branch to not make dust
blo.s +
move.b #2,(v_spindust+anim).w ; set Spin Dash dust anim to 2
+
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
return_1AC8C:
rts
; End of function Sonic_SpinDash
; ---------------------------------------------------------------------------
; Subroutine to update an already-charging spindash
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
Sonic_UpdateSpindash:
move.b (v_jpadhold2).w,d0 ; read controller
btst #bitDn,d0 ; check down button
bne.w Sonic_ChargingSpindash ; if set, branch
; unleash the charged spindash and start rolling quickly:
move.b #$E,y_radius(a0) ; y_radius(a0) is height/2
move.b #7,x_radius(a0) ; x_radius(a0) is width/2
move.b #id_Roll,anim(a0) ; set animation to roll
addq.w #5,y_pos(a0) ; add the difference between Sonic's rolling and standing heights
move.b #0,spindash_flag(a0) ; clear Spin Dash flag
moveq #0,d0
move.b spindash_counter(a0),d0 ; copy charge count
add.w d0,d0 ; double it
move.w SpindashSpeeds(pc,d0.w),inertia(a0) ; get spindash speed
; Determine how long to lag the camera for.
; Notably, the faster Sonic goes, the less the camera lags.
; This is seemingly to prevent Sonic from going off-screen.
move.w inertia(a0),d0 ; get inertia
subi.w #$800,d0 ; $800 is the lowest spin dash speed
add.w d0,d0 ; double it
andi.w #$1F00,d0 ; This line is not necessary, as none of the removed bits are ever set in the first place
neg.w d0 ; negate it
addi.w #$2000,d0 ; add $2000
move.w d0,($FFFFEED0).w ; move to $EED0
btst #0,status(a0) ; is sonic facing right?
beq.s + ; if not, branch
neg.w inertia(a0) ; negate inertia
+
bset #2,status(a0) ; set unused (in s1) flag
move.b #0,(v_spindust+anim).w ; clear Spin Dash dust anim
move.w #sfx_Teleport,d0 ; spindash zoom sound
jsr (PlaySound_Special).l ; play it!
bra.s Sonic_Spindash_ResetScr
; ===========================================================================
SpindashSpeeds:
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
; ===========================================================================
Sonic_ChargingSpindash: ; If still charging the dash...
tst.w spindash_counter(a0) ; check charge count
beq.s + ; if zero, branch
move.w spindash_counter(a0),d0 ; otherwise put it in d0
lsr.w #5,d0 ; shift right 5 (divide it by 32)
sub.w d0,spindash_counter(a0) ; subtract from charge count
bcc.s + ; ??? branch if carry clear
move.w #0,spindash_counter(a0) ; set charge count to 0
+
move.b (v_jpadpress2).w,d0 ; read controller
andi.b #btnB|btnC|btnA,d0 ; pressing A/B/C?
beq.w Sonic_Spindash_ResetScr ; if not, branch
move.w #(id_Spindash<<8)|(id_Walk<<0),anim(a0) ; reset spindash animation
move.w #sfx_Roll,d0 ; was $E0 in sonic 2
jsr (PlaySound_Special).l ; play charge sound
addi.w #$200,spindash_counter(a0) ; increase charge count
cmpi.w #$800,spindash_counter(a0) ; check if it's maxed
blo.s Sonic_Spindash_ResetScr ; if not, then branch
move.w #$800,spindash_counter(a0) ; reset it to max
Sonic_Spindash_ResetScr:
addq.l #4,sp ; increase stack ptr
cmpi.w #(224/2)-16,(v_lookshift).w
beq.s loc_1AD8C
bhs.s +
addq.w #4,(v_lookshift).w
+ subq.w #2,(v_lookshift).w
loc_1AD8C:
bsr.w Sonic_LevelBound
bsr.w Sonic_AnglePos
rts
; End of function Sonic_UpdateSpindash
|Add Spin Dash to Sonic 1 (GitHub)]]