S4 Special Stages - RetroKoH/S1Fixed GitHub Wiki

(Original guide by RetroKoH)
Source: See commits
Original Commit: ff3e948
This mod can be toggled in S1Fixed by setting S4SpecialStages to 0 or 1.

If you've ever played Sonic 4: Episode 1 (perish the thought), you're likely aware of their special stages. Similarly to Sonic 1, they are accessed by a Giant Ring at the end of every act, provided you have 50 rings or more. Once accessed, you are welcomed by a maze of colored bricks and peppermint blocks that looks quite familiar. The key difference, however, is that the stage no longer rotates automatically. You control the rotation with left and right, though you can still jump. Furthermore, due to this change in control scheme, there are no longer any special blocks that change rotation speed or direction, because frankly they aren't necessary. If, for any reason, you want a similar control scheme in your hack, this is the guide for you! Let's get started.

Establish the new control scheme

Open up _incObj/09 Sonic in Special Stage.asm first, as most of our work is going to be done there. Find Obj09_Display and remove these lines:

Obj09_Display:
	bsr.w	Obj09_ChkItems_Nonsolid
	bsr.w	Obj09_ChkItems_Solid
	jsr	(SpeedToPos).l
	bsr.w	SS_FixCamera

-	move.w	(v_ssangle).w,d0
-	add.w	(v_ssrotate).w,d0
-	move.w	d0,(v_ssangle).w

	jsr	(Sonic_Animate).l
	jsr	(Sonic_LoadGfx).l
	jmp	(DisplaySprite).l

These three lines were responsible for taking the rotation speed in v_ssrotate and applying it to the stage angle in v_ssangle, allowing the stage to rotate at a variable speed. Removing these gets rid of the auto rotation. Now, we need to change the left/right controls so that they rotate the stage.

Scroll down to Obj09_MoveLeft, and we are going to remove a TON of stuff, and replace it with instructions to change the Special Stage angle:

Obj09_MoveLeft:
	bset	#staFacing,obStatus(a0)
-	move.w	obInertia(a0),d0
-	beq.s	loc_1BB06
-	bpl.s	loc_1BB1A

-loc_1BB06:
-	subi.w	#$C,d0
-	cmpi.w	#-$800,d0
-	bgt.s	loc_1BB14
-	move.w	#-$800,d0

-loc_1BB14:
-	move.w	d0,obInertia(a0)
-	rts
-; ===========================================================================

-loc_1BB1A:
-	subi.w	#$40,d0
-	bcc.s	loc_1BB22
-	nop	

-loc_1BB22:
-	move.w	d0,obInertia(a0)	

+	move.w	(v_ssangle).w,d0
+	sub.w	(v_ssrotate).w,d0
+	move.w	d0,(v_ssangle).w

	rts
; End of function Obj09_MoveLeft

Now, let's do the same thing to Obj09_MoveRight

Obj09_MoveRight:
	bclr	#staFacing,obStatus(a0)

-	move.w	obInertia(a0),d0
-	bmi.s	loc_1BB48
-	addi.w	#$C,d0
-	cmpi.w	#$800,d0
-	blt.s	loc_1BB42
-	move.w	#$800,d0

-loc_1BB42:
-	move.w	d0,obInertia(a0)
-	rts
; ===========================================================================

-loc_1BB48:
-	addi.w	#$40,d0
-	bcc.s	loc_1BB50
-	nop	

-loc_1BB50:
-	move.w	d0,obInertia(a0)

+	move.w	(v_ssangle).w,d0
+	add.w	(v_ssrotate).w,d0
+	move.w	d0,(v_ssangle).w

	rts
; End of function Obj09_MoveRight

Set the new rotation speed

Now, we're still storing our rotation speed in a RAM variable, and I'd prefer to keep the mod this way, as it makes it easier for users to change the rotation speed when editing code, and it even allows you to implement a new mechanic to change it in runtime, if you so choose. Speaking of, let's make the rotation speed more suitable for the new playstyle. Go to sonic.asm and go to GM_Special. Find this instruction:

	move.w	#$40,(v_ssrotate).w				; set stage rotation speed

Remember that this word value is half integer, half fraction. $40 is equivalent to $40/$100, or 0.25, or 1 px / 4 frames. Let's change it to $100, which is 1 px / frame.

	move.w	#$100,(v_ssrotate).w				; set stage rotation speed

Remove UP/DOWN and R blocks

With this control scheme, it's probably not a good idea to have blocks that affect rotation (or maybe it is, it's up to you). We have two options to get rid of them. You can either use MainMemory's S1SSEdit tool, or SonEd2, to manually remove them, or you can remove them in real-time like I'm going to do here. The only reason I do it in real-time in Fixed is because I wanted this mod to be toggleable, and I didn't feel like having two variations of the same exact layouts in the repo. I think it's also good to know how to do this for other purposes (such as replacing already obtained emeralds with 1-Ups). So, for educational purposes, I'm going to implement the real-time approach below.

Scroll further down in sonic.asm until you see loc_1B6F8. Make the following changes:

loc_1B6F8:
-	move.b	(a0)+,(a1)+			; load the layout item into memory

+; Removes UP, DOWN, and R blocks in real-time
+	move.b	(a0)+,d0			; load the layout item into d0
+	cmpi.b	#SSBlock_UP,d0			; is the item an UP Block?
+	bcs.s	.loaditem
+	cmpi.b	#SSBlock_R,d0			; is the item an R or DOWN Block?
+	bhi.s	.loaditem			; if not any of these items, branch
+	move.b	#SSBlock_GhostSolid,d0		; else, make a solid mint block

+.loaditem:
+	move.b	d0,(a1)+			; load the updated item into memory

	dbf	d2,loc_1B6F8

	lea	$40(a1),a1