Mini-tutorials Thread

Discussion in 'Tutorials' started by DeltaWooloo, Feb 16, 2021.

Tags:
  1. FeroTheInugami

    FeroTheInugami It's in the Blockchain Member

    Joined:
    Jul 10, 2023
    Messages:
    35
    In Sonic CD, Sonic's walking animation is aligned correctly, instead of flicking like Sonic 1. This occurs due to the mappings being different in these sprites.
    So, how I can add these? Well, just use SonMapEd to align the sprites.

    Here's Sonic 1 mappings:
    Code:
    MS_Walk11:    dc.b 4            ; walking 1-1
            dc.b $EB, $D, 0, 0, $EC
            dc.b $FB, 9, 0,    8, $EC
            dc.b $FB, 6, 0,    $E, 4
            dc.b $B, 4, 0, $14, $EC
    MS_Walk12:    dc.b 2            ; walking 1-2
            dc.b $EC, $D, 0, 0, $ED
            dc.b $FC, $E, 0, 8, $F5
    MS_Walk13:    dc.b 2            ; walking 1-3
            dc.b $ED, 9, 0,    0, $F3
            dc.b $FD, $A, 0, 6, $F3
    MS_Walk14:    dc.b 4            ; walking 1-4
            dc.b $EB, 9, 0,    0, $F4
            dc.b $FB, 9, 0,    6, $EC
            dc.b $FB, 6, 0,    $C, 4
            dc.b $B, 4, 0, $12, $EC
    MS_Walk15:    dc.b 2            ; walking 1-5
            dc.b $EC, 9, 0,    0, $F3
            dc.b $FC, $E, 0, 6, $EB
    MS_Walk16:    dc.b 3            ; walking 1-6
            dc.b $ED, $D, 0, 0, $EC
            dc.b $FD, $C, 0, 8, $F4
            dc.b 5,    9, 0, $C, $F4

    Here's Sonic CD mappings:

    Code:
    MS_Walk11:    dc.b 4            ; walking 1-1
            dc.b $EC, $D, 0, 0, $EC
            dc.b $FC, 9, 0, 8, $EC
            dc.b $FC, 6, 0, $E, 4
            dc.b $C, 4, 0, $14, $EC
    MS_Walk12:    dc.b 2            ; walking 1-2
            dc.b $EC, $D, 0, 0, $EC
            dc.b $FC, $E, 0, 8, $F4
    MS_Walk13:    dc.b 2            ; walking 1-3
            dc.b $EC, 9, 0, 0, $F4
            dc.b $FC, $A, 0, 6, $F4
    MS_Walk14:    dc.b 4            ; walking 1-4
            dc.b $EC, 9, 0, 0, $F4
            dc.b $FC, 9, 0, 6, $EC
            dc.b $FC, 6, 0, $C, 4
            dc.b $C, 4, 0, $12, $EC
    MS_Walk15:    dc.b 2            ; walking 1-5
            dc.b $EC, 9, 0, 0, $F4
            dc.b $FC, $E, 0, 6, $EC
    MS_Walk16:    dc.b 3            ; walking 1-6
            dc.b $EC, $D, 0, 0, $EC
            dc.b $FC, $C, 0, 8, $F4
            dc.b 4, 9, 0, $C, $F4

    There you go! Have fun with it!
    EDIT: I forgot to say that the diagonal frames need this too.
     

    Attached Files:

    Last edited: Jul 14, 2023
    Miles Sebas Prower likes this.
  2. Devon

    Devon I'm a loser, baby, so why don't you kill me? Member

    Joined:
    Aug 26, 2013
    Messages:
    1,376
    Location:
    your mom
    The "flicking" is actually more natural looking. When you jog like Sonic does, your legs stretch out and bend more, which causes the rest of your body to move up and down along with them. The CD alignment would look more natural if applied to a proper slower walking animation where the legs don't stretch out and bend as much.
     
  3. Devon

    Devon I'm a loser, baby, so why don't you kill me? Member

    Joined:
    Aug 26, 2013
    Messages:
    1,376
    Location:
    your mom
    There is a possible slight oversight with how the push sensor is positioned. So, if Sonic is moving along a flat enough ground (not a wall or ceiling), then Sonic's push sensor will be moved down 8 pixels. This is so snapping on top of steeper steps can only really be done is you're walking up a slope, and not on flat ground, which would look strange.

    [​IMG]

    The problem is that this lowering of the push sensor doesn't account for when Sonic's smaller hitbox when he starts rolling. As a result, the threshold for the largest step that Sonic can snap on top of lowers.

    [​IMG]

    This is particularly not ideal, because it increases the likely hood of Sonic getting stopped by a decently shallow slope while rolling, due to how low that threshold is.

    [​IMG]

    If you want the push sensor's position to be consistent with the normal hitbox height when on flat ground, the fix is easy.

    In sonic.asm, go to loc_14D24, and add this right under addq.w #8,d2:
    Code:
            btst    #2,obStatus(a0)    ; Is Sonic rolling?
            beq.s   loc_14D3C          ; If not, branch
            subq.w  #5,d2              ; If so, move push sensor up a bit
    And you'll get this:

    [​IMG]
    I will add that Tails in general is more suspect to this oversight, because his natural and rolling hitbox heights are almost the same, and thus the threshold for him is always quite low.

    [​IMG]

    In s2.asm, go to loc_1EBE6, and add this right under addq.w #8,d2:
    Code:
            btst    #2,status(a0)          ; Is the Player rolling?
            bne.s   +                      ; If not, branch
            cmpi.b  #ObjID_Tails,id(a0)    ; Is this Tails?
            bne.s   ++                     ; If not, branch
            addq.w  #1,d2                  ; Offset push sensor movement when not rolling
    +
            subq.w  #5,d2                  ; Move the push sensor up
    Then, change the "+" in this to a "++":
    Code:
          andi.b  #$38,d1
          bne.s   +
    And you'll get this:

    [​IMG]

    You can adjust the numbers to your liking if you please. I will also note that if you make it so that the push sensor is enabled for other directions (like S3K does when running straight up/down on a wall or running on a flat ceiling), then modifications to account for that will need to be made.
     
    Last edited: Aug 10, 2023
  4. JGamer2151

    JGamer2151 Well-Known Member/Lurker Member

    Joined:
    Dec 1, 2020
    Messages:
    96
    Interesting fix. I’ve encountered this problem numerous times in Sonic 2 in Chemical Plant, and with my hack using the wall-rebound feature from Sonic 2 Nick Arcade, it gets a bit tedious and annoying for me. Glad to see this fix, I’ll try it out myself.
     
  5. Inferno

    Inferno Rom Hacker Member

    Joined:
    Oct 27, 2015
    Messages:
    132
    Location:
    Sky Base Zone, South Island
    Happens in every classic Sonic game built off the S1 engine, fun fact! So this has been around since at least February 1990 and wasn't fixed, even when Sonic CD was completely recoded for PC.

    Fix came about because of the issue becoming rather severe in S1D, and us ultimately talking with Devon partly out of a misconception, which led to the realization of the actual error.

    Now, it'll only break down at the precise speed it should, $1000, or a block a frame.
     
    Miles Sebas Prower and ProjectFM like this.
  6. Hame

    Hame Peepee Poopoo Member

    Joined:
    Jan 12, 2021
    Messages:
    57
    Location:
    Spain
    this is great, i was tired of playing sonic 2 and it happens to me every minute, also in other hacks, good job :p



    Now, something I was thinking about is adding a third collision bar to sonic in the middle, I say it so that this doesn't happen:
    https://youtu.be/hFkUwPN6U4M
     
  7. Devon

    Devon I'm a loser, baby, so why don't you kill me? Member

    Joined:
    Aug 26, 2013
    Messages:
    1,376
    Location:
    your mom
    There is an oversight with invisible solid block collision. At the top of its main routine, it checks if it's offscreen:
    Code:
    Invis_Solid:    ; Routine 2
            bsr.w    ChkObjectVisible
            bne.s    .chkdel
    There is a problem though. If we go to the routine:
    Code:
    ; ---------------------------------------------------------------------------
    ; Subroutine to    check if an object is off screen
    
    ; output:
    ;    d0 = flag set if object is off screen
    ; ---------------------------------------------------------------------------
    
    ; ||||||||||||||| S U B    R O U T    I N E |||||||||||||||||||||||||||||||||||||||
    
    
    ChkObjectVisible:
            move.w    obX(a0),d0    ; get object x-position
            sub.w    (v_screenposx).w,d0 ; subtract screen x-position
            bmi.s    .offscreen
            cmpi.w    #320,d0        ; is object on the screen?
            bge.s    .offscreen    ; if not, branch
    
            move.w    obY(a0),d1    ; get object y-position
            sub.w    (v_screenposy).w,d1 ; subtract screen y-position
            bmi.s    .offscreen
            cmpi.w    #224,d1        ; is object on the screen?
            bge.s    .offscreen    ; if not, branch
    
            moveq    #0,d0        ; set flag to 0
            rts   
    
    .offscreen:
            moveq    #1,d0        ; set flag to 1
            rts   
    ; End of function ChkObjectVisible

    It doesn't take into account the size of the hitbox, just the position. Now, it's not very common at all to run into this issue, but you can easily see the effect if you duck the camera down in this area:

    [​IMG]

    To fix this, add this routine that accounts for the hitbox:
    Code:
    ; ---------------------------------------------------------------------------
    ; Subroutine to    check if an object is off screen
    ; Takes both width and height into account
    
    ; output:
    ;    d0 = flag set if object is off screen
    ; ---------------------------------------------------------------------------
    
    ; ||||||||||||||| S U B    R O U T    I N E |||||||||||||||||||||||||||||||||||||||
    
    
    ChkSizedObjVisible:
            moveq    #0,d1                ; Get object's width
            move.b    obActWid(a0),d1
            move.w    obX(a0),d0            ; Get object's X position
            sub.w    (v_screenposx).w,d0        ; Get object's X position on screen
            add.w    d1,d0                ; Is the right side of the object on screen?
            bmi.s    .offscreen2            ; If not, branch
            add.w    d1,d1                ; Is the left side of the object on screen?
            sub.w    d1,d0
            cmpi.w    #320,d0
            bge.s    .offscreen2            ; If not, branch
    
            moveq    #0,d1                ; Get object's height
            move.b    obHeight(a0),d1
            move.w    obY(a0),d0            ; Get object's Y position
            sub.w    (v_screenposy).w,d0        ; Get object's Y position on screen
            add.w    d1,d0                ; Is the bottom side of the object on screen?
            bmi.s    .offscreen2            ; If not, branch
            add.w    d1,d1                ; Is the top side of the object on screen?
            sub.w    d1,d0
            cmpi.w    #224,d1
            bge.s    .offscreen2            ; If not, branch
    
            moveq    #0,d0                ; Visible
            rts
    
    .offscreen2:
            moveq    #1,d0                ; Not visible
            rts    

    And change the call to ChkObjectVisible to ChkSizedObjVisible instead, and tada!

    [​IMG]
     
    ProjectFM and DeltaWooloo like this.
  8. Devon

    Devon I'm a loser, baby, so why don't you kill me? Member

    Joined:
    Aug 26, 2013
    Messages:
    1,376
    Location:
    your mom
    Small bug that's probably really insignificant in the grand scheme of things, but I thought it would be worth documenting.

    The plasma balls in Final Zone's movement code when it first gets spawned has a small bug in it. If we go to loc_1A9C0 in "_incObj/86 FZ Plasma Ball Launcher.asm", we can see this:
    Code:
            move.w    obX(a0),d0
            sub.w    $30(a0),d0
            bcc.s    loc_1A9E6
            clr.w    obVelX(a0)
            add.w    d0,obX(a0)    ; <-- BUG
    The first 2 lines get the distance between the plasma ball's current X position and its target X position. If it has moved past that target X position, that distance value should be negative. The branch after checks if the result underflowed (has gone negative), and if not, it skips over the rest. If it HAS, then it stops its movement and attempts to align it to the target X position by nudging it by the amount that it has moved past.

    The bug in question is that it uses an addition to do that alignment. When you add a negative number, it's a subtraction. In this case, it's basically moving the plasma ball MORE to the left, instead of pushing it towards the right to its actual target X position.

    To fix that, just change the add to a sub. Now, if you're okay with the balls spreading out a bit less like this, you can stop here.

    [​IMG]

    In fact, this is the same behavior as the 2013 Taxman remake, because it doesn't have that bug in it.

    [​IMG]

    But, if you want them to actually spread out further, then go to Obj86_Loop and change
    Code:
            muls.w    #-$4F,d1
    to
    Code:
            muls.w    #-$59,d1
    and you'll get this again.

    [​IMG]

    If you want to have the plasma balls spread out further without adding this bug, then in "SBZ/FZEggman.txt", in the FZEGGMAN_SETUP_PLASMAATTACK case, change the subtraction of 0x4F0000 to 0x590000.
     
    Last edited: Aug 20, 2023
    Selbi, ProjectFM and DeltaWooloo like this.
  9. TheFieldWarrior

    TheFieldWarrior Warrior of the Fields Member

    Joined:
    Oct 9, 2015
    Messages:
    95
    Location:
    United Kingdom
    Sonic 2: Exiting the 2P menu with a button press

    Unlike the Options and Level Select menus there is no method to return to the Sega Screen from the 2P menu. Here's how to add an option to exit the 2P menu by pressing B.


    First, head to LevelSelect2P_Main and look for the following lines:


    Code:
    bne.s   LevelSelect2P_PressStart
    bra.w   LevelSelect2P_Main

    Change LevelSelect2P_Main to LevelSelect2P_PressB. This is our new subroutine to check for a B button press.


    Next, add this before LevelSelect2P_PressStart:


    Code:
    LevelSelect2P_PressB:
        move.b  (Ctrl_1_Press).w,d0
        or.b    (Ctrl_2_Press).w,d0
        andi.b  #button_B_mask,d0
        bne.s   LevelSelect2P_Return
        bra.w   LevelSelect2P_Main


    And finally head to loc_8E3A and after the rts add this:


    Code:
    LevelSelect2P_Return:
        move.b    #GameModeID_SegaScreen,(Game_Mode).w ; => SegaScreen
        rts    

    Build the ROM and now you can press B to exit the 2P menu. Of course you don't have to make B the button to press, you could make it A, C or even all three.
     
  10. EddyTF

    EddyTF 단순 한 사람 Member

    Joined:
    Jan 9, 2022
    Messages:
    70
    Location:
    Russia
    How to make different music sound at different special stages (for Sonic 1 Hivebrain 2005 users)

    This guide is useful if you used the old guide from nineko:
    https://info.sonicretro.org/SCHG_How-to:Play_different_songs_on_different_acts

    It's very easy to do as you thought.

    Looking for a SpecialStage, scroll down a little and find SS_ClrNemRam.

    There you will find it:
    Code:
    move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage BG    music
    And replace it with my code:
    Code:
    cmpi.b    #$1,($FFFFFE16).w    ; is this special stage 1?
            bne.s    @SS_ClrNemRam_cont    ; if not, branch
            move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage 1 BG music
    @SS_ClrNemRam_cont:
            cmpi.b    #$2,($FFFFFE16).w    ; is this special stage 2?
            bne.s    @SS_ClrNemRam_cont2    ; if not, branch
            move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage 2 BG music
    @SS_ClrNemRam_cont2:
            cmpi.b    #$3,($FFFFFE16).w    ; is this special stage 3?
            bne.s    @SS_ClrNemRam_cont3    ; if not, branch
            move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage 3 BG music
    @SS_ClrNemRam_cont3:
            cmpi.b    #$4,($FFFFFE16).w    ; is this special stage 4?
            bne.s    @SS_ClrNemRam_cont4    ; if not, branch
            move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage 4 BG music
    @SS_ClrNemRam_cont4:
            cmpi.b    #$5,($FFFFFE16).w    ; is this special stage 5?
            bne.s    @SS_ClrNemRam_cont5    ; if not, branch
            move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage 5 BG music
    @SS_ClrNemRam_cont5:
            cmpi.b    #$0,($FFFFFE16).w    ; is this special stage 6?
            bne.s    @SS_ClrNemRam_continue    ; if not, branch
            move.w    #$89,d0
            bsr.w    PlaySound    ; play special stage 6 BG music
    @SS_ClrNemRam_continue:
    Next, you will need to expand the music slots, but this will not be a problem, because there are a lot of guides on the Internet.

    In general, that's it. Compile and check. Everything should work perfectly.

    P.S. if you have any problems about this, write to me.
     
    Hame and Nik Pi like this.
  11. TheFieldWarrior

    TheFieldWarrior Warrior of the Fields Member

    Joined:
    Oct 9, 2015
    Messages:
    95
    Location:
    United Kingdom
    How to add Adventure styled Uncurling:

    One cool QOL feature that's only appeared in a couple of hacks is the ability to uncurl from rolling similar to the Adventure games, this can be useful for when you want to maintain your speed from spin dashing or to slow down without needing to jump first. For this tutorial I'll be using Sonic 2 (Github).


    First head to Obj01_MdRoll:


    Code:
    Obj01_MdRoll:
        tst.b    pinball_mode(a0)
        bne.s    +
        bsr.w    Sonic_Jump
    +
        bsr.w    Sonic_RollRepel
        bsr.w    Sonic_RollSpeed
        bsr.w    Sonic_LevelBound
        jsr    (ObjectMove).l
        bsr.w    AnglePos
        bsr.w    Sonic_SlopeRepel
        rts

    Just below the line bsr.w Sonic_Jump add the following:


    Code:
        tst.w   move_lock(a0)
        bne.s   +
        btst    #0,(Ctrl_1_Press_Logical).w
        beq.s   +
        clr.b   anim(a0)
        bra.w   Sonic_CheckRollStop2

    This code checks for an Up button press, if Up is pressed the rolling animation is cleared and will then branch to the new label Sonic_CheckRollStop2. The move_lock(a0) line is there to prevent you from uncurling in situations where you're unable to perform certain actions.


    Next find Sonic_CheckRollStop:


    Code:
    Sonic_CheckRollStop:
        tst.w    inertia(a0)
        bne.s    Obj01_Roll_ResetScr
        tst.b    pinball_mode(a0) ; note: the spindash flag has a different meaning when Sonic's already rolling -- it's used to mean he's not allowed to stop rolling
        bne.s    Sonic_KeepRolling
        bclr    #2,status(a0)
        move.b    #$13,y_radius(a0)
        move.b    #9,x_radius(a0)
        move.b    #AniIDSonAni_Wait,anim(a0)
        subq.w    #5,y_pos(a0)
        bra.s    Obj01_Roll_ResetScr

    You will need to move the line move.b #AniIDSonAni_Wait,anim(a0) and place it underneath the line bne.s Sonic_KeepRolling. Then you will need to add our new label above the bclr #2,status(a0) line like so:

    Code:
        bne.s    Sonic_KeepRolling
        move.b  #AniIDSonAni_Wait,anim(a0)
       
    Sonic_CheckRollStop2:  
        bclr    #2,status(a0)
        move.b    #$13,y_radius(a0)
        move.b    #9,x_radius(a0)
        subq.w    #5,y_pos(a0)
        bra.s    Obj01_Roll_ResetScr

    Our new label executes the second half (minus the moved line) of the original CheckRollStop code where Sonic's roll status is cleared and is returned to standing height.

    Now you'll need to apply these changes to Tails and any extra characters you've added to the game. Just make sure for Tails on the button check line that the label is changed to (Ctrl_2_Press_Logical).

    After applying the changes to the rest of the characters and building the ROM you can now uncurl from rolling by pressing Up. Enjoy!


    EDIT: This tutorial has been rewritten to utilize optimized code by djohe
     
    Last edited: Sep 1, 2023
    JGamer2151 and Hame like this.
  12. Devon

    Devon I'm a loser, baby, so why don't you kill me? Member

    Joined:
    Aug 26, 2013
    Messages:
    1,376
    Location:
    your mom
    The trap doors in Scrap Brain Zone have a small bug where if you are off screen, but not enough to despawn them, their sprite will wrap over and appear at the edge of the screen.

    For example, here's this trap door

    [​IMG]

    And if I move a bit to the left, but not have it despawned yet...

    [​IMG]

    The reason for this is that it has a pretty ridiculously high sprite width set on initialization.

    Code:
            move.b    #$80,obActWid(a0)

    The trap door sprite is $80 pixels wide, but the value set here is supposed to be half of that. The fix is simple: change the $80 to a $40.

    It should be noted that the sound for when it moves is only ever played when the object is on screen. Due to the larger width, the sound could play even with it was just slightly off screen, but with this change, it will no longer play if the object is not off screen at all. If you want to retain that behavior, then you'll have to manually check the camera's position.
     
    Last edited: Sep 12, 2023
    ProjectFM and DeltaWooloo like this.
  13. Miles Sebas Prower

    Miles Sebas Prower Newcomer Member

    Joined:
    Aug 22, 2023
    Messages:
    21
    Location:
    Alvarenga, Charallave
    How to add the MZ Blocks Effects on lava surface to Sonic 1 (REV00)

    So if you've been wanting to add the effects from REV01 to your Sonic 1 hack, and you added one of the effects to it, most likely by following this guide, then you went to Marble Zone, you will see that the blocks that fall into the lava still remain static. If you want to add the blocks effect that REV01 added to Sonic 1 into your hack but have no asm knowledge, then this mini basic guide is for you. Keep in mind that this is specifically for the Hivebrain 2005 disassembly, if you're using the GitHub disassembly, then you don't need to follow this guide

    To add the MZ blocks effects to Sonic 1, first, open your sonic1.asm file, then, with the Find option, make a search for the Obj46_Type03 routine

    Hivebrain
    Code:
    Obj46_Type03:                ; XREF: Obj46_TypeIndex
            bsr.w    SpeedToPos
            addi.w    #$18,$12(a0)    ; increase falling speed
            bsr.w    ObjHitFloor
            tst.w    d1        ; has the block    hit the    floor?
            bpl.w    locret_E8EE    ; if not, branch
            add.w    d1,$C(a0)
            clr.w    $12(a0)        ; stop the block falling
            move.w    $C(a0),$30(a0)
            move.b    #4,$28(a0)
            move.w    (a1),d0
            andi.w    #$3FF,d0
            cmpi.w    #$2E8,d0    ; <--- This makes the blocks that hit the lava remain static
            bcc.s    locret_E8EE
            move.b    #0,$28(a0)
    
    locret_E8EE:
            rts
    This is the reason why the blocks still remain static, if you added the effects, you just added the background effects, but not the effects for the blocks that fall into the lava in MZ, they don't wobble at all, precisely because of the cmpi.w #$2E8,d0 command, which is pointed, the blocks remain static because of it. Now take a look at GitHub's code

    GitHub
    Code:
    Brick_Type03:
            bsr.w    SpeedToPos
            addi.w    #$18,obVelY(a0)    ; increase falling speed
            bsr.w    ObjFloorDist
            tst.w    d1        ; has the block    hit the    floor?
            bpl.w    locret_E8EE    ; if not, branch
            add.w    d1,obY(a0)
            clr.w    obVelY(a0)    ; stop the block falling
            move.w    obY(a0),brick_origY(a0)
            move.b    #4,obSubtype(a0)
            move.w    (a1),d0
            andi.w    #$3FF,d0
            if Revision=0
            cmpi.w    #$2E8,d0
            else
                cmpi.w    #$16A,d0
            endif
            bcc.s    locret_E8EE
            move.b    #0,obSubtype(a0)
    
    locret_E8EE:
            rts
    From what you see here, as you can probably tell, in GitHub, the blocks effects are already added, if you want to add them to your Sonic 1 hack, simple, change the #$2E8 to #$16A, and you should get this

    Code:
    Obj46_Type03:                ; XREF: Obj46_TypeIndex
            bsr.w    SpeedToPos
            addi.w    #$18,$12(a0)    ; increase falling speed
            bsr.w    ObjHitFloor
            tst.w    d1        ; has the block    hit the    floor?
            bpl.w    locret_E8EE    ; if not, branch
            add.w    d1,$C(a0)
            clr.w    $12(a0)        ; stop the block falling
            move.w    $C(a0),$30(a0)
            move.b    #4,$28(a0)
            move.w    (a1),d0
            andi.w    #$3FF,d0
            cmpi.w    #$16A,d0
            bcc.s    locret_E8EE
            move.b    #0,$28(a0)
    
    locret_E8EE:
            rts
    And there you go, after this, you have ported the MZ blocks effects from REV01 into your Sonic 1 hack
    Keep in mind that you don't need to do this if you're using the GitHub disassembly, as it'll just go for the cmpi.w #$16A,d0 command instead of the cmpi.w #$2E8,d0 if you either choose to build the REV01 version or the Sonic Mega Collection version of the game, making the blocks that fall into the lava wobble on the surface

    Now after that, build your rom, and if you go to Marble Zone, you will now see that the blocks will float on the lava instead of remaining static on the surface. That's all for this guide, enjoy the MZ blocks effects from REV01

    EDIT: I just noticed a typo at the beginning, so I just fixed it
     
    Last edited: Oct 17, 2023
    ProjectFM likes this.
  14. Miles Sebas Prower

    Miles Sebas Prower Newcomer Member

    Joined:
    Aug 22, 2023
    Messages:
    21
    Location:
    Alvarenga, Charallave
    If for some reason, you want to change the way Sonic 2's Debug Mode behaves when it comes to deaths and make it behave like Sonic 1 where it doesn't almost let you die at all (Almost because with Debug Mode on, the only possible ways to die while it's on in Sonic 1 is falling to the bottom of the level, getting crushed or drowning), then this guide is going to show you how it's done to change this behavior to Sonic 1. For this guide I will cover the Xenowhirl disassembly of Sonic 2, but I'm sure it'll be easy enough to adapt it to the GitHub disassembly

    First, open your s2.asm file, then, look for the subroutine that handles Sonic and Tails when they get hurt, after that, look for the loc_3F88C routine

    Code:
    loc_3F88C:
        btst    #0,status_secondary(a0)
        bne.s    Hurt_Shield
        tst.w    d0
        beq.w    KillCharacter
        jsr    (SingleObjLoad).l
        bne.s    Hurt_Shield
        _move.b    #$37,0(a1) ; load obj
        move.w    x_pos(a0),x_pos(a1)
        move.w    y_pos(a0),y_pos(a1)
        move.w    a0,parent(a1)
    If you have ever screwed around Sonic 1 and Sonic 2's versions of this routine, then you can clearly see this code is exactly the same as Sonic 1, but with a few changes made to the subroutine code, precisely this code here:

    Code:
    SONIC 1
    HurtSonic:
            tst.b    ($FFFFFE2C).w    ; does Sonic have a shield?
            bne.s    Hurt_Shield    ; if yes, branch
            tst.w    ($FFFFFE20).w    ; does Sonic have any rings?
            beq.w    Hurt_NoRings    ; if not, branch
            jsr    SingleObjLoad
            bne.s    Hurt_Shield
            move.b    #$37,0(a1)    ; load bouncing    multi rings object
            move.w    8(a0),8(a1)
            move.w    $C(a0),$C(a1)
    It's exactly the SAME as Sonic 1, except that this has a branch to a routine that only has a code for Debug Mode, the code above is Sonic 2's version, where it has been changed to branch to the KillCharacter routine. Now check this out:

    Code:
    Hurt_NoRings:
            tst.w    ($FFFFFFFA).w    ; is debug mode    cheat on?
            bne.w    Hurt_Shield    ; if yes, branch
    What this routine does is only check if Debug Mode is on, and IF you are playing the game with it on, after taking damage, it branches to the Hurt_Shield routine, making Sonic almost unable to die. This is only on Sonic 1. This routine doesn't exist in Sonic 2 as they removed it from the code and changed the branch to it to the KillCharacter routine in order for the Debug Mode to let Sonic die while it's on. To change this behavior to Sonic 1, go to the loc_3F88C routine, and on the beq.w line right after the check for the rings, replace KillCharacter with something else, so we can make it branch to a new routine that will do the same as Sonic 1... in my case, I will use Hurt_Debug for this. Anyways, replace this line:
    Code:
        beq.w    KillCharacter
    With this:
    Code:
        beq.w    Hurt_Debug
    Now we need to create a new routine for this, otherwise the rom is not going to build and it will give you a "symbol undefined" error because the routine is non-existent in Sonic 2, so we need to create it. What we need to do to get this behavior is go to the end of the Hurt_Sound routine, also the beginning of the subroutine when Sonic and Tails are killed, and between the Hurt_Sound routine and KillCharacter routine at the start of the subroutine, insert this code:
    Code:
    ; loc_3F920:
    Hurt_Debug:
        tst.w    (Debug_mode_flag).w    ; is Debug Mode on?
        bne.s    Hurt_Shield    ; if yes, branch
    If everything is done properly, you should be able to build the rom without getting any errors. Now build your rom and test it Screenshot_20231018-161953_SuperMD.jpg

    And tada, now Debug Mode will not let Sonic die like in Sonic 1, because you have changed this behavior and made it disable deaths when Debug Mode is on!
    Note: It should be noted that it won't fully disallow you from dying, since you can still die if you fall to the bottom of the level, get crushed or if you drown, as they're the only possible ways to die while Debug Mode is on
     
    Last edited: Oct 19, 2023
    ProjectFM likes this.
  15. Miles Sebas Prower

    Miles Sebas Prower Newcomer Member

    Joined:
    Aug 22, 2023
    Messages:
    21
    Location:
    Alvarenga, Charallave
    If you've removed the Caterkiller PLC from the SYZ data to fix the Roller graphics bug, you used Debug Mode and for some reason you happened to place Caterkillers in Spring Yard Zone, you must have probably noticed this graphics glitch that the badnik suffers in here 20231022_125444.jpg
    This occurs due to part of the Caterkiller data still existent within the SYZ list for the Debug Mode.. so how can we fix it? The fix is quite simple, just go to your disassembly and open the Debug list - SYZ.asm file located on the _inc folder
    Code:
    ; ---------------------------------------------------------------------------
    ; Debug    list - Spring Yard
    ; ---------------------------------------------------------------------------
        dc.w $F
        dc.l Map_obj25+$25000000
        dc.b 0,    0, $27,    $B2
        dc.l Map_obj26+$26000000
        dc.b 0,    0, 6, $80
        dc.l Map_obj36+$36000000
        dc.b 0,    0, 5, $1B
        dc.l Map_obj41+$41000000
        dc.b 0,    0, 5, $23
        dc.l Map_obj43+$43000000
        dc.b 0,    0, 4, $B8
        dc.l Map_obj12+$12000000
        dc.b 0,    0, 0, 0
        dc.l Map_obj47+$47000000
        dc.b 0,    0, 3, $80
        dc.l Map_obj1F+$1F000000
        dc.b 0,    0, 4, 0
        dc.l Map_obj22+$22000000
        dc.b 0,    0, 4, $44
        dc.l Map_obj50+$50000000
        dc.b 0,    0, $24,    $7B
        dc.l Map_obj18a+$18000000
        dc.b 0,    0, $40,    0
        dc.l Map_obj56+$56000000
        dc.b 0,    0, $40,    0
        dc.l Map_obj32+$32000000
        dc.b 0,    0, 5, $13
        dc.l Map_obj78+$78000000
        dc.b 0,    0, $24,    $FF
        dc.l Map_obj79+$79000000
        dc.b 1,    0, 7, $A0
        even
    This is why the player can place Caterkillers in SYZ, the list for this zone for the Debug Mode includes the Obj78, allowing them to place as many badniks as they want or as the game allows it, but the PLC in SYZ are non-existent, causing the Caterkiller graphics glitch
    To fix this, just remove the Obj78 from the code, like so:
    Code:
    ; ---------------------------------------------------------------------------
    ; Debug    list - Spring Yard
    ; ---------------------------------------------------------------------------
        dc.w $F
        dc.l Map_obj25+$25000000
        dc.b 0,    0, $27,    $B2
        dc.l Map_obj26+$26000000
        dc.b 0,    0, 6, $80
        dc.l Map_obj36+$36000000
        dc.b 0,    0, 5, $1B
        dc.l Map_obj41+$41000000
        dc.b 0,    0, 5, $23
        dc.l Map_obj43+$43000000
        dc.b 0,    0, 4, $B8
        dc.l Map_obj12+$12000000
        dc.b 0,    0, 0, 0
        dc.l Map_obj47+$47000000
        dc.b 0,    0, 3, $80
        dc.l Map_obj1F+$1F000000
        dc.b 0,    0, 4, 0
        dc.l Map_obj22+$22000000
        dc.b 0,    0, 4, $44
        dc.l Map_obj50+$50000000
        dc.b 0,    0, $24,    $7B
        dc.l Map_obj18a+$18000000
        dc.b 0,    0, $40,    0
        dc.l Map_obj56+$56000000
        dc.b 0,    0, $40,    0
        dc.l Map_obj32+$32000000
        dc.b 0,    0, 5, $13
        dc.l Map_obj79+$79000000
        dc.b 1,    0, 7, $A0
        even
    There you go, now Debug Mode won't place Caterkillers in SYZ, and it won't be on the zone list because the badnik has been removed from the SYZ list of Debug Mode
     
    Last edited: Oct 23, 2023
    Hame likes this.
  16. Miles Sebas Prower

    Miles Sebas Prower Newcomer Member

    Joined:
    Aug 22, 2023
    Messages:
    21
    Location:
    Alvarenga, Charallave
    Okay, so after asking nineko the permission to use his code for the guide and agreeing with it, here's the guide to implement this feature to Sonic 1, Sonic 2 and Sonic 3 (& Knuckles)
    Actually, these are just minor code changes, but since this guide is really long, I'll use the Spoiler tags to split it separately and separate it respectively

    Okay, most hackers across sites like this must've likely bumped into this years ago, they also must've likely seen this feature added in a few hacks back in the day, but since there's no tutorial or anything about it, here's one for beginners

    How to resume invincibility music when already playing

    If you have destroyed a invincibility monitor before the powerup expires, you have probably noticed that the music restarts if a new monitor is broken before the old one expires
    This happens because the game isn't checking if the sound driver play is currently playing the invincibility music nor if the player is already invincible at all. So if you want the music to resume from where it is instead of restarting in your hacks, then follow this until the end, let's implement this feature!
    Note: This is present in all three games, so we're going to cover this guide for all three games, though in some cases this isn't easy to perform, a few easy examples are GHZ Act 1 in Sonic 1, EHZ Act 1 in Sonic 2, AIZ Act 1 and MHZ Act 2 in Sonic 3 (& Knuckles), but if you're editing the layouts to place invincibility monitors close enough before 20 seconds pass, then this may become more apparent, so let's add this feature to all three games!

    Sonic 1 (Hivebrain 2005)

    Open up the sonic1.asm file and perform a search for the Obj2E_ChkInvinc label, it should look like this
    Code:
    Obj2E_ChkInvinc:
            cmpi.b    #5,d0        ; does monitor contain invincibility?
            bne.s    Obj2E_ChkRings
            move.b    #1,($FFFFFE2D).w ; make    Sonic invincible
            move.w    #$4B0,($FFFFD032).w ; time limit for the power-up
    To make the music resume, change this:
    Code:
            move.b    #1,($FFFFFE2D).w ; make    Sonic invincible
            move.w    #$4B0,($FFFFD032).w ; time limit for the power-up
    To this:
    Code:
            move.w    #$4B0,($FFFFD032).w ; time limit for the power-up
            cmpi.b    #$1,($FFFFFE2D).w    ; is Sonic already invincible?
            beq.s    Obj2E_NoMusic    ; if so, resume invincibility music instead of restarting it
            move.b    #1,($FFFFFE2D).w ; make    Sonic invincible
    Now the invincibility music will resume from where it is instead of restarting if you break a new monitor before the old one expires

    Sonic 1 (GitHub)

    Open up the 2E Monitor Content Power-Up.asm file located at the _incObj folder and perform a search for the Pow_ChkInvinc label, it should look like this
    Code:
    Pow_ChkInvinc:
            cmpi.b    #5,d0        ; does monitor contain invincibility?
            bne.s    Pow_ChkRings
    
    
    
            move.b    #1,(v_invinc).w    ; make Sonic invincible
            move.w    #$4B0,(v_player+$32).w ; time limit for the power-up
    To make the music resume, change this:
    Code:
            move.b    #1,(v_invinc).w    ; make Sonic invincible
            move.w    #$4B0,(v_player+$32).w ; time limit for the power-up
    To this:
    Code:
            move.w    #$4B0,(v_player+$32).w ; time limit for the power-up
            cmpi.b    #$1,(v_invinc).w    ; is Sonic already invincible?
            beq.s    Pow_NoMusic    ; if so, resume invincibility music instead of restarting it
            move.b    #1,(v_invinc).w    ; make Sonic invincible
    Now the invincibility music will resume from where it is instead of restarting if you break a new monitor before the old one expires

    Sonic 2 (Xenowhirl 2007)

    Open up the s2.asm file and perform a search for the invincible_monitor label, it should look like this
    Code:
    invincible_monitor:
        addq.w    #1,(a2)
        tst.b    (Super_Sonic_flag).w
        bne.s    return_12AA4
        bset    #1,status_secondary(a1)
        move.w    #$4B0,invincibility_time(a1)
    To make the music resume, change this:
    Code:
        bset    #1,status_secondary(a1)
        move.w    #$4B0,invincibility_time(a1)
    To this:
    Code:
        move.w    #$4B0,invincibility_time(a1)
        btst    #1,status_secondary(a1)        ; Is Sonic/Tails already invincible?
        bne.s    return_12AA4        ; Resume invincibility music instead of restarting it if Sonic/Tails is already invincible
        bset    #1,status_secondary(a1)
    Now the invincibility music will resume from where it is instead of restarting if you break a new monitor before the old one expires

    Sonic 2 (GitHub)

    Open up the s2.asm file and perform a search for the invincible_monitor label, it should look like this
    Code:
    invincible_monitor:
        addq.w    #1,(a2)
        tst.b    (Super_Sonic_flag).w    ; is Sonic super?
        bne.s    +++    ; rts        ; if yes, branch
        bset    #status_sec_isInvincible,status_secondary(a1)    ; give invincibility status
        move.w    #20*60,invincibility_time(a1) ; 20 seconds
    To make the music resume, change this:
    Code:
        bset    #status_sec_isInvincible,status_secondary(a1)    ; give invincibility status
        move.w    #20*60,invincibility_time(a1) ; 20 seconds
    To this:
    Code:
        move.w    #20*60,invincibility_time(a1) ; 20 seconds
        btst    #status_sec_isInvincible,status_secondary(a1)        ; Is Sonic/Tails already invincible?
        bne.s    +++        ; Resume invincibility music instead of restarting it if Sonic/Tails is already invincible
        bset    #status_sec_isInvincible,status_secondary(a1)    ; give invincibility status
    Now the invincibility music will resume from where it is instead of restarting if you break a new monitor before the old one expires

    Part 1: Sonic 3

    For this guide I will cover the GitHub disassembly of Sonic 3 & Knuckles because it allows you to build the following games, but if you are using any other disassembly, some modifications may need to be made in order for this to work in other disassemblies

    Step 1: Continuous music after breaking a invincibility monitor

    Open up the s3.asm file and perform a search for the Monitor_Give_Invincibility label, it should look like this
    Code:
    Monitor_Give_Invincibility:
            addq.w    #1,(a2)
            tst.b    (Super_Sonic_Knux_flag).w
            bne.s    locret_1B876
            bset    #1,$2B(a1)
            move.b    #$96,$35(a1)
    To make the music resume, change this:
    Code:
            bset    #1,$2B(a1)
            move.b    #$96,$35(a1)
    To this:
    Code:
            move.b    #$96,$35(a1)
            btst    #1,$2B(a1)
            bne.s    locret_1B876
            bset    #1,$2B(a1)
    Now the invincibility music will resume from where it is instead of restarting if you break a new monitor before the old one expires

    But... WAIT, hold up!! This is not all! If you are using Debug Mode and you place a Super Sonic monitor (Correct me if I'm wrong, but I haven't seen a Super Sonic monitor on any of the Sonic 3 levels placed by Sonic Team themselves), and you destroy it while you have invincibility, the music will also restart, it will also restart if you break more monitors while you are already transformed just to get more rings, so let's change this!

    Step 2: Continuous music after breaking a Super Sonic monitor

    To make it resume from where it is after breaking a Super Sonic monitor while invincibility has not expired or while you are already transformed, look for the Monitor_Give_SuperSonic label, which is just a few lines away from the invincibility monitor label, you should see this
    Code:
    Monitor_Give_SuperSonic:
            addq.w    #1,(a2)
            addi.w    #50,(Ring_count).w
            move.b    #1,(Super_palette_status).w
            move.b    #$F,(Palette_timer).w
            move.b    #1,(Super_Sonic_Knux_flag).w
            move.w    #60,(Super_frame_count).w
            move.l    #Map_SuperSonic,(Player_1+mappings).w
            move.b    #$81,(Player_1+object_control).w
            move.b    #$1F,(Player_1+anim).w
            move.l    #Obj_SuperSonic_Stars,(Super_stars).w
            move.w    #$A00,(Max_speed).w
            move.w    #$30,(Acceleration).w
            move.w    #$100,(Deceleration).w
            move.b    #0,(Player_1+invincibility_timer).w
            bset    #1,$2B(a1)
            moveq    #signextendB(sfx_Whistle),d0
            jsr    (Play_SFX).l    ; <-
            moveq    #signextendB(mus_Invincibility),d0
            jmp    (Play_Music).l
    Place this under the pointed line:
    Code:
            btst    #1,$2B(a1)
            bne.s    locret_1B8DF
    And above the rts command right after the jmp command, place the label, like so:
    Code:
    locret_1B8DF:
            rts
    The locret_1B8DF label has to be in, above the rts, which is after the command to play the jmp (Play_Music).l line, this is here to prevent a "symbol undefined" error, otherwise the rom won't build

    Step 3: Fixing a bug

    See this line?
    Code:
            bset    #1,$2B(a1)
    This command sets the invincibility status for Sonic after he destroys the monitor, move it below the bne.s locret_1B8DF line, otherwise there is going to be a new bug where the music won't play at all if Sonic breaks the monitor and he doesn't have invincibility, and we want the invincibility music to play rather than resuming the zone BGM after transforming by destroying the monitor, do we? Then move the bset command below the indicated line

    Now the music will resume from where it is if a Super Sonic monitor is destroyed before the invincibility expires, it will also play if the monitor is broken and Sonic doesn't have invincibility

    Note: It should be noted that this will only apply to Sonic 3 alone, the music will still restart in Sonic & Knuckles and Sonic 3 & Knuckles, so if you don't want it to restart at all in both games, then go to the next part

    Part 2: Sonic & Knuckles/Sonic 3 & Knuckles

    Step 1: Continuous music after breaking a invincibility monitor

    Open up the sonic3k.asm file and perform a search for the Monitor_Give_Invincibility label, it should look like this
    Code:
    Monitor_Give_Invincibility:
            addq.w    #1,(a2)
            tst.b    (Super_Sonic_Knux_flag).w
            bne.s    locret_1DA5E
            tst.b    (Super_Tails_flag).w
            bne.s    locret_1DA5E
            bset    #1,$2B(a1)
            move.b    #$96,$35(a1)
    To make the music resume, change this:
    Code:
            bset    #1,$2B(a1)
            move.b    #$96,$35(a1)
    To this:
    Code:
            move.b    #$96,$35(a1)
            btst    #1,$2B(a1)    ; Are Sonic, Tails or Knuckles already invincible?
            bne.s    locret_1DA5E    ; Resume invincibility music instead of restarting it if they are already invincible
            bset    #1,$2B(a1)
    Now the invincibility music will resume from where it is instead of restarting if you break a new monitor before the old one expires

    But... WAIT, hold up!! This is not all! If you are using Debug Mode and you place a Super/Hyper monitor, and you destroy it while you have invincibility, the music will also restart, it will also restart if you break more monitors while you are already transformed just to get more rings, so let's change this behavior!

    Step 2: Continuous music after breaking a Super Sonic monitor

    To make it resume from where it is after breaking a Super/Hyper monitor while invincibility has not expired or while you are already transformed, look for the Monitor_Give_SuperSonic label, which is just a few lines away from the invincibility monitor label, you should see this
    Code:
    Monitor_Give_SuperSonic:
    [...]
        .continued:
            move.b    #$81,(Player_1+object_control).w
            move.b    #0,(Player_1+invincibility_timer).w
            bset    #Status_Invincible,status_secondary(a1)
            moveq    #signextendB(sfx_SuperTransform),d0
            jsr    (Play_SFX).l        ; <-
            moveq    #signextendB(mus_Invincibility),d0        ; play invincibility theme
            jmp    (Play_Music).l
    Place this below the pointed line:
    Code:
            btst    #Status_Invincible,status_secondary(a1)    ; Is invincility music already playing?
            bne.s    locret_1DB25    ; Resume music instead of restarting it if it's already playing
    And above the rts command right after the jmp command, place the label, like so:
    Code:
    locret_1DB25:
            rts
    The locret_1DB25 label has to be in, above the rts, which is after the command to play the jmp (Play_Music).l line, this is here to prevent a "symbol undefined" error, otherwise the rom won't build

    Step 3: Fixing a bug

    See this line?
    Code:
            bset    #Status_Invincible,status_secondary(a1)
    This command sets the invincibility status for Sonic, Tails and Knuckles after they destroy the monitor, move it below the bne.s locret_1DB25 line, otherwise there is going to be a new bug where the music won't play at all if Sonic, Tails or Knuckles break the monitor and they don't have invincibility, and we want the invincibility music to play rather than resuming the zone BGM after transforming by destroying the monitor, do we? Then move the bset command below the indicated line

    Now the music will resume from where it is if a Super/Hyper monitor is destroyed before the invincibility expires, it will also play if the monitor is broken and Sonic, Tails and Knuckles don't have invincibility

    But WAIT, hold up!! We haven't finished yet!! If Tails transforms while he is invincible, this will also restart the music, so let's change this!

    Step 4: Continuous music when transforming as Tails

    To make the music resume when Tails transforms with his invincibility, look for the Tails_Transform label, you should see this:
    Code:
    Tails_Transform:
            move.b    #1,(Super_palette_status).w    ; set Super/Hyper palette status to 'fading'
            move.b    #$F,(Palette_timer).w
            move.b    #1,(Super_Tails_flag).w            ; set flag to Super Tails
            move.w    #60,(Super_frame_count).w
            move.b    #$81,object_control(a0)
            move.b    #$29,anim(a0)                ; enter 'transformation' animation
            move.l    #Obj_SuperTailsBirds,(Invincibility_stars).w    ; load Super Flickies object
            move.w    #$800,Max_speed_P2-Max_speed_P2(a4)
            move.w    #$18,Acceleration_P2-Max_speed_P2(a4)
            move.w    #$C0,Deceleration_P2-Max_speed_P2(a4)
            move.b    #0,invincibility_timer(a0)
            bset    #Status_Invincible,status_secondary(a0)
            moveq    #signextendB(sfx_SuperTransform),d0
            jsr    (Play_SFX).l    ; <-
            moveq    #signextendB(mus_Invincibility),d0        ; play invincibility theme
            jmp    (Play_Music).l
    ; End of function Tails_JumpHeight
    Under the pointed line, place this:
    Code:
            btst    #Status_Invincible,status_secondary(a0)    ; is Tails already invincible?
            bne.s    locret_151AA    ; resume invincibility music instead of restarting it if he is already invincible
    Again, the bset command is there, so move it below the bne.s locret_151AA line, or the music won't play and instead the BGM will resume if Tails transforms without the invincibility

    And right after the jmp (Play_Music).l command, place this
    Code:
    locret_151AA:
            rts
    This has to be placed below the jmp command, otherwise the rom is not going to build and it will give you a "symbol undefined" error, so place it below the mentioned line to prevent it

    Now the music will resume instead of restarting if Tails transforms and he has invincibility

    WAIT, we are almost finished! Why? Because if Knuckles transforms while he is invincible, this will also restart the music, so let's change this!

    Step 5: Continuous music when transforming as Knuckles

    Look for the Knux_Transform label, this is what it looks like:
    Code:
    Knux_Transform:
    [...]
        .continued:
            move.w    #$800,Max_speed-Max_speed(a4)
            move.w    #$18,Acceleration-Max_speed(a4)
            move.w    #$C0,Deceleration-Max_speed(a4)
            move.b    #0,invincibility_timer(a0)
            bset    #Status_Invincible,status_secondary(a0)
            moveq    #signextendB(sfx_SuperTransform),d0
            jsr    (Play_SFX).l        ; <-
            moveq    #signextendB(mus_Invincibility),d0        ; play invincibility theme
            jmp    (Play_Music).l
    ; End of function Knux_JumpHeight
    Insert these lines under the indicated line:
    Code:
            btst    #Status_Invincible,status_secondary(a0)        ; is Knuckles already invincible?
            bne.s    locret_178D4    ; resume invincibility music instead of restarting it if he is already invincible
    The bset command is here again, so move it below the bne.s locret_178D4 line

    After the jmp (Play_Music).l line, insert this
    Code:
    locret_178D4:
            rts
    I'm not gonna explain why, because you already know what will happen if this isn't in

    Now the music will resume from where it is if Knuckles transforms while invincible

    Extra

    Step 6.1: Continuous music when transforming as Sonic (Sonic 3)

    I'm going to add that the following changes aren't added in the previous steps because Sonic can't transform while invincible, if you attempt to make him transform, nothing much will happen... so basically in Sonic 3 (& Knuckles), Sonic is forced to wait until his powerup expires so he can transform unlike Sonic 2, where he can transform while he has invincibility, but if you're editing the code to make the game allow Sonic to transform while he is invincible like it does in Sonic 2 and you don't want the music to restart if it's already playing, then follow the extra steps

    In s3.asm, look for the Sonic_Transform label, this is what it looks like:
    Code:
    Sonic_Transform:
            move.b    #1,(Super_palette_status).w
            move.b    #$F,(Palette_timer).w
            move.b    #1,(Super_Sonic_Knux_flag).w
            move.w    #60,(Super_frame_count).w
            move.l    #Map_SuperSonic,$C(a0)
            move.b    #$81,$2E(a0)
            move.b    #$1F,$20(a0)
            move.l    #Obj_SuperSonic_Stars,(Super_stars).w
            move.w    #$A00,(a4)
            move.w    #$30,2(a4)
            move.w    #$100,4(a4)
            move.b    #0,$35(a0)
            bset    #1,$2B(a0)
            move.w    #$FF00|sfx_SuperTransform,d0
            jsr    (Play_SFX).l    ; <-
            move.w    #mus_Invincibility,d0
            jmp    (Play_Music).l
    ; End of function Sonic_JumpHeight
    Under the indicated line, insert this:
    Code:
            btst    #1,$2B(a0)
            bne.s    locret_12A2E
    Once again, the bset command is here, so move it below the bne.s locret_12A2E line, or the music is not going to play at all if Sonic transforms and he doesn't have invincibility

    Now place this below the jmp (Play_Music).l line
    Code:
    locret_12A2E:
            rts
    We're going to need this because as I stated earlier, the rom won't build without this, I am not going to explain why because you know already why

    Now the music will resume from where it is if Sonic transforms while invincible if you're allowing him to transform while he has invincibility

    Step 6.2: Continuous music when transforming as Sonic (Sonic & Knuckles/Sonic 3 & Knuckles)

    The changes are only for Sonic 3 alone, if you're editing the code to make the game allow Sonic to transform while he is invincible already in Sonic & Knuckles and/or Sonic 3 & Knuckles, the music will still restart, so let's apply the changes to them

    In sonic3k.asm, look for the Sonic_Transform label, this is what it looks like:
    Code:
    Sonic_Transform:
    [...]
        .continued:
            move.w    #$A00,Max_speed-Max_speed(a4)
            move.w    #$30,Acceleration-Max_speed(a4)
            move.w    #$100,Deceleration-Max_speed(a4)
            move.b    #0,invincibility_timer(a0)
            bset    #Status_Invincible,status_secondary(a0)
            moveq    #signextendB(sfx_SuperTransform),d0
            jsr    (Play_SFX).l    ; <-
            moveq    #signextendB(mus_Invincibility),d0        ; play invincibility theme
            jmp    (Play_Music).l
    Under the pointed line, place this:
    Code:
            btst    #Status_Invincible,status_secondary(a0)        ; is Sonic already invincible?
            bne.s    locret_11A1B    ; resume invincibility music instead of restarting it if he is already invincible
    After the jmp line, insert this:
    Code:
    locret_11A1B:
            rts
    The bset command is in once more, so move it below the bne.s locret_11A1B

    Now the music will resume from where it is if Sonic transforms while invincible if you're allowing him to transform while he has invincibility

    If everything is done properly, you should be able to build the rom without getting any errors

    Congratulations! You've implemented into all Genesis Sonic games a new feature where the invincibility music will resume from where it is instead of restarting if a new invincibility monitor is broken before the old one expires if it's already playing! It will also resume in Sonic 3 (& Knuckles) if you destroy a Super/Hyper monitor while the music is already playing, and if you transform while you have invincibility as well!

    The original continuous invincibility music code for Sonic 1 is made by nineko, the conversion to Sonic 1 (GitHub) is made by me, as well as the codes for Sonic 2 and Sonic 3 & Knuckles

    Now with this feature implemented into all three games, enjoy the continuous invincibility music! Because with the changes applied, there will be definitely no more restarts if the invincibility music is already playing!

    Note: It should be noted that this shouldn't be used if you've replaced the original invincibility songs with new ones that aren't designed to loop, otherwise you won't hear anything once the song finishes until the powerup expires, which makes the level music play. But because the original songs are designed to loop, if you keep the original ones or replace them with new ones and they are designed to loop, then there you go
     
    Last edited: Oct 29, 2023
  17. nineko

    nineko I am the Holy Cat Member

    Joined:
    Mar 24, 2008
    Messages:
    1,902
    Location:
    italy
    I like how I gave you literally four lines of code, and you got the inspiration to write several paragraphs of tutorial, excellent work.
     
    Miles Sebas Prower likes this.
  18. PsychoSk8r

    PsychoSk8r HighKnights Member

    Joined:
    Aug 9, 2007
    Messages:
    271
    Location:
    Birmingham, UK
    Unnecessarily restarting music is the name of any experience, nice work!

    I’ve been considering looking to see if it’s possible to implement the same kind of thing with dying and resuming a level, to let that music continue instead of restarting.

    I expect a bit of lag perhaps as the level mode reloads, but I may look into it once I’ve fixed up some bugs with 30 day project: revisited from over a decade ago =P
     
    Miles Sebas Prower likes this.
  19. Miles Sebas Prower

    Miles Sebas Prower Newcomer Member

    Joined:
    Aug 22, 2023
    Messages:
    21
    Location:
    Alvarenga, Charallave
    If you want to include a similar feature to your hacks, but with the level music instead, you can check this out, the guide above shows you how to make the level music resume from where it is

    EDIT: Now that you mention it, I recall seeing this feature in Sonic 3 CZ where the music resumes instead of restarting when you die, I have to say it's a nice feature to include, since this let me listen to my favorite parts of songs like the Doomsday Zone theme despite dying, even though I haven't added it to V1.3 of a Sonic 1 hack I made back in the day that I'm thinking about releasing it here, since restarting the level music after death makes sense, at least to me it does
     
    Last edited: Oct 29, 2023
    PsychoSk8r likes this.
  20. PsychoSk8r

    PsychoSk8r HighKnights Member

    Joined:
    Aug 9, 2007
    Messages:
    271
    Location:
    Birmingham, UK
    Sweet, I totally missed that post, so it’s nice to see someone had the same idea!
    It should be a standard for all games, to interrupt music as little as possible!