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

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

private alias object.type 			: player.type
private alias object.entityPos 		: player.entityPos
private alias object.state 			: player.state
private alias object.priority 		: player.priority
private alias object.visible 		: player.visible
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.rotation 		: player.rotation
private alias object.angle 			: player.angle
private alias object.frame 			: player.frame
private alias object.animation 		: player.animation
private alias object.animationSpeed : player.animationSpeed
private alias object.animationTimer : player.animationTimer
private alias object.gravity 		: player.gravity
private alias object.controlMode 	: player.controlMode

// Object input values
private alias object.up 		: player.up
private alias object.down 		: player.down
private alias object.left 		: player.left
private alias object.right 		: player.right
private alias object.jumpPress 	: player.jumpPress
private alias object.jumpHold 	: player.jumpHold

// Value Aliases
private alias object.value0  : player.rings
private alias object.value1  : player.timer
private alias object.value2  : player.zpos
private alias object.value3  : player.xBoundsR
private alias object.value4  : player.frameOffset
// value5 is unused
private alias object.value6  : player.spinTimer
private alias object.value7  : player.invincibilityTimer
// value8 is unused
private alias object.value9  : player.xBoundsL
private alias object.value10 : player.groundPos
private alias object.value11 : player.localShadowPos.x
private alias object.value12 : player.localShadowPos.y
private alias object.value13 : player.localShadowPos.u
private alias object.value14 : player.localPos.z
private alias object.value15 : player.localPos.x
private alias object.value16 : player.localPos.y
private alias object.value17 : player.tailFrame
private alias object.value18 : player.stateInput
private alias object.value19 : player.vsCheckpointID
private alias object.value20 : player.vsCheckpoint1Rings
private alias object.value21 : player.vsCheckpoint2Rings
private alias object.value22 : player.vsCheckpoint3Rings
private alias object.value23 : player.playerTagFrame
private alias object.value24 : player.currentLeader
private alias object.value25 : player.changedLeader
private alias object.value26 : player.automoveActive
private alias object.value27 : player.jumpInTimer

// Halfpipe Aliases
private alias object.value13 : halfpipe.playerPos.z
private alias object.value14 : halfpipe.worldTranslation.x
private alias object.value15 : halfpipe.worldTranslation.y
private alias object.value16 : halfpipe.worldTranslation.z
private alias object.value17 : halfpipe.worldRotation.x
private alias object.value18 : halfpipe.worldRotation.y
private alias object.value19 : halfpipe.worldRotation.z

// LoseRing Aliases
private alias object.value1 : loseRing.gravityStrength

// Title Card aliases
private alias 8 : TITLECARD_FADETOTITLE

// Face Buffer Flag Aliases
private alias 6 : FACE_TEXTURED_C_BLEND
private alias 7 : FACE_SPRITE_3D


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

reserve function Player_Input_Gamepad
reserve function Player_Input_AI_Follow
reserve function Player_State_Ground
reserve function Player_State_Air
reserve function Player_State_Hurt
reserve function Player_LoseRings
reserve function Player_Draw_Normal
reserve function Player_Input_Gamepad_2P
reserve function Player_Input_VSPlayer_2P


// ========================
// Static Values
// ========================

private value Player_stateUp 		= 0
private value Player_stateDown 		= 0
private value Player_stateLeft 		= 0
private value Player_stateRight 	= 0
private value Player_stateJumpPress = 0
private value Player_stateJumpHold 	= 0

private value Player_attractTable 		= 0
private value Player_attractTablePos 	= 0
private value Player_attractTableSize 	= 0
private value Player_attractFrameCount 	= 0
private value Player_attractDuration 	= 0
private value Player_playerSlot 		= 0

// What player is in front right now
private value Player_leadingPlayer = 0

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

// Shadow Y Positions, with index 0 being as far left/right as it goes, and the last index being the center
private table Player_shadowYPosTable
	-128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -127, -127, -127, -127
	-127, -127, -127, -127, -126, -126, -126, -126, -126, -126, -125, -125, -125, -125, -124, -124
	-124, -124, -123, -123, -123, -123, -122, -122, -122, -121, -121, -121, -120, -120, -119, -119
	-119, -118, -118, -117, -117, -116, -116, -116, -115, -115, -114, -114, -113, -112, -112, -111
	-111, -110, -110, -109, -108, -108, -107, -106, -106, -105, -104, -104, -103, -102, -101, -101
	-100,  -99,  -98,  -97,  -96,  -96,  -95,  -94,  -93,  -92,  -91,  -90,  -89,  -88,  -87,  -85
	-84,   -83,  -82,  -81,  -79,  -78,  -77,  -75,  -74,  -72,  -71,  -69,  -68,  -66,  -64,  -62
	-61,   -59,  -57,  -54,  -52,  -50,  -47,  -45,  -42,  -39,  -35,  -31,  -27,  -21,  -13,    0
end table


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

public function Player_Draw_Normal
	MatrixRotateXYZ(MAT_WORLD, halfpipe[0].worldRotation.x, halfpipe[0].worldRotation.y, halfpipe[0].worldRotation.z)
	MatrixTranslateXYZ(MAT_TEMP, halfpipe[0].worldTranslation.x, halfpipe[0].worldTranslation.y, halfpipe[0].worldTranslation.z)
	MatrixMultiply(MAT_WORLD, MAT_TEMP)

	arrayPos0 = scene3D.vertexCount
	arrayPos1 = scene3D.faceCount

	// Draw Shadow
	faceBuffer[arrayPos1].flag = FACE_TEXTURED_C_BLEND
	faceBuffer[arrayPos1].a = arrayPos0
	vertexBuffer[arrayPos0].x = player.localShadowPos.x
	vertexBuffer[arrayPos0].y = player.localShadowPos.y
	vertexBuffer[arrayPos0].z = player.zpos
	vertexBuffer[arrayPos0].u = player.localShadowPos.u
	vertexBuffer[arrayPos0].v = 50
	arrayPos2 = arrayPos0
	arrayPos0++
	TransformVertices(MAT_WORLD, arrayPos2, arrayPos0)

	faceBuffer[arrayPos1].b = arrayPos0
	vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
	vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
	vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
	if player.zpos > 0x800
		vertexBuffer[arrayPos0].u = 0x600
		vertexBuffer[arrayPos0].v = 0x600
	else
		vertexBuffer[arrayPos0].u = 0x580
		vertexBuffer[arrayPos0].v = 0x580
	end if
	arrayPos0++

	faceBuffer[arrayPos1].c = arrayPos0
	vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
	vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
	vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
	vertexBuffer[arrayPos0].u = 17
	vertexBuffer[arrayPos0].v = 16
	arrayPos0++

	faceBuffer[arrayPos1].d = arrayPos0
	vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
	vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
	vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
	arrayPos0++

	arrayPos1++
	scene3D.vertexCount += 4
	scene3D.faceCount++

	// Draw the Player
	if player.visible == true
		arrayPos2 = arrayPos0
		arrayPos0++

		faceBuffer[arrayPos1].flag = FACE_SPRITE_3D
		faceBuffer[arrayPos1].a = arrayPos2
		vertexBuffer[arrayPos2].x = player.xpos
		vertexBuffer[arrayPos2].x >>= 1
		player.localPos.x = vertexBuffer[arrayPos2].x

		vertexBuffer[arrayPos2].y = player.ypos
		vertexBuffer[arrayPos2].y >>= 1
		player.localPos.y = vertexBuffer[arrayPos2].y

		vertexBuffer[arrayPos2].z = player.zpos
		TransformVertices(MAT_WORLD, arrayPos2, arrayPos0)

		vertexBuffer[arrayPos2].u = TypeName[Player Object]
		vertexBuffer[arrayPos2].v = FX_ROTATE
		faceBuffer[arrayPos1].b = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		vertexBuffer[arrayPos0].u = player.frame
		vertexBuffer[arrayPos0].v = 0
		arrayPos0++

		faceBuffer[arrayPos1].c = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		vertexBuffer[arrayPos0].u = 0x200
		vertexBuffer[arrayPos0].v = player.rotation
		arrayPos0++

		faceBuffer[arrayPos1].d = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		scene3D.vertexCount += 4
		scene3D.faceCount++

 		// If the character is Tails, then draw his Tails
		if player.frameOffset == 16
			if player.animation == 0
				arrayPos0++
				arrayPos1++
				faceBuffer[arrayPos1].flag = FACE_SPRITE_3D
				faceBuffer[arrayPos1].a = arrayPos0
				vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
				vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
				vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
				vertexBuffer[arrayPos0].u = TypeName[Player Object]
				vertexBuffer[arrayPos0].v = FX_ROTATE
				arrayPos0++

				faceBuffer[arrayPos1].b = arrayPos0
				vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
				vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
				vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
				vertexBuffer[arrayPos0].u = player.tailFrame
				vertexBuffer[arrayPos0].u >>= 2
				vertexBuffer[arrayPos0].u += 48
				vertexBuffer[arrayPos0].v = 0
				arrayPos0++

				faceBuffer[arrayPos1].c = arrayPos0
				vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
				vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
				vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
				vertexBuffer[arrayPos0].u = 0x200
				vertexBuffer[arrayPos0].v = player.rotation
				arrayPos0++

				faceBuffer[arrayPos1].d = arrayPos0
				vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
				vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
				vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
				scene3D.vertexCount += 4
				scene3D.faceCount++
			end if
		end if
	end if

	// If active, draw 1P/2P Tag
	if player.playerTagFrame > 0
		arrayPos0++
		arrayPos1++
		faceBuffer[arrayPos1].flag = FACE_SPRITE_3D
		faceBuffer[arrayPos1].a = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		vertexBuffer[arrayPos0].u = TypeName[Player Object]
		vertexBuffer[arrayPos0].v = FX_ROTATE
		arrayPos0++

		faceBuffer[arrayPos1].b = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		vertexBuffer[arrayPos0].u = player.playerTagFrame
		vertexBuffer[arrayPos0].v = 0
		arrayPos0++

		faceBuffer[arrayPos1].c = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		vertexBuffer[arrayPos0].u = 0x200
		vertexBuffer[arrayPos0].v = 0
		arrayPos0++

		faceBuffer[arrayPos1].d = arrayPos0
		vertexBuffer[arrayPos0].x = vertexBuffer[arrayPos2].x
		vertexBuffer[arrayPos0].y = vertexBuffer[arrayPos2].y
		vertexBuffer[arrayPos0].z = vertexBuffer[arrayPos2].z
		scene3D.vertexCount += 4
		scene3D.faceCount++
	end if
end function


public function Player_Input_Gamepad
	if options.attractMode == false
#platform: USE_STANDALONE
		if options.touchControls == true
			if player.controlMode == CONTROLMODE_P1
				CheckTouchRect(0, 96, screen.xcenter, screen.ysize)
				if checkResult > -1
					arrayPos0 = checkResult
					temp0 = touchscreen[arrayPos0].xpos
					temp0 -= saveRAM[39]
					if temp0 > 0
						keyDown[1].right = true
					else
						keyDown[1].left = true
					end if
				end if

				CheckTouchRect(screen.xcenter, 96, screen.xsize, 240)
				if checkResult > -1
					keyDown[1].buttonA = true
				end if

				if touchJump == false
					keyPress[1].buttonA |= keyDown[1].buttonA
				end if

				touchJump = keyDown[1].buttonA
				if stage.debugMode == true
					CheckTouchRect(0, 0, 112, 56)
					if checkResult > -1
						keyDown[1].buttonB = true
					end if

					if touchDebug == false
						keyPress[1].buttonB |= keyDown[1].buttonB
					end if
					
					touchDebug = keyDown[1].buttonB
				end if

				CheckTouchRect(240, 0, screen.xsize, 40)
				if checkResult > -1
					if options.vsMode == false
						PlaySfx(SfxName[Menu Back], false)
						engine.state = 5
					end if
				end if

				if keyPress[0].start == true
					if options.vsMode == false
						PlaySfx(SfxName[Menu Back], false)
						engine.state = 5
					end if
				end if
			end if
		end if
#endplatform

#platform: USE_ORIGINS
		if keyPress[0].start == true
			if options.vsMode == false
				// Those SFX should never even be playing in the Special Stage...
				StopSfx(SfxName[Flying])
				StopSfx(SfxName[Tired])
				engine.state = 5
			end if
		end if
#endplatform

		ProcessObjectControl()

		if options.vsMode == true
			player.currentLeader = Player_leadingPlayer
			CallNativeFunction2(SendEntity, 2, false)
		end if
	else
		// There aren't even any demos in the Special Stages, but may as well leave in the implementation for it anyways...
		
#platform: USE_STANDALONE
		CheckTouchRect(0, 0, screen.xsize, screen.ysize)
		if keyPress[0].start == true
			checkResult = false
		end if

		if checkResult > -1
			if Player_attractDuration > 1
				Player_attractDuration = 1
			end if
		end if
#endplatform

		if keyPress[0].start == true
			Player_attractDuration = 1
		end if
		
		Player_attractFrameCount--
		if Player_attractFrameCount < 1
			if Player_attractTablePos < Player_attractTableSize
				GetTableValue(temp0, Player_attractTablePos, Player_attractTable)
				GetBit(player.up, temp0, 0)
				GetBit(player.down, temp0, 1)
				GetBit(player.left, temp0, 2)
				GetBit(player.right, temp0, 3)
				GetBit(player.jumpPress, temp0, 4)
				GetBit(player.jumpHold, temp0, 5)
				Player_attractTablePos++

				GetTableValue(Player_attractFrameCount, Player_attractTablePos, Player_attractTable)
				Player_attractTablePos++
			end if
		else
			if player.jumpPress == true
				player.jumpPress = false
			end if
		end if

		if Player_attractDuration > 0
			Player_attractDuration--
			if Player_attractDuration < 1
				// This code is copied directly from S1 SS's version of the demo handler, including a fade out for ending the demo
				// However, this includes a mention of the Title Card object, which doesn't exist in Sonic 2 Special Stages...
				// (TypeName[Title Card] will resolve as 0, a Blank Object, for the reason above)
				
				arrayPos0 = Player_playerSlot
				object[arrayPos0].type = TypeName[Title Card]
				object[arrayPos0].state = TITLECARD_FADETOTITLE
				object[arrayPos0].priority = PRIORITY_ACTIVE
				object[arrayPos0].drawOrder = 6
			end if
		end if
	end if

	if player.invincibilityTimer > 0
		player.invincibilityTimer--
		GetBit(temp0, player.invincibilityTimer, 2)
		if temp0 == true
			player.visible = false
		else
			player.visible = true
		end if
	end if
end function


public function Player_Input_AI_Follow
	Player_stateUp <<= 1
	Player_stateUp |= player[2].up
	Player_stateUp &= 0xFFFF

	Player_stateDown <<= 1
	Player_stateDown |= player[2].down
	Player_stateDown &= 0xFFFF

	Player_stateLeft <<= 1
	Player_stateLeft |= player[2].left
	Player_stateLeft &= 0xFFFF

	Player_stateRight <<= 1
	Player_stateRight |= player[2].right
	Player_stateRight &= 0xFFFF

	Player_stateJumpPress <<= 1
	Player_stateJumpPress |= player[2].jumpPress
	Player_stateJumpPress &= 0xFFFF

	Player_stateJumpHold <<= 1
	Player_stateJumpHold |= player[2].jumpHold
	Player_stateJumpHold &= 0xFFFF

	temp0 = Player_stateUp
	temp0 >>= 15
	player.up = temp0

	temp0 = Player_stateDown
	temp0 >>= 15
	player.down = temp0

	temp0 = Player_stateLeft
	temp0 >>= 15
	player.left = temp0

	temp0 = Player_stateRight
	temp0 >>= 15
	player.right = temp0

	temp0 = Player_stateJumpPress
	temp0 >>= 15
	player.jumpPress = temp0

	temp0 = Player_stateJumpHold
	temp0 >>= 15
	player.jumpHold = temp0

	if player.invincibilityTimer > 0
		player.invincibilityTimer--
		GetBit(temp0, player.invincibilityTimer, 2)
		if temp0 == true
			player.visible = false
		else
			player.visible = true
		end if
	end if
	
#platform: USE_ORIGINS
	temp0 = keyDown[2].left
	temp0 |= keyDown[2].right
	temp0 |= keyDown[2].buttonA
	temp0 |= keyDown[2].buttonB
	temp0 |= keyDown[2].buttonC
	if temp0 == true
		player.jumpInTimer = 0
		player.stateInput = Player_Input_Gamepad_2P
	end if
#endplatform
end function


public function Player_Input_Gamepad_2P
#platform: USE_ORIGINS
	ProcessObjectControl()
	if player.invincibilityTimer > 0
		player.invincibilityTimer--
		GetBit(temp0, player.invincibilityTimer, 2)
		if temp0 == true
			player.visible = false
		else
			player.visible = true
		end if
	end if
	
	temp0 = player.left
	temp0 |= player.right
	temp0 |= player.jumpHold
	if temp0 == false
		player.jumpInTimer++
		if player.jumpInTimer >= 600
			player.stateInput = Player_Input_AI_Follow
			AssignInputSlotToDevice(2, -1)
		end if
	else
		player.jumpInTimer = 0
	end if
#endplatform
end function


public function Player_Input_VSPlayer_2P
#platform: USE_STANDALONE
	temp0 = player.playerTagFrame
	CallNativeFunction2(ReceiveEntity, 3, true)
	player.type = TypeName[Player Object]
	player.stateInput = Player_Input_VSPlayer_2P
	player.playerTagFrame = temp0
#endplatform

#platform: USE_ORIGINS
	ProcessObjectControl()
#endplatform

	if vs.playerID == 1
		Player_leadingPlayer = player.currentLeader
	end if

	if player.invincibilityTimer > 0
		player.invincibilityTimer--
		GetBit(temp0, player.invincibilityTimer, 2)
		if temp0 == true
			player.visible = false
		else
			player.visible = true
		end if
	end if
end function


public function Player_State_Ground
	if player.right == true
		player.speed -= 48
		if player.speed < -0x600
			player.speed = -0x600
		end if

		player.automoveActive = false
		player.timer = 0
	else
		if player.left == true
			player.speed += 48
			if player.speed > 0x600
				player.speed = 0x600
			end if

			player.automoveActive = false
			player.timer = 0
		else
			if player.automoveActive == false
				player.timer = 60
				player.automoveActive = true
			end if

			temp0 = player.speed
			temp0 >>= 4
			player.speed -= temp0
		end if
	end if

	temp0 = player.speed
	if player.rotation != 0
		temp0 /= 256
		temp0 *= 128
	else
		temp0 >>= 1
	end if

	player.angle += temp0
	player.angle &= 0xFFFF
	player.rotation = player.angle
	player.rotation >>= 7

	// Wait for it....
	if player.timer > 0
		player.timer--
	else
		// Move to center
		temp1 = player.rotation
		temp1 >>= 1
		Sin256(temp0, temp1)
		temp0 *= -80
		temp0 >>= 9
		player.speed += temp0
	end if

	Sin(player.xpos, player.rotation)
	player.xpos *= player.xBoundsL
	player.xpos >>= 9

	Cos(player.ypos, player.rotation)
	player.ypos *= player.xBoundsL
	player.ypos >>= 9

	player.animation = 0
	player.animationSpeed = 60
	temp0 = player.rotation
	player.changedLeader = false

	if player.jumpPress == true
		player.speed = 0
		Sin(player.xvel, player.rotation)
		player.xvel *= 0x5A0
		player.xvel >>= 9
		Cos(player.yvel, player.rotation)
		player.yvel *= 0x980
		player.yvel >>= 9
		player.state = Player_State_Air
		player.rotation = 0
		player.frame = 8
		player.frame += player.frameOffset
		player.animation = 8
		player.animationSpeed = 60
		player.animationTimer = 0
		player.gravity = GRAVITY_AIR
		PlaySfx(SfxName[Jump], false)
	else
		if player.rotation > 152
			if player.rotation < 360
				if player.speed < 128
					if player.speed > -128
						player.xvel = player.speed
						player.speed = 0
						player.state = Player_State_Air
						player.yvel = 0
					end if
				end if
			end if
		end if
	end if
	if temp0 > 128
		if temp0 < 384
			temp0 += 256
			FlipSign(temp0)
			temp0 &= 511
		end if
	end if

	Sin(player.localShadowPos.x, temp0)
	player.localShadowPos.x *= -52
	player.localShadowPos.x >>= 1

	Cos(player.localShadowPos.y, temp0)
	player.localShadowPos.y *= -52
	player.localShadowPos.y >>= 1

	player.localShadowPos.u = 116

	if temp0 < 256
		if temp0 > 112
			player.localShadowPos.u = 224
		else
			if temp0 > 39
				player.localShadowPos.u = 149
			end if
		end if
	else
		if temp0 < 400
			player.localShadowPos.u = 215
		else
			if temp0 < 473
				player.localShadowPos.u = 182
			end if
		end if
	end if

	if player.rotation < 40
		player.rotation = 0
	end if

	if player.rotation > 472
		player.rotation = 0
	end if
end function


public function Player_State_Air
	if options.vsMode == true
		if player.changedLeader == false
			Player_leadingPlayer ^= 1
			player.changedLeader = true
		end if
	end if

	player.xpos += player.xvel
	if player.xpos <= player.xBoundsL
		player.xpos = player.xBoundsL
	end if

	if player.xpos >= player.xBoundsR
		player.xpos = player.xBoundsR
	end if

	if player.left == true
		player.xvel -= 32
	end if

	if player.right == true
		player.xvel += 32
	end if

	player.ypos += player.yvel
	player.yvel -= 88
	if player.ypos < 0
		temp0 = player.xpos
		temp0 *= player.xpos
		temp1 = player.ypos
		temp1 *= player.ypos
		temp0 += temp1
		if temp0 > player.groundPos
			player.state = Player_State_Ground
			player.gravity = GRAVITY_GROUND
			ATan2(player.angle, player.ypos, player.xpos)
			player.angle += 128
			player.angle <<= 8
		end if
	end if

	if player.rotation < 0x100
		if player.rotation > 0
			player.rotation -= 8
		else
			player.rotation = 0
		end if
	else
		if player.rotation < 0x200
			player.rotation += 8
		else
			player.rotation = 0
		end if
	end if

	player.localShadowPos.x = player.xpos
	player.localShadowPos.x *= 144
	player.localShadowPos.x >>= 8

	temp0 = player.xpos
	temp0 *= 45
	temp0 >>= 13
	Abs(temp0)
	GetTableValue(player.localShadowPos.y, temp0, Player_shadowYPosTable)
	temp0 = player.localShadowPos.y
	player.localShadowPos.y *= 105

	player.localShadowPos.u = 116

	// Shadow frame funky stuff
	if temp0 > -112
		if temp0 > -24
			if player.xpos < 0
				player.localShadowPos.u = 224
			else
				player.localShadowPos.u = 215
			end if
		else
			if player.xpos < 0
				player.localShadowPos.u = 149
			else
				player.localShadowPos.u = 182
			end if
		end if
	end if
end function


public function Player_State_Hurt
	if options.vsMode == true
		if vs.playerID == 0
			if player.changedLeader == false
				Player_leadingPlayer = player.entityPos
				Player_leadingPlayer -= 2
				Player_leadingPlayer ^= vs.playerID
				Player_leadingPlayer ^= 1
				player.changedLeader = true
			end if
		end if
	end if
	
	if player.spinTimer < 512
		player.spinTimer += 16
		player.rotation += 16
	else
		player.speed = 0
		player.spinTimer = 0
		player.invincibilityTimer = 60
		if player.gravity == 0
			player.state = Player_State_Ground
		else
			player.state = Player_State_Air
		end if
	end if
end function


public function Player_LoseRings
	if player[currentPlayer].rings > 0
		PlaySfx(SfxName[Lose Rings], false)
		player[currentPlayer].rings -= 10

		temp0 = 10
		if player[currentPlayer].rings < 0
			temp0 += player[currentPlayer].rings
			player[currentPlayer].rings = 0
		end if

		temp2 = temp0
		temp2 >>= 1
		temp2 <<= 5
		temp1 = 384
		temp1 -= temp2
		temp2 = player.angle
		temp2 >>= 7
		temp1 += temp2
		temp3 = player[currentPlayer].xpos
		temp3 *= 224
		temp4 = player[currentPlayer].ypos
		temp4 *= -220
		temp5 = player[2].ypos
		temp5 *= -48
		temp4 += 0x600000
		temp4 += temp5
		while temp0 > 0
			CreateTempObject(TypeName[Lose Ring], 0, temp3, temp4)
			Cos(object[tempObjectPos].xvel, temp1)
			Sin(object[tempObjectPos].yvel, temp1)
			object[tempObjectPos].xvel <<= 9
			object[tempObjectPos].yvel <<= 10
			object[tempObjectPos].scale = 0x140
			Rand(loseRing[tempObjectPos].gravityStrength, 0x2000)
			loseRing[tempObjectPos].gravityStrength += 0x7000
			Rand(object[tempObjectPos].animationSpeed, 128)
			object[tempObjectPos].animationSpeed += 64
			temp0--
			temp1 += 32
		loop
	end if
end function


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

event ObjectUpdate
	CallFunction(player.stateInput)
	CallFunction(player.state)

	if options.vsMode == true
		temp0 = player.entityPos
		temp0 -= 2
		temp0 ^= vs.playerID
		temp0 ^= Player_leadingPlayer
		if temp0 == 0
			if player.zpos < 0x1000
				player.zpos += 64
			end if
		else
			if player.zpos > 0x400
				player.zpos -= 64
			end if
		end if
	end if

	player.localPos.z = player.zpos
	player.localPos.z <<= 8
	player.localPos.z += halfpipe[0].playerPos.z
	player.localPos.z += 0xC0000

	player.animationTimer += player.animationSpeed
	if player.animationTimer >= 240
		player.animationTimer -= 240
		player.frame++
		player.frame &= 7
		player.frame += player.animation
		player.frame += player.frameOffset
	end if

	CallFunction(Player_Draw_Normal)
	player.tailFrame++
	player.tailFrame %= 28
end event


event ObjectStartup
	LoadSpriteSheet("Special/Objects.gif")

	// Running - Sonic
	SpriteFrame(-16, -28, 32, 56, 1, 68)
	SpriteFrame(-16, -28, 32, 56, 34, 68)
	SpriteFrame(-16, -28, 32, 56, 67, 68)
	SpriteFrame(-16, -28, 32, 56, 100, 68)
	SpriteFrame(-16, -28, 32, 56, 1, 125)
	SpriteFrame(-16, -28, 32, 56, 34, 125)
	SpriteFrame(-16, -28, 32, 56, 67, 125)
	SpriteFrame(-16, -28, 32, 56, 100, 125)

	// Jumping - Sonic
	SpriteFrame(-16, -20, 32, 40, 133, 67)
	SpriteFrame(-16, -20, 32, 40, 166, 67)
	SpriteFrame(-16, -20, 32, 40, 133, 67)
	SpriteFrame(-16, -20, 32, 40, 166, 67)
	SpriteFrame(-16, -20, 32, 40, 133, 67)
	SpriteFrame(-16, -20, 32, 40, 166, 67)
	SpriteFrame(-16, -20, 32, 40, 133, 67)
	SpriteFrame(-16, -20, 32, 40, 166, 67)

	// Running - Tails
	SpriteFrame(-12, -24, 24, 48, 199, 67)
	SpriteFrame(-16, -24, 32, 48, 224, 67)
	SpriteFrame(-16, -24, 32, 48, 257, 67)
	SpriteFrame(-16, -24, 32, 48, 290, 67)
	SpriteFrame(-12, -24, 24, 48, 199, 116)
	SpriteFrame(-16, -24, 32, 48, 224, 116)
	SpriteFrame(-16, -24, 32, 48, 257, 116)
	SpriteFrame(-16, -24, 32, 48, 290, 116)

	// Jumping - Tails
	SpriteFrame(-16, -16, 32, 32, 133, 108)
	SpriteFrame(-16, -16, 32, 32, 166, 108)
	SpriteFrame(-16, -16, 32, 32, 133, 108)
	SpriteFrame(-16, -16, 32, 32, 166, 108)
	SpriteFrame(-16, -16, 32, 32, 133, 108)
	SpriteFrame(-16, -16, 32, 32, 166, 108)
	SpriteFrame(-16, -16, 32, 32, 133, 108)
	SpriteFrame(-16, -16, 32, 32, 166, 108)

	// Running - Knux
	SpriteFrame(-16, -28, 32, 56, 1, 182)
	SpriteFrame(-16, -28, 32, 56, 34, 182)
	SpriteFrame(-16, -28, 32, 56, 67, 182)
	SpriteFrame(-16, -28, 32, 56, 100, 182)
	SpriteFrame(-16, -28, 32, 56, 133, 182)
	SpriteFrame(-16, -28, 32, 56, 166, 182)
	SpriteFrame(-16, -28, 32, 56, 199, 182)
	SpriteFrame(-16, -28, 32, 56, 232, 182)

	// Jumping - Knux
	SpriteFrame(-16, -20, 32, 40, 133, 141)
	SpriteFrame(-16, -20, 32, 40, 166, 141)
	SpriteFrame(-16, -20, 32, 40, 133, 141)
	SpriteFrame(-16, -20, 32, 40, 166, 141)
	SpriteFrame(-16, -20, 32, 40, 133, 141)
	SpriteFrame(-16, -20, 32, 40, 166, 141)
	SpriteFrame(-16, -20, 32, 40, 133, 141)
	SpriteFrame(-16, -20, 32, 40, 166, 141)

	// Tails's Tails
	SpriteFrame(-6, -6, 16, 24, 323, 58)
	SpriteFrame(-8, 0, 24, 24, 340, 58)
	SpriteFrame(-8, 8, 24, 16, 365, 58)
	SpriteFrame(-9, -1, 16, 24, 390, 58)
	SpriteFrame(-11, -9, 16, 32, 407, 58)
	SpriteFrame(-16, -9, 24, 24, 424, 58)
	SpriteFrame(-16, -9, 24, 24, 323, 83)
	
	// 1P/2P tags, with flipped versions for Mirror Mode
	// See playerTagFrame
#platform: USE_ORIGINS
	if game.playMode == BOOT_PLAYMODE_MIRRORING
		SpriteFrame(-8, -40, 16, 7, 348, 145)
		SpriteFrame(-8, -40, 16, 7, 348, 153)
	else
#endplatform
		SpriteFrame(-8, -40, 16, 7, 348, 100)
		SpriteFrame(-8, -40, 16, 7, 348, 108)
#platform: USE_ORIGINS
	end if
#endplatform

	player[2].type = TypeName[Player Object]
	player[2].priority = PRIORITY_ACTIVE
	player[2].zpos = 0x1000
	player[2].state = Player_State_Ground
	player[2].stateInput = Player_Input_Gamepad
	switch stage.playerListPos
	case PLAYER_SONIC_ALONE
		player[2].xBoundsR = 88
		player[2].frameOffset = 0
		break
		
	case PLAYER_TAILS_ALONE
		player[2].xBoundsR = 90
		player[2].frameOffset = 16
		break
		
	case PLAYER_KNUX_ALONE
		player[2].xBoundsR = 88
		player[2].frameOffset = 32
		break
		
	end switch

	player[2].xBoundsR <<= 8
	player[2].xBoundsL = player[2].xBoundsR
	FlipSign(player[2].xBoundsL)
	player[2].groundPos = player[2].xBoundsR
	player[2].groundPos *= player[2].xBoundsR
	player[2].groundPos -= 0x10000
	player[2].frame = player[2].frameOffset

	if options.vsMode == true
		player[3].type = TypeName[Player Object]
		player[3].priority = PRIORITY_ACTIVE
		player[3].playerTagFrame = vs.playerID
		player[3].playerTagFrame ^= 1
		player[3].playerTagFrame += 55
		if vs.playerID == 0
			player[2].zpos = 0x1000
			player[3].zpos = 0x400
		else
			player[2].zpos = 0x400
			player[3].zpos = 0x1000
		end if

		player[3].state = Player_State_Ground
#platform: USE_ORIGINS
		player[3].controlMode = CONTROLMODE_P2
#endplatform
		player[3].stateInput = Player_Input_VSPlayer_2P
		switch vs.player2Type
		case 0
			player[3].xBoundsR = 88
			player[3].frameOffset = 0
			break
			
		case 1
			player[3].xBoundsR = 90
			player[3].frameOffset = 16
			break
			
		case 2
			player[3].xBoundsR = 88
			player[3].frameOffset = 32
			break
			
		end switch
		
		player[3].xBoundsR <<= 8
		player[3].xBoundsL = player[3].xBoundsR
		FlipSign(player[3].xBoundsL)
		player[3].groundPos = player[3].xBoundsR
		player[3].groundPos *= player[3].xBoundsR
		player[3].groundPos -= 0x10000
		player[3].frame = player[3].frameOffset

		stage.player2Enabled = false
		playerCount = 2
		CallNativeFunction2(ReceiveEntity, 0x400, true)
		ResetObjectEntity(0x400, TypeName[Blank Object], 0, 0, 0)
	end if

	if stage.player2Enabled == true
		player[3].type = TypeName[Player Object]
		player[3].priority = PRIORITY_ACTIVE
		player[3].zpos = 0x400
		player[3].state = Player_State_Ground
#platform: USE_ORIGINS
		object[3].controlMode = CONTROLMODE_P2
#endplatform
		player[3].stateInput = Player_Input_AI_Follow
		player[3].xBoundsR = 90
		player[3].frameOffset = 16
		player[3].xBoundsR <<= 8
		player[3].xBoundsL = player[3].xBoundsR
		FlipSign(player[3].xBoundsL)
		player[3].groundPos = player[3].xBoundsR
		player[3].groundPos *= player[3].xBoundsR
		player[3].groundPos -= 0x10000
		player[3].frame = player[3].frameOffset
#platform: USE_ORIGINS
		AssignInputSlotToDevice(2, -1)
#endplatform
	end if
end event


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

event RSDKDraw
	DrawSprite(0)
end event


event RSDKLoad
	LoadSpriteSheet("Global/Display.gif")
	SpriteFrame(-16, -16, 32, 32, 1, 143)
	
	SetVariableAlias(ALIAS_VAR_PROPVAL, "unused")
end event
