// ----------------------------------
// RSDK Project: Sonic 1
// Script Description: Checkered Ball Object
// Script Author: Christian Whitehead/Simon Thomley
// Unpacked by Rubberduckycooly's script unpacker
// ----------------------------------

// While the rest of GHZ is structed like v3-esque objects, this one was *definitely* made afterwards, given how it is
// Makes sense though, since it's just a little bonus added in post

// ========================
// Aliases
// ========================

private alias object.value1  : object.roundedPos.x
private alias object.value2  : object.roundedPos.y
private alias object.value3  : object.ballAngle
private alias object.value4  : object.groundAngle
private alias object.value5  : object.angleVel
private alias object.value6  : object.playerControlled
private alias object.value7  : object.startPos.x
private alias object.value8  : object.startPos.y
// value9 is unused
private alias object.value10 : object.activePlayers
private alias object.value11 : object.badnikBonus
private alias object.value12 : object.collisionOffset.x
private alias object.value13 : object.collisionOffset.y
private alias object.value14 : object.wreckingBallPos.x
private alias object.value15 : object.wreckingBallPos.y

// Player Aliases
private alias object.xpos : player.xpos
private alias object.ypos : player.ypos
private alias object.xvel : player.xvel
private alias object.yvel : player.yvel
private alias object.speed : player.speed
private alias object.right : player.right
private alias object.left : player.left
private alias object.gravity : player.gravity
private alias object.pushing : player.pushing
private alias object.controlLock : player.controlLock
private alias object.controlMode : player.controlMode
private alias object.collisionLeft : player.collisionLeft
private alias object.collisionRight : player.collisionRight

private alias object.value20 : player.topSpeed
private alias object.value21 : player.acceleration

// Super States
private alias 1 : SUPERSTATE_SUPER
private alias 2 : SUPERSTATE_UNTRANSFORM

// Newtron Fly Aliases
private alias 2 : NEWTRONFLY_APPEARED
private alias 3 : NEWTRONFLY_MOVING

// Newtron Shoot Aliases
private alias 2 : NEWTRONSHOOT_SHOOT
private alias 3 : NEWTRONSHOOT_SHOOTDELAY

// Monitor Aliases
private alias object.value0 : monitor.contentsPos.y
private alias object.value1 : monitor.timer

private alias 0 : MONITOR_STATE_IDLE

// Spring Aliases
private alias object.value0 : spring.timer 
private alias object.value1 : spring.active
private alias object.value2 : spring.extraVelocity

// Plane Sw V Aliases
private alias object.value0 : planeSwV.extendTop
private alias object.value1 : planeSwV.extendBottom
private alias object.value2 : planeSwV.planeR
private alias object.value3 : planeSwV.planeL
private alias object.value4 : planeSwV.drawOrderR
private alias object.value5 : planeSwV.drawOrderL
private alias object.value6 : planeSwV.onGround

private alias object.value0 : planeSwV.extendLeft
private alias object.value1 : planeSwV.extendRight

// Plane Sw H Aliases
private alias object.value0 : planeSwH.extendLeft
private alias object.value1 : planeSwH.extendRight
private alias object.value2 : planeSwH.planeR
private alias object.value3 : planeSwH.planeL
private alias object.value4 : planeSwH.drawOrderR
private alias object.value5 : planeSwH.drawOrderL
private alias object.value6 : planeSwH.onGround

// CLedge Aliases
private alias 0 : CLEDGE_ACTIVE
private alias 1 : CLEDGE_COLLAPSE
private alias 3 : CLEDGE_NONE

// Bridge Aliases
private alias object.value0 : bridge.activePlayerCount
private alias object.value1 : bridge.timer
private alias object.value2 : bridge.stoodPos
private alias object.value3 : bridge.depression
private alias object.value4 : bridge.bridgeDepth
private alias object.value5 : bridge.playerID
private alias object.value6 : bridge.startPos
private alias object.value7 : bridge.endPos

// FallPlatform Aliases
private alias object.value0 : fallPlatform.stood
private alias object.value1 : fallPlatform.collisionOffset.y

// HPlatform Aliases
private alias object.value0 : hPlatform.stood
private alias object.value3 : hPlatform.collisionOffset.x
private alias object.value4 : hPlatform.collisionOffset.y

// VPlatform Aliases
private alias object.value1 : vPlatform.collisionOffset.y

// VPlatform2 Aliases
private alias object.value1 : vPlatform2.collisionOffset.y

// SwingPlatform
private alias object.value1 : swingPlatform.drawPos.x
private alias object.value2 : swingPlatform.drawPos.y
private alias object.value3 : swingPlatform.collisionOffset.x
private alias object.value4 : swingPlatform.collisionOffset.y

// GHZEggman Aliases
private alias object.value0 : ghzEggman.timer
private alias object.value1 : ghzEggman.health
private alias object.value4 : ghzEggman.invincibilityTimer
private alias object.value6 : ghzEggman.flameAnim

private alias 4  : GHZEGGMAN_INITIALSWING
private alias 5  : GHZEGGMAN_MOVERIGHT
private alias 6  : GHZEGGMAN_MOVELEFT
private alias 7  : GHZEGGMAN_EXPLODE

private alias 3 : GHZEGGANI_HIT
private alias 4 : GHZEGGANI_DEFEATED

private alias 0 : FLAME_INACTIVE
private alias 2 : FLAME_EXPLODE

// Wrecking Ball Aliases
private alias 1 : WRECKINGBALL_ACTIVE
private alias 2 : WRECKINGBALL_EXPLODE

// Animal Prison Aliases
private alias 0 : ANIMALPRISON_AWAITOPEN
private alias 1 : ANIMALPRISON_OPENED


// ========================
// Function Declarations
// ========================

reserve function CheckeredBall_DebugDraw
reserve function CheckeredBall_DebugSpawn
reserve function CheckeredBall_HandlePlayerMove
reserve function CheckeredBall_PlayerInteraction
reserve function CheckeredBall_BadnikBreak
reserve function CheckeredBall_ReflectProjectile
reserve function CheckeredBall_SpringBounce
reserve function CheckeredBall_CLedgeCollapse
reserve function CheckeredBall_HandlePhysics


// ========================
// Tables
// ========================

private table CheckeredBall_heightArray
	0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20
end table

// Seems to be unused, and as such, its purpose is unknown...
// This table also exists (and is still unused) in S2's OOZ Checkered Ball and Mania's CheckerBall, both of which are based on this object 
private table CheckeredBall_unusedTable
	253, 251, 248, 245, 243, 240, 237, 235, 232, 229, 226, 224, 221, 219, 216, 213, 210, 208, 205, 203, 200, 197, 195, 192
end table


// ========================
// Function Definitions
// ========================

private function CheckeredBall_DebugDraw
	DrawSprite(1)
end function


private function CheckeredBall_DebugSpawn
	CreateTempObject(TypeName[Checkered Ball], 0, object.xpos, object.ypos)
	object[tempObjectPos].gravity = GRAVITY_AIR
	object[tempObjectPos].startPos.x = object.xpos
	object[tempObjectPos].startPos.y = object.ypos
	object[tempObjectPos].roundedPos.x = object.xpos
	object[tempObjectPos].roundedPos.y = object.ypos
end function


private function CheckeredBall_HandlePlayerMove
	object.activePlayers = 0
	temp2 = 0
	foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
		// This object is never placed into a level, at least in the final game, so this attract mode check never returns true...
		CheckEqual(options.attractMode, true)
		temp1 = checkResult
		CheckEqual(currentPlayer, 1)
		temp1 &= checkResult
		
		temp0 = player[currentPlayer].acceleration
		temp0 >>= 1
		if player[currentPlayer].gravity == GRAVITY_GROUND
			if player[currentPlayer].xvel < 0
				BoxCollisionTest(C_TOUCH, object.entityPos, 0, -22, 23, 22, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
				if checkResult == true
					if player[currentPlayer].left == true
						if temp1 == false
							SetBit(object.activePlayers, temp2, true)
							temp1 = player[currentPlayer].topSpeed
							FlipSign(temp1)
							if object.speed > temp1
								object.speed -= temp0
							end if
							object.playerControlled = true
						end if

						player[currentPlayer].xpos = player[currentPlayer].collisionRight
						player[currentPlayer].xpos += 22
						player[currentPlayer].xpos <<= 16
						player[currentPlayer].xpos += object.roundedPos.x

						if object.xvel < 0
							if player[currentPlayer].speed < object.speed
								player[currentPlayer].xvel = object.xvel
								player[currentPlayer].speed = object.speed
							end if
						end if

						player[currentPlayer].pushing = 2
					end if
				end if
			else
				if player[currentPlayer].xvel > 0
					BoxCollisionTest(C_TOUCH, object.entityPos, -23, -22, 0, 22, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
					if checkResult == true
						if player[currentPlayer].right == true
							if temp1 == false
								SetBit(object.activePlayers, temp2, true)
								if object.speed < player[currentPlayer].topSpeed
									object.speed += temp0
								end if
								object.playerControlled = true
							end if

							player[currentPlayer].xpos = player[currentPlayer].collisionLeft
							player[currentPlayer].xpos += -22
							player[currentPlayer].xpos <<= 16
							player[currentPlayer].xpos += object.roundedPos.x

							if object.xvel > 0
								if player[currentPlayer].speed > object.speed
									player[currentPlayer].xvel = object.xvel
									player[currentPlayer].speed = object.speed
								end if
							end if

							player[currentPlayer].pushing = 2
						end if
					end if
				end if
			end if
		end if
		
		temp2++
	next
end function


private function CheckeredBall_PlayerInteraction
	temp6 = object.xpos
	temp6 &= 0xFFFF0000
	temp6 -= object.roundedPos.x
	
	temp7 = object.ypos
	temp7 &= 0xFFFF0000
	temp7 -= object.roundedPos.y
	
	temp4 = object.xpos
	temp5 = object.ypos
	
	object.xpos = object.roundedPos.x
	object.ypos = object.roundedPos.y
	
	temp2 = 0
	foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
		GetBit(temp0, object.activePlayers, temp2)
		if temp0 == true
			if player[currentPlayer].xpos < object.xpos
				temp1 = player[currentPlayer].collisionRight
				FlipSign(temp1)
				temp1 -= 22
			else
				temp1 = player[currentPlayer].collisionLeft
				FlipSign(temp1)
				temp1 += 22
			end if
			temp1 <<= 16
			
			player[currentPlayer].xpos = temp1
			player[currentPlayer].xpos += temp4
		else
			BoxCollisionTest(C_SOLID, object.entityPos, -22, -22, 22, 22, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
			if checkResult == COL_TOP
				player[currentPlayer].xpos += temp6
				temp0 = temp6
				temp0 >>= 1
				player[currentPlayer].xpos += temp0
				player[currentPlayer].ypos += temp7
				if player[currentPlayer].xvel != 0
					temp0 = player[currentPlayer].acceleration
					temp0 >>= 2
					if player[currentPlayer].xvel > 0
						FlipSign(temp0)
					end if
				else
					temp0 = 0
				end if
				object.speed += temp0
				
				if temp0 != 0
					object.playerControlled = true
				end if
				
				object.xpos = temp4
				object.ypos = temp5
				BoxCollisionTest(C_SOLID, object.entityPos, -22, -22, 22, 22, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
				object.xpos = object.roundedPos.x
				object.ypos = object.roundedPos.y
				if checkResult == COL_NONE
					temp0 = temp6
					temp1 = temp6
					temp0 >>= 1
					temp1 += temp0
					player[currentPlayer].speed += temp0
					player[currentPlayer].xvel = player[currentPlayer].speed
					player[currentPlayer].gravity = GRAVITY_AIR
					player[currentPlayer].controlLock = 15
				end if
			end if

			if checkResult == COL_BOTTOM
				if object.yvel >= 0
					if player[currentPlayer].gravity == GRAVITY_GROUND
						// The Ball's falling downwards on top of the player, crush them
						CallFunction(Player_Kill)
					end if
				end if
			end if
		end if
		temp2++
	next
	
	object.xpos = temp4
	object.ypos = temp5
end function


private function CheckeredBall_BadnikBreak
	temp0 = object.xvel
	temp0 |= object.yvel
	
	if temp0 != 0
		ResetObjectEntity(object[arrayPos0].entityPos, TypeName[Blank Object], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		Rand(checkResult, 32)
		if checkResult >= 16
			CreateTempObject(animalType1, 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		else
			CreateTempObject(animalType2, 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		end if
		object[tempObjectPos].priority = PRIORITY_ACTIVE_SMALL
		
		CreateTempObject(TypeName[Smoke Puff], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		object[tempObjectPos].drawOrder = 4
		
		CreateTempObject(TypeName[Object Score], object.badnikBonus, object[arrayPos0].xpos, object[arrayPos0].ypos)
		object[tempObjectPos].drawOrder = 4
		
		PlaySfx(SfxName[Destroy], false)
		
		switch object.badnikBonus
		case 0
			player.score += 100
			break
			
		case 1
			player.score += 200
			break
			
		case 2
			player.score += 500
			break
			
		case 3 // Fall through
		case 4
		case 5
		case 6
		case 7
		case 8
		case 9
		case 10
		case 11
		case 12
		case 13
		case 14
			player.score += 1000
			break
			
		case 15
			player.score += 10000
			break
			
		end switch

		if object.badnikBonus < 15
			object.badnikBonus++
		end if
	end if
end function


private function CheckeredBall_ReflectProjectile
	// Do some maths to find where the projectile should go now
	temp0 = object.xpos
	temp0 -= object[arrayPos0].xpos
	temp1 = object.ypos
	temp1 -= object[arrayPos0].ypos
	
	ATan2(temp2, temp0, temp1)
	Sin256(temp0, temp2)
	Cos256(temp1, temp2)
	
	object[arrayPos0].xvel = temp1
	object[arrayPos0].xvel *= -0x800
	object[arrayPos0].yvel = temp0
	object[arrayPos0].yvel *= -0x800
end function


private function CheckeredBall_SpringBounce
	switch object[arrayPos0].propertyValue
	case 0
		// Upwards Spring
		
		temp0 = spring[arrayPos0].active
		if object.gravity == GRAVITY_AIR
			temp0 = true
		end if
		
		if object.collisionMode > CMODE_FLOOR
			if object.yvel < 0
				temp0 = true
			end if
		end if
		
		if temp0 == false
			BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -14, -8, 14, 8, object.entityPos, -22, -22, 22, 22)
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -14, -10, 14, -6, object.entityPos, -22, -22, 22, 22)
			if checkResult == true
				spring[arrayPos0].timer = 1
				object.tileCollisions = true
				object.gravity = GRAVITY_AIR
				object.speed = object.xvel
				object.yvel = temp7
				FlipSign(object.yvel)
				
				object.yvel += spring[arrayPos0].extraVelocity
				PlaySfx(SfxName[Spring], false)
			end if
		else
			if object.yvel >= 0
				BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -14, -8, 14, 8, object.entityPos, -22, -22, 22, 22)
				BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -14, -10, 14, -6, object.entityPos, -22, -22, 22, 22)
				if checkResult == true
					spring[arrayPos0].timer = 1
					object.tileCollisions = true
					object.gravity = GRAVITY_AIR
					object.speed = object.xvel
					object.yvel = temp7
					FlipSign(object.yvel)
					
					object.yvel += spring[arrayPos0].extraVelocity
					PlaySfx(SfxName[Spring], false)
				end if
			end if
		end if
		break
		
	case 1
		// Spring facing Right
		
		BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -8, -14, 8, 14, object.entityPos, -22, -22, 22, 22)
		if object.gravity == GRAVITY_GROUND
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, 6, -14, 10, 14, object.entityPos, -22, -22, 22, 22)
			if checkResult == true
				spring[arrayPos0].timer = 1
				object.tileCollisions = true
				object.speed = temp7
				object.collisionMode = CMODE_FLOOR
				object.direction = FACING_RIGHT // The Checkered Ball's direction doesn't matter too much, but sure
				PlaySfx(SfxName[Spring], false)
			end if
		end if
		break
		
	case 2
		// Spring facing Left
		
		BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -8, -14, 8, 14, object.entityPos, -22, -22, 22, 22)
		if object.gravity == GRAVITY_GROUND
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -10, -14, -6, 14, object.entityPos, -22, -22, 22, 22)
			if checkResult == true
				spring[arrayPos0].timer = 1
				object.tileCollisions = true
				object.speed = temp7
				FlipSign(object.speed)
				object.collisionMode = CMODE_FLOOR
				object.direction = FACING_LEFT // I mean if you really wanna ig?
				PlaySfx(SfxName[Spring], false)
			end if
		end if
		break

	case 3
		// Downwards Spring
		
		BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -14, -8, 14, 8, object.entityPos, -22, -22, 22, 22)
		BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -14, 6, 14, 10, object.entityPos, -22, -22, 22, 22)
		if checkResult == true
			spring[arrayPos0].timer = 1
			if object.collisionMode == CMODE_ROOF
				FlipSign(object.speed)
				FlipSign(object.xvel)
			end if
			object.tileCollisions = true
			object.gravity = GRAVITY_AIR
			object.speed = object.xvel
			object.yvel = temp7
			PlaySfx(SfxName[Spring], true)
		end if
		break
		
	end switch
end function


private function CheckeredBall_CLedgeCollapse
	if object[arrayPos0].state < CLEDGE_NONE
		if object.yvel >= 0
			temp0 = object.xpos
			temp0 -= object[arrayPos0].xpos
			temp0 >>= 17
			temp0 += 24
			if temp0 < 0
				temp0 = 0
			end if
			
			if temp0 > 47
				temp0 = 47
			end if
			
			if temp7 == false
				// Ledge is facing Right
				GetTableValue(temp1, temp0, CLedgeLeft_heightArray)
			else
				// Ledge is facing Left, invert the value to fetch
				temp2 = 47
				temp2 -= temp0
				GetTableValue(temp1, temp2, CLedgeLeft_heightArray)
			end if
			
			temp1 -= 84
			temp0 = temp1
			temp0 += 32
			BoxCollisionTest(C_PLATFORM, arrayPos0, -48, temp1, 48, temp0, object.entityPos, -18, -22, 18, 22)
			if checkResult == true
				object.ypos += 0x40000
				if object[arrayPos0].state == CLEDGE_ACTIVE
					object[arrayPos0].state = CLEDGE_COLLAPSE
				end if
			end if
		end if
	end if
end function


private function CheckeredBall_HandlePhysics
	if object.gravity == GRAVITY_AIR
		object.yvel += 0x3800
		object.speed = object.xvel
	else
		Sin256(temp0, object.groundAngle)
		temp0 *= 0x2000
		temp0 >>= 8
		object.speed += temp0
		object.angleVel = object.speed
		if temp0 == 0
			if object.playerControlled == false
				if object.speed > 0
					object.speed -= 0x400
					if object.speed < 0
						object.speed = 0
					end if
				else
					object.speed += 0x400
					if object.speed > 0
						object.speed = 0
					end if
				end if
			end if
		end if
		
		Cos256(temp0, object.groundAngle)
		temp0 *= object.speed
		temp0 >>= 8
		object.xvel = temp0
		
		Sin256(temp0, object.groundAngle)
		temp0 *= object.speed
		temp0 >>= 8
		object.yvel = temp0
	end if
	
	object.playerControlled = false
end function


// ========================
// Events
// ========================

event ObjectUpdate
	if object.priority != PRIORITY_XBOUNDS_DESTROY
		object.priority = PRIORITY_ACTIVE
	end if
	
	CallFunction(CheckeredBall_HandlePlayerMove)
	
	object.ballAngle += object.angleVel
	
	// These don't actually do anything - the engine doesn't support direct setting of these values by script
	// Instead, the Checkered Ball's collision values are set with the ani file
	object.collisionLeft = -22
	object.collisionTop = -22
	object.collisionRight = 22
	object.collisionBottom = 22
	
	CallFunction(CheckeredBall_HandlePhysics)
	
	ProcessObjectMovement()
	
	object.groundAngle = object.angle
	
	// Originally, this whole interaction system would cycle though *all* objects in the stage and use a giant case-switch statement for object type, containing all possible interactions
	// Later, however, Taxman updated this code to use separate, individual foreach checks for every interaction instead of the old giant foreach + case-switch
	// This updated script was never implemented into the mobile version of Sonic 1, but it was in Origins (and Mania's followed the same structure as well)
	
	foreach (TypeName[Motobug], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -14, -14, 14, 14)
		
		if checkResult == true
			CallFunction(CheckeredBall_BadnikBreak)
		end if
	next
	
	foreach (TypeName[Buzz Bomber], arrayPos0, ACTIVE_ENTITIES)
		// The Buzz Bomber's hitbox is normally -24, -12, 24, 12...
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -16, -7, 16, 7)
		if checkResult == true
			CallFunction(CheckeredBall_BadnikBreak)
		end if
	next
	
	foreach (TypeName[Crabmeat], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -14, -14, 14, 14)
		if checkResult == true
			CallFunction(CheckeredBall_BadnikBreak)
		end if
	next
	
	foreach (TypeName[Chopper], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -12, -14, 12, 14)
		if checkResult == true
			CallFunction(CheckeredBall_BadnikBreak)
		end if
	next
	
	foreach (TypeName[Splats], arrayPos0, ACTIVE_ENTITIES)
		// A fellow unused object, go ahead and interact with Splats too
		// Something interesting - this interaction didn't even exist in the original S1 release,
		// it was only added later in an update
		// Why they decided to update an unused object to add an unused interaction for an unused enemy is beyond me, though...
		
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -10, -20, 6, 20)
		if checkResult == true
			CallFunction(CheckeredBall_BadnikBreak)
		end if
	next
	
	foreach (TypeName[Newtron Fly], arrayPos0, ACTIVE_ENTITIES)
		// Only hurt Newtrons when they're active, not in their hidden states
		switch object[arrayPos0].state
		case NEWTRONFLY_APPEARED
		case NEWTRONFLY_MOVING
			BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -16, -8, 16, 8)
			if checkResult == true
				CallFunction(CheckeredBall_BadnikBreak)
			end if
			break
			
		end switch
	next
	
	foreach (TypeName[Newtron Shoot], arrayPos0, ACTIVE_ENTITIES)
		// Only hurt Newtrons when they're active, not in their hidden states
		switch object[arrayPos0].state
		case NEWTRONSHOOT_SHOOT
		case NEWTRONSHOOT_SHOOTDELAY
			BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -12, -14, 12, 14)
			if checkResult == true
				CallFunction(CheckeredBall_BadnikBreak)
			end if
			break
			
		end switch
	next
	
	foreach (TypeName[Buzz Bomber Shot], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -6, -6, 6, 6)
		if checkResult == true
			CallFunction(CheckeredBall_ReflectProjectile)
		end if
	next
	
	foreach (TypeName[Crabmeat Shot], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -6, -6, 6, 6)
		if checkResult == true
			CallFunction(CheckeredBall_ReflectProjectile)
		end if
	next
	
	foreach (TypeName[Newtron Shot], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -6, -6, 6, 6)
		if checkResult == true
			CallFunction(CheckeredBall_ReflectProjectile)
		end if
	next
	
	foreach (TypeName[Monitor], arrayPos0, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -22, -22, 22, 22, arrayPos0, -16, -14, 16, 16)
		if checkResult == true
			object[arrayPos0].state = MONITOR_STATE_IDLE
			CreateTempObject(TypeName[Smoke Puff], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
			object[tempObjectPos].drawOrder = 4
			object[arrayPos0].type = TypeName[Broken Monitor]
			
			if object[arrayPos0].priority != PRIORITY_XBOUNDS_DESTROY
				object[arrayPos0].priority = PRIORITY_ACTIVE
			end if
			
			object[arrayPos0].alpha = 0xFF
			monitor[arrayPos0].contentsPos.y = object[arrayPos0].ypos
			monitor[arrayPos0].timer = -0x30000
			
			PlaySfx(SfxName[Destroy], false)
		end if
	next
	
	foreach (TypeName[Rock], arrayPos0, ACTIVE_ENTITIES)
		// Do note that here the order in this collision test function is switched, the Rock is in the first slot and then the Ball's in the second
		BoxCollisionTest(C_SOLID, arrayPos0, -16, -16, 16, 16, object.entityPos, -22, -22, 22, 22)
		switch checkResult
		case COL_TOP
			object.gravity = GRAVITY_GROUND
			break
			
		case COL_LEFT
			if object.speed > 0
				object.speed = 0
			end if
			break
			
		case COL_RIGHT
			if object.speed < 0
				object.speed = 0
			end if
			break
			
		end switch
	next
	
	foreach (TypeName[Spikes], arrayPos0, ACTIVE_ENTITIES)
		// Have different tests for each of the Spike types
		switch object[arrayPos0].propertyValue
		case 0
			// 3 Spikes (Up)
			BoxCollisionTest(C_SOLID, arrayPos0, -20, -16, 20, 16, object.entityPos, -22, -22, 22, 22)
			break
			
		case 1
			// 3 Spikes (Right)
			BoxCollisionTest(C_SOLID, arrayPos0, -16, -20, 15, 20, object.entityPos, -22, -22, 22, 22)
			break
			
		case 2
			// 3 Spikes (Left)
			BoxCollisionTest(C_SOLID, arrayPos0, -15, -20, 16, 20, object.entityPos, -22, -22, 22, 22)
			break
			
		case 3
			// 3 Spikes (Down)
			BoxCollisionTest(C_SOLID, arrayPos0, -20, -16, 20, 15, object.entityPos, -22, -22, 22, 22)
			break
			
		case 4
			// 1 Spike (Up)
			BoxCollisionTest(C_SOLID, arrayPos0, -4, -16, 4, 16, object.entityPos, -22, -22, 22, 22)
			break
			
		case 5
			// 1 Spike (Right)
			BoxCollisionTest(C_SOLID, arrayPos0, -16, -4, 15, 4, object.entityPos, -22, -22, 22, 22)
			break
			
		case 6
			// 1 Spike (Left)
			BoxCollisionTest(C_SOLID, arrayPos0, -15, -4, 16, 4, object.entityPos, -22, -22, 22, 22)
			break
			
		case 7
			// 1 Spike (Down)
			BoxCollisionTest(C_SOLID, arrayPos0, -4, -16, 4, 15, object.entityPos, -22, -22, 22, 22)
			break
			
		case 8
			// 3 Spikes (Spaced Out) (Up)
			BoxCollisionTest(C_SOLID, arrayPos0, -28, -16, 28, 16, object.entityPos, -22, -22, 22, 22)
			break
			
		case 9
			// 3 Spikes (Spaced Out) (Right)
			BoxCollisionTest(C_SOLID, arrayPos0, -16, -28, 15, 28, object.entityPos, -22, -22, 22, 22)
			break
			
		case 10
			// 3 Spikes (Spaced Out) (Left)
			BoxCollisionTest(C_SOLID, arrayPos0, -15, -28, 16, 28, object.entityPos, -22, -22, 22, 22)
			break
			
		case 11
			// 3 Spikes (Spaced Out) (Down)
			BoxCollisionTest(C_SOLID, arrayPos0, -28, -16, 28, 15, object.entityPos, -22, -22, 22, 22)
			break
			
		case 12
			// 6 Spikes (Spaced Out) (Up)
			BoxCollisionTest(C_SOLID, arrayPos0, -64, -16, 64, 16, object.entityPos, -22, -22, 22, 22)
			break
			
		case 13
			// 6 Spikes (Spaced Out) (Right)
			BoxCollisionTest(C_SOLID, arrayPos0, -16, -64, 15, 64, object.entityPos, -22, -22, 22, 22)
			break
			
		case 14
			// 6 Spikes (Spaced Out) (Left)
			BoxCollisionTest(C_SOLID, arrayPos0, -15, -64, 16, 64, object.entityPos, -22, -22, 22, 22)
			break
			
		case 15
			// 6 Spikes (Spaced Out) (Down)
			BoxCollisionTest(C_SOLID, arrayPos0, -64, -16, 64, 15, object.entityPos, -22, -22, 22, 22)
			break
			
		end switch
		
		switch checkResult
		case COL_TOP
			object.gravity = GRAVITY_GROUND
			break
			
		case COL_LEFT
			if object.speed > 0
				object.speed = 0
			end if
			break
			
		case COL_RIGHT
			if object.speed < 0
				object.speed = 0
			end if
			break
			
		end switch
	next
	
	foreach (TypeName[Breakable Wall], arrayPos0, ACTIVE_ENTITIES)
		// Only interact with solid walls, not wall fragments
		if object[arrayPos0].propertyValue < 3
			
			// Get the absolute version of this object's speed
			temp0 = object.speed
			Abs(temp0)
			
			// Unlike Players, who need to be going at a speed of 4.5 pixels per frame, the Checkered Ball's speed requirement is only 2px per frame
			if temp0 >= 0x20000
				BoxCollisionTest(C_TOUCH, arrayPos0, -17, -32, 17, 32, object.entityPos, -22, -22, 22, 22)
				if checkResult == true
					// Destroy the wall
					
					// Delete the wall object
					object[arrayPos0].type = TypeName[Blank Object]
					
					// Bug Details:
					// -> Although this worked in initial versions of S1, it now plays the jump sound instead
					//    This is because the earliest revisions of S1 only had sfx [Ledge Break], as opposed to later versions where it was split into [Ledge Break L] and [Ledge Break R]
					// -> This use of SfxName was neglected to be updated, which is why this now invalid TypeName plays sfx ID 0 (the jump sound) instead
					PlaySfx(SfxName[Ledge Break], false)
					
					temp0 = object[arrayPos0].propertyValue
					temp0 <<= 1
					temp0 += 3
					
					temp1 = temp0
					temp1++
					
					temp2 = object[arrayPos0].xpos
					temp2 -= 0x80000
					
					temp3 = object[arrayPos0].xpos
					temp3 += 0x80000
					
					temp4 = object[arrayPos0].ypos
					temp4 -= 0x180000
					
					// Determine which way to blow wall fragments based on which way the ball is "facing"
					if object.xpos < object[arrayPos0].xpos
						// Sending the fragments left
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = -0x60000
						object[tempObjectPos].yvel = -0x60000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = -0x40000
						object[tempObjectPos].yvel = -0x50000
						object[tempObjectPos].drawOrder = 4
						
						temp4 += 0x100000
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = -0x80000
						object[tempObjectPos].yvel = -0x20000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = -0x60000
						object[tempObjectPos].yvel = -0x10000
						object[tempObjectPos].drawOrder = 4
						
						temp4 += 0x100000
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = -0x80000
						object[tempObjectPos].yvel = 0x20000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = -0x60000
						object[tempObjectPos].yvel = 0x10000
						object[tempObjectPos].drawOrder = 4
						
						temp4 += 0x100000
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = -0x60000
						object[tempObjectPos].yvel = 0x60000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = -0x40000
						object[tempObjectPos].yvel = 0x50000
						object[tempObjectPos].drawOrder = 4
					else
						// Sending the fragments right
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = 0x40000
						object[tempObjectPos].yvel = -0x50000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = 0x60000
						object[tempObjectPos].yvel = -0x60000
						object[tempObjectPos].drawOrder = 4
						
						temp4 += 0x100000
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = 0x60000
						object[tempObjectPos].yvel = -0x10000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = 0x80000
						object[tempObjectPos].yvel = -0x20000
						object[tempObjectPos].drawOrder = 4
						
						temp4 += 0x100000
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = 0x60000
						object[tempObjectPos].yvel = 0x10000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = 0x80000
						object[tempObjectPos].yvel = 0x20000
						object[tempObjectPos].drawOrder = 4
						
						temp4 += 0x100000
						
						CreateTempObject(TypeName[Breakable Wall], temp0, temp2, temp4)
						object[tempObjectPos].xvel = 0x40000
						object[tempObjectPos].yvel = 0x50000
						object[tempObjectPos].drawOrder = 4
						
						CreateTempObject(TypeName[Breakable Wall], temp1, temp3, temp4)
						object[tempObjectPos].xvel = 0x60000
						object[tempObjectPos].yvel = 0x60000
						object[tempObjectPos].drawOrder = 4
					end if
				end if
			else
				BoxCollisionTest(C_SOLID, arrayPos0, -17, -32, 17, 32, object.entityPos, -22, -22, 22, 22)
				switch checkResult
				case COL_TOP
					object.gravity = GRAVITY_GROUND
					break
					
				case COL_LEFT
					if object.speed > 0
						object.speed = 0
					end if
					break
					
				case COL_RIGHT
					if object.speed < 0
						object.speed = 0
					end if
					break
					
				end switch
			end if
		end if
	next
	
	foreach (TypeName[Bridge], arrayPos0, ACTIVE_ENTITIES)
		if object.xpos > bridge[arrayPos0].startPos
			if object.xpos < bridge[arrayPos0].endPos
				if object.entityPos == bridge[arrayPos0].playerID
					bridge[arrayPos0].stoodPos = object.xpos
					bridge[arrayPos0].stoodPos -= bridge[arrayPos0].startPos
					temp0 = bridge[arrayPos0].stoodPos
					temp0 >>= 8
					temp1 = bridge[arrayPos0].endPos
					temp1 -= bridge[arrayPos0].startPos
					temp2 = temp1
					temp2 >>= 16
					temp0 /= temp2
					Sin(bridge[arrayPos0].depression, temp0)
					temp1 >>= 13
					bridge[arrayPos0].depression *= temp1
					temp0 = object[arrayPos0].ypos
					temp0 -= 0x300000
					if object.ypos > temp0
						if object.yvel >= 0
							temp2 = object.collisionBottom
							FlipSign(temp2)
							temp2 <<= 16
							temp2 += bridge[arrayPos0].bridgeDepth
							temp2 -= 0x80000
							bridge[arrayPos0].activePlayerCount++
							object.ypos = object[arrayPos0].ypos
							object.ypos += temp2
							object.gravity = GRAVITY_GROUND
							object.yvel = 0
							object.floorSensorL = true
							object.floorSensorC = true
							object.floorSensorR = true
						else
							bridge[arrayPos0].playerID = -2
						end if
					end if
				else
					if object.yvel >= 0
						temp0 = object.xpos
						temp0 -= bridge[arrayPos0].startPos
						if temp0 > bridge[arrayPos0].stoodPos
							temp0 = bridge[arrayPos0].endPos
							temp0 -= object.xpos
							temp3 = bridge[arrayPos0].endPos
							temp3 -= bridge[arrayPos0].startPos
							temp3 -= bridge[arrayPos0].stoodPos
							temp1 = temp0
							temp1 <<= 7
							temp1 /= temp3
						else
							temp1 = temp0
							temp1 <<= 7
							temp1 /= bridge[arrayPos0].stoodPos
						end if
						Sin(temp2, temp1)
						temp2 *= bridge[arrayPos0].bridgeDepth
						temp2 >>= 9
						temp2 -= 0x80000
						if object.yvel < 0x8000
							temp3 = temp2
							temp3 >>= 16
							temp4 = temp3
							temp3 -= 8
						else
							temp3 = temp2
							temp3 >>= 16
							temp4 = temp3
							temp4 += 8
						end if
						
						BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -0x400, temp3, 0x400, temp4, object.entityPos, -18, -22, 18, 22)
						if checkResult == true
							bridge[arrayPos0].activePlayerCount++
							object.ypos = object.collisionBottom
							FlipSign(object.ypos)
							object.ypos <<= 16
							object.ypos += object[arrayPos0].ypos
							object.ypos += temp2
							object.floorSensorL = true
							object.floorSensorC = true
							object.floorSensorR = true
							bridge[arrayPos0].playerID = object.entityPos
							object.gravity = GRAVITY_GROUND
							object.yvel = 0
						end if
					end if
				end if
			else
				if object.entityPos == bridge[arrayPos0].playerID
					bridge[arrayPos0].playerID = -2
					bridge[arrayPos0].stoodPos = 32
				end if
			end if
		else
			if object.entityPos == bridge[arrayPos0].playerID
				bridge[arrayPos0].playerID = -2
				bridge[arrayPos0].stoodPos = 32
			end if
		end if
	next
	
	foreach (TypeName[Yellow Spring], arrayPos0, ACTIVE_ENTITIES)
		temp7 = 0xA0000
		CallFunction(CheckeredBall_SpringBounce)
	next
	
	foreach (TypeName[Red Spring], arrayPos0, ACTIVE_ENTITIES)
		temp7 = 0x100000
		CallFunction(CheckeredBall_SpringBounce)
	next
	
	foreach (TypeName[Fall Platform], arrayPos0, ACTIVE_ENTITIES)
		object[arrayPos0].ypos -= fallPlatform[arrayPos0].collisionOffset.y
		BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -32, -8, 32, 0, object.entityPos, -18, -22, 18, 22)
		if checkResult == true
			fallPlatform[arrayPos0].stood = true
			object.collisionOffset.y = fallPlatform[arrayPos0].collisionOffset.y
		end if
		object[arrayPos0].ypos += fallPlatform[arrayPos0].collisionOffset.y
	next
	
	foreach (TypeName[H Platform], arrayPos0, ACTIVE_ENTITIES)
		object[arrayPos0].xpos -= hPlatform[arrayPos0].collisionOffset.x
		object[arrayPos0].ypos -= hPlatform[arrayPos0].collisionOffset.y
		BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -32, -8, 32, 12, object.entityPos, -18, -22, 18, 22)
		if checkResult == true
			hPlatform[arrayPos0].stood = true
			object.collisionOffset.x = hPlatform[arrayPos0].collisionOffset.x
			object.collisionOffset.y = hPlatform[arrayPos0].collisionOffset.y
		end if
		object[arrayPos0].xpos += hPlatform[arrayPos0].collisionOffset.x
		object[arrayPos0].ypos += hPlatform[arrayPos0].collisionOffset.y
	next
	
	foreach (TypeName[V Platform], arrayPos0, ACTIVE_ENTITIES)
		object[arrayPos0].ypos -= vPlatform[arrayPos0].collisionOffset.y
		BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -32, -8, 32, 12, object.entityPos, -18, -22, 18, 22)
		if checkResult == true
			object.collisionOffset.y = vPlatform[arrayPos0].collisionOffset.y
		end if
		object[arrayPos0].ypos += vPlatform[arrayPos0].collisionOffset.y
	next
	
	foreach (TypeName[V Platform 2], arrayPos0, ACTIVE_ENTITIES)
		object[arrayPos0].ypos -= vPlatform2[arrayPos0].collisionOffset.y
		BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -32, -2, 32, 12, object.entityPos, -18, -22, 18, 22)
		if checkResult == true
			object.collisionOffset.y = vPlatform2[arrayPos0].collisionOffset.y
		end if
		object[arrayPos0].ypos += vPlatform2[arrayPos0].collisionOffset.y
	next
	
	foreach (TypeName[Swing Platform], arrayPos0, ACTIVE_ENTITIES)
		temp0 = object[arrayPos0].xpos
		temp1 = object[arrayPos0].ypos
		object[arrayPos0].xpos = swingPlatform[arrayPos0].drawPos.x
		object[arrayPos0].ypos = swingPlatform[arrayPos0].drawPos.y
		object[arrayPos0].xpos -= swingPlatform[arrayPos0].collisionOffset.x
		object[arrayPos0].ypos -= swingPlatform[arrayPos0].collisionOffset.y
		BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -24, -8, 24, 8, object.entityPos, -18, -22, 18, 22)
		if checkResult == true
			object.collisionOffset.x = swingPlatform[arrayPos0].collisionOffset.x
			object.collisionOffset.y = swingPlatform[arrayPos0].collisionOffset.y
		end if
		object[arrayPos0].xpos = temp0
		object[arrayPos0].ypos = temp1
	next
	
	foreach (TypeName[C Ledge Left], arrayPos0, ACTIVE_ENTITIES)
		temp7 = true
		CallFunction(CheckeredBall_CLedgeCollapse)
	next
	
	foreach (TypeName[C Ledge Right], arrayPos0, ACTIVE_ENTITIES)
		temp7 = false
		CallFunction(CheckeredBall_CLedgeCollapse)
	next
	
	foreach (TypeName[Plane Sw V], arrayPos0, ACTIVE_ENTITIES)
		CheckEqual(planeSwV[arrayPos0].onGround, false)
		temp0 = checkResult
		CheckEqual(object.gravity, GRAVITY_GROUND)
		temp0 |= checkResult
		if temp0 == true
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -12, planeSwV[arrayPos0].extendTop, 12, planeSwV[arrayPos0].extendBottom, object.entityPos, -10, -22, 10, 22)
			if checkResult == true
				if object.xvel > 0
					object.collisionPlane = planeSwV[arrayPos0].planeL
					object.drawOrder = planeSwV[arrayPos0].drawOrderL
				else
					object.collisionPlane = planeSwV[arrayPos0].planeR
					object.drawOrder = planeSwV[arrayPos0].drawOrderR
				end if
			end if
		end if
	next
	
	// Bug Details:
	// -> This is supposed to check for Plane Sw H, not V again...
	// -> This was only broken in an update, during the giant case-switch to individual foreach transfer
	//    Earlier versions of the script correcty used [Plane Sw H] here, though the latest versions don't and those are what we're sticking with
	foreach (TypeName[Plane Sw V], arrayPos0, ACTIVE_ENTITIES)
		CheckEqual(planeSwV[arrayPos0].onGround, false)
		temp0 = checkResult
		CheckEqual(object.gravity, GRAVITY_GROUND)
		temp0 |= checkResult
		if temp0 == true
			// This is looking at a Sw H but using its values as a Sw V, this only works perchance
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, planeSwV[arrayPos0].extendLeft, -12, planeSwV[arrayPos0].extendRight, 12, object.entityPos, -22, -10, 22, 10)
			if checkResult == true
				if object.yvel > 0
					object.collisionPlane = planeSwV[arrayPos0].planeL
					object.drawOrder = planeSwV[arrayPos0].drawOrderL
				else
					object.collisionPlane = planeSwV[arrayPos0].planeL
					object.drawOrder = planeSwV[arrayPos0].drawOrderR
				end if
			end if
		end if
	next
	
	foreach (TypeName[Eggman], arrayPos0, ACTIVE_ENTITIES)
		arrayPos1 = arrayPos0
		arrayPos1--
		if ghzEggman[arrayPos1].invincibilityTimer == 0
			switch object[arrayPos0].state
			case GHZEGGMAN_INITIALSWING
			case GHZEGGMAN_MOVELEFT
			case GHZEGGMAN_MOVERIGHT
				BoxCollisionTest(C_TOUCH, arrayPos0, -20, -16, 20, 16, object.entityPos, -22, -22, 22, 22)
				if checkResult == true
					ghzEggman[arrayPos0].health--
					if ghzEggman[arrayPos0].health == 0
						// Sonic Team forgot to add the boss callbacks here LOL
						player.score += 1000
						object[arrayPos0].animationTimer = 0
						object[arrayPos0].animation = GHZEGGANI_DEFEATED
						object[arrayPos1].state = WRECKINGBALL_EXPLODE
						ghzEggman[arrayPos1].flameAnim = FLAME_INACTIVE
						arrayPos1--
						ResetObjectEntity(arrayPos1, TypeName[Explosion], 0, object[arrayPos1].xpos, object[arrayPos1].ypos)
						arrayPos1--
						ResetObjectEntity(arrayPos1, TypeName[Explosion], 0, object[arrayPos1].xpos, object[arrayPos1].ypos)
						arrayPos1--
						ResetObjectEntity(arrayPos1, TypeName[Explosion], 0, object[arrayPos1].xpos, object[arrayPos1].ypos)
						arrayPos1--
						ResetObjectEntity(arrayPos1, TypeName[Explosion], 0, object[arrayPos1].xpos, object[arrayPos1].ypos)
						arrayPos1--
						ResetObjectEntity(arrayPos1, TypeName[Explosion], 0, object[arrayPos1].xpos, object[arrayPos1].ypos)
						ghzEggman[arrayPos0].timer = 0
						object[arrayPos0].state = GHZEGGMAN_EXPLODE
					else
						object[arrayPos0].animationTimer = 0
						object[arrayPos0].animation = GHZEGGANI_HIT
						ghzEggman[arrayPos1].invincibilityTimer = 32
						PlaySfx(SfxName[Boss Hit], false)
					end if
				end if
				break
			end switch
		end if
	next
	
	foreach (TypeName[Wrecking Ball], arrayPos0, ACTIVE_ENTITIES)
		if object[arrayPos0].state == WRECKINGBALL_ACTIVE
			BoxCollisionTest(C_TOUCH, arrayPos0, -22, -22, 22, 22, object.entityPos, -22, -22, 22, 22)
			if checkResult == true
				temp0 = object[arrayPos0].xpos
				temp0 -= object.wreckingBallPos.x
				
				temp1 = object[arrayPos0].ypos
				temp1 -= object.wreckingBallPos.y
				
				if object.xvel == 0
					object.xvel = temp0
					object.speed = object.xvel
				else
					if object.xvel < 0
						if temp0 > 0
							temp2 = temp0
							temp2 >>= 1
							temp0 += temp2
							object.xvel = temp0
							object.speed = object.xvel
						end if
					else
						if temp0 < 0
							temp2 = temp0
							temp2 >>= 1
							temp0 += temp2
							object.xvel = temp0
							object.speed = object.xvel
						end if
					end if
				end if
				
				if object.yvel == 0
					object.yvel = temp1
				else
					if object.yvel < 0
						if temp1 > 0
							temp2 = temp1
							temp2 >>= 1
							temp1 += temp2
							object.yvel = temp1
						end if
					else
						if temp1 < 0
							temp2 = temp1
							temp2 >>= 1
							temp1 += temp2
							object.yvel = temp1
						end if
					end if
				end if
			end if
		end if
		
		// Store the Wrecking Ball's position for next frame
		object.wreckingBallPos.x = object[arrayPos0].xpos
		object.wreckingBallPos.y = object[arrayPos0].ypos
	next
	
	foreach (TypeName[Checkered Ball], arrayPos0, ACTIVE_ENTITIES)
		// It can interact with other checkered balls, too!
		if arrayPos0 != object.entityPos // (Check to make sure this object isn't interacting with itself)
			temp0 = object.xpos
			temp0 -= object[arrayPos0].xpos
			Abs(temp0)
			temp0 >>= 17
			if temp0 >= 24
				temp0 = 23
			end if
			GetTableValue(temp6, temp0, CheckeredBall_heightArray)
			
			temp0 = temp6
			temp0 >>= 1
			temp6 += temp0
			temp6 -= 22
			temp7 = temp6
			FlipSign(temp7)
			temp0 = object.xvel
			temp1 = object.yvel
			temp2 = object.speed
			BoxCollisionTest(C_SOLID, arrayPos0, -22, temp6, 22, temp7, object.entityPos, -22, -22, 22, 22)
			switch checkResult
			case COL_LEFT
			case COL_RIGHT
				CheckGreater(temp0, 0)
				temp3 = checkResult
				CheckLower(object.xpos, object[arrayPos0].xpos)
				temp3 &= checkResult
				CheckLower(temp0, 0)
				temp4 = checkResult
				CheckGreater(object.xpos, object[arrayPos0].xpos)
				temp4 &= checkResult
				temp3 |= temp4
				if temp3 != false
					object.xvel = object[arrayPos0].xvel
					object.speed = object[arrayPos0].speed
					object[arrayPos0].xvel = temp0
					object[arrayPos0].speed = temp2
				end if
				break
				
			case COL_TOP
				if object.xpos < object[arrayPos0].xpos
					object.speed -= 0xC00
					object.xvel -= 0xC00
					object[arrayPos0].speed += 0xC00
					object[arrayPos0].xvel += 0xC00
					object[arrayPos0].angleVel += 0xC00
				else
					object.speed += 0xC00
					object.xvel += 0xC00
					object[arrayPos0].speed -= 0xC00
					object[arrayPos0].xvel -= 0xC00
					object[arrayPos0].angleVel -= 0xC00
				end if
				// [Fallthrough]
			case COL_BOTTOM
				CheckGreater(temp1, 0)
				temp3 = checkResult
				CheckLower(object.ypos, object[arrayPos0].ypos)
				temp3 &= checkResult
				CheckLower(temp1, 0)
				temp4 = checkResult
				CheckGreater(object.ypos, object[arrayPos0].ypos)
				temp4 &= checkResult
				temp3 |= temp4
				if temp3 != false
					object.yvel = object[arrayPos0].yvel
					object[arrayPos0].yvel = temp1
				end if
				break
				
			end switch
			
			BoxCollisionTest(C_TOUCH, arrayPos0, -22, temp6, 22, temp7, object.entityPos, -22, -22, 22, 22)
			if checkResult == true
				if object.xpos < object[arrayPos0].xpos
					object.xpos -= 0x10000
				else
					object.xpos += 0x10000
				end if
				
				if object.ypos < object[arrayPos0].ypos
					object.ypos -= 0x10000
				else
					object.ypos += 0x10000
				end if
			end if
		end if
	next
	
	foreach (TypeName[Animal Prison], arrayPos0, ACTIVE_ENTITIES)
		switch object[arrayPos0].state
		case ANIMALPRISON_AWAITOPEN
			BoxCollisionTest(C_SOLID, arrayPos0, -32, -24, 32, 32, object.entityPos, -18, -22, 18, 22)
			BoxCollisionTest(C_SOLID, arrayPos0, -11, -48, 11, -24, object.entityPos, -18, -22, 18, 22)
			if checkResult == COL_TOP
				// Open the prison
				object[arrayPos0].state = ANIMALPRISON_OPENED
				stage.timeEnabled = false
				if Player_superState == SUPERSTATE_SUPER
					Player_superState = SUPERSTATE_UNTRANSFORM
				end if
				
				player[0].controlMode = CONTROLMODE_NONE
				
				// Origins's NOTIFY_BOSS_END isn't called here, so if you ended a level in the Boss Rush with a Checkered Ball + Animal Prison for... some reason, then you could have a little fun
			end if
			break
			
		case ANIMALPRISON_OPENED
			BoxCollisionTest(C_SOLID, arrayPos0, -32, -24, 32, 32, object.entityPos, -18, -22, 18, 22)
			BoxCollisionTest(C_SOLID, arrayPos0, -11, -40, 11, -24, object.entityPos, -18, -22, 18, 22)
			break
			
		end switch
	next
	
	CallFunction(CheckeredBall_PlayerInteraction)
	
	temp0 = object.collisionOffset.x
	temp0 |= object.collisionOffset.y
	if temp0 != 0
		foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
			BoxCollisionTest(C_SOLID, object.entityPos, -22, -22, 22, 22, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
			if checkResult == COL_TOP
				player[currentPlayer].xpos += object.collisionOffset.x
				player[currentPlayer].ypos += object.collisionOffset.y
			end if
		next
		object.xpos += object.collisionOffset.x
		object.ypos += object.collisionOffset.y
		object.collisionOffset.x = 0
		object.collisionOffset.y = 0
	end if
	
	if object.outOfBounds == true
		// The usual OOB checks here, where if the object is off screen and its starting position is too, we can safely teleport back to where we started
		
		// Store the ball's current position and move it to its origin pos
		temp0 = object.xpos
		temp1 = object.ypos
		object.xpos = object.startPos.x
		object.ypos = object.startPos.y
		
		// Is its starting position out of bounds too?
		if object.outOfBounds == true
			object.roundedPos.x = object.xpos
			object.roundedPos.y = object.ypos
			object.xvel = 0
			object.yvel = 0
			object.speed = 0
			object.badnikBonus = 0
			object.groundAngle = 0
			object.activePlayers = 0
			object.ballAngle = 0
			object.angleVel = 0
			object.playerControlled = 0
			if object.priority != PRIORITY_XBOUNDS_DESTROY
				object.priority = PRIORITY_BOUNDS
			end if
			object.state = 0
		else
			object.xpos = temp0
			object.ypos = temp1
		end if
	end if
	
	object.roundedPos.x = object.xpos
	object.roundedPos.x &= 0xFFFF0000
	object.roundedPos.y = object.ypos
	object.roundedPos.y &= 0xFFFF0000
end event


event ObjectDraw
	object.rotation = object.ballAngle
	object.rotation >>= 14
	
	temp0 = object.rotation
	temp0 += 8
	temp0 >>= 4
	temp0 &= 7
	temp0++
	
	DrawSprite(temp0)
	
	// Draw the shine on top of the ball too
	object.inkEffect = INK_ADD
	object.alpha = 160
	DrawSpriteFX(0, FX_INK, object.xpos, object.ypos)
end event


event ObjectStartup
	LoadSpriteSheet("GHZ/Objects2.gif")
	
	// Ball frames
	SpriteFrame(-24, -24, 48, 48, 1, 77)
	SpriteFrame(-24, -24, 48, 48, 50, 77)
	SpriteFrame(-24, -24, 48, 48, 148, 126)
	SpriteFrame(-24, -24, 48, 48, 99, 126)
	SpriteFrame(-24, -24, 48, 48, 50, 126)
	SpriteFrame(-24, -24, 48, 48, 1, 126)
	SpriteFrame(-24, -24, 48, 48, 197, 77)
	SpriteFrame(-24, -24, 48, 48, 148, 77)
	SpriteFrame(-24, -24, 48, 48, 99, 77)
	
	// Load the animation file
	// - Note that, even if loaded, the object is never drawn with this animation
	// - A script-based animation and drawing system is used, instead
	
	// A note from RDC: this isn't here for sprites, it's here to provide the inner/outer boxes for ProcessObjectMovement()
	// since that requires the animation file to work with what's been setup here
	
	// A note from Lave: this isn't here for hitboxes, it seems actually serve no purpose at all instead
	// This didn't even exist in initial S1 releases - it was only added after S2 (and its similar OOZ ball) were released
	// Hitboxes are set via script, instead
	
	// TODO: Remove/"steamline" the above two comment note sections, I thought it would be funny but nah it hardly works at all :(
	// Anyway, that's enough notes, I hope you're ready for your quiz tomorrow
	
	LoadAnimation("WreckingBall.ani")
	
	// Cycle through all Checkered Ball objects and init their values
	// Note that this never actually does anything, as no Checkered Ball objects exist in S1's levels
	foreach (TypeName[Checkered Ball], arrayPos0, ALL_ENTITIES)
		object[arrayPos0].gravity = GRAVITY_AIR
		
		object[arrayPos0].startPos.x = object.xpos
		object[arrayPos0].startPos.y = object.ypos
	next
	
	SetTableValue(TypeName[Checkered Ball], DebugMode_ObjCount, DebugMode_TypesTable)
	SetTableValue(CheckeredBall_DebugDraw, DebugMode_ObjCount, DebugMode_DrawTable)
	SetTableValue(CheckeredBall_DebugSpawn, DebugMode_ObjCount, DebugMode_SpawnTable)
	DebugMode_ObjCount++
end event


// ========================
// Editor Events
// ========================

event RSDKDraw
	DrawSprite(1)
	object.inkEffect = INK_ADD
	object.alpha = 160
	DrawSpriteFX(0, FX_INK, object.xpos, object.ypos)
end event


event RSDKLoad
	LoadSpriteSheet("GHZ/Objects2.gif")
	SpriteFrame(-24, -24, 48, 48, 1, 77)
	SpriteFrame(-24, -24, 48, 48, 50, 77)
	
	SetVariableAlias(ALIAS_VAR_PROPVAL, "unused")
end event
