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. 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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: Code (ASM): SonicAniData: include "_anim\Sonic.asm" In my case, I would change it to: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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 Code (ASM): align macro cnop 0,\1 endm You want to add this equate: Code (ASM): 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 Code (ASM): 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 Code (ASM): 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: Code (ASM): 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: Code (ASM): 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.) Code (ASM): moveq #3,d0 bsr.w PalLoad1 ; load Sonic's pallet With this: Code (ASM): 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: Code (ASM): ; =========================================================================== ; --------------------------------------------------------------------------- ; 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: Code (ASM): 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 Code (ASM): 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 Code (ASM): 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: Code (ASM): 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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. Code (ASM): ; 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: Code (ASM): move.l #Map_Sonic,4(a0) with: Code (ASM): bsr.w SetPlayerDisplay version 2: Spoiler Above Obj01, put this code: Code (ASM): ; --------------------------------------------------------------------------- ; 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. Code (ASM): ; 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: Code (ASM): move.l #Map_Sonic,4(a0) with: Code (ASM): bsr.w SetPlayerDisplay version 3: Spoiler At Obj01_Main and Obj09_Main replace: Code (ASM): move.l #Map_Sonic,4(a0) with: Code (ASM): 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: Code (ASM): Player_MapLoc: dc.l Map_Sonic, Map_Tails Place it above Obj01. Animations version 1: Spoiler First, go to Sonic_Animate, and replace: Code (ASM): lea (SonicAniData).l,a1 With this: Code (ASM): move.l Player_AniDat.w,a1 Now, at loc_13A9C, replace: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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: Code (ASM): lea (SonAni_Push).l,a1 With: Code (ASM): move.l PAni_Push.w,a1 ; use pushing animation version 2: Spoiler First, go to Sonic_Animate, and replace: Code (ASM): lea (SonicAniData).l,a1 With this: Code (ASM): move.l Player_AniDat.w,a1 Now, at loc_13A9C, replace: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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: Code (ASM): lea (SonAni_Push).l,a1 With: Code (ASM): 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: Code (ASM): 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: Code (ASM): lea (SonicAniData).l,a1 With this: Code (ASM): 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: Code (ASM): AniDat: dc.l SonicAniData, TailsAniData Now, at loc_13A9C, replace: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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: Code (ASM): 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: Code (ASM): lea (SonAni_Push).l,a1 With: Code (ASM): 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: Code (ASM): 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 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: Code (ASM): ; --------------------------------------------------------------------------- ; 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.