Mini-tutorials Thread

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

Tags:
  1. DeltaWooloo

    DeltaWooloo The noob next door Member

    Joined:
    Aug 7, 2019
    Messages:
    375
    I don't think my tutorial, that was quoted posted, works. I got it to partially work at one point but haven't got it sorted and I don't think I have time to do so.

    If anyone can find the issue and supply a fix, then I'll gladly update my tutorial with credit where's due.
     
    PsychoSk8r likes this.
  2. Hame

    Hame Peepee Poopoo Member

    Joined:
    Jan 12, 2021
    Messages:
    57
    Location:
    Spain
    How to fix a bug with resetting the animation

    So, here is a bug what i saw in most of hacks in sonic 1 (idk if sonic 2 or even sonic 3 has the same bug).

    Now, fixing it is the easiest thing to do.
    In Sonic_ResetOnFloor (inside _incobj), you need find those lines:

    Code:
        btst    #2,$22(a0)
    
        beq.s   loc_137E4
        bclr    #2,$22(a0)
        move.b  #$13,obHeight(a0)
        move.b  #9,obWidth(a0)
        move.b  #id_Walk,obAnim(a0) ;<-- this is the line what we want
        subq.w  #5,obY(a0)
    Now, what you have to do is move the line before the btst.


    Code:
          move.b  #id_Walk,obAnim(a0) ;<-- this is the line what we moved
        btst    #2,$22(a0)
        beq.s   loc_137E4
        bclr    #2,$22(a0)
        move.b  #$13,obHeight(a0)
        move.b  #9,obWidth(a0)
        subq.w  #5,obY(a0)
    Doing this will cause the animation to restart every time you call sonicresetfloor.

    The bug is that when Sonic lands on an object or is in places like this in Spring Yard, if you stop touching any button before he lands it will cause the animation to not restart.

    As far as I know, I haven't found any bugs in Sonic 1.
    This guide also works in hivebrain 2005 using the equivalent values. In constant.asm on github you find everything.




     
    Last edited: Feb 7, 2024
  3. Grosy

    Grosy Romhacker + owns a mega drive 2 Member

    Joined:
    Dec 30, 2021
    Messages:
    38
    How to Pitch Down or Pitch Up a Song in Sonic 1 Github AS
    Alrighty So first you must be using a sonic 1 disasm with the .asm songs addition.
    So open up your bgm/sfx file of choice in a text editor you perfer (I am gonna use notepad++ for this tutorial but you can use other programs) and I will use Green hill zones bgm for demonstrating.
    So if you want to pitch down an FM channel you will have to modify the values that are circled in the image upload_2023-11-8_12-28-4.png
    If you want to step the FM channels 4 semitones lower it will look something like this upload_2023-11-8_12-29-1.png

    Stepping down psg is the same process so have fun pitching down songs or sound effects I guess.
     
    Hame and EddyTF like this.
  4. Speems

    Speems Well-Known Member Member

    Joined:
    Mar 14, 2017
    Messages:
    84
    Location:
    Rochester Hills, MI
    I found the placement for Sonic 1 implementation, the first block of code pasted in Obj01_MdRoll goes above the Sonic_RollRepel command like so:
    Code:
    Obj01_MdRoll:                ; XREF: Obj01_Modes
            bsr.w    Sonic_Jump
            tst.w   $2E(a0)
            bne.s    @cont
            btst    #0,($FFFFF603).w
            beq.s    @cont
            clr.b    $1C(a0)
            bra.w    Sonic_CheckRollStop2
    
    @cont:
            bsr.w    Sonic_RollRepel
            bsr.w    Sonic_RollSpeed
            bsr.w    Sonic_LevelBound
            jsr    SpeedToPos
            bsr.w    Sonic_AnglePos
            bsr.w    Sonic_SlopeRepel
            rts    
    And the second part involves the label loc_131AA, with this:
    Code:
    loc_131AA:
            tst.w    $14(a0)        ; is Sonic moving?
            bne.s    loc_131CC    ; if yes, branch
            move.b    #5,$1C(a0)    ; use "standing" animation
    
    Sonic_CheckRollStop2:
            bclr    #2,$22(a0)
            move.b    #$13,$16(a0)
            move.b    #9,$17(a0)
            subq.w    #5,$C(a0)
    It works in the Hivebrain 2005 disassembly for S1, and should be no different in the current Github disassembly.
     
  5. Dark Shamil Khan

    Dark Shamil Khan TASer lol Member

    Joined:
    Nov 7, 2021
    Messages:
    92
    Location:
    Pakistan
    Now here's something I was trying to achieve for the few months until a few days ago, I finally got the thing I wanted.
    FM6/DAC Switching
    Now what do I mean by that? Some sound drivers have a thing in them where if FM6 is playing, DAC would stop. And vice versa. I have seen 3 sound drivers do this as of now. AMPS, Clonedriver, And Valley Bell's edited driver for S2Recreation.
    And after a small convo with Valley Bell on a server. He gave me the idea on how to do it.

    For starting, for Hivebrain 2005 users, open sonic 1.asm. For Github. go to s1.sounddriver. And find the label: loc_726E2 or FMNoteOn
    It should look something like this.
    Code:
                    btst    #1,(a5)
                    bne.s   locret_726FC
                    btst    #2,(a5)
                    bne.s   locret_726FC
                    moveq   #$28,d0
                    move.b  1(a5),d1
                    ori.b   #$F0,d1
                    bra.w   sub_7272E
    For Github. It's:
    Code:
            btst    #1,(a5)        ; Is track resting? (TrackPlaybackControl)
            bne.s    @locret        ; Return if so
            btst    #2,(a5)        ; Is track being overridden? (TrackPlaybackControl)
            bne.s    @locret        ; Return if so
            moveq    #$28,d0        ; Note on/off register
            move.b    TrackVoiceControl(a5),d1 ; Get channel bits
            ori.b    #$F0,d1        ; Note on on all operators
            bra.w    WriteFMI
    Now, you see that moveq command there? Right above it and under the bne.s command, add this:
    Hivebrain:
    Code:
            cmpi.b    #6,1(a5)                ; Is this FM channel 6?
            bne.s    @notfm6                    ; If not, branch
            moveq    #$2B,d0                    ; DAC enable/disable register
            moveq    #0,d1                    ; Disable DAC (letting FM6 run)
            bsr.w    sub_7272E
    @notfm6:
    For Github:
    Code:
            cmpi.b    #6,TrackVoiceControl(a5); Is this FM channel 6?
            bne.s    @notfm6                    ; If not, branch
            moveq    #$2B,d0                    ; DAC enable/disable register
            moveq    #0,d1                    ; Disable DAC (letting FM6 run)
            bsr.w    WriteFMI
    @notfm6:
    And... that's about it actually. Now if a song has 6 fm channels+a DAC. It will try to play FM6 channel and would turn off DAC. And if DAC is playing, FM6 would turn off.
    Credits:
    Valley Bell: (SMPS Research Pack), and the idea on how to put it.
    Clownacy: CloneDriver, and a huge inspiration on how to ROM hack.
    Myself: Because Why not? =P
     
    Last edited: Dec 23, 2023
    TheInvisibleSun, JGamer2151 and Hame like this.
  6. 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
    Avoiding CalcAngle When Performing Collision in the Air

    The way Sonic handles collision while he is airborne involves calculating the general direction that he is moving, and then jumping to the appropriate code for handling collision for that specific direction. If he is moving downwards, he would check collision with the walls and the floor. If he is moving left or right, he would check collision with the wall he is moving towards and the ceiling and floor. If he is moving upwards, he would check collision with the walls and ceiling. The way it checks that direction is to take the angle he is moving at by using CalcAngle with his X and Y speeds as the parameters, and the output angle value determines the direction he is moving.

    [​IMG]

    That works and all, but we can definitely use a less intensive calculation to handle this.

    What we can do is actually just directly check the speed values ourselves. What we can then deduce from this is that we can basically determine Sonic's direction depending on if he is moving more horizontally than vertically and the direction of the larger speed value. We know this, because the angle values that separate the different directions are all angles where the absolute values of X and Y are the same.

    Here is a chart showing the new logic with the conditions that should be checked:

    [​IMG]

    Here's a couple example implementations.

    Replace:
    Code:
    Sonic_Floor:
                    move.w  obVelX(a0),d1
                    move.w  obVelY(a0),d2
                    jsr     (CalcAngle).l
                    move.b  d0,(v_unused3).w
                    subi.b  #$20,d0
                    move.b  d0,(v_unused4).w
                    andi.b  #$C0,d0
                    move.b  d0,(v_unused5).w
                    cmpi.b  #$40,d0
                    beq.w   loc_13680
                    cmpi.b  #$80,d0
                    beq.w   loc_136E2
                    cmpi.b  #$C0,d0
                    beq.w   loc_1373E
    with:
    Code:
    Sonic_Floor:
                    move.w  obVelX(a0),d0                   ; Get X speed
                    move.w  obVelY(a0),d1                   ; Get Y speed
                    bpl.s   SonAirCol_PosY                  ; If it's positive, branch
    
                    cmp.w   d0,d1                           ; Are we moving towards the left?
                    bgt.w   loc_13680                       ; If so, branch
    
                    neg.w   d0                              ; Are we moving towards the right?
                    cmp.w   d0,d1
                    bge.w   loc_1373E                       ; If so, branch
    
                    bra.w   loc_136E2                       ; We are moving upwards
    
    SonAirCol_PosY:
                    cmp.w   d0,d1                           ; Are we moving towards the right?
                    blt.w   loc_1373E                       ; If so, branch
    
                    neg.w   d0                              ; Are we moving towards the left?
                    cmp.w   d0,d1
                    ble.w   loc_13680                       ; If so, branch
    Replace:
    Code:
    Sonic_DoLevelCollision:
            move.l  #Primary_Collision,(Collision_addr).w
            cmpi.b  #$C,top_solid_bit(a0)
            beq.s   +
            move.l  #Secondary_Collision,(Collision_addr).w
    +
            move.b  lrb_solid_bit(a0),d5
            move.w  x_vel(a0),d1
            move.w  y_vel(a0),d2
            jsr     (CalcAngle).l
            subi.b  #$20,d0
            andi.b  #$C0,d0
            cmpi.b  #$40,d0
            beq.w   Sonic_HitLeftWall
            cmpi.b  #$80,d0
            beq.w   Sonic_HitCeilingAndWalls
            cmpi.b  #$C0,d0
            beq.w   Sonic_HitRightWall
    with:
    Code:
    Sonic_DoLevelCollision:
            move.l  #Primary_Collision,(Collision_addr).w
            cmpi.b  #$C,top_solid_bit(a0)
            beq.s   +
            move.l  #Secondary_Collision,(Collision_addr).w
    +
            move.b  lrb_solid_bit(a0),d5
    
            move.w  x_vel(a0),d0                            ; Get X speed
            move.w  y_vel(a0),d1                            ; Get Y speed
            bpl.s   SonAirCol_PosY                          ; If it's positive, branch
    
            cmp.w   d0,d1                                   ; Are we moving towards the left?
            bgt.w   Sonic_HitLeftWall                       ; If so, branch
    
            neg.w   d0                                      ; Are we moving towards the right?
            cmp.w   d0,d1
            bge.w   Sonic_HitRightWall                      ; If so, branch
    
            bra.w   Sonic_HitCeilingAndWalls                ; We are moving upwards
    
    SonAirCol_PosY:
            cmp.w   d0,d1                                   ; Are we moving towards the right?
            blt.w   Sonic_HitRightWall                      ; If so, branch
    
            neg.w   d0                                      ; Are we moving towards the left?
            cmp.w   d0,d1
            ble.w   Sonic_HitLeftWall                       ; If so, branch
    Also do this for Tails_DoLevelCollision.

    You can apply these changes to any other disassembly that has this sort of code, just watch out for any other places that perform this sort of logic.
     
    Last edited: Dec 22, 2023
    vladikcomper and DeltaWooloo like this.
  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
    Full Position Alignment With Block Collision

    The way stage block collision works is that a function is called to determine the distance away or inside a block. Typically, if an object is inside an block, their speed is set to 0 and their position is aligned against the block. However, only the integer half of the position value tends to be affected, but not the fraction half.

    For example, let's say your Y position is 38.75, and you are inside a block 6 pixels deep downwards. The alignment would subtract your Y position by 6 pixels upwards, and your final Y position would end up being 32.75. In most cases, not clearing out the fraction part doesn't really seem to affect anything, but can still creep in some edge cases when it comes to movement on the ground, and mess up the collision.

    So, if you so desire to do a full alignment where the fraction part is cleared out (in the previous example, the final Y position ends up just being 32), it's rather easy: just search for the calls for the various collision detection functions, and in the part where the position value is aligned, also clear out the fraction part.

    For example:
    Code:
    loc_13680:
            bsr.w    Sonic_HitWall
            tst.w    d1
            bpl.s    loc_1369A
            sub.w    d1,obX(a0)
            clr.w    obX+2(a0)            ; <-- Add this
            move.w   #0,obVelX(a0)
            move.w   obVelY(a0),obInertia(a0)
            rts    
    In Sonic 1, the functions to look for are Sonic_HitFloor, Sonic_HitWall, Sonic_DontRunOnWalls, sub_14EB4, ObjFloorDist, ObjFloorDist2, ObjHitWallRight, ObjHitCeiling, ObjHitWallLeft, and Sonic_WalkSpeed. Also FindFloor and FindWall calls throughout Sonic_AnglePos.

    In Sonic 2, the functions to look for are Sonic_CheckFloor, CheckRightWallDist, Sonic_CheckCeiling, CheckLeftWallDist, ChkFloorEdge, ChkFloorEdge_Part2, ChkFloorEdge2, ObjCheckFloorDist, FireCheckFloorDist, RingCheckFloorDist, ObjCheckRightWallDist, ObjCheckCeilingDist, ObjCheckLeftWallDist, and CalcRoomInFront. Also FindFloor and FindWall calls throughout AnglePos.

    Also, it might be worth capping Sonic's top ground speed at something like 15.75 ($FC0) in both directions and replace the rolling speed cap (that is only done to the X speed) to prevent further collision errors.
     
    Last edited: Dec 22, 2023
    vladikcomper 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
    Here's a fun little oddity. The rolling speed cap only applies horizontally, but not vertically. If you don't want that, and instead would rather have it be consistent with the regular ground movement speed cap, just go to Sonic_RollSpeed, and change:
    Code:
                    move.b  obAngle(a0),d0
                    jsr     (CalcSine).l
                    muls.w  obInertia(a0),d0
                    asr.l   #8,d0
                    move.w  d0,obVelY(a0)
                    muls.w  obInertia(a0),d1
                    asr.l   #8,d1
                    cmpi.w  #$1000,d1
                    ble.s   loc_131F0
                    move.w  #$1000,d1
    
    loc_131F0:
                    cmpi.w  #-$1000,d1
                    bge.s   loc_131FA
                    move.w  #-$1000,d1
    
    loc_131FA:
                    move.w  d1,obVelX(a0)
    to:
    Code:
                    move.b  obAngle(a0),d0
                    jsr     (CalcSine).l
                    move.w  obInertia(a0),d2
                    cmpi.w  #$1000,d2
                    ble.s   loc_131F0
                    move.w  #$1000,d2
    
    loc_131F0:
                    cmpi.w  #-$1000,d2
                    bge.s   loc_131FA
                    move.w  #-$1000,d2
    
    loc_131FA:
                    muls.w  d2,d0
                    asr.l   #8,d0
                    move.w  d0,obVelY(a0)
                    muls.w  d2,d1
                    asr.l   #8,d1
                    move.w  d1,obVelX(a0)
    Code:
            move.b  angle(a0),d0
            jsr     (CalcSine).l
            muls.w  inertia(a0),d0
            asr.l   #8,d0
            move.w  d0,y_vel(a0)    ; set y velocity based on $14 and angle
            muls.w  inertia(a0),d1
            asr.l   #8,d1
            cmpi.w  #$1000,d1
            ble.s   +
            move.w  #$1000,d1       ; limit Sonic's speed rolling right
    +
            cmpi.w  #-$1000,d1
            bge.s   +
            move.w  #-$1000,d1      ; limit Sonic's speed rolling left
    +
            move.w  d1,x_vel(a0)    ; set x velocity based on $14 and angle
    to:
    Code:
            move.b  angle(a0),d0
            jsr     (CalcSine).l
            move.w  inertia(a0),d2
            cmpi.w  #$1000,d2
            ble.s   +
            move.w  #$1000,d2       ; limit Sonic's speed rolling
    +
            cmpi.w  #-$1000,d2
            bge.s   +
            move.w  #-$1000,d2      ; limit Sonic's speed rolling
    +
            muls.w  d2,d0
            asr.l   #8,d0
            move.w  d0,y_vel(a0)    ; set y velocity based on $14 and angle
            muls.w  d2,d1
            asr.l   #8,d1
            move.w  d1,x_vel(a0)    ; set x velocity based on $14 and angle
     
    ProjectFM and DeltaWooloo like this.
  9. Dark Shamil Khan

    Dark Shamil Khan TASer lol Member

    Joined:
    Nov 7, 2021
    Messages:
    92
    Location:
    Pakistan
    So a few days ago, I thought of redoing TempoWait or DoTempo thingy in Sonic 1. Because it was so annoying to see the original sonic 1 code having to be on 2 places instead of being in one routine or subroutine. I am talking about sub_7260C by the way.
    So in this, I'll be showing a better optimized version of that. It really isn't necessary to add this tutorial, especially when you're using a custom sound driver like AMPS, Flamedriver, Clonedriver and possibly more to come which change that thing.
    Please note that this was made for Hivebrain 2005 disassembly, however porting to Github is easy and I'll make a github version sooner or later.
    Firstly up, go to loc_71B82 in your main sonic1.asm file. You'll see something like this:
    Code:
            lea    ($FFF000).l,a6
            clr.b    $E(a6)
            tst.b    3(a6)        ; is music paused?
            bne.w    loc_71E50    ; if yes, branch
            subq.b    #1,1(a6)    
            bne.s    loc_71B9E
            jsr    sub_7260C(pc)
    
    loc_71B9E:
    In that code, you'll see a subq command and a bne.s command which right under it, comment both of them out, and the loc_71B9E label as well. Not like it's going to cause any issues, it's removed for convienience sake.
    Anyhow, now that has been done. Go to sub_7260C, which would look something like this:
    Code:
    sub_7260C:      
             move.b    2(a6),1(a6)
             lea    $4E(a6),a0
             moveq    #$30,d0
             moveq    #9,d1
    
    loc_7261A:
             addq.b    #1,(a0)
             adda.w    d0,a0
             dbf    d1,loc_7261A
    
             rts  
    ; End of function sub_7260C
    Comment or delete all of that (except the label at the start you don't have to), and now we're going to put this code instead:
    Code:
    sub_7260C:                ; XREF: sub_71B4C (aka The Tempo for music)
            tst.b    2(a6)         ; Check to see if song's tempo exists.
            beq.s    loc_7261A    ; If yes, then skip, otherwise branch.
            subq.b    #1,1(a6)    ; Has main tempo timer expired?
            bne.s    loc_7261A    ; if yes, return!
            move.b    2(a6),1(a6)    ; Move the song's tempo to timeout tempo.
            lea    $40(a6),a0        ; Load Music RAM to a0
            moveq    #$30,d0        ; Quickly move Track Size (?)
            moveq    #9,d1        ; 1 DAC + 6 FM + 3 PSG channels
    
    @loop:
            tst.b    (a0)        ; Test to see if music is active. ; Hame: this is faster.
            beq.s   @delay        ; if that is false. Delay.
            tst.b    (a0)        ; Test to see if music is active.
            bpl.s    @skip        ; if that is true. then skip.
    @delay:
            addq.b    #1,$E(a0)    ; Delay note by 1 frame
    
    @skip:
            adda.w    d0,a0        ; Advance to next track
            dbf    d1,@loop        ; Loop 9 times!
    
    loc_7261A:
            rts
    ; End of function sub_7260C
    My comments might not be right here, but yeah, that should work. Anyhow, I believe that's going to be it. Because it's doing the exact same code as the original sonic 1,just done in one subroutine instead of doing it in 2 places. But yeah, I hope you found this helpful or somewhat better than the original code.
    If you found any oddities or any glitches or any errors, let me know here or on Discord.
    Credits:
    Brain:
    Okay, you need to credit the brain sometimes y'know.
    Valley Bell: The SMPS Research Pack and the Drivers Disassembly pack. Without it, I would never had the thought of making this tutorial in the first place.
    Edit:
    4/18/24: Added Hame's edit to it, apparently this is faster. See the code above.
     
    Last edited: Apr 18, 2024
    JGamer2151, Thiago REzEnDe and Hame like this.