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

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

public alias 0x100 		: GROUP_PLAYERS
public alias 0x101 		: GROUP_BLOCKS
public alias arrayPos6 	: currentPlayer
public alias arrayPos7 	: playerCount

// Gravity
public alias 0 : GRAVITY_GROUND
public alias 1 : GRAVITY_AIR

// Priority
public alias 0 : PRIORITY_BOUNDS
public alias 1 : PRIORITY_ACTIVE
public alias 2 : PRIORITY_ALWAYS
public alias 3 : PRIORITY_XBOUNDS
public alias 4 : PRIORITY_XBOUNDS_DESTROY
public alias 5 : PRIORITY_INACTIVE
public alias 6 : PRIORITY_BOUNDS_SMALL
public alias 7 : PRIORITY_ACTIVE_SMALL

// Control Modes
public alias -1 : CONTROLMODE_NONE
public alias  0 : CONTROLMODE_P1
public alias  1 : CONTROLMODE_P2
public alias  2 : CONTROLMODE_P3
public alias  3 : CONTROLMODE_P4

// Camera Styles
public alias 0 : CAMERASTYLE_FOLLOW
public alias 1 : CAMERASTYLE_EXTENDED
public alias 2 : CAMERASTYLE_EXTENDED_OFFSET_L
public alias 3 : CAMERASTYLE_EXTENDED_OFFSET_R
public alias 4 : CAMERASTYLE_HLOCKED

// Ink Effects
public alias 0 : INK_NONE
public alias 1 : INK_BLEND
public alias 2 : INK_ALPHA
public alias 3 : INK_ADD
public alias 4 : INK_SUB

// Flip Directions
public alias 0 : FLIP_NONE
public alias 1 : FLIP_X
public alias 2 : FLIP_Y
public alias 3 : FLIP_XY

// Collision Sides
public alias 0 : CSIDE_FLOOR
public alias 1 : CSIDE_LWALL
public alias 2 : CSIDE_RWALL
public alias 3 : CSIDE_ROOF

// Collision Modes
public alias 0 : CMODE_FLOOR
public alias 1 : CMODE_LWALL
public alias 2 : CMODE_ROOF
public alias 3 : CMODE_RWALL

// Collision Directions
public alias 0 : COL_NONE
public alias 1 : COL_TOP
public alias 2 : COL_LEFT
public alias 3 : COL_RIGHT
public alias 4 : COL_BOTTOM

// Variables
private alias object.type			 : player.type
private alias object.groupID		 : player.groupID 			// Normally GROUP_PLAYERS, unless in Debug Mode
private alias object.entityPos		 : player.entityPos 		// Where the player is on the object list - P1 should be 0, P2 should be 1
private alias object.state			 : player.state
private alias object.priority		 : player.priority
private alias object.xpos			 : player.xpos				// In total world-space position (0x10000 == 1.0)
private alias object.ypos			 : player.ypos
private alias object.ixpos			 : player.ixpos				// In screen space position (1 == 1)
private alias object.iypos			 : player.iypos
private alias object.xvel			 : player.xvel				// Based on world-space
private alias object.yvel			 : player.yvel
private alias object.speed			 : player.speed				// Also based on world-space
private alias object.rotation		 : player.rotation
private alias object.direction		 : player.direction
private alias object.gravity		 : player.gravity
private alias object.frame			 : player.frame
private alias object.animation		 : player.animation
private alias object.animationSpeed	 : player.animationSpeed
private alias object.drawOrder		 : player.drawOrder
private alias object.controlMode	 : player.controlMode		// See control mode aliases
private alias object.interaction	 : player.interaction		// Will the object interact with other objects?
private alias object.tileCollisions	 : player.tileCollisions

// *Object-wise* input, not to be confused with keyPress and keyDown
private alias object.jumpPress 	: player.jumpPress
private alias object.jumpHold 	: player.jumpHold
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.value0  : player.rings
private alias object.value1  : player.timer
private alias object.value2  : player.moveSpeed
private alias object.value3  : player.acceleration
private alias object.value5  : player.rollSpeed
// value6 is unused
private alias object.value7  : player.jumpStrength
private alias object.value8  : player.moveVelocity.x
private alias object.value9  : player.moveVelocity.y
private alias object.value10 : debugMode.currentSelection
private alias object.value11 : player.collisionFlags
private alias object.value12 : player.worldVelocity.x
private alias object.value13 : player.worldVelocity.y
private alias object.value14 : player.rotateBlockTimer
private alias object.value15 : player.upDownBlockTimer

private alias 0 : SLOT_PLAYER1

// Player List Pos Aliases
public alias 0 : PLAYER_SONIC_ALONE
public alias 1 : PLAYER_TAILS_ALONE
public alias 2 : PLAYER_KNUX_ALONE
public alias 3 : PLAYER_SONIC_AND_TAILS

// TitleCard aliases
private alias 8 : TITLECARD_FADETOTITLE


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

reserve function Player_ProcessUpdate
reserve function Player_State_Static
reserve function Player_HandleRollAnimSpeed
reserve function Player_State_SS_Roll
reserve function Player_SetupAttractDemo
reserve function Player_HandleGroundCollision
reserve function Player_HandleAirCollision


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

public value Player_stageRotation 		= 0
public value Player_rotateSpeed 		= 0
public value Player_rotateDir 			= 0
public value Player_isWinner 			= 0
public value Player_attractTable 		= 0
public value Player_attractTablePos 	= 0
public value Player_attractTableSize	= 0
public value Player_attractFrameCount 	= 0
public value Player_attractDuration 	= 0
public value Player_playerSlot 			= 0

// All these below are unused
private value Player_unusedValue1 = 0
private value Player_unusedValue2 = 0
private value Player_unusedValue3 = 0
private value Player_unusedValue4 = 0
private value Player_unusedValue5 = 0
private value Player_unusedValue6 = 0
private value Player_unusedValue7 = 0


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

public function Player_SetupAttractDemo
	Player_attractTablePos = 2
	Player_attractFrameCount = 1
	
	currentPlayer = 0
	while currentPlayer < playerCount
		GetTableValue(player[currentPlayer].xpos, 0, Player_attractTable)
		GetTableValue(player[currentPlayer].ypos, 1, Player_attractTable)
		player[currentPlayer].controlMode 	= CONTROLMODE_NONE
		player[currentPlayer].up 			= false
		player[currentPlayer].down 			= false
		player[currentPlayer].left 			= false
		player[currentPlayer].right			= false
		player[currentPlayer].jumpPress 	= false
		player[currentPlayer].jumpHold 		= false
		player[currentPlayer].timer 		= 0
		currentPlayer++
	loop
	
	// 0 = SLOT_PLAYER1
	camera[0].xpos = player[0].ixpos
	camera[0].ypos = player[0].iypos
end function


public function Player_ProcessUpdate
	if options.attractMode == false
#platform: USE_STANDALONE
		if options.touchControls == true
			if player.controlMode == CONTROLMODE_P1
				// Update virtual stick inputs
				CheckTouchRect(0, 96, screen.xcenter, screen.ysize)
				if checkResult > -1
					arrayPos0 = checkResult
					
					temp0 = touchscreen[arrayPos0].xpos
					temp0 -= saveRAM[39]
					
					temp1 = touchscreen[arrayPos0].ypos
					temp1 -= saveRAM[40]
					
					ATan2(temp2, temp0, temp1)
					
					temp2 += 32
					temp2 &= 255
					temp2 >>= 6
					
					switch temp2
					case 0
						keyDown[1].right = true
						break

					case 1
						keyDown[1].down = true
						break

					case 2
						keyDown[1].left = true
						break

					case 3
						keyDown[1].up = true
						break
					end switch
				end if

				// Check if the jump button is touched
				CheckTouchRect(screen.xcenter, 96, screen.xsize, 240)
				if checkResult > -1
					keyDown[1].buttonA = true
				end if

				if touchJump == false
					// If the jump button only started being touched this frame, then update the press variable accordingly
					keyPress[1].buttonA |= keyDown[1].buttonA
				end if
				
				// And now store the current held state for next frame
				touchJump = keyDown[1].buttonA

				if stage.debugMode == true
					// And now to the same process for the B button (debug mode buttom)
					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
					PlaySfx(SfxName[Menu Back], false)
					engine.state = 5
				end if

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

#platform: USE_ORIGINS
		if object.controlMode == CONTROLMODE_P1
			if keyPress[0].start == true
				// These are left over from the standalone versions, as evident by the fact you can't fly in special stages
				StopSfx(SfxName[Flying])
				StopSfx(SfxName[Jump])
				
				engine.state = 5
			end if
		end if
#endplatform
		
		ProcessObjectControl()
	else
#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
				arrayPos0 = Player_playerSlot
				player[arrayPos0].type = TypeName[Title Card]
				player[arrayPos0].state = TITLECARD_FADETOTITLE
				player[arrayPos0].priority = PRIORITY_ACTIVE
				player[arrayPos0].drawOrder = 6
			end if
		end if
	end if
end function

public function Player_State_Static
	checkResult = false
end function


public function Player_HandleRollAnimSpeed
	if stage.playerListPos == PLAYER_TAILS_ALONE
		player.animationSpeed = 120
	else
		player.animationSpeed = player.moveSpeed
		Abs(player.animationSpeed)
		player.animationSpeed *= 240
		player.animationSpeed /= 0x60000
		player.animationSpeed += 48
	end if
end function


public function Player_HandleGroundCollision
	if player.worldVelocity.x > 0
		GetBit(temp0, player.collisionFlags, 2)
	else
		GetBit(temp0, player.collisionFlags, 3)
	end if

	if temp0 != 0
		player.worldVelocity.x = 0
		player.gravity = GRAVITY_GROUND
	end if

	if player.worldVelocity.y > 0
		GetBit(temp0, player.collisionFlags, 1)
	else
		GetBit(temp0, player.collisionFlags, 4)
	end if

	if temp0 != 0
		player.worldVelocity.y = 0
		player.gravity = GRAVITY_GROUND
	end if
end function


public function Player_HandleAirCollision
	// Get the nearest rotation quarter
	temp0 = Player_stageRotation
	temp0 >>= 1
	temp0 -= 0xE0
	temp0 &= 0xC0
	switch temp0
	case 0x00
		if player.moveSpeed > 0
			GetBit(temp0, player.collisionFlags, 2)
		else
			GetBit(temp0, player.collisionFlags, 3)
		end if
		break

	case 0x40
		if player.moveSpeed > 0
			GetBit(temp0, player.collisionFlags, 1)
		else
			GetBit(temp0, player.collisionFlags, 4)
		end if
		break

	case 0x80
		if player.moveSpeed > 0
			GetBit(temp0, player.collisionFlags, 3)
		else
			GetBit(temp0, player.collisionFlags, 2)
		end if
		break

	case 0xC0
		if player.moveSpeed > 0
			GetBit(temp0, player.collisionFlags, 4)
		else
			GetBit(temp0, player.collisionFlags, 1)
		end if
		break
		
	end switch

	if temp0 == true
		player.moveSpeed = 0
	end if

	player.collisionFlags = 0
end function


public function Player_State_SS_Roll
	CallFunction(Player_HandleGroundCollision)
	if player.gravity == GRAVITY_AIR
		if player.rotateBlockTimer != 0
			player.rotateBlockTimer--
		end if

		if player.upDownBlockTimer != 0
			player.upDownBlockTimer--
		end if
	end if

	temp2 = Player_stageRotation
	temp2 &= 504
	if player.gravity == GRAVITY_GROUND
		if player.jumpPress == true
			checkResult = true
		else
			checkResult = false
		end if
	else
		checkResult = false
	end if

	if checkResult == true
		Sin(temp0, temp2)
		temp0 *= player.jumpStrength
		temp0 >>= 9

		Cos(temp1, temp2)
		temp1 *= player.jumpStrength
		temp1 >>= 9

		player.worldVelocity.x = 0
		player.worldVelocity.x += temp0
		player.worldVelocity.y = 0
		player.worldVelocity.y -= temp1
		PlaySfx(SfxName[Jump], false)
	else
		Sin(temp0, temp2)
		temp0 *= player.rollSpeed
		temp0 >>= 9

		Cos(temp1, temp2)
		temp1 *= player.rollSpeed
		temp1 >>= 9

		player.worldVelocity.x -= temp0
		player.worldVelocity.y += temp1
	end if

	CallFunction(Player_HandleAirCollision)

	temp0 = 0
	if player.left == true
		player.moveSpeed -= player.acceleration
		if player.moveSpeed < -0x80000
			player.moveSpeed = -0x80000
		end if

		player.direction = FACING_LEFT
		temp0 = true
	end if

	if player.right == true
		player.moveSpeed += player.acceleration
		if player.moveSpeed > 0x80000
			player.moveSpeed = 0x80000
		end if

		player.direction = FACING_RIGHT
		temp0 = true
	end if

	if temp0 == false
		if player.moveSpeed != 0
			if player.moveSpeed > 0
				player.moveSpeed -= player.acceleration
				if player.moveSpeed < 0
					player.moveSpeed = 0
				end if
			else
				player.moveSpeed += player.acceleration
				if player.moveSpeed > 0
					player.moveSpeed = 0
				end if
			end if
		end if
	end if

	temp0 = Player_stageRotation
	temp0 += 0x40
	temp0 &= 0x180

	Cos(player.moveVelocity.x, temp0)
	player.moveVelocity.x *= player.moveSpeed
	player.moveVelocity.x >>= 9

	Sin(player.moveVelocity.y, temp0)
	player.moveVelocity.y *= player.moveSpeed
	player.moveVelocity.y >>= 9

	player.xvel = player.worldVelocity.x
	player.xvel += player.moveVelocity.x
	player.yvel = player.worldVelocity.y
	player.yvel += player.moveVelocity.y
	CallFunction(Player_HandleRollAnimSpeed)
end function


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

event ObjectUpdate
	if stage.debugMode == true
		CallFunction(Player_ProcessUpdate)
		if keyPress[0].buttonB == true
			player.type = TypeName[Debug Mode]
			
			if stage.playerListPos == PLAYER_TAILS_ALONE
				// Clear the following Tails Object if needed too
				object[+1].type = TypeName[Blank Object]
			end if
			
			player.moveSpeed = 0
			player.rotation = 0
			player.frame = 0
			Player_stageRotation = 0
			Player_rotateSpeed = 2
			player.frame = debugMode.currentSelection
			player.interaction = false
		else
			CallFunction(player.state)
			ProcessAnimation()

			if player.state != Player_State_Static
				ProcessObjectMovement()
			end if

			player.gravity = GRAVITY_AIR
		end if
	else
		CallFunction(Player_ProcessUpdate)
		CallFunction(player.state)
		ProcessAnimation()

		if player.state != Player_State_Static
			ProcessObjectMovement()
		end if

		player.gravity = GRAVITY_AIR
	end if
end event


event ObjectDraw
	DrawObjectAnimation()
end event


event ObjectStartup
	LoadSpriteSheet("Global/Display.gif") // We don't even use any Sprite Frames, but go ahead and load the sheet anyways..?
	
	foreach (TypeName[Player Object], arrayPos0, ALL_ENTITIES)
		if stage.playerListPos >= PLAYER_SONIC_AND_TAILS
			stage.playerListPos = PLAYER_SONIC_ALONE
			stage.player2Enabled = true
		end if

		// 0 = SLOT_PLAYER1
		ResetObjectEntity(SLOT_PLAYER1, TypeName[Player Object], 0, player[arrayPos0].xpos, player[arrayPos0].ypos)
		camera[0].xpos = player[0].ixpos
		camera[0].ypos = player[0].iypos
		player[0].groupID = GROUP_PLAYERS
		player[0].xvel = 0
		player[0].yvel = 0
		player[0].speed = 0
		Player_playerSlot = player.entityPos

		switch stage.playerListPos
		case PLAYER_SONIC_ALONE
			LoadAnimation("SonicSS.ani")
			player[0].animationSpeed = 48
			break
			
		case PLAYER_TAILS_ALONE
			LoadAnimation("TailsSS.ani")
			player[0].animationSpeed = 120
			break
			
		case PLAYER_KNUX_ALONE
			LoadAnimation("KnucklesSS.ani")
			player[0].animationSpeed = 48
			break
		end switch
		
		// Note - player.animation never needs to be set since the desired animation is animation ID 0 in the file

		player[0].state = Player_State_SS_Roll
		player[0].priority = PRIORITY_ACTIVE
		player[0].drawOrder = 4
		player[0].acceleration = 0xC00
		player[0].jumpStrength = 0x68000
		player[0].rollSpeed = 0x2A00
		ResetObjectEntity(arrayPos0, TypeName[Blank Object], 0, 0, 0)
		player[0].tileCollisions = false
		player[0].gravity = GRAVITY_AIR
		Player_stageRotation = 0
		Player_rotateSpeed = 0
		Player_rotateDir = 0
		Player_isWinner = false
	next
	
	options.touchControls = true
end event


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

event RSDKDraw
	DrawSprite(0)
end event


event RSDKLoad
	LoadSpriteSheet("Players/Sonic1.gif")
	SpriteFrame(-16, -16, 32, 32, 42, 118)
	
	SetVariableAlias(ALIAS_VAR_PROPVAL, "unused")
end event
