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
  2. Devon

    Devon Powered by a malfunctioning Motorola 68000 Member

    Joined:
    Aug 26, 2013
    Messages:
    1,315
    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 Powered by a malfunctioning Motorola 68000 Member

    Joined:
    Aug 26, 2013
    Messages:
    1,315
    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:
    91
    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.
     
    ProjectFM likes this.
  6. Hame

    Hame Active Member Member

    Joined:
    Jan 12, 2021
    Messages:
    38
    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 Powered by a malfunctioning Motorola 68000 Member

    Joined:
    Aug 26, 2013
    Messages:
    1,315
    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 Powered by a malfunctioning Motorola 68000 Member

    Joined:
    Aug 26, 2013
    Messages:
    1,315
    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
    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.
     
    KCEXE and DeltaWooloo like this.
  10. EddyTF

    EddyTF #romhacker Member

    Joined:
    Jan 9, 2022
    Messages:
    69
    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 Powered by a malfunctioning Motorola 68000 Member

    Joined:
    Aug 26, 2013
    Messages:
    1,315
    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.