As we don't have a working multi-character guide anywhere (Trust me, this one doesn't work), so I thought I'd make a full guide on this. the good part about this guide is that: 1: It uses techniques to load the art/mappings/DPLC as fast as possible, leaving more time for whatever else code the game is running; 2: You have multiple options on how to do things, so you can select the most fitting way; 3: You can have up to 64 characters using this guide, so you should not run out of them in a while. The character IDs are always in increments of 4, to save some processor cycles; 4: It is easy to add more characters, you have to do barely anything! Just note that this is based on the HiveBrain 2005 disassembly, and you may need to do some extra work for SVN/HG/Git disassemblies. Also note that this will NOT work on Sonic 2 or Sonic 3! I am also going to use Tails as an example, so replace each reference to Tails with your characters name!
Step 1: Preparations.
Step 2: The actual code.
Troubleshooting
Step 1: Preparations.
Spoiler
File includes
That said, lets begin! So, the first thing you want to do, is get the proper art, mappings, DPLC, Animation, and palette files for your new character. If you miss one of these, this tutorial wont work. The files should be located at same places where normal Sonic's files of same type are at. As an example, you would put the new character DPLC file at the _inc folder (in case of HiveBrain 2005). As we are on the subject of the files, lets include them to the ROM:
First, go to label "Art_Sonic", and you'll see something like this:
What you want to do this, is copy this, and replace each Sonic with Tails. Also make sure You use the correct file name (If Tails.bin does not exist, make sure to change either the incbin reference or the original file name to match). So, in my case, this is what I would have:
Note that this should be located either before or after the Sonic's sequence, as so:
Ok, next up mappings and DPLC. Go to "Map_Sonic", and you'll see something along the lines of this:
Do something similar to it too, than with the art. So, this is what I'd have:
Next, let's do the animations file. Go to "SonicAniData", and you'll see this:
In my case, I would change it to:
All we need to include now is the palette. Go to "Pal_Sonic", and you'll see something like this:
Still, do the same thing than with the art. I'd do this:
Sonic 2's DPLC
if you have followed either Add Spin Dash to Sonic 1 (Part 3), or How to optimize Shield Art loading in Sonic 1 (Step 1), you have already it on your hack. If not, however, you must add it. I suggest following the latter link.
Different versions
Good! Now we should have everything we need for your new character to work! Well, except all the code behind it of course. But before we start on it, build your ROM, and make sure it wont complain about any "undefined errors" or "this and that not found". If some of them comes up, fix them up first. If not, lets continue. Now. you should decide which version you want to use, and find free RAM space.
Version 1: Everything is stored via RAM. Requires 32 bytes of free RAM. Fastest.
Version 2: Animation offsets are referenced via tables. Requires 8 bytes of free RAM. Slower.
version 3: Everything is referenced via tables. Requires 0 bytes of free RAM. Slowest.
So, after you have decided the technique you want to use, you want to go to beginning of your ROM, and just before
You want to add this equate:
Now, you must find 1 free byte of RAM. I suggest you to use "$FFFFFFF9", as it is 1-byte free address. In any case, put the address after the "equ" (You need at least 1 space between them). If you want to know more about unused addresses, go here. What this flag is used for, is to store what character you are using. Note that referencing "Current_Character" and not the RAM address you chose is much better, as you can easily mass-change the RAM itself, and it is easier to identify what it is used for.
You also need some other RAM equates, but they depend on the version you use. Here is what you need for version 1:
Version 2:
Version 3: none.
File includes
That said, lets begin! So, the first thing you want to do, is get the proper art, mappings, DPLC, Animation, and palette files for your new character. If you miss one of these, this tutorial wont work. The files should be located at same places where normal Sonic's files of same type are at. As an example, you would put the new character DPLC file at the _inc folder (in case of HiveBrain 2005). As we are on the subject of the files, lets include them to the ROM:
First, go to label "Art_Sonic", and you'll see something like this:
; --------------------------------------------------------------------------- ; Uncompressed graphics - Sonic ; --------------------------------------------------------------------------- Art_Sonic: incbin artunc\sonic.bin ; Sonic even
What you want to do this, is copy this, and replace each Sonic with Tails. Also make sure You use the correct file name (If Tails.bin does not exist, make sure to change either the incbin reference or the original file name to match). So, in my case, this is what I would have:
; --------------------------------------------------------------------------- ; Uncompressed graphics - Tails ; --------------------------------------------------------------------------- Art_Tails: incbin artunc\tails.bin ; Tails' art even
Note that this should be located either before or after the Sonic's sequence, as so:
; --------------------------------------------------------------------------- ; Uncompressed graphics - Sonic ; --------------------------------------------------------------------------- Art_Sonic: incbin artunc\sonic.bin ; Sonic even ; --------------------------------------------------------------------------- ; Uncompressed graphics - Tails ; --------------------------------------------------------------------------- Art_Tails: incbin artunc\tails.bin ; Tails' art even
Ok, next up mappings and DPLC. Go to "Map_Sonic", and you'll see something along the lines of this:
; --------------------------------------------------------------------------- ; Sprite mappings - Sonic ; --------------------------------------------------------------------------- Map_Sonic: include "_maps\Sonic.asm" ; --------------------------------------------------------------------------- ; Uncompressed graphics loading array for Sonic ; --------------------------------------------------------------------------- SonicDynPLC: include "_inc\Sonic dynamic pattern load cues.asm"
Do something similar to it too, than with the art. So, this is what I'd have:
; --------------------------------------------------------------------------- ; Sprite mappings - Sonic ; --------------------------------------------------------------------------- Map_Sonic: include "_maps\Sonic.asm" ; --------------------------------------------------------------------------- ; Uncompressed graphics loading array for Sonic ; --------------------------------------------------------------------------- SonicDynPLC: include "_inc\Sonic dynamic pattern load cues.asm" ; --------------------------------------------------------------------------- ; Sprite mappings - Tails ; --------------------------------------------------------------------------- Map_Tails: include "_maps\Tails.asm" ; --------------------------------------------------------------------------- ; Uncompressed graphics loading array for Tails ; --------------------------------------------------------------------------- TailsDynPLC: include "_inc\Tails dynamic pattern load cues.asm"
Next, let's do the animations file. Go to "SonicAniData", and you'll see this:
SonicAniData: include "_anim\Sonic.asm"
In my case, I would change it to:
SonicAniData: include "_anim\Sonic.asm" TailsAniData: include "_anim\Tails.asm"
All we need to include now is the palette. Go to "Pal_Sonic", and you'll see something like this:
Pal_Sonic: incbin pallet\sonic.bin ... Pal_LZSonWater: incbin pallet\son_lzuw.bin ; Sonic (underwater in LZ) pallet Pal_SBZ3SonWat: incbin pallet\son_sbzu.bin ; Sonic (underwater in SBZ act 3) pallet
Still, do the same thing than with the art. I'd do this:
Pal_Sonic: incbin pallet\sonic.bin ; Sonic's normal palette Pal_Tails: incbin pallet\Tails.bin ; Tails' normal palette ... Pal_LZSonWater: incbin pallet\son_lzuw.bin ; Sonic (underwater in LZ) palette Pal_SBZ3SonWat: incbin pallet\son_sbzu.bin ; Sonic (underwater in SBZ act 3) palette Pal_TailsLZ: incbin pallet\tails_lz.bin ; Tails (underwater in LZ) palette Pal_TailsSBZ3: incbin pallet\tails_sbz3.bin ; Tails (underwater in SBZ act 3) palette
Sonic 2's DPLC
if you have followed either Add Spin Dash to Sonic 1 (Part 3), or How to optimize Shield Art loading in Sonic 1 (Step 1), you have already it on your hack. If not, however, you must add it. I suggest following the latter link.
Different versions
Good! Now we should have everything we need for your new character to work! Well, except all the code behind it of course. But before we start on it, build your ROM, and make sure it wont complain about any "undefined errors" or "this and that not found". If some of them comes up, fix them up first. If not, lets continue. Now. you should decide which version you want to use, and find free RAM space.
Version 1: Everything is stored via RAM. Requires 32 bytes of free RAM. Fastest.
Version 2: Animation offsets are referenced via tables. Requires 8 bytes of free RAM. Slower.
version 3: Everything is referenced via tables. Requires 0 bytes of free RAM. Slowest.
So, after you have decided the technique you want to use, you want to go to beginning of your ROM, and just before
align macro cnop 0,\1 endm
You want to add this equate:
Current_Character equ ; whatever the character you are using is
Now, you must find 1 free byte of RAM. I suggest you to use "$FFFFFFF9", as it is 1-byte free address. In any case, put the address after the "equ" (You need at least 1 space between them). If you want to know more about unused addresses, go here. What this flag is used for, is to store what character you are using. Note that referencing "Current_Character" and not the RAM address you chose is much better, as you can easily mass-change the RAM itself, and it is easier to identify what it is used for.
You also need some other RAM equates, but they depend on the version you use. Here is what you need for version 1:
Spoiler
Player_ArtLoc equ ; 4 bytes ; pointer for players art location (can also be used for special art if needed to ;)/> ) Player_DPLCLoc equ ; 4 bytes ; pointer for players DPLC location (can be used for special DPLCs) Player_AniDat equ ; 4 bytes ; pointer for players animation data location (can be used for special animations) PAni_Run equ ; 4 bytes ; pointer for players Run animation (can be used for special animation) PAni_Walk equ ; 4 bytes ; pointer for players Walk animation (can be used for special animation) PAni_Roll equ ; 4 bytes ; pointer for players Roll animation (can be used for special animation) PAni_Roll2 equ ; 4 bytes ; pointer for players Roll animation (can be used for special animation) PAni_Push equ ; 4 bytes ; pointer for players Push animation (can be used for special animation)
Version 2:
Spoiler
Player_ArtLoc equ ; 4 bytes ; pointer for players art location (can also be used for special art if needed to ;)/> ) Player_DPLCLoc equ ; 4 bytes ; pointer for players DPLC location (can be used for special DPLCs) Player_AniDat equ ; 4 bytes ; pointer for players animation data location (can be used for special animations)
Version 3: none.
Step 2: The actual code.
Spoiler
Palette
So, lets get to work! First of all, we want to do the simplest part, which will probably be palette. Head to "Level_LoadPal". You will see the following code:
We don't want this. Replace with:
There are, however, few locations where Sonic's palette is loaded. It is optional to fix any of them, but for completions sake, here is how: In Title_ClrPallet, loc_3946, End_LoadData, and Cred_ClrPallet, replace following piece of code: (Note: Title screen uses it's own palette for Sonic, you need to find your own way to replace that.)
With this:
Now we need the actual code to load the palette. Somewhere near "Pal_Sonic", insert this code:
Next, we need to create the array of pointers for our desired player palettes. This isn't exactly complicated, so let me show example of how I would do it:
It makes most sense to place this either under or above the LoadPlayerPalettes code I did show you above.
Art and dynamic pattern load cues
First off, go to LoadSonicDynPLC, and replace the entire routine with this:
version 1 and version 2:
version 3:
Great! Now your character loads its own art and DPLC.
Mappings
version 1:
version 2:
version 3:
Animations
version 1:
version 2:
version 3:
Congratulations! Now you should have extra character in your hack! If you have any trouble, please look into troubleshooting section prior to asking any questions.
Palette
So, lets get to work! First of all, we want to do the simplest part, which will probably be palette. Head to "Level_LoadPal". You will see the following code:
moveq #3,d0 bsr.w PalLoad2 ; load Sonic's pallet line cmpi.b #1,($FFFFFE10).w ; is level LZ? bne.s Level_GetBgm ; if not, branch moveq #$F,d0 ; pallet number $0F (LZ) cmpi.b #3,($FFFFFE11).w ; is act number 3? bne.s Level_WaterPal ; if not, branch moveq #$10,d0 ; pallet number $10 (SBZ3) Level_WaterPal: bsr.w PalLoad3_Water ; load underwater pallet (see d0) tst.b ($FFFFFE30).w beq.s Level_GetBgm move.b ($FFFFFE53).w,($FFFFF64E).w
We don't want this. Replace with:
lea $FFFFFB00,a2 ; normal palette lea $FFFFFA80,a3 ; underwater palette bsr LoadPlayerPalettes ; load palette for current character cmpi.b #1,$FFFFFE10.w ; is level LZ? bne.s Level_GetBgm ; if not, branch tst.b $FFFFFE30.w ; has lamppost been hit? beq.s Level_GetBgm ; if not, branch move.b $FFFFFE53.w,$FFFFF64E.w ; copy water direction to lamppost RAM
There are, however, few locations where Sonic's palette is loaded. It is optional to fix any of them, but for completions sake, here is how: In Title_ClrPallet, loc_3946, End_LoadData, and Cred_ClrPallet, replace following piece of code: (Note: Title screen uses it's own palette for Sonic, you need to find your own way to replace that.)
moveq #3,d0 bsr.w PalLoad1 ; load Sonic's pallet
With this:
lea $FFFFFB80,a2 ; normal target palette lea $FFFFFA00,a3 ; underwater target palette bsr LoadPlayerPalettes ; load palette for current character
Now we need the actual code to load the palette. Somewhere near "Pal_Sonic", insert this code:
; ===========================================================================
; ---------------------------------------------------------------------------
; Subroutine to load correct player pallets
; ---------------------------------------------------------------------------
LoadPlayerPalettes:
moveq #0,d1
move.b Current_Character.w,d1 ; get character ID
move.l CharPalList(pc,d1.w),a1 ; get normal palette to a1
moveq #7,d0 ; 16 palette entries
bsr.s Loc_Pal ; load palettes to RAM
cmpi.b #1,$FFFFFE10.w ; is LZ?
bne.s LPP_rts ; if not, branch
move.l CharPalListLZ(pc,d1.w),a1 ; get underwater palette to a1
cmpi.b #3,$FFFFFE11.w ; is act number 3?
bne.s LPP_UWPal ; if not, branch
move.l CharPalListSBZ3(pc,d1.w),a1 ; get SBZ3 underwater palette to a1
LPP_UWPal:
moveq #7,d0 ; 16 palette entries
lea (a3),a2 ; put water palette to a2
bsr.s Loc_Pal ; load to RAM
LPP_rts:
rts ; return
; ===========================================================================
; ---------------------------------------------------------------------------
; copy d0 + 1 longwords from a1 to a2
; ---------------------------------------------------------------------------
loc_Pal:
move.l (a1)+,(a2)+ ; process next 2 palette entries
dbf d0,loc_Pal ; keep looping until d0 is 0
rts ; return
Next, we need to create the array of pointers for our desired player palettes. This isn't exactly complicated, so let me show example of how I would do it:
CharPalList: dc.l Pal_Sonic, Pal_Tails CharPalListLZ: dc.l Pal_LZSonWater, Pal_TailsLZ CharPalListSBZ3: dc.l Pal_SBZ3SonWat, Pal_TailsSBZ3
It makes most sense to place this either under or above the LoadPlayerPalettes code I did show you above.
Art and dynamic pattern load cues
First off, go to LoadSonicDynPLC, and replace the entire routine with this:
version 1 and version 2:
Spoiler
LoadSonicDynPLC: movea.l Player_DPLCLoc,a2 ; get DPLC location move.w #$F000,d4 ; offset in VRAM to store art move.l Player_ArtLoc,d6 ; get art location moveq #0,d0 move.b $1A(a0),d0 ; load frame number cmp.b $FFFFF766.w,d0 ; check if equal with last queued frame beq.s DPLC_End ; if is, don't load new DPLC move.b d0,$FFFFF766.w ; remember queued frame ; End of function LoadSonicDynPLC ; --------------------------------------------------------------------------- ; Subroutine to queue any pattern load cue ; Input: a2 - DPLC file, d4 - VRAM address, d6 - Art file, d0 - frame number ; --------------------------------------------------------------------------- Load_DPLC: add.w d0,d0 ; multiply by 2 adda.w (a2,d0.w),a2 ; get the right DPLC location moveq #0,d5 ; quckly clear d5 move.b (a2)+,d5 ; then move the amount of requests to d5 subq.w #1,d5 ; subtract 1 bmi.s DPLC_End ; if negative, branch away DPLC_ReadEntry: moveq #0,d1 move.b (a2)+,d1 ; get first byte to d1, and increment pointer lsl.w #8,d1 ; shift 8 bits left move.b (a2)+,d1 ; move second byte to d1 move.w d1,d3 ; move d1 to d3 lsr.w #8,d3 ; shift 8 bits right andi.w #$F0,d3 ; leave only bits 7, 6, 5, and 4 addi.w #$10,d3 ; add $10 to d3 andi.w #$FFF,d1 ; filter out bits 15, 14, 13 and 12 lsl.l #5,d1 ; shift 5 bits left add.l d6,d1 ; add the art address to d1 move.w d4,d2 ; move VRAM location to d2 add.w d3,d4 ; add d3 to VRAM address add.w d3,d4 ; add d3 to VRAM address jsr QueueDMATransfer; Save it to the DMA queue dbf d5,DPLC_ReadEntry; repeat for number of requests DPLC_End: rts ; return
version 3:
Spoiler
Next we need to create an array to store direct locations to art and DPLC data. It should be very simple, so I'll just show an example:
Put it right above LoadSonicDynPLC.
LoadSonicDynPLC: moveq #0,d0 ; quickly clear d0 move.b Current_Character.w,d0 ; get character ID movea.l Player_DPLCLoc(pc,d0.w),a2 ; put players DPLC location to a2 move.l Player_ArtLoc(pc,d0.w),d6 ; put players art location to a2 move.w #$F000,d4 ; offset in VRAM to store art moveq #0,d0 move.b $1A(a0),d0 ; load frame number cmp.b $FFFFF766.w,d0 ; check if equal with last queued frame beq.s DPLC_End ; if is, don't load new DPLC move.b d0,$FFFFF766.w ; remember queued frame ; End of function LoadSonicDynPLC ; --------------------------------------------------------------------------- ; Subroutine to queue any pattern load cue ; Input: a2 - DPLC file, d4 - VRAM address, d6 - Art file, d0 - frame number ; --------------------------------------------------------------------------- Load_DPLC: add.w d0,d0 ; multiply by 2 adda.w (a2,d0.w),a2 ; get the right DPLC location moveq #0,d5 ; quckly clear d5 move.b (a2)+,d5 ; then move the amount of requests to d5 subq.w #1,d5 ; subtract 1 bmi.s DPLC_End ; if negative, branch away DPLC_ReadEntry: moveq #0,d1 move.b (a2)+,d1 ; get first byte to d1, and increment pointer lsl.w #8,d1 ; shift 8 bits left move.b (a2)+,d1 ; move second byte to d1 move.w d1,d3 ; move d1 to d3 lsr.w #8,d3 ; shift 8 bits right andi.w #$F0,d3 ; leave only bits 7, 6, 5, and 4 addi.w #$10,d3 ; add $10 to d3 andi.w #$FFF,d1 ; filter out bits 15, 14, 13 and 12 lsl.l #5,d1 ; shift 5 bits left add.l d6,d1 ; add the art address to d1 move.w d4,d2 ; move VRAM location to d2 add.w d3,d4 ; add d3 to VRAM address add.w d3,d4 ; add d3 to VRAM address jsr QueueDMATransfer; Save it to the DMA queue dbf d5,DPLC_ReadEntry; repeat for number of requests DPLC_End: rts ; return
Next we need to create an array to store direct locations to art and DPLC data. It should be very simple, so I'll just show an example:
Player_DPLCLoc: dc.l SonicDynPLC, TailsDynPLC Player_ArtLoc: dc.l Art_Sonic, Art_Tails
Put it right above LoadSonicDynPLC.
Great! Now your character loads its own art and DPLC.
Mappings
version 1:
Spoiler
Above Obj01, put this code:
Next, we are going to create an array containing all this data. It's format is described in the comment.
Put it below SetPlayerDisplay. Next, at Obj01_Main and Obj09_Main replace:
with:
Above Obj01, put this code:
; --------------------------------------------------------------------------- ; Subroutine to set Player-specific variables ; --------------------------------------------------------------------------- SetPlayerDisplay: moveq #0,d0 ; quickly clear d0 move.b Current_Character,d0 ; get character ID move.w d0,d1 ; store to d1 as well lsl.w #3,d0 ; shift left 4 bits add.w d1,d0 ; add d1 to d0 lea PlayerDataList(pc,d0.w),a1; get the table to a1 move.l (a1)+,Player_ArtLoc ; player art location move.l (a1)+,4(a0) ; get mappings move.l (a1)+,Player_DPLCLoc ; FPLC location move.l (a1)+,Player_AniDat ; Animation data location move.l (a1)+,PAni_Walk ; walk animation move.l (a1)+,PAni_Run ; run animation move.l (a1)+,PAni_Roll ; roll animation move.l (a1)+,PAni_Roll2 ; roll animation move.l (a1)+,PAni_Push ; push animation rts ; return
Next, we are going to create an array containing all this data. It's format is described in the comment.
; Art file, Map file, DPLC file, Ani file, Walk ani ptr, Run ani ptr, Roll ani ptr, Roll2 ani ptr, Push ani ptr PlayerDataList: dc.l Art_Sonic, Map_Sonic, SonicDynPLC, SonicAniData, SonAni_Walk, SonAni_Run, SonAni_Roll, SonAni_Roll2, SonAni_Push ; Sonic dc.l Art_Tails, Map_Tails, TailsDynPLC, TailsAniData, TailsAni_Walk, TailsAni_Run, TailsAni_Roll, TailsAni_Roll2, TailsAni_Push ; Tails
Put it below SetPlayerDisplay. Next, at Obj01_Main and Obj09_Main replace:
move.l #Map_Sonic,4(a0)
with:
bsr.w SetPlayerDisplay
version 2:
Spoiler
Above Obj01, put this code:
Next, we are going to create an array containing all this data. It's format is described in the comment.
Put it below SetPlayerDisplay. Next, at Obj01_Main and Obj09_Main replace:
with:
Above Obj01, put this code:
; --------------------------------------------------------------------------- ; Subroutine to set Player-specific variables ; --------------------------------------------------------------------------- SetPlayerDisplay: moveq #0,d0 ; quickly clear d0 move.b Current_Character,d0 ; get character ID lsl.w #2,d0 ; shift left 2 bits lea PlayerDataList(pc,d0.w),a1; get the table to a1 move.l (a1)+,Player_ArtLoc ; player art location move.l (a1)+,4(a0) ; get mappings move.l (a1)+,Player_DPLCLoc ; FPLC location move.l (a1),Player_AniDat ; Animation data location rts ; return
Next, we are going to create an array containing all this data. It's format is described in the comment.
; Art file, Map file, DPLC file, Ani file PlayerDataList: dc.l Art_Sonic, Map_Sonic, SonicDynPLC, SonicAniData ; Sonic dc.l Art_Tails, Map_Tails, TailsDynPLC, TailsAniData ; Tails
Put it below SetPlayerDisplay. Next, at Obj01_Main and Obj09_Main replace:
move.l #Map_Sonic,4(a0)
with:
bsr.w SetPlayerDisplay
version 3:
Spoiler
At Obj01_Main and Obj09_Main replace:
with:
Now we need table for mappings. This, again, isnt all that complicated, so have an example:
Place it above Obj01.
At Obj01_Main and Obj09_Main replace:
move.l #Map_Sonic,4(a0)
with:
moveq #0,d0 ; quickly clear d0 move.b Current_Character.w,d0 ; get character ID move.l #Player_MapLoc,a1 ; get players mapping location array add.l d0,a1 ; get correct mapping for player move.l (a1),4(a0) ; put it to Sonic's mappings
Now we need table for mappings. This, again, isnt all that complicated, so have an example:
Player_MapLoc: dc.l Map_Sonic, Map_Tails
Place it above Obj01.
Animations
version 1:
Spoiler
First, go to Sonic_Animate, and replace:
With this:
Now, at loc_13A9C, replace:
With:
Go to loc_13ADE, and replace:
With:
Go to loc_13B26, and replace:
With:
First, go to Sonic_Animate, and replace:
lea (SonicAniData).l,a1
With this:
move.l Player_AniDat.w,a1
Now, at loc_13A9C, replace:
lea (SonAni_Run).l,a1 ; use running animation cmpi.w #$600,d2 ; is Sonic at running speed? bcc.s loc_13AB4 ; if yes, branch lea (SonAni_Walk).l,a1 ; use walking animation
With:
move.l PAni_Run.w,a1 ; use running animation cmpi.w #$600,d2 ; is Sonic at running speed? bcc.s loc_13AB4 ; if yes, branch move.l PAni_Walk.w,a1 ; use walking animation
Go to loc_13ADE, and replace:
lea (SonAni_Roll2).l,a1 ; use fast animation cmpi.w #$600,d2 ; is Sonic moving fast? bcc.s loc_13AF0 ; if yes, branch lea (SonAni_Roll).l,a1 ; use slower animation
With:
move.l PAni_Roll2.w,a1 ; use fast animation cmpi.w #$600,d2 ; is Sonic moving fast? bcc.s loc_13AF0 ; if yes, branch move.l PAni_Roll.w,a1 ; use slower animation
Go to loc_13B26, and replace:
lea (SonAni_Push).l,a1
With:
move.l PAni_Push.w,a1 ; use pushing animation
version 2:
Spoiler
First, go to Sonic_Animate, and replace:
With this:
Now, at loc_13A9C, replace:
With:
Go to loc_13ADE, and replace:
With:
Go to loc_13B26, and replace:
With:
Just above SAnim_RollJump, we make few arrays. They are pretty simple so I'll show an example:
First, go to Sonic_Animate, and replace:
lea (SonicAniData).l,a1
With this:
move.l Player_AniDat.w,a1
Now, at loc_13A9C, replace:
lea (SonAni_Run).l,a1 ; use running animation cmpi.w #$600,d2 ; is Sonic at running speed? bcc.s loc_13AB4 ; if yes, branch lea (SonAni_Walk).l,a1 ; use walking animation
With:
moveq #0,d4 ; quickly clear d0 move.b Current_Character.w,d4 ; get character ID movea.l PAni_Run(pc,d4.w),a1 ; put players running animation to a1 cmpi.w #$600,d2 ; is Sonic at running speed? bcc.s loc_13AB4 ; if yes, branch movea.l PAni_Walk(pc,d4.w),a1 ; put players walking animation to a1
Go to loc_13ADE, and replace:
lea (SonAni_Roll2).l,a1 ; use fast animation cmpi.w #$600,d2 ; is Sonic moving fast? bcc.s loc_13AF0 ; if yes, branch lea (SonAni_Roll).l,a1 ; use slower animation
With:
moveq #0,d4 ; quickly clear d0 move.b Current_Character.w,d4 ; get character ID movea.l PAni_Roll2(pc,d4.w),a1 ; put players fast rolling animation to a1 cmpi.w #$600,d2 ; is Sonic moving fast? bcc.s loc_13AF0 ; if yes, branch movea.l PAni_Roll(pc,d4.w),a1 ; put players slow rolling animation to a1
Go to loc_13B26, and replace:
lea (SonAni_Push).l,a1
With:
moveq #0,d4 ; quickly clear d0 move.b Current_Character.w,d4 ; get character ID movea.l PAni_Push(pc,d4.w),a1 ; put players fast rolling animation to a1
Just above SAnim_RollJump, we make few arrays. They are pretty simple so I'll show an example:
PAni_Run: dc.l SonAni_Run, TailsAni_Run PAni_Walk: dc.l SonAni_Walk, TailsAni_Walk PAni_Roll2: dc.l SonAni_Roll2, TailsAni_Roll2 PAni_Roll: dc.l SonAni_Roll, TailsAni_Roll PAni_Push: dc.l SonAni_Push, TailsAni_Push
version 3:
Spoiler
First, go to Sonic_Animate, and replace:
With this:
Above Sonic_Animate, put array. This is pretty simple so here is example:
Now, at loc_13A9C, replace:
With:
Go to loc_13ADE, and replace:
With:
Go to loc_13B26, and replace:
With:
Just above SAnim_RollJump, we make few arrays. They are pretty simple so I'll show an example:
First, go to Sonic_Animate, and replace:
lea (SonicAniData).l,a1
With this:
moveq #0,d0 ; quickly clear d0 move.b Current_Character.w,d0 ; get character ID movea.l Player_AniDat(pc,d0.w),a1 ; put players animation data to a1
Above Sonic_Animate, put array. This is pretty simple so here is example:
AniDat: dc.l SonicAniData, TailsAniData
Now, at loc_13A9C, replace:
lea (SonAni_Run).l,a1 ; use running animation cmpi.w #$600,d2 ; is Sonic at running speed? bcc.s loc_13AB4 ; if yes, branch lea (SonAni_Walk).l,a1 ; use walking animation
With:
moveq #0,d4 ; quickly clear d0 move.b Current_Character.w,d4 ; get character ID movea.l PAni_Run(pc,d4.w),a1 ; put players running animation to a1 cmpi.w #$600,d2 ; is Sonic at running speed? bcc.s loc_13AB4 ; if yes, branch movea.l PAni_Walk(pc,d4.w),a1 ; put players walking animation to a1
Go to loc_13ADE, and replace:
lea (SonAni_Roll2).l,a1 ; use fast animation cmpi.w #$600,d2 ; is Sonic moving fast? bcc.s loc_13AF0 ; if yes, branch lea (SonAni_Roll).l,a1 ; use slower animation
With:
moveq #0,d4 ; quickly clear d0 move.b Current_Character.w,d4 ; get character ID movea.l PAni_Roll2(pc,d4.w),a1 ; put players fast rolling animation to a1 cmpi.w #$600,d2 ; is Sonic moving fast? bcc.s loc_13AF0 ; if yes, branch movea.l PAni_Roll(pc,d4.w),a1 ; put players slow rolling animation to a1
Go to loc_13B26, and replace:
lea (SonAni_Push).l,a1
With:
moveq #0,d4 ; quickly clear d0 move.b Current_Character.w,d4 ; get character ID movea.l PAni_Push(pc,d4.w),a1 ; put players fast rolling animation to a1
Just above SAnim_RollJump, we make few arrays. They are pretty simple so I'll show an example:
PAni_Run: dc.l SonAni_Run, TailsAni_Run PAni_Walk: dc.l SonAni_Walk, TailsAni_Walk PAni_Roll2: dc.l SonAni_Roll2, TailsAni_Roll2 PAni_Roll: dc.l SonAni_Roll, TailsAni_Roll PAni_Push: dc.l SonAni_Push, TailsAni_Push
Congratulations! Now you should have extra character in your hack! If you have any trouble, please look into troubleshooting section prior to asking any questions.
Troubleshooting
Spoiler
Garbled graphics/parts of graphics
Sometimes graphics don't update
Garbled graphics/parts of graphics
Spoiler
This is due to your characters art being inbetween 2 128KB ROM "banks". VDP can only DMA from one bank per transfer, and because of this the art can only lie on one bank. You can put align $20000 just before the art file causing issue. Note: This will put 00's until next address dividable by 131072 (0x20000). Here is an example:
This is due to your characters art being inbetween 2 128KB ROM "banks". VDP can only DMA from one bank per transfer, and because of this the art can only lie on one bank. You can put align $20000 just before the art file causing issue. Note: This will put 00's until next address dividable by 131072 (0x20000). Here is an example:
; --------------------------------------------------------------------------- ; Uncompressed graphics - Sonic ; --------------------------------------------------------------------------- Art_Sonic: incbin artunc\sonic.bin ; Sonic even ; --------------------------------------------------------------------------- ; Uncompressed graphics - Tails ; --------------------------------------------------------------------------- align $20000 ; align to next bank Art_Tails: incbin artunc\tails.bin ; Tails' art even
Sometimes graphics don't update
Spoiler
I suggest following this guide, and trying again.
I suggest following this guide, and trying again.
This post has been edited by Green Snake: 02 October 2014 - 03:02 PM


01