Mini-tutorials Thread

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

Tags:
  1. DeltaW

    DeltaW The noob next door Staff

    Joined:
    Aug 7, 2019
    Messages:
    374
    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.
     
  2. Hame

    Hame Peepee Poopoo Member

    Joined:
    Jan 12, 2021
    Messages:
    66
    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:
    43
    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:
    88
    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:
    95
    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 La mer va embrassé moi et délivré moi lakay. Member

    Joined:
    Aug 26, 2013
    Messages:
    1,412
    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 DeltaW like this.
  7. Devon

    Devon La mer va embrassé moi et délivré moi lakay. Member

    Joined:
    Aug 26, 2013
    Messages:
    1,412
    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 DeltaW like this.
  8. Devon

    Devon La mer va embrassé moi et délivré moi lakay. Member

    Joined:
    Aug 26, 2013
    Messages:
    1,412
    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 DeltaW like this.
  9. Dark Shamil Khan

    Dark Shamil Khan TASer lol Member

    Joined:
    Nov 7, 2021
    Messages:
    95
    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.
  10. Red2010 is now

    Red2010 is now A Normal RomHacker with occupations. Member

    Joined:
    Apr 24, 2023
    Messages:
    58
    Location:
    Somewhere in Spain
    Working with Regional Checks

    Have you always wanted to make regional differences in your Sonic Rom Hack and don't know how to do it?

    At least in Sonic 1 Github we have the variable of:
    Code:
    v_megadrive
    I didn't consider making a separate guide for this since it depends on each person what they are going to do with the Regional check.

    What I will give is a small example of how to use this variable (as far as I remember, there are examples of how to use it in the disassembly)
    Code:
            tst.b   (v_megadrive).w
            bmi.s   .name ; branch if Mega Drive is Japanese
            bsr.w    .otherName
            bra.s .continue
    
    .NameOfRoutine:
    ; Insert Something Here
    
    .continue:
    ; The code Continues Here
    I don't know if it will be useful to anyone but I hope I have contributed a little.
     
  11. Devon

    Devon La mer va embrassé moi et délivré moi lakay. Member

    Joined:
    Aug 26, 2013
    Messages:
    1,412
    Location:
    your mom
    The I/O register actually has 2 bits you can use to deduce which region your console is from. Bit 7, which is the bit you are effectively checking in your example, determines whether the console is "domestic" (i.e. from Japan, bit cleared) or "overseas" (i.e. outside of Japan, bit set). Unfortunately, that doesn't cover things like PAL/SECAM regions, which is where bit 6 comes in. If it's cleared, it's an NTSC model, and if it's set, it's a PAL/SECAM model. You can actually see an example of this bit being checked in the V-BLANK handler in the Sonic games.

    Here's a quick cheat sheet:
    Code:
    Bit 7 clear, Bit 6 clear (%00xxxxxx): Domestic NTSC (i.e. Japan / parts of Asia)
    Bit 7 set,   Bit 6 clear (%10xxxxxx): Overseas NTSC (i.e. North America, and I think Brazil (it's around the same speed as NTSC))
    Bit 7 clear, Bit 6 set   (%01xxxxxx): Domestic PAL/SECAM (I'm not sure if this is actually used or not)
    Bit 7 set,   Bit 6 set   (%11xxxxxx): Overseas PAL/SECAM (i.e. Europe / Australia)
     
    Red2010 is now likes this.
  12. Devon

    Devon La mer va embrassé moi et délivré moi lakay. Member

    Joined:
    Aug 26, 2013
    Messages:
    1,412
    Location:
    your mom
    I mentioned a bug with the bubbles that come out of Sonic's mouth when he drowns in this post on Sonic Retro. In summary, the larger bubbles that come out of his mouth, if they hit the water surface, then, due to the setup of the animations, it will end up with an invalid animation ID.
    Code:
    Drown_ChkWater: ; Routine 4
                  move.w  (v_waterpos1).w,d0
                  cmp.w   obY(a0),d0                      ; has bubble reached the water surface?
                  blo.s   .wobble                         ; if not, branch
    
                  move.b  #id_Drown_Display,obRoutine(a0) ; goto Drown_Display next
                  addq.b  #7,obAnim(a0)
                  cmpi.b  #$D,obAnim(a0)
                  beq.s   Drown_Display
                  bra.s   Drown_Display
    [​IMG]

    [​IMG]

    Sonic 2 fixed this, so we can just port the fix from that.
    Code:
    Drown_ChkWater: ; Routine 4
                  move.w  (v_waterpos1).w,d0
                  cmp.w   obY(a0),d0                      ; has bubble reached the water surface?
                  blo.s   .wobble                         ; if not, branch
    
                  move.b  #id_Drown_Display,obRoutine(a0) ; goto Drown_Display next
                  addq.b  #7,obAnim(a0)
                  cmpi.b  #$D,obAnim(a0)
                  bls.s   Drown_Display                   ; DEV: Combine the "beq" and "blo" into "bls"
                  move.b  #$D,obAnim(a0)                  ; DEV: Set to "pop animation"
                  bra.s   Drown_Display
     
    PeanutNoceda, ProjectFM and DeltaW like this.
  13. Speems

    Speems Well-Known Member Member

    Joined:
    Mar 14, 2017
    Messages:
    88
    Location:
    Rochester Hills, MI
    CD-Inspired Drowning Countdown

    We are using the current Github disassembly for this guide. It should be easy to translate to other disassemblies, regardless of assembler or overall structure.

    1. Go to 0A Drowning Countdown.asm within the _incObj folder. Scroll down until you get to the Countdown section.
    Code:
    Drown_Countdown:; Routine $A
            tst.w    objoff_2C(a0)
            bne.w    .loc_13F86
            cmpi.b    #6,(v_player+obRoutine).w
            bhs.w    .nocountdown
            btst    #6,(v_player+obStatus).w ; is Sonic underwater?
            beq.w    .nocountdown    ; if not, branch
    
            subq.w    #1,drown_time(a0)    ; decrement timer
            bpl.w    .nochange    ; branch if time remains
            move.w    #59,drown_time(a0)
            move.w    #1,objoff_36(a0)
            jsr    (RandomNumber).l
            andi.w    #1,d0
            move.b    d0,objoff_34(a0)
            move.w    (v_air).w,d0    ; check air remaining
            cmpi.w    #25,d0
            beq.s    .warnsound    ; play sound if    air is 25
            cmpi.w    #20,d0
            beq.s    .warnsound
            cmpi.w    #15,d0
            beq.s    .warnsound
            cmpi.w    #12,d0
            bhi.s    .reduceair    ; if air is above 12, branch
    
            bne.s    .skipmusic    ; if air is less than 12, branch
            move.w    #bgm_Drowning,d0
            jsr    (PlaySound).l    ; play countdown music
    You'll see a command to play the drowning countdown music. Delete those two lines. When building, you'll see an error. This will be fixed in the above instruction to skipmusic with changing bne.s to bne.w.

    Now the drowning countdown will not play any music, but collecting an air bubble or exiting the water will still restart the music. Not even commenting out ResumeMusic here works, but we can make sure the respective music values don't kick in.

    2. Go to the sonic.asm file in the root of your disassembly, and scroll down to ResumeMusic.
    Code:
    ResumeMusic:
            cmpi.w    #12,(v_air).w    ; more than 12 seconds of air left?
            bhi.s    .over12        ; if yes, branch
            move.w    #bgm_LZ,d0    ; play LZ music
            cmpi.w    #(id_LZ<<8)+3,(v_zone).w ; check if level is 0103 (SBZ3)
            bne.s    .notsbz
            move.w    #bgm_SBZ,d0    ; play SBZ music
    
    .notsbz:
            if Revision<>0
                tst.b    (v_invinc).w ; is Sonic invincible?
                beq.s    .notinvinc ; if not, branch
                move.w    #bgm_Invincible,d0
    .notinvinc:
                tst.b    (f_lockscreen).w ; is Sonic at a boss?
                beq.s    .playselected ; if not, branch
                move.w    #bgm_Boss,d0
    .playselected:
            endif
    
            jsr    (PlaySound).l
    
    .over12:
            move.w    #30,(v_air).w    ; reset air to 30 seconds
            clr.b    (v_sonicbubbles+$32).w
            rts   
    ; End of function ResumeMusic
    Simply comment out the two lines regarding the Labyrinth and Scrap Brain music. This will unfortunately induce another error, but change the bne.s to bne.w for notsbz and it will work. Now the standard zone music will not be interrupted when we rescue ourselves from drowning.

    3. Go back to 0A Drowning Countdown.asm and scroll to Drown_ShowNumber.
    Code:
    Drown_ShowNumber:
            tst.w    drown_time(a0)
            beq.s    .nonumber
            subq.w    #1,drown_time(a0)    ; decrement timer
            bne.s    .nonumber    ; if time remains, branch
            cmpi.b    #7,obAnim(a0)
            bhs.s    .nonumber
    
            move.w    #15,drown_time(a0)
            clr.w    obVelY(a0)
            move.b    #$80,obRender(a0)
            move.w    obX(a0),d0
            sub.w    (v_screenposx).w,d0
            addi.w    #$80,d0
            move.w    d0,obX(a0)
            move.w    obY(a0),d0
            sub.w    (v_screenposy).w,d0
            addi.w    #$80,d0
            move.w    d0,obScreenY(a0)
            move.b    #id_Drown_AirLeft,obRoutine(a0) ; goto Drown_AirLeft next
    After the second branch to nonumber, there's an empty line of code. In its place, we insert a command to play a sound effect. We'll use AB as it is unused. Plus we can swap out the file for the exact sound used in CD (like check out Devon's Sonic CD disassembly and grab the DF file). Now the sound effect will play every time a big number is displayed in the countdown sequence.

    After all this, the drowning countdown will now function like how it does in Sonic CD.
     
  14. BL3H

    BL3H idiot sandwich Member

    Joined:
    Jul 19, 2022
    Messages:
    15
    Location:
    Tails' Workshop, Angel Island
    Music Switch Monitor
    This one is a really easy so I decided to put it here, but it is really cool. This is meant for Sonic 1 and the 2005 Hivebrain ASM68K assembly, but I'm sure you GitHub users could figure out the rest. First, go to Obj2E_ChkS and you should see this:
    Code:
    Obj2E_ChkS:
            cmpi.b    #7,d0        ; does monitor contain 'S'
            bne.s    Obj2E_ChkEnd
            nop    
    Now for this tutorial, I'm just gonna use the base level songs in Sonic 1, but you can edit it and add more if you want, and I'll show how at the end.
    Find an empty & unused RAM address for this, for this example, I'll use $FFFFFFF6.
    Replace all of that with this:
    Code:
    Obj2E_ChkS:
            cmpi.b    #7,d0        ; does monitor contain S?
            bne.s    Obj2E_ChkEnd
            cmpi.w    #0,($FFFFFFF6).w ; is Green Hill playing?
            beq.s    @mz ; if so, branch to MZ playback
            cmpi.w    #1,($FFFFFFF6).w ; is Marble playing?
            beq.s    @syz ; if so, branch to SYZ playback
            cmpi.w    #2,($FFFFFFF6).w ; is Spring Yard playing?
            beq.s    @lz ; if so, branch to LZ playback
            cmpi.w    #3,($FFFFFFF6).w ; is Labyrinth playing?
            beq.s    @slz ; if so, branch to SLZ playback
            cmpi.w    #4,($FFFFFFF6).w ; is Star Light playing?
            beq.s    @sbz ; if so, branch to SBZ playback
            cmpi.w    #5,($FFFFFFF6).w ; is Scrap Brain playing?
            beq.s    @ns ; if so, branch to GHZ playback
    @jghz:
            jmp    @ghz
    @mz:
            clr.w    $FFFFFFF6
            move.w    #1,($FFFFFFF6).w
            move.w    #$83,d0
            jmp    (PlaySound).l    ; play MZ
            rts
    @syz:
            clr.w    $FFFFFFF6
            move.w    #2,($FFFFFFF6).w
            move.w    #$85,d0
            jmp    (PlaySound).l    ; play SYZ
            rts
    @lz:
            clr.w    $FFFFFFF6
            move.w    #3,($FFFFFFF6).w
            move.w    #$82,d0
            jmp    (PlaySound).l    ; play LZ
            rts
    @slz:
            clr.w    $FFFFFFF6
            move.w    #4,($FFFFFFF6).w
            move.w    #$84,d0
            jmp    (PlaySound).l    ; play SLZ
            rts
    @sbz:
            clr.w    $FFFFFFF6
            move.w    #5,($FFFFFFF6).w
            move.w    #$86,d0
            jmp    (PlaySound).l    ; play SBZ
            rts
    @ns:
            clr.w    $FFFFFFF6
            move.w    #6,($FFFFFFF6).w
            move.w    #$E4,d0
            jmp    (PlaySound).l    ; stop all music
            rts
    @ghz:
            clr.w    $FFFFFFF6
            move.w    #0,($FFFFFFF6).w
            move.w    #$81,d0
            jmp    (PlaySound).l    ; play GHZ
            rts
    Now if you build, you may get a "Branch out of Range" error. To fix that, simply above the Obj2E_ChkS label, insert this:
    Code:
    JmpChkEnd:
            jmp    Obj2E_ChkEnd
    Now, replace Obj2E_ChkEnd in
    Code:
    bne.s    Obj2E_ChkEnd
    with your new label (for this example, it would be JmpChkEnd)

    Place down the S monitor in your level and build and you should now be able to cycle through music! Enjoy!
     
  15. BL3H

    BL3H idiot sandwich Member

    Joined:
    Jul 19, 2022
    Messages:
    15
    Location:
    Tails' Workshop, Angel Island
    Apologies for the double post, but here's a way to extend the length of your SEGA sound or PCM track. If you were to replace it at all to something much longer, you may have noticed it was cut off (I would know). Here's a quick and easy fix that isn't gamebreaking, but be sure to have the SEGA sound fixed via this guide! This is for Sonic 1.

    For Hivebrain 2005 users, Simply go to Sound_E1 and where you see this line:
    Code:
    move.l    #$6978,d3            ; Load the size of the SEGA PCM sample into d3 
    Replace the value moved into d3 with #$FFFF to make it play to the end.
    For Github users, find the constant "Size_of_SegaPCM" in Constants.asm and change that value to either one mentioned above. However, there is an extra step.
    Go to s1.sounddriver.asm and remove these lines:

    Code:
    ; Don't let Sega sample cross $8000-byte boundary
            ; (DAC driver doesn't switch banks automatically)
            if ((*)&$7FFF)+Size_of_SegaPCM>$8000
                align $8000
            endif
    
    Code:
    if SegaPCM_End-SegaPCM>$8000
                fatal "Sega sound must fit within $8000 bytes, but you have a $\{SegaPCM_End-SegaPCM} byte Sega sound."
            endif
            if SegaPCM_End-SegaPCM>Size_of_SegaPCM
                fatal "Size_of_SegaPCM = $\{Size_of_SegaPCM}, but you have a $\{SegaPCM_End-SegaPCM} byte Sega sound."
            endif
    
    The reason these lines must be removed is because if the Size of the SEGA PCM variable is larger than $8000 bytes, your ROM won't export. Upon testing it after removing these lines, everything works the way it should be.
     
  16. Blue Gamer

    Blue Gamer Newcomer Trialist

    Joined:
    Aug 16, 2024
    Messages:
    10
    How to add the Universal Voice Bank to Sonic 1
    (So I'm not sure if this Tutorial works with newer ASM disassemblys)
    (also, this only really helps if you're porting sonic 3 songs)

    So first you need a sonic 1 Disassembly (AS) and Flame Driver (ASM).

    Next get UniBank.asm from Flame Driver and put it into the sound folder on your disassembly.

    Afterwards insert this before the end of the rom:
    Code:
    include "sound/UniBank.asm"
    your end result after following that step should look like this:
    Code:
        include "sound/UniBank.asm"
    EndOfRom:
    
            END
    Then, go into UniBank.asm and insert this before the instruments but after the source driver part:
    Code:
    Snd_UVB_Voices:
    the start of your code should look like this:
    Code:
    ; ===========================================================================
    ; FM Universial Voice Bank
    ; ===========================================================================
    
    SourceSMPS2ASM set 0
    SourceDriver set 3
    
    Snd_UVB_Voices:
    ; Synth Bass 2
    ;    Voice $00
    ;    $3C
    ;    $01, $00, $00, $00,     $1F, $1F, $15, $1F,     $11, $0D, $12, $05
    ;    $07, $04, $09, $02,     $55, $3A, $25, $1A,     $1A, $80, $07, $80
        smpsVcAlgorithm     $04
        smpsVcFeedback      $07
        smpsVcUnusedBits    $00
        smpsVcDetune        $00, $00, $00, $00
        smpsVcCoarseFreq    $00, $00, $00, $01
        smpsVcRateScale     $00, $00, $00, $00
        smpsVcAttackRate    $1F, $15, $1F, $1F
        smpsVcAmpMod        $00, $00, $00, $00
        smpsVcDecayRate1    $05, $12, $0D, $11
        smpsVcDecayRate2    $02, $09, $04, $07
        smpsVcDecayLevel    $01, $02, $03, $05
        smpsVcReleaseRate   $0A, $05, $0A, $05
        smpsVcTotalLevel    $00, $07, $00, $1A
    
    ; Trumpet 1
    ;    Voice $01
    ;    $3D
    ;    $01, $01, $01, $01,     $94, $19, $19, $19,     $0F, $0D, $0D, $0D
    ;    $07, $04, $04, $04,     $25, $1A, $1A, $1A,     $15, $80, $80, $80
        smpsVcAlgorithm     $05
        smpsVcFeedback      $07
        smpsVcUnusedBits    $00
        smpsVcDetune        $00, $00, $00, $00
        smpsVcCoarseFreq    $01, $01, $01, $01
        smpsVcRateScale     $00, $00, $00, $02
        smpsVcAttackRate    $19, $19, $19, $14
        smpsVcAmpMod        $00, $00, $00, $00
        smpsVcDecayRate1    $0D, $0D, $0D, $0F
        smpsVcDecayRate2    $04, $04, $04, $07
        smpsVcDecayLevel    $01, $01, $01, $02
        smpsVcReleaseRate   $0A, $0A, $0A, $05
        smpsVcTotalLevel    $00, $00, $00, $15
    
    ; Slap Bass 2
    ;    Voice $02
    ;    $03
    ;    $00, $D7, $33, $02,     $5F, $9F, $5F, $1F,     $13, $0F, $0A, $0A
    ;    $10, $0F, $02, $09,     $35, $15, $25, $1A,     $13, $16, $15, $80
        smpsVcAlgorithm     $03
        smpsVcFeedback      $00
        smpsVcUnusedBits    $00
        smpsVcDetune        $00, $03, $0D, $00
        smpsVcCoarseFreq    $02, $03, $07, $00
        smpsVcRateScale     $00, $01, $02, $01
        smpsVcAttackRate    $1F, $1F, $1F, $1F
        smpsVcAmpMod        $00, $00, $00, $00
        smpsVcDecayRate1    $0A, $0A, $0F, $13
        smpsVcDecayRate2    $09, $02, $0F, $10
        smpsVcDecayLevel    $01, $02, $01, $03
        smpsVcReleaseRate   $0A, $05, $05, $05
        smpsVcTotalLevel    $00, $15, $16, $13
    
    ; Synth Bass 1
    ;    Voice $03
    ;    $34
    ;    $70, $72, $31, $31,     $1F, $1F, $1F, $1F,     $10, $06, $06, $06
    ;    $01, $06, $06, $06,     $35, $1A, $15, $1A,     $10, $83, $18, $83
        smpsVcAlgorithm     $04
        smpsVcFeedback      $06
        smpsVcUnusedBits    $00
        smpsVcDetune        $03, $03, $07, $07
        smpsVcCoarseFreq    $01, $01, $02, $00
        smpsVcRateScale     $00, $00, $00, $00
        smpsVcAttackRate    $1F, $1F, $1F, $1F
        smpsVcAmpMod        $00, $00, $00, $00
        smpsVcDecayRate1    $06, $06, $06, $10
        smpsVcDecayRate2    $06, $06, $06, $01
        smpsVcDecayLevel    $01, $01, $01, $03
        smpsVcReleaseRate   $0A, $05, $0A, $05
        smpsVcTotalLevel    $03, $18, $03, $10
    
    ; Bell Synth 1
    ;    Voice $04
    ;    $3E
    ;    $77, $71, $32, $31,     $1F, $1F, $1F, $1F,     $0D, $06, $00, $00
    ;    $08, $06, $00, $00,     $15, $0A, $0A, $0A,     $1B, $80, $80, $80
        smpsVcAlgorithm     $06
        smpsVcFeedback      $07
        smpsVcUnusedBits    $00
        smpsVcDetune        $03, $03, $07, $07
        smpsVcCoarseFreq    $01, $02, $01, $07
        smpsVcRateScale     $00, $00, $00, $00
        smpsVcAttackRate    $1F, $1F, $1F, $1F
        smpsVcAmpMod        $00, $00, $00, $00
        smpsVcDecayRate1    $00, $00, $06, $0D
        smpsVcDecayRate2    $00, $00, $06, $08
        smpsVcDecayLevel    $00, $00, $00, $01
        smpsVcReleaseRate   $0A, $0A, $0A, $05
        smpsVcTotalLevel    $00, $00, $00, $1B
    
    ; Bell Synth 2
    ;    Voice $05
    ;    $34
    ;    $33, $41, $7E, $74,     $5B, $9F, $5F, $1F,     $04, $07, $07, $08
    ;    $00, $00, $00, $00,     $FF, $FF, $EF, $FF,     $23, $80, $29, $87
    Now, replace smpsHeaderVoiceUVB with smpsHeaderVoice Snd_UVB_Voices.
    your music header should look like this(AIZ2 if your wondering what song this ported header is used from)
    Code:
    Snd_AIZ2_Header:
        smpsHeaderStartSong 3
        smpsHeaderVoice        Snd_UVB_Voices
        smpsHeaderChan      $06, $03
        smpsHeaderTempo     $01, $15
    
    Now, if you followed this tutorial, You now added the Universal Voice Bank to sonic 1 !
     
    Last edited: Nov 21, 2024
  17. Slatersingtogether

    Slatersingtogether Newcomer Trialist

    Joined:
    Sep 11, 2024
    Messages:
    2
    I try change the lava graphics on sonic 1 and got the graphic out of the noise. I figured out how to edit this but not found anything about how this work and how this edit (apart from one post from 2006 by Hivebrain). That's why i'm writing this post.

    How contains lava (magma) graphics in Sonic 1

    Lava graphics create for 32x32 tiles format, but mega drive cant work with this format and read graphic file in 8x8 tile format.
    upload_2024-11-30_2-7-30.png
    Game convert this file from 32x32 in 8x8x tile format (this code in AnimateLevelGFX) and load in VDP. 8x8 tiles contains data from 2 rows of 32x32 tile.
    upload_2024-11-30_2-7-45.png

    This may be to edit on graphics editors (like paint.net or photoshop) but i wrote simple python script for this, which converted the png image into raw graphic file in 32x32 tiles (or any other) format.
    Code:
    from PIL import Image
    from numpy import asarray
    
    img = asarray(Image.open("lavaforconvertation.png")).flatten()
    print(len(img))
    binfile = open("lavaforconvertation.bin","wb")
    
    for count in range(0,len(img),2):
        if count+1<=len(img):
            binfile.write(bytes([img[count]*16+img[count+1]]))
        else:
            binfile.write(bytes([img[count]*16]))
    You need to create a png image 32 pixels wide and with color indexed in the palette from the game and load in load the python script from in command terminal with path to file.
    I attached python script and png file sample for convert in this post for you can try it yourself - link.
     
    Devon likes this.
  18. Devon

    Devon La mer va embrassé moi et délivré moi lakay. Member

    Joined:
    Aug 26, 2013
    Messages:
    1,412
    Location:
    your mom
    It uses software to move the lava horizontally without making a bunch of copies of the tiles at different offsets (it would need 32 different sets), thus saving a good chunk of ROM space. The format it is stored in allows the game to do that operation optimally, since it needs to be able to easily pick out a 32px wide slice for each row to store in VRAM. The normal tile format would have made it more complicated and slow.
     
    Last edited: Nov 30, 2024 at 12:02 AM
  19. Slatersingtogether

    Slatersingtogether Newcomer Trialist

    Joined:
    Sep 11, 2024
    Messages:
    2
    Thank for the clarification!
     
  20. Psi

    Psi Well-Known Member Member

    Joined:
    Dec 20, 2014
    Messages:
    106
    Toggle the Sonic 2 VS Mode Countdown:

    Anyone wanna edit the 2 player VS countdown? Has some interesting nuances.

    There are five different entries for the countdown, two in the HUD routine, three in the signpost routine. Curiously there are THREE ways to activate it. One by deactivate Sonic's timer by crossing the signpost, one by deactivating Tails' the same way, and another that activates purely by you loading the signpost art. You ever stepped close to the finish line only to mess around or go back for some items or points, only to randomly hear the countdown theme play? This is that check, and was likely added to put a stop to your antics. :p

    Code:
    loc_1922C:
        addq.b    #2,routine(a0) ; => Obj0D_Main
        bsr.w    Adjust2PArtPointer
        move.b    #4,render_flags(a0)
        move.b    #$18,width_pixels(a0)
        move.b    #4,priority(a0)
        move.w    #$3C3C,(Loser_Time_Left).w ;signpost load counter
    .......

    Code:
    loc_192BC:
        tst.w    (Two_player_mode).w
        beq.w    loc_19350
        move.w    #$3C3C,(Loser_Time_Left).w ;Tails' counter
        move.w    #$D3,d0        ; play different spinning sound
        jsr    (PlaySound).l
        bra.s    loc_19350
    ........

    Code:
    loc_1932E:
        tst.b    obj0D_finalanim(a0)
        bne.s    loc_19350
        move.b    #4,obj0D_finalanim(a0)
        tst.w    (Two_player_mode).w
        beq.s    loc_19350
        move.w    #$3C3C,(Loser_Time_Left).w ;Sonic's counter
        move.w    #$D3,d0
        jsr    (PlaySound).l
    All three activation checks are separate and can be given different timer settings ($3C3C represents the frames and seconds, 60). You could even add a handicap for one player, giving Sonic, say $1E3C (30 seconds) and Tails $5A3C (90 seconds). It's quite flexible, but remember it can only go up to double digits, so the timer won't display correctly past 99 seconds ($633C).

    [​IMG]

    If you wanna turn the timer off completely, just comment out all three Loser_Time_Left entries, leaving it inactive and at 00.

    [​IMG]

    If you wanna get rid of the display as well, go to the HUD routine and add a jump branch over each entry where it checks for Sonic and Tails' timers deactivating:

    Code:
    loc_4088C:
    
        move.w    #$90,d3
        move.w    #$188,d2
        lea    (HUD_MapUnc_40BEA).l,a1
        movea.w    #$8365,a3
        add.w    d1,d1
        adda.w    (a1,d1.w),a1
        move.w    (a1)+,d1
        subq.w    #1,d1
        bsr.w    JmpTo_loc_16DC6
        move.w    #$B8,d3
        move.w    #$108,d2
        movea.w    #$8371,a3
        moveq    #0,d7
        move.b    (Timer_minute).w,d7
        bsr.w    sub_4092E
        bsr.w    sub_4096A
        moveq    #0,d7
        move.b    (Timer_second).w,d7
        bsr.w    loc_40938
        move.w    #$C0,d3
        move.w    #$118,d2
        movea.w    #$8371,a3
        moveq    #0,d7
        move.w    (Ring_count).w,d7
        bsr.w    sub_40984
        tst.b    (Update_HUD_timer_2P).w
        bne.s    loc_40908
        tst.b    (Update_HUD_timer).w
        beq.s    loc_40908
        jmp    loc_40908    ;skip Sonic's countdown art
        move.w    #$110,d3
        move.w    #$1B8,d2
        movea.w    #$8371,a3
        moveq    #0,d7
        move.b    (Loser_Time_Left).w,d7
        bsr.w    loc_40938
    
    loc_40908:
    
        moveq    #4,d1
        move.w    #$90,d3
        move.w    #$188,d2
        lea    (HUD_MapUnc_40BEA).l,a1
        movea.w    #$8365,a3
        add.w    d1,d1
        adda.w    (a1,d1.w),a1
        move.w    (a1)+,d1
        subq.w    #1,d1
        bsr.w    JmpTo_loc_16DC6
        moveq    #0,d4
        rts
    Code:
    loc_409F8:
    
        move.w    #$90,d3
        move.w    #$268,d2
        lea    (HUD_MapUnc_40BEA).l,a1
        movea.w    #$8365,a3
        add.w    d1,d1
        adda.w    (a1,d1.w),a1
        move.w    (a1)+,d1
        subq.w    #1,d1
        bsr.w    JmpTo_loc_16DC6
        move.w    #$B8,d3
        move.w    #$1E8,d2
        movea.w    #$8371,a3
        moveq    #0,d7
        move.b    (Timer_minute_2P).w,d7
        bsr.w    sub_4092E
        bsr.w    sub_4096A
        moveq    #0,d7
        move.b    (Timer_second_2P).w,d7
        bsr.w    loc_40938
        move.w    #$C0,d3
        move.w    #$1F8,d2
        movea.w    #$8371,a3
        moveq    #0,d7
        move.w    (Ring_count_2P).w,d7
        bsr.w    sub_40984
        tst.b    (Update_HUD_timer).w
        bne.s    loc_40A74
        tst.b    (Update_HUD_timer_2P).w
        beq.s    loc_40A74
        jmp    loc_40A74    ;skip Tails' countdown art
        move.w    #$110,d3
        move.w    #$298,d2
        movea.w    #$8371,a3
        moveq    #0,d7
        move.b    (Loser_Time_Left).w,d7
        bsr.w    loc_40938
    
    loc_40A74:
    
        moveq    #5,d1
        move.w    #$90,d3
        move.w    #$268,d2
        lea    (HUD_MapUnc_40BEA).l,a1
        movea.w    #$8340,a3
        add.w    d1,d1
        adda.w    (a1,d1.w),a1
        move.w    (a1)+,d1
        subq.w    #1,d1
        bsr.w    JmpTo_loc_16DC6
        moveq    #0,d4
        rts

    Very special thanx to MoDule for their usual coding expertise.
     
    Last edited: Dec 1, 2024 at 11:36 PM