Proper Invisible Solids Detection While Ducking - RetroKoH/S1Fixed GitHub Wiki

(Original guide by Devon)
Source: Sonic Retro thread
Commit: 8e930d0

There is an oversight with invisible solid block collision. At the top of its main routine, it checks if it's offscreen:

Invis_Solid:	; Routine 2
	bsr.w	ChkObjectVisible
	bne.s	.chkdel

There is a problem though. If we go to the routine:

; ---------------------------------------------------------------------------
; Subroutine to	check if an object is off screen
; output:
;	d0 = flag set if object is off screen
; ---------------------------------------------------------------------------
; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||
ChkObjectVisible:
		move.w	obX(a0),d0	; get object x-position
		sub.w	(v_screenposx).w,d0 ; subtract screen x-position
		bmi.s	.offscreen
		cmpi.w	#320,d0		; is object on the screen?
		bge.s	.offscreen	; if not, branch
		move.w	obY(a0),d1	; get object y-position
		sub.w	(v_screenposy).w,d1 ; subtract screen y-position
		bmi.s	.offscreen
		cmpi.w	#224,d1		; is object on the screen?
		bge.s	.offscreen	; if not, branch
		moveq	#0,d0		; set flag to 0
		rts	
.offscreen:
		moveq	#1,d0		; set flag to 1
		rts	
; End of function ChkObjectVisible

It doesn't take into account the size of the hitbox, just the position. Now, it's not very common at all to run into this issue, but you can easily see the effect if you duck the camera down in this area in Marble Zone Act 2:
aOO3gq6

To fix this, add this routine that accounts for the hitbox:

; ---------------------------------------------------------------------------
; Subroutine to    check if an object is off screen
; Takes both width and height into account

; output:
;    d0 = flag set if object is off screen
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||


ChkSizedObjVisible:
	; Ralakimus Checking For Solids Fix
		moveq	#0,d1				; Get object's width
		move.b	obActWid(a0),d1
		move.w	obX(a0),d0			; Get object's X position
		sub.w	(v_screenposx).w,d0	; Get object's X position on screen
		add.w	d1,d0				; Is the right side of the object on screen?
		bmi.s	.offscreen2			; If not, branch
		add.w	d1,d1				; Is the left side of the object on screen?
		sub.w	d1,d0
		cmpi.w	#320,d0
		bge.s	.offscreen2			; If not, branch

		moveq	#0,d1				; Get object's height
		move.b	obHeight(a0),d1
		move.w	obY(a0),d0			; Get object's Y position
		sub.w	(v_screenposy).w,d0	; Get object's Y position on screen
		add.w	d1,d0				; Is the bottom side of the object on screen?
		bmi.s	.offscreen2			; If not, branch
		add.w	d1,d1				; Is the top side of the object on screen?
		sub.w	d1,d0
		cmpi.w	#224,d1
		bge.s	.offscreen2			; If not, branch

		moveq	#0,d0				; Visible
		rts

.offscreen2:
		moveq	#1,d0				; Not visible
		rts 
; End of function ChkSizedObjVisible

Now go back and change the branch call from ChkObjectVisible to ChkSizedObjVisible:

Invis_Solid:	; Routine 2
!	bsr.w	ChkSizedObjVisible
	bne.s	.chkdel

Problem solved! Take a look:
oE4h3VC