Some changes and fixes for Sonic 2

Discussion in 'Engineering & Reverse Engineering' started by Esrael, Jun 7, 2012.

  1. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    CNZ's bumpers are prone to very well hidden bug, which will trigger if you move your data around a lot or try optimising the code near SpecialCNZBumpers_Act1. The bug is that the large bumpers no longer bounce Sonic or Tails off of them. There doesn't seems to be any collision-related effects at all: no sound, nothing. I personally found this bug when I changed the nearby 'jmp (PlaySound).l' to a 'jmp (PlaySound).w'. Boy, was I surprised to find that was the cause of my problem!

    The bumpers' layout files contain entries that are 6 bytes in size. Two dummy entries are needed for the bumper manager to function properly: one at the beginning, and one at the end. I believe the first dummy needs an X-pos of $0000, while the last one needs $FFFF. Now, CNZ act 2's bumpers file starts and ends with dummy entries, but act 1... it doesn't start with one. In fact, the jmp right before the binclude just happens to have $0000 in the same place as an entry's X-pos (jmp $0000XXXX), so it works. This could have been a real goofy space optimisation by Sonic Team.

    To fix this, you just gotta shove 6 blank bytes in the beginning of the CNZ act 1 bumper layout file. Tada. However, there is a 'better' way. By exploiting the bumper manager's logic, you can shave some bytes off:

    First off, remove the first two bytes of your new blank dummy entry at the beginning of your file. Then, find the pointer to SpecialCNZBumpers_Act1, and just append a '-2' to the end of the address. The first byte of information is unneeded as only the X-pos is of any use to dummy entries. Next, the dummy entry at the end of the file can also be trimmed. It should be '0000FFFF0000'. You can remove the last two bytes. Again, we only need the X-pos. You can also apply this to act 2's file.
     
  2. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,584
    0
    0
    United Kingdom
    YouTuber
    I haven't provided any fixes for a while; mainly due to the fact I hardly hack at all these days, you know, with real life getting in the way. Anyway, I was playing Sonic 2 the other day and realised a fair few issues with the MCZ boss. You may consider these bugs, you may not, but to me, they're not right.


    • Eggman will stop moving sideways on the floor when he hurts Tails, then goes back up into the ground (stop-and-drill routine), yet he won't do any of this when he hits Sonic.
    • Unless he hits Tails to go into the stop-and-drill routine, Eggman will not laugh when he hits Sonic OR Tails.
    • If Sonic alone or Tails alone, Eggman won't laugh or enter the stop-and-drill routine at all.
    • When Eggman comes to the floor, he turns and aims for Sonic every time. This is fine the first time round when the boss starts as he starts in the middle, but the rest of the time he spawns on the sides; you don't want him turning ideally.
    • Screen still shakes when you're dead, a bit weird since everything else like the falling rocks have come to a standstill.
    • MCZ boss doesn't actually start in the middle of the screen (design choice more than a bug).
    • When Eggman comes to the floor when he first starts, he turns and aims for Sonic but never for Tails (again, more of a design change than a bug).


    So there's at least 5 bugs; 7 if you want to make the design changes. Let's get coding! I'm using Xenowhirl's disassembly as a guide.

    Yeah, yeah, shoot me now, if your newer disassembly is so much better, you'll have no issues applying this anyway :)




    Step 1 - Making the "stop-and-drill" routine occur every time Sonic and Tails are hurt


    [​IMG]
    Eggman finding it funny when Tails is hit

    [​IMG]
    Not so much when Sonic is hurt though


    Go to "loc_3124A:" and you'll see this:

    Code (ASM):
    1. loc_3124A:
    2.     subi.w  #1,($FFFFF75C).w
    3.     cmpi.w  #$28,($FFFFF75C).w
    4.     bgt.w   loc_312E8
    5.     move.b  #1,($FFFFF73F).w
    6.     tst.w   ($FFFFF75C).w
    7.     bpl.w   loc_312E8
    8.     tst.b   objoff_38(a0)
    9.     beq.s   loc_31274
    10.     sf  objoff_38(a0)
    11.     bra.s   loc_31298

    See the "tst.b objoff_38(a0)" bit? From there, delete it all. So now you have a gap between "bpl.w loc_312E8" and "loc_31274:". The code you just deleted, we're going to replace with this:

    Code (ASM):
    1.     cmpi.b  #4,(MainCharacter+routine).w    ; Did Sonic (or Sonic alone or Tails alone) get hit?
    2.     bge.s   +               ; If so, branch
    3.     cmpi.b  #4,(Sidekick+routine).w     ; Did Tails (Sidekick) get hit?
    4.     blt.s   loc_31274           ; If not, branch away
    5. +
    6.     sf  objoff_38(a0)           ; Set the stop-and-drill routine
    7.     bra.s   loc_312A2           ; And branch to take effect

    So you'll end up with this:

    Code (ASM):
    1. loc_3124A:
    2.     subi.w  #1,($FFFFF75C).w
    3.     cmpi.w  #$28,($FFFFF75C).w
    4.     bgt.w   loc_312E8
    5.     move.b  #1,($FFFFF73F).w
    6.     tst.w   ($FFFFF75C).w
    7.     bpl.w   loc_312E8
    8.     cmpi.b  #4,(MainCharacter+routine).w    ; Did Sonic (or Sonic alone or Tails alone) get hit?
    9.     bge.s   +               ; If so, branch
    10.     cmpi.b  #4,(Sidekick+routine).w     ; Did Tails (Sidekick) get hit?
    11.     blt.s   loc_31274           ; If not, branch away
    12. +
    13.     sf  objoff_38(a0)           ; Set the stop-and-drill routine
    14.     bra.s   loc_312A2           ; And branch to take effect
    15. ; ===========================================================================
    16.  
    17. loc_31274:

    Now, Eggman will enter his "stop-and-drill" routine every time Sonic and Tails have been hit. Yet, he won't laugh at all anymore. That's the next bit.




    Step 2 - Make Eggman laugh every time Sonic or Tails has been hit

    [​IMG]
    Suddenly Eggman isn't in a laughing mood


    Go to "loc_31298:"

    Code (ASM):
    1. loc_31298:
    2.     lea ($FFFFF740).w,a1
    3.     move.b  #$30,7(a1)

    This bit of coding is no longer being used after what we just did in Step 1. Good, this is in a bad place really. If we left the code we changed to branch here, Eggman will indeed laugh when Sonic and Tails have been hit, but only when he changes into his "stop-and-drill routine". Meaning, if you get hit when he has stopped, or when he is about to drill, or is coming down from drilling, whatever, he still will not laugh.

    Anyway, delete the two lines under the label "loc_31298:". We're moving it. Because the label no longer exists, directly above is a branch to the label to what used to be underneath. This is illegal (most assemblers will create a "nop" to avoid this but still). So under "loc_31288:", delete this line:

    Code (ASM):
    1.     bra.s   loc_312A2

    So, you'll end up with this:

    Code (ASM):
    1. loc_31288:
    2.     cmpi.w  #$2200,($FFFFF750).w
    3.     blt.s   loc_312E8
    4.     move.w  #$2200,($FFFFF750).w
    5. ; ===========================================================================
    6.  
    7. loc_312A2:
    8.     move.w  #0,($FFFFF758).w
    9.     move.b  #0,angle(a0)
    10.     lea ($FFFFF740).w,a1
    11.     andi.b  #$F0,2(a1)
    12.     ori.b   #$B,2(a1)
    13.     andi.b  #$F0,8(a1)
    14.     ori.b   #$B,8(a1)
    15.     move.b  #0,1(a1)
    16.     andi.b  #$F0,6(a1)
    17.     ori.b   #$D,6(a1)
    18.     move.w  #$64,($FFFFF75C).w
    19.     move.w  #-$C0,($FFFFF75A).w

    Next, go to "loc_31470:" and directly below this label, insert this code:

    Code (ASM):
    1.     cmpi.b  #4,(MainCharacter+routine).w    ; Did Sonic (or Sonic alone or Tails alone) get hit?
    2.     bge.s   +               ; If so, branch
    3.     cmpi.b  #4,(Sidekick+routine).w     ; Did Tails (Sidekick) get hit?
    4.     blt.s   ++              ; If not, branch away
    5. +
    6.     lea ($FFFFF740).w,a1        ; Load Eggman's face animation into a1
    7.     cmpi.b  #$30,7(a1)          ; Is Eggman laughing already?
    8.     bgt.s   +               ; If so, branch, and do not reset
    9.     move.b  #$30,7(a1)          ; Start Eggman's laughing animation
    10. +

    So you'll end up with this:

    Code (ASM):
    1. loc_31470:
    2.     cmpi.b  #4,(MainCharacter+routine).w    ; Did Sonic (or Sonic alone or Tails alone) get hit?
    3.     bge.s   +               ; If so, branch
    4.     cmpi.b  #4,(Sidekick+routine).w     ; Did Tails (Sidekick) get hit?
    5.     blt.s   ++              ; If not, branch away
    6. +
    7.     lea ($FFFFF740).w,a1        ; Load Eggman's face animation into a1
    8.     cmpi.b  #$30,7(a1)          ; Is Eggman laughing already?
    9.     bgt.s   +               ; If so, branch, and do not reset
    10.     move.b  #$30,7(a1)          ; Start Eggman's laughing animation
    11. +
    12.     cmpi.b  #8,angle(a0)
    13.     bcc.s   return_314B6
    14.     tst.b   objoff_32(a0)
    15.     beq.s   loc_314B8
    16.     tst.b   collision_flags(a0)
    17.     bne.s   return_314B6
    18.     tst.b   objoff_14(a0)
    19.     bne.s   loc_3149A
    20.     move.b  #$20,objoff_14(a0)
    21.     move.w  #$AC,d0
    22.     jsr (PlaySound).l

    Now, every time you're hurt, at any point during the boss, Eggman will laugh, like he is meant to! The reason why we check to see if Eggman is already laughing is because when set, a countdown gets set and counts down 1 every frame. But as soon as it goes down by 1, without the check, it'll set the counter back to 30 and it'll do this for every frame until you fall on the floor. Because of this, Eggman will display the laughing face but won't actually animate, because the counter is stuck on 30. Doing this check will set it to 30, but then it will let it countdown like it is meant to, making his laugh animate.

    That's the first 3 bugs fixed already after 2 steps!

    [​IMG]
    Eggman now a bit happier Sonic is hurt and decides to stop and prepare his drills

    [​IMG]
    Eggman really is happier now




    Step 3 - Stop the screen shaking when dead

    When you die in this boss, like all other bosses and when in normal levels, nearly everything comes to a standstill. In this boss, rings, Eggman, and the falling rocks comes to a standstill. Yet, if you die when the screen was shaking, it will continue to shake. It looks awfully weird that the screen continues to shake yet the falling rocks have stopped mid-air. Let's fix this.


    Go to "loc_CD54:" (which is nothing to do with the MCZ boss code, but with MCZ layer deformation), and after where you see:

    Code (ASM):
    1. loc_CD54:
    2.     swap    d0
    3.     moveq   #6,d6
    4.     bsr.w   loc_D940
    5.     move.w  ($FFFFEE0C).w,($FFFFF618).w
    6.     moveq   #0,d2
    7.     tst.b   ($FFFFEEBD).w
    8.     beq.s   +

    Insert this:

    Code (ASM):
    1.     lea (MainCharacter).w,a1        ; Load the Main Character into a1
    2.     cmpi.b  #6,routine(a1)          ; is the Main Character dead?
    3.     bcc.s   +               ; if so, branch and skip shaking

    So you end up with this:

    Code (ASM):
    1. loc_CD54:
    2.     swap    d0
    3.     moveq   #6,d6
    4.     bsr.w   loc_D940
    5.     move.w  ($FFFFEE0C).w,($FFFFF618).w
    6.     moveq   #0,d2
    7.     tst.b   ($FFFFEEBD).w
    8.     beq.s   +
    9.     lea (MainCharacter).w,a1        ; Load the Main Character into a1
    10.     cmpi.b  #6,routine(a1)          ; is the Main Character dead?
    11.     bcc.s   +               ; if so, branch and skip shaking
    12.     move.w  (Timer_frames).w,d0
    13.     andi.w  #$3F,d0
    14.     lea SwScrl_RippleData(pc),a1
    15.     lea (a1,d0.w),a1
    16.     moveq   #0,d0
    17.     move.b  (a1)+,d0
    18.     add.w   d0,(Vscroll_Factor).w
    19.     add.w   d0,($FFFFF618).w
    20.     add.w   d0,($FFFFEEF4).w
    21.     move.b  (a1)+,d2
    22.     add.w   d2,($FFFFEEF0).w
    23. +

    This will stop the screen shaking when you die if it was already shaking. BUT WAIT, the screen shaking noise is still present, let's stop that.


    Go to "LevEvents_MCZ2_Routine4:" (which is nothing to do with the layer deformation code, but with MCZ's level events), after:

    Code (ASM):
    1. LevEvents_MCZ2_Routine4:
    2.     tst.b   ($FFFFEEBD).w
    3.     beq.s   +

    Again, insert this:

    Code (ASM):
    1.     lea (MainCharacter).w,a1        ; Load the Main Character into a1
    2.     cmpi.b  #6,routine(a1)          ; is the Main Character dead?
    3.     bcc.s   +               ; if so, branch and skip shaking noise

    So you end up with this:

    Code (ASM):
    1. LevEvents_MCZ2_Routine4:
    2.     tst.b   ($FFFFEEBD).w
    3.     beq.s   +
    4.     cmpi.b  #6,(MainCharacter+routine).w    ; is Sonic dead?
    5.     bcc.s   +               ; if so, branch and skip shaking noise
    6.     move.w  (Timer_frames).w,d0
    7.     andi.w  #$1F,d0
    8.     bne.s   +
    9.     move.w  #$E1,d0
    10.     bsr.w   JmpTo3_PlaySound
    11. +

    This will stop the noise happening when you die.




    Step 4 - Disable Eggman from turning and facing Sonic when on the side of the screen

    [​IMG]
    Eggman locates Sonic, but he won't do too much


    You can argue this isn't a bug, but I believe it is. Eggman spawns on the left side of the screen facing right. If you're to the left of him (hugging the left screen), he will turn and try to move, then pretty much go into his "stop-and-drill" routine pretty quickly because he doesn't move anywhere (this happens even without the first 3 bugs we've just fixed). Eggman is on the left and is facing right, let him go right. This happens on the other side of the screen too (As seen in the picture). Luckily, the fix is very simple!


    Go to "loc_311AA:". After the "move.b #$30,1(a1)" line, insert this:

    Code (ASM):
    1.     cmpi.w  #$21A0,($FFFFF750).w    ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)
    2.     bne.s   loc_31210       ; First time coming down?  Ah, aim then

    When Eggman comes down at the beginning of the boss fight, he spawns in the middle; #$21A0 being his X co-ordinate. So what it is doing here, is if Eggman is at this position, then do NOT branch and aim for Sonic.


    The rest of the time when he comes down, he'll be at the left or right of the screen. So, he won't be in this X co-ordinate anymore. Therefore, he will no longer aim for Sonic. So, if he spawns on the left, he will be facing right, and he'll go right!

    [​IMG]
    That's more like it


    Step 5 - Make Eggman actually start in the middle of the screen (optional)


    [​IMG]
    No, not quite centre


    This is more of a design choice, but could be considered a bug. I just find it weird that he spawns just too little to the right of the centre of the screen. Anyway, simple fix if you want him dead centre. Go to "loc_30FB8:" and 4th line down, change:

    Code (ASM):
    1.     move.w  #$21A0,x_pos(a0)

    to this:

    Code (ASM):
    1.     move.w  #$2190,x_pos(a0)        ; Make Eggman start dead-centre of the screen

    He'll now start in the centre instead. BUT WAIT! Did you follow step 4? If so, step 4 will no longer work, so go back to "loc_311AA:" and change:

    Code (ASM):
    1.     cmpi.w  #$21A0,($FFFFF750).w    ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)

    to this:

    Code (ASM):
    1.     cmpi.w  #$2190,($FFFFF750).w    ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)

    And step 4 won't need to be re-done.

    [​IMG]
    Eggman likes to be precise with his entrance


    Step 6 - Make Eggman at the beginning aim for Tails too (optional)


    Again, design choice. When Eggman comes down at the beginning, he faces and aims for Sonic. Completely ignores Tails. Strange that at first he only used to laugh when he hit Tails and not care about Sonic. What we're going to do here is when Eggman comes down, whoever is closest, he will aim and go for!


    At "loc_311AA:" and after "bclr #0,render_flags(a0)" you'll see this code:

    Code (ASM):
    1.     move.w  (MainCharacter+x_pos).w,d0
    2.     sub.w   ($FFFFF750).w,d0
    3.     bmi.s   loc_31210
    4.     bset    #0,render_flags(a0)

    Delete it. And replace it with this:

    Code (ASM):
    1. ; Check who is closer out of Sonic or Tails
    2.     move.w  (MainCharacter+x_pos).w,d0  ; Move Sonic's x_pos into d0
    3.     move.w  (Sidekick+x_pos).w,d1       ; Move Tails's x_pos into d1
    4.     move.w  ($FFFFF750).w,d2        ; Move Eggman's x_pos into d2
    5.     move.w  d2,d3               ; Move Eggman's x_pos into d3
    6.     sub.w   d0,d2               ; Subtract Sonic's x_pos from Eggman's
    7.     bpl.s   +               ; If result is a positive, branch
    8.     neg.w   d2              ; As the result is a negative, negate it to positive
    9. +
    10.     sub.w   d1,d3               ; Subtract Tails's x_pos from Eggman's
    11.     bpl.s   +               ; If result is a positive, branch
    12.     neg.w   d3              ; As the result is a negative, negate it to positive
    13. +
    14.     cmp.w   d2,d3               ; Is Sonic Closer?
    15.     bgt.s   +               ; If so, branch
    16.     ; Tails is closer
    17.     cmpi.b  #6,(Maincharacter+routine).w    ; dead?
    18.     bge.s   +               ; As Tails is dead, ignore Tails and aim for Sonic
    19.     sub.w   ($FFFFF750).w,d1
    20.     bmi.s   loc_31210
    21.     bset    #0,render_flags(a0)
    22.     bra.s   loc_31210
    23. +   ; Sonic is closer
    24.     sub.w   ($FFFFF750).w,d0
    25.     bmi.s   loc_31210
    26.     bset    #0,render_flags(a0)

    So, you'll end up with this:

    Code (ASM):
    1. loc_311AA:
    2.     bsr.w   loc_2D5DE
    3.     cmpi.w  #$660,($FFFFF754).w
    4.     blt.s   loc_31228
    5.     move.w  #$660,($FFFFF754).w
    6.     addq.b  #2,angle(a0)
    7.     lea ($FFFFF740).w,a1
    8.     andi.b  #$F0,2(a1)
    9.     ori.b   #6,2(a1)
    10.     andi.b  #$F0,8(a1)
    11.     ori.b   #6,8(a1)
    12.     andi.b  #$F0,6(a1)
    13.     ori.b   #$D,6(a1)
    14.     move.b  #$20,5(a1)
    15.     move.w  #$64,($FFFFF75C).w
    16.     move.b  #$30,1(a1)
    17.     cmpi.w  #$2190,($FFFFF750).w        ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)
    18.     bne.s   loc_31210           ; First time coming down?  Ah, aim then
    19.     bclr    #0,render_flags(a0)
    20. ; Check who is closer out of Sonic or Tails
    21.     move.w  (MainCharacter+x_pos).w,d0  ; Move Sonic's x_pos into d0
    22.     move.w  (Sidekick+x_pos).w,d1       ; Move Tails's x_pos into d1
    23.     move.w  ($FFFFF750).w,d2        ; Move Eggman's x_pos into d2
    24.     move.w  d2,d3               ; Move Eggman's x_pos into d3
    25.     sub.w   d0,d2               ; Subtract Sonic's x_pos from Eggman's
    26.     bpl.s   +               ; If result is a positive, branch
    27.     neg.w   d2              ; As the result is a negative, negate it to positive
    28. +
    29.     sub.w   d1,d3               ; Subtract Tails's x_pos from Eggman's
    30.     bpl.s   +               ; If result is a positive, branch
    31.     neg.w   d3              ; As the result is a negative, negate it to positive
    32. +
    33.     cmp.w   d2,d3               ; Is Sonic Closer?
    34.     bgt.s   +               ; If so, branch
    35.     ; Tails is closer
    36.     cmpi.b  #6,(Maincharacter+routine).w    ; dead?
    37.     bge.s   +               ; As Tails is dead, ignore Tails and aim for Sonic
    38.     sub.w   ($FFFFF750).w,d1
    39.     bmi.s   loc_31210
    40.     bset    #0,render_flags(a0)
    41.     bra.s   loc_31210
    42. +   ; Sonic is closer
    43.     sub.w   ($FFFFF750).w,d0
    44.     bmi.s   loc_31210
    45.     bset    #0,render_flags(a0)
    46.  
    47. loc_31210:
    48.     move.w  #-$200,($FFFFF758).w
    49.     move.w  #0,($FFFFF75A).w
    50.     btst    #0,render_flags(a0)
    51.     beq.s   loc_31228
    52.     neg.w   ($FFFFF758).w

    Eggman will now aim for whoever is closer out of Sonic and Tails. Because you followed step 4, this will only do it when the boss starts (unless you didn't do step 5, and if you didn't, make sure you change the:

    Code (ASM):
    1.     cmpi.w  #$2190,($FFFFF750).w        ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)

    back to it's normal:

    Code (ASM):
    1.     cmpi.w  #$21A0,($FFFFF750).w        ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)

    so this step takes effect).

    [​IMG]
    Tails is a lot closer, think Eggman will go for him instead


    Step 7 - Quick branch fix


    Chances are your disassembly will come with an out-of-reach error. At "loc_311AA:", change:

    Code (ASM):
    1.     blt.s   loc_31228

    to:

    Code (ASM):
    1.     blt.w   loc_31228

    All done. That's all bugs fixed!
     
  3. KingofHarts

    KingofHarts

    Member Member
    1,617
    0
    16
    Project Sonic 8x16
    This is some pretty cool stuff... that said the only one I take issue with is having him always face the opposite side he comes down on when he's at the end of the screen. You basically made him go into empty space like a total derp. Outside of that, excellent stuff. Great to see you back RHS.
     
  4. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,584
    0
    0
    United Kingdom
    YouTuber
    How do you mean? Where have I made him come down in an empty space? Except from making him start dead centre at the beginning only, the rest of his "coming down" places haven't changed. In the normal S2(even after these fixes), if you're closer to the right side, Eggman will spawn on the left and face right, ready to come at you. Vice versa if you're at the left (if this is what you mean?).
     
  5. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    (Ripped largely from ReadySonic)

    Ever get snared in a bridge while in debug mode? Obviously, those things don't make any attempt to avoid interacting with the player while in debug mode, while many other objects do.

    First up, let's fix up the debug object itself: go to Debug_Init and, below the 'move.b #AniIDSonAni_Walk,anim(a0)', add this:

    Code (ASM):
    1.     btst    #3,status(a0)   ; is Sonic standing on an object?
    2.     beq.s   +       ; if not, branch
    3.     bclr    #3,status(a0)   ; clear Sonic's standing flag
    4.     move.b  interact(a0),d0 ; get object ID
    5.     clr.b   interact(a0)    ; clear object ID
    6.     lsl.w   #6,d0
    7.     addi.w  #Object_RAM,d0
    8.     movea.w d0,a2
    9.     bclr    #3,status(a2)   ; clear object's standing flag
    10. +
    Since Sonic and certain objects keep track of if they're being touched, and what by, we need to clear all that when entering debug mode. Without this additional fix, our fix would cause another bug where bridges stay deformed when entering debug mode.

    Next up, the bridge itself. sub_F872 is what handles deforming the bridge, and changing the player's Y position accordingly. Since AI Tails doesn't enter debug mode with the player, we don't want to ignore the entire subroutine; only the part involving the main character.

    Above 'lea (MainCharacter).w,a1', add this check:

    Code (ASM):
    1.     tst.w   (Debug_placement_mode).w
    2.     bne.s   +++
    Now we need to mess with those annoying nameless labels. Add a '+' before the 'rts', and change the 'blo.s ++' to 'blo.s +++'.
     
  6. ThomasSpeedrunner

    ThomasSpeedrunner

    resident lowercaser Member
    13
    0
    0
    the void between universes.
    not really any specific hack...too many to list.
    Xenowhirl conversion powers activate!

    For the above: Go to Debug_Main and under this line in loc_41AAE:
    Code (ASM):
    1.     move.b  #0,anim(a0)
    Paste this:
    Code (ASM):
    1.     btst    #3,status(a0)   ; is Sonic standing on an object?
    2.     beq.s   +       ; if not, branch
    3.     bclr    #3,status(a0)   ; clear Sonic's standing flag
    4.     move.b  interact(a0),d0 ; get object ID
    5.     clr.b   interact(a0)    ; clear object ID
    6.     lsl.w   #6,d0
    7.     addi.w  #Object_RAM,d0
    8.     movea.w d0,a2
    9.     bclr    #3,status(a2)   ; clear object's standing flag
    10. +
    And above:
    Code (ASM):
    1.     lea (MainCharacter).w,a1 ; a1=character
    2.     subq.b  #1,d6
    3.     moveq   #$3F,d5
    4. +
    Paste this:
    Code (ASM):
    1.     tst.w   (Debug_placement_mode).w
    2.     bne.s   ++
    Now just add a + before the rts and you're done. =)
     
  7. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Because how dare I expect Xeno users to have their own skills in porting. How about porting the other fifty non-Xeno guides first, preferably without turning them into copy/paste guides?

    I've pushed a commit to the Git disasm to make it more obvious, but this easily applies to Xeno, as well.

    clearRAM, in S2, at least, is used incorrectly quite a lot: the amount of data to clear is sometimes 4 bytes too big. This can lead to problems stemming from small bits of RAM being accidentally cleared, such as this error with flamewing's DMA queue.

    Spotting these bugged clearRAMs is easy. Seriously, spot the bugged ones in this block:

    Code (ASM):
    1.     clearRAM Sprite_Table,$284  ; Sprite attribute table buffer
    2.     clearRAM Horiz_Scroll_Buf,$404  ; H scroll table buffer, $404 bytes
    3.     clearRAM PNT_Buffer,$C04    ; PNT buffer
    4.     clearRAM Sprite_Table_Input,$400
    5.     clearRAM Object_RAM,$2000
    ...which, in latest Git, has become this:

    Code (ASM):
    1.     ; These '+4's shouldn't be here; clearRAM accidentally clears an additional 4 bytes
    2.     clearRAM SS_Sprite_Table,(SS_Sprite_Table_End-SS_Sprite_Table)+4
    3.     clearRAM SS_Horiz_Scroll_Buf_1,(SS_Horiz_Scroll_Buf_1_End-SS_Horiz_Scroll_Buf_1)+4
    4.     clearRAM SS_Misc_Variables,(SS_Misc_Variables_End-SS_Misc_Variables)+4
    5.     clearRAM SS_Sprite_Table_Input,(SS_Sprite_Table_Input_End-SS_Sprite_Table_Input)
    6.     clearRAM SS_Object_RAM,(SS_Object_RAM_End-SS_Object_RAM)
    On a related note, under Level_ClrRam, CNZ_saucer_data has $100 bytes cleared, even though only $40 are used (an AND ensures it). Also, RingsManager_Setup, only $40 bytes of Ring_consumption_table is cleared, even though it's $80 bytes in size.
     
  8. MarkeyJester

    MarkeyJester

    My predecessors have nothing on me. Tech Member
    The exact same issue occurs with Sonic 1. In the "ClearScreen" routine specifically, both clear loops clear one long-word extra, as the value loaded to the data register does not take into account the nature of the dbf instruction. I'll tell you what, I'm glad you're taking the time to fix bugs such as this, as these are the vital ones that are potentially game breaking.
     
  9. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,138
    0
    16
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    Hm. I am wondering if the clearRAM macro should not be changed to take starting and ending addresses instead of start address and length. For one, it would simplify the expressions a lot…
     
  10. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    I started looking into the VDP, and I think Horiz_Scroll_Buf can be reduced to $380 bytes. For one, S&K does this. Two, only $380 bytes of Horiz_Scroll_Buf is DMA'd to VRAM. This is down to how HScroll is configured: it's in 'per-scanline' mode, so there's a word of data for each line on each of plane A and plane B, totalling (224 * 2 * 2) $380. I'm not sure how PAL ties into this. Looking at each level's SwScrl code, the ones I can understand, anyway, they only seem to modify the first $380, too. In fact, OOZ starts at the $380 mark, working up.

    EDIT: Actually, it seems CPZ affects $3C0 bytes.
     
  11. vladikcomper

    vladikcomper

    Tech Members Tech Member
    182
    0
    0
    Russia
    Sonic Warped
    Now that you mention it, I might contribute to this topic as well.


    Fixing the CPZ deformation routine

    In both Sonic 1 and Sonic 2, exactly $400 bytes are allocated for the Horizontal Scroll RAM buffer, although only the first $380 bytes are transferred and affect the actual screen. For more information, refer to Clownacy's post above.

    As it has been already explained, the buffer can be reduced to $380 bytes, freeing the whole $80 bytes for you. Most of background scrolling/deformation routines are indeed designed to fill exactly the first $380 bytes of the buffer, leaving no overhead. However, the routine for Chemical Plant zone is special. It's written in such a way that it mistakenly fills up to $3C0 bytes under certain circumstances.

    Since all the data past first $380 bytes of the buffer is unused, it can be considered that there are programming errors in the original algorithm, as it seems to fill the buffer with different overhead (if I recall correctly). However, the programmer let it be, since the HSRAM buffer itself was allocated with certain overhead to begin with.

    In order to fix that, find SwScrl_CPZ and replace all its code with this:

    http://pastebin.com/yWdtnRbU

    This is an alternative deformation routine I wrote back in 2012 by request. Although re-written for the most part, it should be pixel-perfectly identical to the original CPZ scrolling.

    Now, that CPZ deformation algorithm is upgraded and fixed, you can go to s2.constants.asm and change this:

    Code (Text):
    1. Horiz_Scroll_Buf:       ds.b    $400
    into this:

    Code (Text):
    1. Horiz_Scroll_Buf:       ds.b    $380

    EDIT: Clownacy's post quoted due to a new page.
     
  12. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,138
    0
    16
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    Ah, yes, the CPZ deformation code. I mentioned it a few times in IRC. For reference, what the code does (both the original and vladik's version) is split the background into 3 different regions: the top 228 lines scroll with Camera_BG_X_pos, the next 16 lines scroll with Camera_BG_X_pos plus a ripple effect, and the remainder scrolls with Camera_BG2_X_pos. This simple description belies the monstrous complexity of the original code, which does not correctly account for the number of lines it generates, leading to the noted overflow. Here is a visual aid.

    Edit: after taking a look at vladik's code, the compulsive optimizer in me basically forced me to do this. Also guaranteed never to exceed the bounds of the HScroll table, and probably leaves stock code in the dust.
     
  13. KingofHarts

    KingofHarts

    Member Member
    1,617
    0
    16
    Project Sonic 8x16
    Speaking of background deformation in general, and I apologize if this had been mentioned in the past... is there any substantial documentation on how that works at all? I made a duplicate GHZ in Sonic 1 to serve as Bridge Zone, and am trying to make a background that would be 3 chunks tall as opposed to two... Needless to say this breaks the level...

    I know nothing about how the background handling works... Kinda derailing but perhaps if it also applies to Sonic 2 as well than it can contribute to Sonic 2 discussion.
     
  14. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,138
    0
    16
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    Not publicly, at least as far as I am aware. I have been doing some work on that regard, but haven't released anything (yet). What I can tell you, though, is that S1's deformation code is a mess, and S2's deformation code is a clusterfuck.

    Anyway: new fix for S1/S2:
    Fixing collisions with objects from below
    Anyone that has tried porting Knuckles' climbing (or a similar ability) to S1 or S2 has probably run into this: if you climb a wall up into a solid object, you can climb through said object. At some point you get ejected into the wall.

    What is going on is that there is a bug in the SolidObject routine that causes your position to not be updated when you hit such an object from underneath with either a zero speed or while going down. S3&K and KiS2 fix this, but in a different way than what I say here (with the S3&K/KiS2 way being also buggy). In fact, the way S3&K does it will kill y speed and inertia in cases where it should not, such as when you are on the air and going down, and clip the edge of a solid object — you will suddenly lose all downward speed.

    Here is how to fix it:
    Sonic 1
    Open up "_inc/sub SolidObject.asm" and find this block:
    [68k]Solid_Below:
    tst.w obVelY(a1) ; is Sonic moving vertically?
    beq.s Solid_Squash ; if not, branch
    bpl.s Solid_TopBtmAir ; if moving downwards, branch
    tst.w d3 ; is Sonic above the object?
    bpl.s Solid_TopBtmAir ; if yes, branch
    sub.w d3,obY(a1) ; correct Sonic's position
    move.w #0,obVelY(a1) ; stop Sonic moving

    Solid_TopBtmAir:
    moveq #-1,d4
    rts[/68k]
    Change it to this:
    [68k]Solid_Below:
    tst.w obVelY(a1) ; is Sonic moving vertically?
    beq.s Solid_Squash ; if not, branch
    bpl.s Solid_TopBtmAir ; if moving downwards, branch
    tst.w d3 ; is Sonic above the object?
    bpl.s Solid_TopBtmAir ; if yes, branch
    move.w #0,obVelY(a1) ; stop Sonic moving

    Solid_TopBtmAir:
    sub.w d3,obY(a1) ; correct Sonic's position
    moveq #-1,d4
    rts[/68k]
    What this does is prevent you from going through the bottom of a solid object; so bug fixed. NEXT!

    Sonic 2
    Find this block:
    [68k]loc_19B06:
    tst.w y_vel(a1)
    beq.s loc_19B28
    bpl.s loc_19B1C
    tst.w d3
    bpl.s loc_19B1C
    sub.w d3,y_pos(a1)
    move.w #0,y_vel(a1)

    loc_19B1C:
    move.w d6,d4
    addi.b #($10-p1_standing_bit+p1_touch_bottom_bit),d4
    bset d4,d6 ; This sets bits 2 (Sonic) or 3 (Tails) of high word of d6
    moveq #-2,d4
    rts[/68k]
    and change it to this:
    [68k]loc_19B06:
    tst.w y_vel(a1)
    beq.s loc_19B28
    bpl.s loc_19B1C
    tst.w d3
    bpl.s loc_19B1C
    move.w #0,y_vel(a1)

    loc_19B1C:
    sub.w d3,y_pos(a1)
    move.w d6,d4
    addi.b #($10-p1_standing_bit+p1_touch_bottom_bit),d4
    bset d4,d6 ; This sets bits 2 (Sonic) or 3 (Tails) of high word of d6
    moveq #-2,d4
    rts[/68k]
    What this does is prevent you from going through the bottom of a solid object; so bug fixed. NEXT!

    Sonic & Knuckles
    We will be undoing S3&K's buggy bugfix and doing it the right way. You want this code:
    [68k]loc_1E0E0:
    btst #1,$2A(a1)
    bne.s loc_1E0F6
    tst.w $1A(a1)
    beq.s loc_1E126
    bpl.s loc_1E10E
    tst.w d3
    bpl.s loc_1E10E
    bra.s loc_1E0FC
    ; ---------------------------------------------------------------------------

    loc_1E0F6:
    move.w #0,$1C(a1)

    loc_1E0FC:
    tst.b (Reverse_gravity_flag).w
    beq.s loc_1E104
    neg.w d3

    loc_1E104:
    sub.w d3,$14(a1)
    move.w #0,$1A(a1)

    loc_1E10E:

    tst.b $37(a1)
    bpl.s loc_1E11A
    bset #5,$37(a1)

    loc_1E11A:
    move.w d6,d4
    addi.b #$F,d4
    bset d4,d6
    moveq #-2,d4
    rts[/68k]
    Issues with this code: (1) it clears your ground speed when you are on air and hit the bottom of an object (this causes Knuckles to lose all speed when gliding under an object, and nothing else); (2) it makes redundant a check in loc_1E126; (3) it clears your vertical speed when you are moving down and touch the bottom of a solid object (for example, by clipping the corner). We will fix all 3 by making it look like the versions for S1 and S2:
    [68k]loc_1E0E0:
    tst.w $1A(a1)
    beq.s loc_1E126
    bpl.s loc_1E10E
    tst.w d3
    bpl.s loc_1E10E
    move.w #0,$1A(a1)

    loc_1E10E:
    tst.b (Reverse_gravity_flag).w
    beq.s loc_1E104
    neg.w d3

    loc_1E104:
    sub.w d3,$14(a1)
    tst.b $37(a1)
    bpl.s loc_1E11A
    bset #5,$37(a1)

    loc_1E11A:
    move.w d6,d4
    addi.b #$F,d4
    bset d4,d6
    moveq #-2,d4
    rts[/68k]
    As far as I can tell, there are no ill effects of fixing (1), and (3) can cause some issues in random places. I discovered it because I had several portions of S3&K's solid object routine in SCH, and the rotating disc junction in SBZ1 fails when shooting you down with it.

    Now mind you, these fixes scratch the surface of everything that is wrong with these functions. You will still not be crushed while on the floor if you have any vertical speed, and you will still not be crushed if you are on air (or climbing) when an object pushes you into terrain from above. Fixing those requires a more extensive overhaul, and I intend to release SCH with those fixes before releasing the fixes to the public.
     
  15. Natsumi

    Natsumi

    Miss Fox Tech Member
    183
    0
    16
    Long and dangerous river
    Navigating said river
    Coincidentally this also fixes those nasty collision clip issues on Sonic1/2 (really notably MZ chandeliers, though the edges have some quirkyness still). Awesome and simple fix!
     
  16. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,138
    0
    16
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    It is not a coincidence, actually, but yeah, I guess I should have mentioned.

    The major issues of these solid routines are that (1) they don't care at all about terrain and (2) you can only collide with one edge of any given solid in any given frame. The game decides which edge you should collide with and ignores all others.

    With those chandeliers, they are moving down quickly. Without the fix I gave above, they simply go through you if you are on air and not moving up, until the game decides you are closer to the sides than the bottom. So you now collide with the sides, and get ejected out.

    With the fix, the chandelier will no longer go through you because you will be pushed down by it. At least, that is the theory. Depending on your exact position relative to the chandelier, when it moves it may still end up in a position where it decides you should collide with one of the edges and push you out anyway. But it becomes much rarer.
     
  17. Natsumi

    Natsumi

    Miss Fox Tech Member
    183
    0
    16
    Long and dangerous river
    Navigating said river
    well I should have used other phrase I guess, I more meant "as a result". Either way this fixes lots of nasty clips you could do in my hack too, one of which skips a boss even, so its really nice to have it. Not to mention how easy it was just to stand against a wall and let the chandelier drop on you making these cuts really easy as long as your had a ring to spare. Of course the issue is cycles, the collision was made to be simple to not waste time, and as a result there are some particular issues like this. Luckily its a lot harder to notice too, so its not a big issue per se. Of course with enough speed you can practically break any collision on the game, but its not like its a problem with normal gameplay. Either way thanks for the fix!
     
  18. pukulan10jurus

    pukulan10jurus

    New Member
    0
    0
    0
    I can't find this file or this code in the 2005 Hivebrain disassembly.
     
  19. MarkeyJester

    MarkeyJester

    My predecessors have nothing on me. Tech Member
    The equivalent in the 2005 Hiverbrain disassembly is; loc_FBBC.

    Code (Text):
    1. loc_FBBC:
    2.         tst.w   $12(a1)
    3.         beq.s   loc_FBD6
    4.         bpl.s   loc_FBD2
    5.         tst.w   d3
    6.         bpl.s   loc_FBD2
    7.         sub.w   d3,$C(a1)
    8.         move.w  #0,$12(a1)  ; stop Sonic moving
    9.  
    10. loc_FBD2:
    11.         moveq   #-1,d4
    12.         rts
    EDIT: Inside the main "sonic1.asm" source file that is.
     
  20. pukulan10jurus

    pukulan10jurus

    New Member
    0
    0
    0
    Thank you so much! Would've never figured that out on my own.