How to fix multiple MCZ boss bugs in Sonic 2

Discussion in 'Tutorials Archive' started by redhotsonic, Apr 29, 2015.

Thread Status:
Not open for further replies.
  1. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    I posted this at Sonic Retro, and then I was going to post it here straight afterwards, then something important came up that I had to rush out. 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:

    loc_3124A:
    subi.w #1,($FFFFF75C).w
    cmpi.w #$28,($FFFFF75C).w
    bgt.w loc_312E8
    move.b #1,($FFFFF73F).w
    tst.w ($FFFFF75C).w
    bpl.w loc_312E8
    tst.b objoff_38(a0)
    beq.s loc_31274
    sf objoff_38(a0)
    bra.s loc_31298See 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:
    	cmpi.b	#4,(MainCharacter+routine).w	; Did Sonic (or Sonic alone or Tails alone) get hit?
    	bge.s	+				; If so, branch
    	cmpi.b	#4,(Sidekick+routine).w		; Did Tails (Sidekick) get hit?
    	blt.s	loc_31274			; If not, branch away
    +
    	sf	objoff_38(a0)			; Set the stop-and-drill routine
    	bra.s	loc_312A2			; And branch to take effect
    So you'll end up with this:

    Code:
    loc_3124A:
    	subi.w	#1,($FFFFF75C).w
    	cmpi.w	#$28,($FFFFF75C).w
    	bgt.w	loc_312E8
    	move.b	#1,($FFFFF73F).w
    	tst.w	($FFFFF75C).w
    	bpl.w	loc_312E8
    	cmpi.b	#4,(MainCharacter+routine).w	; Did Sonic (or Sonic alone or Tails alone) get hit?
    	bge.s	+				; If so, branch
    	cmpi.b	#4,(Sidekick+routine).w		; Did Tails (Sidekick) get hit?
    	blt.s	loc_31274			; If not, branch away
    +
    	sf	objoff_38(a0)			; Set the stop-and-drill routine
    	bra.s	loc_312A2			; And branch to take effect
    ; ===========================================================================
    
    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:"

    loc_31298:
    lea ($FFFFF740).w,a1
    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:

    bra.s loc_312A2So, you'll end up with this:

    Code:
    loc_31288:
    	cmpi.w	#$2200,($FFFFF750).w
    	blt.s	loc_312E8
    	move.w	#$2200,($FFFFF750).w
    ; ===========================================================================
    
    loc_312A2:
    	move.w	#0,($FFFFF758).w
    	move.b	#0,angle(a0)
    	lea	($FFFFF740).w,a1
    	andi.b	#$F0,2(a1)
    	ori.b	#$B,2(a1)
    	andi.b	#$F0,8(a1)
    	ori.b	#$B,8(a1)
    	move.b	#0,1(a1)
    	andi.b	#$F0,6(a1)
    	ori.b	#$D,6(a1)
    	move.w	#$64,($FFFFF75C).w
    	move.w	#-$C0,($FFFFF75A).w
    Next, go to "loc_31470:" and directly below this label, insert this code:

    Code:
    	cmpi.b	#4,(MainCharacter+routine).w	; Did Sonic (or Sonic alone or Tails alone) get hit?
    	bge.s	+				; If so, branch
    	cmpi.b	#4,(Sidekick+routine).w		; Did Tails (Sidekick) get hit?
    	blt.s	++				; If not, branch away
    +
    	lea	($FFFFF740).w,a1		; Load Eggman's face animation into a1
    	cmpi.b	#$30,7(a1)			; Is Eggman laughing already?
    	bgt.s	+				; If so, branch, and do not reset
    	move.b	#$30,7(a1)			; Start Eggman's laughing animation
    +
    So you'll end up with this:

    Code:
    loc_31470:
    	cmpi.b	#4,(MainCharacter+routine).w	; Did Sonic (or Sonic alone or Tails alone) get hit?
    	bge.s	+				; If so, branch
    	cmpi.b	#4,(Sidekick+routine).w		; Did Tails (Sidekick) get hit?
    	blt.s	++				; If not, branch away
    +
    	lea	($FFFFF740).w,a1		; Load Eggman's face animation into a1
    	cmpi.b	#$30,7(a1)			; Is Eggman laughing already?
    	bgt.s	+				; If so, branch, and do not reset
    	move.b	#$30,7(a1)			; Start Eggman's laughing animation
    +
    	cmpi.b	#8,angle(a0)
    	bcc.s	return_314B6
    	tst.b	objoff_32(a0)
    	beq.s	loc_314B8
    	tst.b	collision_flags(a0)
    	bne.s	return_314B6
    	tst.b	objoff_14(a0)
    	bne.s	loc_3149A
    	move.b	#$20,objoff_14(a0)
    	move.w	#$AC,d0
    	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:

    loc_CD54:
    swap d0
    moveq #6,d6
    bsr.w loc_D940
    move.w ($FFFFEE0C).w,($FFFFF618).w
    moveq #0,d2
    tst.b ($FFFFEEBD).w
    beq.s +Insert this:

    Code:
    	lea	(MainCharacter).w,a1		; Load the Main Character into a1
    	cmpi.b	#6,routine(a1)			; is the Main Character dead?
    	bcc.s	+				; if so, branch and skip shaking
    So you end up with this:

    Code:
    loc_CD54:
    	swap	d0
    	moveq	#6,d6
    	bsr.w	loc_D940
    	move.w	($FFFFEE0C).w,($FFFFF618).w
    	moveq	#0,d2
    	tst.b	($FFFFEEBD).w
    	beq.s	+
    	lea	(MainCharacter).w,a1		; Load the Main Character into a1
    	cmpi.b	#6,routine(a1)			; is the Main Character dead?
    	bcc.s	+				; if so, branch and skip shaking
    	move.w	(Timer_frames).w,d0
    	andi.w	#$3F,d0
    	lea	SwScrl_RippleData(pc),a1
    	lea	(a1,d0.w),a1
    	moveq	#0,d0
    	move.b	(a1)+,d0
    	add.w	d0,(Vscroll_Factor).w
    	add.w	d0,($FFFFF618).w
    	add.w	d0,($FFFFEEF4).w
    	move.b	(a1)+,d2
    	add.w	d2,($FFFFEEF0).w
    +
    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:

    LevEvents_MCZ2_Routine4:
    tst.b ($FFFFEEBD).w
    beq.s +Again, insert this:

    Code:
    	lea	(MainCharacter).w,a1		; Load the Main Character into a1
    	cmpi.b	#6,routine(a1)			; is the Main Character dead?
    	bcc.s	+				; if so, branch and skip shaking noise
    So you end up with this:

    Code:
    LevEvents_MCZ2_Routine4:
    	tst.b	($FFFFEEBD).w
    	beq.s	+
    	cmpi.b	#6,(MainCharacter+routine).w	; is Sonic dead?
    	bcc.s	+				; if so, branch and skip shaking noise
    	move.w	(Timer_frames).w,d0
    	andi.w	#$1F,d0
    	bne.s	+
    	move.w	#$E1,d0
    	bsr.w	JmpTo3_PlaySound
    +
    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:

    cmpi.w #$21A0,($FFFFF750).w ; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)
    bne.s loc_31210 ; First time coming down? Ah, aim thenWhen 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:

    move.w #$21A0,x_pos(a0)to this:

    Code:
    	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:
    	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:
    	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:

    move.w (MainCharacter+x_pos).w,d0
    sub.w ($FFFFF750).w,d0
    bmi.s loc_31210
    bset #0,render_flags(a0)Delete it. And replace it with this:

    Code:
    ; Check who is closer out of Sonic or Tails
    	move.w	(MainCharacter+x_pos).w,d0	; Move Sonic's x_pos into d0
    	move.w	(Sidekick+x_pos).w,d1		; Move Tails's x_pos into d1
    	move.w	($FFFFF750).w,d2		; Move Eggman's x_pos into d2
    	move.w	d2,d3				; Move Eggman's x_pos into d3
    	sub.w	d0,d2				; Subtract Sonic's x_pos from Eggman's
    	bpl.s	+				; If result is a positive, branch
    	neg.w	d2				; As the result is a negative, negate it to positive
    +
    	sub.w	d1,d3				; Subtract Tails's x_pos from Eggman's
    	bpl.s	+				; If result is a positive, branch
    	neg.w	d3				; As the result is a negative, negate it to positive
    +
    	cmp.w	d2,d3				; Is Sonic Closer?
    	bgt.s	+				; If so, branch
    	; Tails is closer
    	cmpi.b	#6,(Maincharacter+routine).w	; dead?
    	bge.s	+				; As Tails is dead, ignore Tails and aim for Sonic
    	sub.w	($FFFFF750).w,d1
    	bmi.s	loc_31210
    	bset	#0,render_flags(a0)
    	bra.s	loc_31210
    +	; Sonic is closer
    	sub.w	($FFFFF750).w,d0
    	bmi.s	loc_31210
    	bset	#0,render_flags(a0)
    So, you'll end up with this:

    Code:
    loc_311AA:
    	bsr.w	loc_2D5DE
    	cmpi.w	#$660,($FFFFF754).w
    	blt.s	loc_31228
    	move.w	#$660,($FFFFF754).w
    	addq.b	#2,angle(a0)
    	lea	($FFFFF740).w,a1
    	andi.b	#$F0,2(a1)
    	ori.b	#6,2(a1)
    	andi.b	#$F0,8(a1)
    	ori.b	#6,8(a1)
    	andi.b	#$F0,6(a1)
    	ori.b	#$D,6(a1)
    	move.b	#$20,5(a1)
    	move.w	#$64,($FFFFF75C).w
    	move.b	#$30,1(a1)
    	cmpi.w	#$2190,($FFFFF750).w		; Only aim for Sonic at the beginning (so he doesn't turn on the side of the screen)
    	bne.s	loc_31210			; First time coming down?  Ah, aim then	
    	bclr	#0,render_flags(a0)
    ; Check who is closer out of Sonic or Tails
    	move.w	(MainCharacter+x_pos).w,d0	; Move Sonic's x_pos into d0
    	move.w	(Sidekick+x_pos).w,d1		; Move Tails's x_pos into d1
    	move.w	($FFFFF750).w,d2		; Move Eggman's x_pos into d2
    	move.w	d2,d3				; Move Eggman's x_pos into d3
    	sub.w	d0,d2				; Subtract Sonic's x_pos from Eggman's
    	bpl.s	+				; If result is a positive, branch
    	neg.w	d2				; As the result is a negative, negate it to positive
    +
    	sub.w	d1,d3				; Subtract Tails's x_pos from Eggman's
    	bpl.s	+				; If result is a positive, branch
    	neg.w	d3				; As the result is a negative, negate it to positive
    +
    	cmp.w	d2,d3				; Is Sonic Closer?
    	bgt.s	+				; If so, branch
    	; Tails is closer
    	cmpi.b	#6,(Maincharacter+routine).w	; dead?
    	bge.s	+				; As Tails is dead, ignore Tails and aim for Sonic
    	sub.w	($FFFFF750).w,d1
    	bmi.s	loc_31210
    	bset	#0,render_flags(a0)
    	bra.s	loc_31210
    +	; Sonic is closer
    	sub.w	($FFFFF750).w,d0
    	bmi.s	loc_31210
    	bset	#0,render_flags(a0)
    
    loc_31210:
    	move.w	#-$200,($FFFFF758).w
    	move.w	#0,($FFFFF75A).w
    	btst	#0,render_flags(a0)
    	beq.s	loc_31228
    	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:
    	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:
    	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:
    	blt.s	loc_31228
    to:

    Code:
    	blt.w	loc_31228
    All done. That's all bugs fixed!
     
    Last edited by a moderator: Apr 29, 2015
Thread Status:
Not open for further replies.