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

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

private alias object.value0  : object.startPos.x
private alias object.value1  : object.startPos.y
private alias object.value2  : object.turnTimer
private alias object.value3  : object.timer
private alias object.value4  : object.drawingIndex
private alias object.value6  : object.clampFrame
private alias object.value7  : object.wheelFrame
private alias object.value8  : object.targetPlayer
private alias object.value9  : object.struggleDelay
private alias object.value10 : object.struggleTimer
private alias object.value11 : object.struggleFlags
private alias object.value12 : object.explodeTimer

// States
private alias 0 : GRABBER_MOVING
private alias 1 : GRABBER_TRYGRAB
private alias 2 : GRABBER_GRABDELAY
private alias 3 : GRABBER_MISSEDGRAB
private alias 4 : GRABBER_GRABBED
private alias 5 : GRABBER_STRUGGLE
private alias 6 : GRABBER_ESCAPED

// Animations
private alias 0 : GRABBER_ANI_IDLE
private alias 1 : GRABBER_ANI_OPENCLAMP
private alias 2 : GRABBER_ANI_GRABBED

// Player Aliases
private alias object.state : player.state
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.direction : player.direction
private alias object.gravity : player.gravity
private alias object.animation : player.animation
private alias object.left : player.left
private alias object.right : player.right
private alias object.value1 : player.timer
private alias object.value40 : player.hitboxLeft
private alias object.value38 : player.hitboxTop
private alias object.value41 : player.hitboxRight
private alias object.value39 : player.hitboxBottom


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

reserve function Grabber_Setup
reserve function Grabber_DebugDraw
reserve function Grabber_DebugSpawn


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

private function Grabber_Setup
	object[arrayPos0].xpos = object[arrayPos0].startPos.x
	object[arrayPos0].ypos = object[arrayPos0].startPos.y
	object[arrayPos0].turnTimer = 256
	object[arrayPos0].timer = 0
	object[arrayPos0].drawingIndex = 0
	object[arrayPos0].targetPlayer = -1
	object[arrayPos0].struggleDelay = 0
	object[arrayPos0].struggleTimer = 0
	object[arrayPos0].struggleFlags = 0
	object[arrayPos0].animation = GRABBER_ANI_IDLE
	object[arrayPos0].state = GRABBER_MOVING
	if object[arrayPos0].propertyValue == 0
		object[arrayPos0].direction = FLIP_NONE
		object[arrayPos0].xvel = -0x4000
	else
		object[arrayPos0].direction = FLIP_X
		object[arrayPos0].xvel = 0x4000
	end if
	object[arrayPos0].priority = PRIORITY_BOUNDS
end function


private function Grabber_DebugDraw
	temp0 = object.ypos
	temp0 -= 0xC0000
	DrawSpriteFX(3, FX_FLIP, object.xpos, temp0)
	DrawSpriteFX(0, FX_FLIP, object.xpos, object.ypos)
	DrawSpriteFX(5, FX_FLIP, object.xpos, object.ypos)
end function


private function Grabber_DebugSpawn
	CreateTempObject(TypeName[Grabber], temp0, object.xpos, object.ypos)
	object[tempObjectPos].startPos.x = object.xpos
	object[tempObjectPos].startPos.y = object.ypos
	arrayPos0 = object[tempObjectPos].entityPos
	object[tempObjectPos].propertyValue = object.direction
	CallFunction(Grabber_Setup)
end function


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

event ObjectUpdate
	object.priority = PRIORITY_ACTIVE
	switch object.state
	case GRABBER_MOVING
		object.turnTimer--
		if object.turnTimer <= 0
			object.turnTimer = 256
			FlipSign(object.xvel)
			object.direction ^= FLIP_X
		end if

		object.xpos += object.xvel

		foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
			BoxCollisionTest(C_TOUCH, object.entityPos, -64, 0, 64, 128, currentPlayer, 1, 1, 1, 1)
			if checkResult == true
				object.state = GRABBER_GRABDELAY
				object.timer = 16
			end if
		next
		break

	case GRABBER_GRABDELAY
		object.timer--
		if object.timer < 0
			object.timer = 32
			object.state = GRABBER_TRYGRAB
			object.animation = GRABBER_ANI_OPENCLAMP
		end if
		break

	case GRABBER_TRYGRAB
		object.timer--
		if object.timer < 0
			object.state = GRABBER_MISSEDGRAB
		end if

		object.ypos += 0x20000
		break

	case GRABBER_MISSEDGRAB
		object.timer++
		if object.timer >= 32
			object.timer = 0
			object.state = GRABBER_MOVING
			object.animation = GRABBER_ANI_IDLE
		end if

		object.ypos -= 0x20000
		break

	case GRABBER_GRABBED
		object.timer++
		if object.timer >= 32
			object.turnTimer = 1
			object.timer = 16
			object.animation = GRABBER_ANI_GRABBED
			object.state++
		end if

		object.ypos -= 0x20000
		currentPlayer = object.targetPlayer
		player[currentPlayer].xpos = object.xpos
		player[currentPlayer].ypos = object.ypos
		player[currentPlayer].ypos += 0x100000
		break

	case GRABBER_STRUGGLE
		if object.targetPlayer != -1
			currentPlayer = object.targetPlayer
			if object.struggleFlags != 0
				object.struggleDelay--
				if object.struggleDelay == 0
					object.struggleTimer = 0
					object.struggleFlags = 0
				end if

				temp0 = 0
				if player[currentPlayer].left != false
					temp0 |= 1
				end if

				if player[currentPlayer].right != false
					temp0 |= 2
				end if

				if temp0 != 0
					if temp0 != 3
						if temp0 != object.struggleFlags
							object.struggleFlags = temp0
							object.struggleTimer++
							if object.struggleTimer >= 4
								player[currentPlayer].state = Player_State_Air
								object.targetPlayer = -1
								object.explodeTimer = 16
								object.state = GRABBER_ESCAPED
							else
								object.struggleDelay = 32
							end if
						end if
					end if
				end if
			else
				if player[currentPlayer].left != false
					object.struggleFlags |= 1
					object.struggleDelay = 32
				end if
				if player[currentPlayer].right != false
					object.struggleFlags |= 2
					object.struggleDelay = 32
				end if
			end if

			player[currentPlayer].xpos = object.xpos
			player[currentPlayer].ypos = object.ypos
			player[currentPlayer].ypos += 0x100000
		end if
		break

	case GRABBER_ESCAPED
		break

	end switch

	if object.outOfBounds == true
		temp0 = object.xpos
		temp1 = object.ypos
		object.xpos = object.startPos.x
		object.ypos = object.startPos.y
		if object.outOfBounds == true
			arrayPos0 = object.entityPos
			CallFunction(Grabber_Setup)
		else
			object.xpos = temp0
			object.ypos = temp1
		end if
	end if

	if object.explodeTimer > 0
		object.explodeTimer--
	end if

	temp7 = object.targetPlayer
	foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
		if currentPlayer != temp7
			if object.targetPlayer == -1
				if player[currentPlayer].state != Player_State_Static
					if object.animation == GRABBER_ANI_OPENCLAMP
						BoxCollisionTest(C_TOUCH, object.entityPos, -8, 0, 8, 16, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
						if checkResult == true
							object.state = GRABBER_GRABBED
							object.animation = GRABBER_ANI_IDLE
							object.targetPlayer = currentPlayer
							player[currentPlayer].xvel = 0
							player[currentPlayer].yvel = 0
							player[currentPlayer].speed = 0
							player[currentPlayer].timer = 0
							player[currentPlayer].state = Player_State_Static
							player[currentPlayer].gravity = GRAVITY_AIR
							player[currentPlayer].animation = ANI_GRABBED
							player[currentPlayer].direction = object.direction
							player[currentPlayer].direction ^= FLIP_X
							checkResult = true
						end if
					else
						checkResult = false
					end if
				end if
			end if

			if player[currentPlayer].state != Player_State_Static
				if object.explodeTimer == 0
					if checkResult == false
						BoxCollisionTest(C_TOUCH, object.entityPos, -8, -8, 8, 8, currentPlayer, player[currentPlayer].hitboxLeft, player[currentPlayer].hitboxTop, player[currentPlayer].hitboxRight, player[currentPlayer].hitboxBottom)
						if checkResult == true
							CallFunction(Player_BadnikBreak)
						end if
					end if
				end if
			end if
		end if
	next

	switch object.animation
	case GRABBER_ANI_IDLE
		object.frame = 0
		object.clampFrame = 5
		GetBit(object.wheelFrame, object.turnTimer, 2)
		object.wheelFrame += 3
		break

	case GRABBER_ANI_OPENCLAMP
		object.animationTimer++
		object.animationTimer &= 15
		object.frame = object.animationTimer
		object.frame >>= 3
		object.clampFrame = object.frame
		object.clampFrame += 5
		break

	case GRABBER_ANI_GRABBED
		object.turnTimer--
		if object.turnTimer == 0
			if object.frame == 0
				object.frame = 2
			else
				object.frame = 0
			end if

			object.turnTimer = object.timer
			object.timer--
			if object.timer == 0
				if object.targetPlayer != -1
					currentPlayer = object.targetPlayer
					if player[currentPlayer].state == Player_State_Static
						CallFunction(Player_Hit)
						if player[currentPlayer].state != Player_State_GotHit
							player[currentPlayer].state = Player_State_Air
						end if

						object.targetPlayer = -1
						temp7 = -1
					end if
				end if

				ResetObjectEntity(object.entityPos, TypeName[Smoke Puff], 0, object.xpos, object.ypos)
				object.drawOrder = 4
				PlaySfx(SfxName[Destroy], false)
			end if
		end if
		break
		
	end switch

	if object.type != TypeName[Grabber]
		if temp7 != -1
			currentPlayer = temp7
			player[currentPlayer].state = Player_State_Air
		end if
	end if
end event


event ObjectDraw
	if object.drawingIndex == 0
		temp0 = object.startPos.y
		temp0 -= 0x80000
		temp1 = object.ypos
		temp1 -= object.startPos.y
		temp1 >>= 20
		temp1 += 7
		DrawSpriteFX(temp1, FX_FLIP, object.xpos, temp0)
		temp0 -= 0x40000
		DrawSpriteFX(object.wheelFrame, FX_FLIP, object.xpos, temp0)
		DrawSpriteFX(object.frame, FX_FLIP, object.xpos, object.ypos)
		AddDrawListEntityRef(5, object.entityPos)
	else
		DrawSpriteFX(object.clampFrame, FX_FLIP, object.xpos, object.ypos)
	end if

	object.drawingIndex ^= 1
end event


event ObjectStartup
	// This sheet's loaded here regardless of the current stage folder, and then the proper sheet is loaded again later anyways...
	LoadSpriteSheet("CPZ/Objects.gif")

	CheckCurrentStageFolder("Zone02")
	if checkResult == true
		LoadSpriteSheet("CPZ/Objects.gif") // Kinda redundant for the reason up above
		
		// Body Frames
		SpriteFrame(-27, -8, 40, 32, 5, 74)
		SpriteFrame(-27, -8, 43, 31, 5, 107)
		
		// Flashing Body Frame
		SpriteFrame(-27, -8, 40, 32, 73, 41)
		
		// Wheel Frames
		// They're labelled as "Unused Projectile Frames" in the S2 3.0.1 leftover dev data, but that doesn't seem like quite the right name...
		SpriteFrame(-4, -4, 8, 8, 46, 74)
		SpriteFrame(-4, -4, 8, 8, 114, 42)
		
		// Front Clamp Frames
		SpriteFrame(-6, 8, 23, 16, 5, 140)
		SpriteFrame(-6, 8, 30, 15, 29, 140)
		
		// String Frames
		SpriteFrame(-1, 0, 1, 16, 2, 74)
		SpriteFrame(-1, 0, 1, 32, 2, 74)
		SpriteFrame(-1, 0, 1, 48, 2, 74)
		SpriteFrame(-1, 0, 1, 64, 2, 74)
		SpriteFrame(-1, 0, 1, 80, 2, 74)
		SpriteFrame(-1, 0, 1, 96, 2, 74)
		SpriteFrame(-1, 0, 1, 112, 2, 74)
		SpriteFrame(-1, 0, 1, 128, 2, 74)
		SpriteFrame(-1, 0, 1, 128, 2, 74)
		SpriteFrame(0, 0, 1, 1, 1, 74)
	else
		LoadSpriteSheet("MBZ/Objects.gif") // Even if the CPZ sheet has been loaded, load the MBZ sheet too
		
		// Body Frames
		SpriteFrame(-27, -8, 40, 32, 131, 280)
		SpriteFrame(-27, -8, 43, 31, 172, 280)
		
		// Flashing Body Frame
		SpriteFrame(-27, -8, 40, 32, 217, 280)
		
		// Wheel Frames
		// They're labelled as "Unused Projectile Frames" in the S2 3.0.1 leftover data, but that doesn't seem like quite the right name...
		SpriteFrame(-4, -4, 8, 8, 102, 302)
		SpriteFrame(-4, -4, 8, 8, 145, 334)
		
		// Front Clamp Frames
		SpriteFrame(-6, 8, 23, 16, 154, 313)
		SpriteFrame(-6, 8, 30, 15, 178, 313)
		
		// String Frames
		SpriteFrame(-1, 0, 1, 16, 124, 501)
		SpriteFrame(-1, 0, 1, 32, 124, 501)
		SpriteFrame(-1, 0, 1, 48, 124, 501)
		SpriteFrame(-1, 0, 1, 64, 124, 501)
		SpriteFrame(-1, 0, 1, 80, 124, 501)
		SpriteFrame(-1, 0, 1, 112, 124, 501)
		SpriteFrame(-1, 0, 1, 96, 124, 501)
		SpriteFrame(-1, 0, 1, 128, 124, 501)
		SpriteFrame(-1, 0, 1, 128, 124, 501)
		SpriteFrame(0, 0, 1, 1, 123, 501)
	end if

	foreach (TypeName[Grabber], arrayPos0, ALL_ENTITIES)
		object[arrayPos0].startPos.x = object[arrayPos0].xpos
		object[arrayPos0].startPos.y = object[arrayPos0].ypos
		CallFunction(Grabber_Setup)
	next

	SetTableValue(TypeName[Grabber], DebugMode_ObjCount, DebugMode_TypesTable)
	SetTableValue(Grabber_DebugDraw, DebugMode_ObjCount, DebugMode_DrawTable)
	SetTableValue(Grabber_DebugSpawn, DebugMode_ObjCount, DebugMode_SpawnTable)
	DebugMode_ObjCount++
end event


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

event RSDKEdit
	if editor.returnVariable == true
		switch editor.variableID
		case EDIT_VAR_PROPVAL // property value
			checkResult = object.propertyValue
			checkResult &= 1
			break
			
		case 0 // direction
			GetBit(checkResult, object.propertyValue, 0)
			break
			
		end switch
	else
		switch editor.variableID
		case EDIT_VAR_PROPVAL // property value
			object.propertyValue = editor.variableValue
			object.propertyValue &= 1
			break
			
		case 0 // direction
			CheckNotEqual(editor.variableValue, false)
			SetBit(object.propertyValue, 0, checkResult)
			break
			
		end switch
	end if
end event


event RSDKDraw
	if object.propertyValue == 0
		object.direction = FLIP_NONE
	else
		object.direction = FLIP_X
	end if

	temp0 = object.ypos
	temp0 -= 0xC0000
	DrawSpriteFX(1, FX_FLIP, object.xpos, temp0)
	DrawSpriteFX(0, FX_FLIP, object.xpos, object.ypos)
	DrawSpriteFX(2, FX_FLIP, object.xpos, object.ypos)
end event


event RSDKLoad
	CheckCurrentStageFolder("Zone02")
	if checkResult == true
		LoadSpriteSheet("CPZ/Objects.gif")
		SpriteFrame(-27, -8, 40, 32, 5, 74)
		SpriteFrame(-4, -4, 8, 8, 46, 74)
		SpriteFrame(-6, 8, 23, 16, 5, 140)
	else
		LoadSpriteSheet("MBZ/Objects.gif")
		SpriteFrame(-27, -8, 40, 32, 131, 280)
		SpriteFrame(-4, -4, 8, 8, 145, 334)
		SpriteFrame(-6, 8, 30, 15, 178, 313)
	end if

	AddEditorVariable("direction")
	SetActiveVariable("direction")
	AddEnumVariable("Left", 0)
	AddEnumVariable("Right", 1)
end event
