;-----------------------------------------------------------------;
;--------Global Character Animation and Action Functions----------;
;-----------------------------------------------------------------;

Number of Player Funcs:  31

; 0 - Input Left Standing
; 1 - Input Right Standing
; 2 - Input Left Spinning
; 3 - Input Right Spinning
; 4 - Input Jump
; 5 - Unused
; 6 - Input Spin or Duck
; 7 - Unused
; 8 - Spindash Release Sentry
; 9 - Spindash Viewport Sentry
;10 - Toggle Debug Mode
;11 - Standing Mode Movement
;12 - Standing Mode Update
;13 - Spinning Mode Movement
;14 - Spinning Mode Update
;15 - Hurting Mode Movement
;16 - Hurting Mode Update
;17 - Debug Movement Handler
;18 - Debug Scroll Foreward
;19 - Debug Scroll Backward or Place Object
;20 - Debug Save Objects
;21 - Dying Mode Movement
;22 - Player Init/Sentry
;23 - Spawn Skidding Puff
;24 - P2 AI
;25 - Input Looking Up
;26 - Demo Control
;27 - Forced Rightward Run
;28 - Set Speed Values
;29 - Super Transformation
;30 - Lock Controls And Pose After Landing

;===================================================================================
;========================
;Function 0 - Input Left=
;========================

	.Alias NegativeMax

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Ignore when holding Right
	;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_If(KeyState)							;If holding the Right direction
	: PH3										;Holding Right (Current Player)
	:_On
		#_StopCurrentFunc					;Can't do left and right at the same time, stop
	#_EndIf								;Holding Right

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If something solid is blocking the way, don't apply acceleration
	;(acceleration must be prevented to avoid collision friction)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Path_Mode == _PPM_Floor				;Player only pushes when he's on the path mode matching his gravity direction
		~If _Player_Cant_Go_Left != 0				;If player is blocked to the left by something solid
			~Eval _Player_Pushing = 1			;Flag for pushing animation
			#_GoTo						;Don't allow input to increase velocity, but make sure player isn't facing right for some reason
			: @FixDir
		~EndIf							;Player_Cant_Go_Left test
	~EndIf								;Player on gravity path test

	;;;;;;;;;;;;;;;;
	;Freespace input
	;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0					;If player is not on the ground

		~Eval _NegativeMax = -_Player_MaxVel			;Find the "Maximum" negative velocity

		~If _NegativeMax >= _Player_Path_Vel			;If player is already at max negative velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing right for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Negative Velocity Test

		~Eval _Player_Path_Vel -= _Player_Accel			;Apply Acceleration

		;;;
		;Freespace uses double acceleration, test to see if the acceleration can be applied again without going over
		;;;

		~If _NegativeMax >= _Player_Path_Vel				;If player is already at max negative velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing right for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Negative Velocity Test

		~Eval _Player_Path_Vel -= _Player_Accel			;Apply Acceleration

	~Else								;Otherwise, if the player is on a path

		;;;
		;Ignore when ducking
		;;;

		~If _Player_Ducking != 0				;If player is set for ducking, spindashing, etc
			#_StopCurrentFunc				;Can't move while ducking
		~EndIf							;Player Invulnerability Flag

		~Eval _Player_InputMotionFlag = _On			;Flag that vector velocity input is being made or attempted (Not if ducking, but ok if slipping)

		;;;
		;Ignore when "Slip Timer" is active
		;;;

		~If _Player_SlipTimer > 0				;If player slip counter is active
			#_StopCurrentFunc				;Can't control during a slip
		~EndIf							;Player Slip Counter

		;;;
		;Process leftward acceleration
		;;;

		~Eval _NegativeMax = -_Player_MaxVel			;Find the "Maximum" negative velocity


		~If _NegativeMax >= _Player_Path_Vel			;If player is already at max negative velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing right for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf 							;Maximum Negative Velocity Test

		~If _Player_Path_Vel <= 0				;If player's current velocity is leftward

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Only apply acceleration in this case, so that it doesn't add
			;to the deceleration when moving in the opposite direction
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Path_Vel -= _Player_Accel		;Apply Acceleration

			#_GoTo
			: @FixDir

		~EndIf							;Test Velocity Direction

		~If _Player_Path_Mode != _PPM_Floor			;If player is not in the path mode that corresponds with the current gravity mode

			#_GoTo						;Don't change player direction, just make sure he's not skidding
			: @StopSkid

		~EndIf							;Test Path Mode

		~Eval _Player_Path_Vel -= _Player_SkidDecel		;Apply Skid Friction

		~If _Player_Path_Vel > 511				;If player's current velocity is fast enough to skid

			~If _Player_X_Dir == _Right			;If player is facing right

				#_PlaySound
				:_SFX_Skid
				:_Snd_Once

				~Eval _Player_Skidding = 3		;Signal for Skidding Animation (Player is actively applying a skid)

			~EndIf						;Player facing right

		~EndIf							;Velocity > 512

		#_StopCurrentFunc					;Stop function so shield effect doesn't happen

	~EndIf								;Test if touching a path

@FixDir

	~Eval _Player_X_Dir = _Left					;Make player face left

@StopSkid

	~Eval _Player_Skidding = 0					;Make sure skidding animation isn't on

# End	;Terminate Function

;===================================================================================
;=========================
;Function 1 - Input Right=
;=========================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;
	;Ignore when holding Left
	;;;;;;;;;;;;;;;;;;;;;;;;;

	#_If(KeyState)							;If holding the Left direction
	: PH2										;Holding Left (Current Player)
	:_On
		#_StopCurrentFunc					;Can't do left and right at the same time, stop
	#_EndIf								;Holding Left

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If something solid is blocking the way, don't apply acceleration
	;(acceleration must be prevented to avoid collision friction)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Path_Mode == _PPM_Floor				;Player only pushes when he's on the path mode matching his gravity direction
		~If _Player_Cant_Go_Right != 0				;If player is blocked to the right by something solid
			~Eval _Player_Pushing = 1			;Flag for pushing animation
			#_GoTo						;Don't allow input to increase velocity, but make sure player isn't facing right for some reason
			: @FixDir
		~EndIf							;Player_Cant_Go_Left test
	~EndIf								;Player on gravity path test

	;;;;;;;;;;;;;;;;
	;Freespace input
	;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0					;If player is not on the ground

		~If _Player_Path_Vel >= _Player_MaxVel			;If player is already at max velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing left for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Velocity Test

		~Eval _Player_Path_Vel += _Player_Accel			;Apply Acceleration

		;;;
		;Freespace uses double acceleration, test to see if the acceleration can be applied again without going over
		;;;

		~If _Player_Path_Vel >= _Player_MaxVel			;If player is already at max velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing left for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Velocity Test

		~Eval _Player_Path_Vel += _Player_Accel			;Apply Acceleration

	~Else								;Otherwise, if the player is on a path

		;;;
		;Ignore when ducking
		;;;

		~If _Player_Ducking != 0				;If player is set for ducking, spindashing, etc
			#_StopCurrentFunc				;Can't move while ducking
		~EndIf							;Player Invulnerability Flag

		~Eval _Player_InputMotionFlag = _On			;Flag that vector velocity input is being made or attempted (Not if ducking, but ok if slipping)

		;;;
		;Ignore when "Slip Timer" is active
		;;;

		~If _Player_SlipTimer > 0				;If player slip counter is active
			#_StopCurrentFunc				;Can't control during a slip
		~EndIf							;Player Slip Counter

		;;;
		;Process rightward acceleration
		;;;

		~If _Player_Path_Vel >= _Player_MaxVel			;If player is already at max velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing left for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Velocity Test

		~If _Player_Path_Vel >= 0				;If player's current velocity is Rightward

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Only apply acceleration in this case, so that it doesn't add
			;to the deceleration when moving in the opposite direction
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Path_Vel += _Player_Accel		;Apply Acceleration

			#_GoTo
			: @FixDir

		~EndIf							;Test Velocity Direction

		~If _Player_Path_Mode != _PPM_Floor			;If player is not in the path mode that corresponds with the current gravity mode

			#_GoTo						;Don't change player direction, just make sure he's not skidding
			: @StopSkid

		~EndIf							;Test Path Mode

		~Eval _Player_Path_Vel += _Player_SkidDecel		;Apply Skid Friction

		~If _Player_Path_Vel < -511				;If player's current velocity is fast enough to skid

			~If _Player_X_Dir == _Left			;If player is facing left

				#_PlaySound
				:_SFX_Skid
				:_Snd_Once

				~Eval _Player_Skidding = 3		;Signal for Skidding Animation

			~EndIf						;Player facing right

		~EndIf							;Velocity > 512

		#_StopCurrentFunc					;Stop function so shield effect doesn't happen

	~EndIf								;Test if touching a path

@FixDir

	~Eval _Player_X_Dir = _Right					;Make player face right

@StopSkid

	~Eval _Player_Skidding = 0					;Make sure skidding animation isn't on

# End	;Terminate Function

;===================================================================================
;=================================
;Function 2 - Input Left Spinning=
;=================================

	.Alias NegativeMax

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Ignore when holding Right
	;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_If(KeyState)							;If holding the Right direction
	: PH3										;Holding Right (Current Player)
	:_On
		#_StopCurrentFunc					;Can't do left and right at the same time, stop
	#_EndIf								;Holding Right

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If something solid is blocking the way, don't apply acceleration
	;(acceleration must be prevented to avoid collision friction)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Cant_Go_Left != 0					;If player is blocked to the left by something solid
		#_StopCurrentFunc
	~EndIf								;Player_Cant_Go_Left test

	;;;;;;;;;;;;;;;;
	;Freespace input
	;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0					;If player is not on the ground

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;If the player was spinning when he jumped, he should not have horizontal control while in freespace
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Skidding != 0				;The "Skidding" flag is used for this

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;The player jumped while spinning. Exit this function to prevent him from having horizontal control
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_StopCurrentFunc

		~EndIf

		~Eval _NegativeMax = -_Player_MaxVel			;Find the "Maximum" negative velocity

		~If _NegativeMax >= _Player_Path_Vel			;If player is already at max negative velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing right for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Negative Velocity Test

		~Eval _Player_Path_Vel -= _Player_Accel			;Apply Acceleration

		;;;
		;Freespace uses double acceleration, test to see if the acceleration can be applied again without going over
		;;;

		~If _NegativeMax >= _Player_Path_Vel			;If player is already at max negative velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing right for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			: @FixDir

		~EndIf							;Maximum Negative Velocity Test

		~Eval _Player_Path_Vel -= _Player_Accel			;Apply Acceleration

	~Else								;Otherwise, if the player is on a path

		;;;
		;Ignore when "Slip Timer" is active
		;;;

		~If _Player_SlipTimer > 0				;If player slip counter is active
			#_StopCurrentFunc				;Can't control during a slip
		~EndIf							;Player Slip Counter

		;;;
		;Process leftward acceleration
		;;;

		~If _Player_Path_Vel < 0				;If player's current velocity is leftward
			#_GoTo
			:  @FixDir
		~EndIf							;Test Velocity Direction

		~Eval _Player_Path_Vel -= 32				;Apply Deceleration

		#_StopCurrentFunc					;Don't change direction

	~EndIf								;Test if touching a path
@FixDir
	~Eval _Player_X_Dir = _Left					;Make player face left

	~If _Player_Type == _Tails					;If the player is Tails
		~If _Tails_CarriedPlayerPtr != 0			;If Tails is carrying the other player

			#_FetchPlayer_CurrChr				;Get the pointer to the carried player
			:_Ref_Dest
			:_Tails_CarriedPlayerPtr					;(Chr0)

			~Eval _Player_X_Dir = _Left			;Make player face left

		~EndIf
	~EndIf

# End	;Terminate Function

;===================================================================================
;==================================
;Function 3 - Input Right Spinning=
;==================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;
	;Ignore when holding Left
	;;;;;;;;;;;;;;;;;;;;;;;;;

	#_If(KeyState)							;If holding the Left direction
	: PH2										;Holding Left (Current Player)
	:_On
		#_StopCurrentFunc					;Can't do left and right at the same time, stop
	~EndIf								;Holding Right

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If something solid is blocking the way, don't apply acceleration
	;(acceleration must be prevented to avoid collision friction)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Cant_Go_Right != 0					;If player is blocked to the right by something solid
		#_StopCurrentFunc
	~EndIf								;Player_Cant_Go_Left test

	;;;;;;;;;;;;;;;;
	;Freespace input
	;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0					;If player is not on the ground

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;If the player was spinning when he jumped, he should not have horizontal control while in freespace
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Skidding != 0				;The "Skidding" flag is used for this

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;The player jumped while spinning. Exit this function to prevent him from having horizontal control
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_StopCurrentFunc

		~EndIf

		~If _Player_Path_Vel >= _Player_MaxVel			;If player is already at max velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing left for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			:  @FixDir

		~EndIf							;Maximum Velocity Test

		~Eval _Player_Path_Vel += _Player_Accel			;Apply Acceleration

		;;;
		;Freespace uses double acceleration, test to see if the acceleration can be applied again without going over
		;;;

		~If _Player_Path_Vel >= _Player_MaxVel			;If player is already at max velocity

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Don't allow input to increase beyond max velocity, but
			;make sure player isn't facing left for some reason
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_GoTo
			:  @FixDir

		~EndIf							;Maximum Velocity Test

		~Eval _Player_Path_Vel += _Player_Accel			;Apply Acceleration

	~Else								;Otherwise, if the player is on a path

		;;;
		;Ignore when "Slip Timer" is active
		;;;

		~If _Player_SlipTimer > 0				;If player slip counter is active
			#_StopCurrentFunc				;Can't control during a slip
		~EndIf							;Player Slip Counter

		;;;
		;Process rightward acceleration
		;;;

		~If _Player_Path_Vel > 0				;If player's current velocity is rightward

			#_GoTo
			:  @FixDir

		~EndIf							;Test Velocity Direction

		~Eval _Player_Path_Vel += 32				;Apply Deceleration

		#_StopCurrentFunc					;Don't change direction

	~EndIf								;Test if touching a path
@FixDir
	~Eval _Player_X_Dir = _Right					;Make player face right

	~If _Player_Type == _Tails					;If the player is Tails
		~If _Tails_CarriedPlayerPtr != 0			;If Tails is carrying the other player

			#_FetchPlayer_CurrChr				;Get the pointer to the carried player
			:_Ref_Dest
			:_Tails_CarriedPlayerPtr					;(Chr0)

			~Eval _Player_X_Dir = _Right			;Make player face right

		~EndIf
	~EndIf

# End	;Terminate Function

;===================================================================================
;========================
;Function 4 - Input Jump=
;========================

	.Alias XVel
	.Alias YVel
	.Alias Keys
	.Alias MaxVel

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If Debug Mode is available, intercept "B" button presses and use to toggle Debug Mode instead of jump
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~IfFlag _CheatFlags[2] == _On							;If the Debug Mode Cheat is enabled

		#_If(KeyState)								;If the key used was B
		: PT5											;Tapped B (Current Player)
		:_On

			#_RunFunc
			:  10 ;Toggle Debug Mode

			#_StopCurrentFunc

		~EndIf									;If tapped B

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Ignore jump during forced-spin
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_TubeFlag != 0							;Check the forced-spin flag
		#_StopCurrentFunc							;Player being forced to spin can't jump
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Check for "double-jump" (special move while in midair)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0							;If player is not standing on a path or a solid object, this button triggers special moves

		~If _Player_JumpedFlag == 3						;If the player is in the middle of a non-height-controlled jump and hasn't used the special move yet
			#_GoTo
			: @Jump_DoSpecial
		~EndIf

		~If _Player_JumpedFlag == 1						;If the player is in the middle of a height-controlled jump and hasn't used the special move yet
@Jump_DoSpecial
			~Eval _Player_JumpedFlag = 2					;Flag the special move as used so it can't be used again until the next jump
			~Eval _Player_Skidding   = 0					;"Skidding" flag was used to disable horizontal control when jumping from a spin. Disable this flag to restore movement control

			~If _Option_PlayMode == 0					;If in normal playmode
				~IfFlag _GameState[_GSF_ReturnFromSpecial] == _False	;If not in a Special or Bonus Stage
					~If _EmeraldsCollected == 7			;If all Chaos Emeralds have been collected
						~If _Player_Rings >= 50			;If the player has 50 or more rings
							~If _Player_SuperFlag == 0	;If the player is not yet in Super state

								#_RunFunc_CurrPlayer_Const	;Trigger Player's "Super" Transformation
								: 29 ;Super Transformation

								#_StopCurrentFunc	;Transformation has occurred. Exit function to skip double- and normal jump code

							~EndIf
						~EndIf
					~EndIf
				~EndIf
			~EndIf

			#_RunFunc_CharVar						;Perform the character's special move
			:c_Player_SpecialMove


		~EndIf									;If player is in a jump and hasn't used special move yet

		#_StopCurrentFunc							;Double-Jump has occurred. Exit function to skip normal jump code

	~EndIf										;If player is in freespace

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Correct some special animation flags before jumping
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_FloatingFlag = 0							;Disable the "floating" animation, in case it was on when player jumped/spindashed (one or the other is about to happen)
	~Eval _Player_Skidding     = 0							;Force disable skidding in case it was on

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Determine if this is jump or spindash input
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Ducking == 1							;If player is ducking, this button charges the spindash

		#_GoTo
		: @DoSpindash

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Genesis Sonic games' jump input effectively skips the first frame of motion. Wait until the
	;next frame so the height control will properly cap the upward speed on the first frame if
	;the key was tapped that quickly. This routine will finish running during "sentry" time
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FuncBreak

	#_FetchPlayer									;Re-obtain the player pointer, since the Function processing was just broken
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Check whether or not jumping is still appropriate, since
	;circumstances may have changed during the previous frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode > _PMM_Spinning					;If player is not in standing or spinning mode
		#_StopCurrentFunc							;He must have been hurt or entered a special state, cancel the jump
	~EndIf

	~If _Player_On_Path == 0							;If player is not on a path
		#_StopCurrentFunc							;He must have walked off the path, hit a spring, etc.., cancel the jump
	~EndIf

	;;;
	;Perform the jump
	;;;
	#_Reg=SplitVector(ChrS,ChrA)							;Use the angle of the path the player is standing on to calculate X and Y jump velocities, so that the jump is perpendicular to the path
	:_Player_JumpForce										;(Chr0 Speed)
	:_Player_Path_Angle										;(Chr1 Angle)
	:_YVel												;(Reg X)      Yes, X and Y get flip-flopped
	:_XVel												;(Reg Y)

	~Eval -_YVel									;Negate the Y Velocity to make the jump
	~Eval  _Player_Y_Vel      += _YVel						;(For Horizontal Gravity) Setting the Y velocity instead of path velocity is important, because it is copied to the Path velocity when the movement function detects that the player has just left the path
	~Eval  _Player_Path_Vel    = _Player_X_Vel + _XVel				;(For vertical Gravity) Setting the X velocity instead of path velocity is important, because it is copied to the Path velocity when the movement function detects that the player has just left the path

	~Eval  _Player_JumpedFlag  = 1							;Signal a normal jump
	~Eval  _Player_On_Path     = 0							;Clear the "on path" flag after jump
	~Eval  _Player_Path_Mode   = _PPM_Floor						;Set the player to "floor path" movement for a proper jump
	~Eval  _Player_Path_Angle  = 0							;Clear angle after jump

	#_PlaySound
	:_SFX_Jump
	:_Snd_Once

	~If _Player_Movement_Mode != _PMM_Spinning					;If player is not spinning

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Put the player into spinning mode, and adjust his VP and Y position to compensate
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		.Include macro\spin.def

	~Else										;If Player is spinning

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Jumping from a spin causes loss of horizontal control while
		;in the air. Use the "Skidding" flag to signal this
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Skidding = 1

	~EndIf										;If player is spinning

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Perform jump height control once now, since this is now the frame
	;immediately following the frame on which jump input was made
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_ForceJumpFlag != 0							;Is this a forced jump?
		#_GoTo									;Skip manual height control to sustain the forced jump
		: @ForcedJump
	~EndIf

	#_Keys_Pack									;Get player's key states
	:r_Curr_Player
	:_Keys											;(Reg) Player KeyState

	~Eval _Keys &= $70								;Keep only the jump keys

	~If _Keys == 0									;If the player isn't holding a jump button

		~If _Player_WaterFlag == 0							;If player isn't under water
			~Eval _MaxVel = -$400						;Set the upward velocity cap
		~Else									;If Player Under Water
			~Eval _MaxVel = -$200						;Set the upward velocity cap
		~EndIf

		~If _MaxVel > _Player_Y_Vel						;If the player's upward velocity is beyond the maximum
			~Eval _Player_Y_Vel = _MaxVel
		~EndIf

	~EndIf										;Test for jump height control input
@ForcedJump
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Set the animation delay value properly for the spinning animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_AniDelay   = $500 - _Player_AniSpeed				;Create the delay value ($500 Delay at minimum speed)
	~Eval _Player_AniDelay >>= 8							;Use only the full pixel portion of the speed

	~If   _Player_AniDelay   < 1							;If the delay value is less than 1 (Minimum Delay)
		~Eval _Player_AniDelay = 1						;Set minimum delay
	~EndIf										;If the delay value is less than 1

	;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;End of normal jump routine
	;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_StopCurrentFunc								;Stop function so spindash code doesn't happen

@DoSpindash

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the player was ducking or already charging a spindash, this Function charges the spindash
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_SpindashCtr <= 0							;If player is not charging the spindash

		~Eval _Player_SpindashCtr = 1						;Start the spindash counter at the appropriate starting value

		#_Player_ChangeAnimations						;Setup Spindashing Animation
		:   1											;1 Animation
		: l13											;(RefID 0) Current Ducking Animation
		: l15											;(NewID 0) Spindashing Animation
		:   0											;(Smooth)  No

		#_CallFunc
		:   8 ;Spindash Release Sentry

	~Else										;Player is already charging spindash

		~Eval _Player_SpindashCtr += $200					;Increase the spindash charge

		~If _Player_SpindashCtr > $800						;If this is in excess of the maximum spindash charge
			~Eval _Player_SpindashCtr = $800				;Force the charge value to maximum
		~EndIf

	~EndIf

	#_PlaySound
	:_SFX_DashCharge
	:_Snd_Once

# End	;Terminate Function

;===================================================================================
;====================
;Function 5 - Unused=
;====================

	;;;;;;;;;;;;;;;;;;;;;;;;
	;This function is unused
	;;;;;;;;;;;;;;;;;;;;;;;;

# End	;Terminate Function

;===================================================================================
;================================
;Function 6 - Input Spin or Duck=
;================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	~Eval _Player_Skidding = 0							;Force disable skidding in case it was on

	~If _Player_FloatingFlag == 0							;If the player isn't playing a "floating" (Special) animation

		~If _Player_On_Path == 0						;If player is not on the ground

			~Eval _Player_Movement_Mode   = _PMM_Spinning			;Set the player to spinning mode
			~Eval _Player_VPCenterOffset += _Player_SpinOffset		;Perform the vp offset immediately also so that it takes effect this frame, because input was taken after the sentry combines the two offsets

		~Else									;If player is on the ground

			~Eval _Player_AniSpeed = +_Player_Path_Vel			;When player is on a path (or solid object in path direction), set his animation speed to his path speed (if it's negative, it should be made positive)

			~If _Player_AniSpeed <= 256					;If player is not moving fast enough to spin

				~Eval _Player_Ducking = 1				;Disable player's ability to jump to allow the spindash

				~If _Player_SpindashCtr <= 0				;If the player isn't spindashing
					~If _Player_VPPanTimer < 120			;If the viewport scroll timer isn't at full time
						~Eval _Player_VPPanTimer++		;Increase the timer
					~Else 						;If the timer has reached its limit
						~If _Player_VPPanOffset < 88		;If the viewport hasn't panned to its maximum
							~Eval _Player_VPPanOffset += 2	;Pan the screen down
						~EndIf
					~EndIf
				~EndIf

			~Else								;If player is moving fast enough to spin

				~Eval _Player_Ducking = 0				;Make sure ducking isn't triggered when entering spin mode

				#_PlaySound
				:_SFX_Spin
				:_Snd_Once

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Put the player into spinning mode, and adjust his VP and Y position to compensate
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				.Include macro\spin.def

			~EndIf								;Player path speed test

		~EndIf

	~EndIf

# End	;Terminate Function

;===================================================================================
;====================
;Function 7 - Unused=
;====================

	;;;;;;;;;;;;;;;;;;;;;;;;
	;This function is unused
	;;;;;;;;;;;;;;;;;;;;;;;;

# End	;Terminate Function

;===================================================================================
;=====================================
;Function 8 - Spindash Release Sentry=
;=====================================

	.Alias Index
	.Alias Decrease
@Repeat
	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Break spindash if player movement mode has changed
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode != _PMM_Standing					;If player isn't in floor standing mode

		~Eval _Player_SpindashCtr = 0						;Zero out the Spindash speed, spindash has somehow been cancelled

		#_Player_ChangeAnimations						;Restore the Ducking Animation
		:   1											;1 Animation
		: l13											;(Ref ID) Current Ducking Animation
		: l14											;(New ID) Ducking Animation
		:_AniChange_Restart									;(Smooth)

		#_StopCurrentFunc							;Spindash is over

	~EndIf										;If player isn't in floor standing mode

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Spindash can't be released unless the player is on a path
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0							;If the player is not on a path or a solid object

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Skip the release code to wait until he strikes a path
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_BreakTo
		: @Repeat

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for Spindash release
	;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_If(KeyState)									;Letting go of down key releases spindash
	: PH1												;Holding Down (Current Player)
	:_Off

		;;;;;;;;;;;;
		;Apply speed
		;;;;;;;;;;;;

		~Eval   _Index = _Player_SpindashCtr >> 8				;Use the upper byte of the counter value as an index into the spindash speeds array

		~If _Player_SuperFlag != 0						;If player is Super
			~TableW _Player_Path_Vel = _Table_DashSpeedSuper[_Index]	;Obtain the appropriate speed value
		~Else
			~TableW _Player_Path_Vel = _Table_DashSpeedNormal[_Index]	;Obtain the appropriate speed value
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Set the screen lock wait timer based on the speed obtained from the charge
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_SpindashCtr   = _Player_Path_Vel - $800
		~Eval _Player_SpindashCtr >>= 8
		~Eval _Player_SpindashCtr  &= $0F
		~Eval _Player_SpindashCtr  -= $10

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Ensure player can dash through destructable solid objects (Monitor,Breakable Wall,...)
		;and that the speed is applied in the correct direction
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_X_Dir == _Right						;If player facing right
			~Eval  _Player_Cant_Go_Right = _False				;Clear collision in this direction in case Player is spindashing against a destructable solid object
		~Else 									;If player facing left
			~Eval  _Player_Cant_Go_Left  = _False				;Clear collision in this direction in case Player is spindashing against a destructable solid object
			~Eval -_Player_Path_Vel						;Reverse Spindash Speed for leftward motion
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Play the sound and transition to spinning mode
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Ducking     = _False					;Player is no longer ducking

		#_Player_ChangeAnimations						;Restore the Ducking Animation
		:   1											;1 Animation
		: l13											;(Ref ID) Current Ducking Animation
		: l14											;(New ID) Ducking Animation
		:_AniChange_Restart									;(Smooth)

		#_PlaySound
		:_SFX_Dash										;(Sound)
		:_Snd_Once										;(Loop)
		#_StopSound								;Dash sound interrupts dash charging sound if it is still playing
		:_SFX_DashCharge

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Put the player into spinning mode, and adjust his VP and Y position to compensate
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		.Include macro\spin.def

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Prepare and call the sentry function for temporarily locking the screen
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_VPXBackup = _Player_VPX					;Copy VPX position for locking

		#_CallFunc
		:   9 ;Spindash Viewport Sentry

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Spindash is over, terminate this sentry
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_StopCurrentFunc

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Spindash loses potential speed over time
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Decrease            = _Player_SpindashCtr >> 5				;Calculate the amount of potential to remove from the index counter
	~Eval _Player_SpindashCtr -= _Decrease						;Decrease the index counter to lower potential speed

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Run this sentry code again on the next frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_BreakTo
	: @Repeat

# End	;Terminate Function

;===================================================================================
;======================================
;Function 9 - Spindash Viewport Sentry=
;======================================

	.Alias Pos
	.Alias Dist
@Repeat
	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Entering a special mode or having movement stopped will release the lock
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode > _PMM_Spinning			;If player movement mode isn't standing or spinning
		~Eval _Player_SpindashCtr = 0				;Set no screenlock and no charge
		#_StopCurrentFunc					;If player mode has changed, spindash screenlock should end
	~EndIf

	~If _Player_Path_Vel == 0					;If player path motion has stopped, spindash screenlock should end
		~Eval _Player_SpindashCtr = 0				;Set no screenlock and no charge
		#_StopCurrentFunc					;If player mode has changed, spindash screenlock should end
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Force-set the viewport to keep it in the same position
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Pos = _Player_VPXBackup + 160				;Calculate screen horizontal center

	~If   _Pos > _Player_X_Pos					;If player is on the left half of the screen

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;16-pixel horizontal scroll leniency is entirely on the left half of the screen
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Dist = _Player_X_Pos - _Pos			;Find out how many pixels player is away from the center
		~If   _Dist <= -16					;If player isn't within 16 pixels of screen center
			~Eval _Player_VPX = _Player_VPXBackup + 16	;Force screen position
		~EndIf

	~Else								;If player is on the right half of the screen
		~Eval _Player_VPX = _Player_VPXBackup - 16		;Force screen position
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Decrease the lock timer and release the screen if it has expired
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_SpindashCtr++					;Count down the lock wait timer
	~If  _Player_SpindashCtr == 0					;If there is any wait time left
		#_StopCurrentFunc					;Timer has expired, spindash screenlock should end
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Run this sentry code again on the next frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_BreakTo
	: @Repeat

# End	;Terminate Function

;===================================================================================
;================================
;Function 10 - Toggle Debug Mode=
;================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	~IfFlag _CheatFlags[2] == _On						;If the Debug Mode Cheat is enabled

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Don't allow P2 AI to enter debug mode
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Option_PlayMode == 0					;If the mode is single-player)
			~If _Curr_Player == _Player2				;If Player 2 is using AI
				~If _P2_InputTimer == 0
					#_StopCurrentFunc			;Stop function to prevent AI from sending P2 into debug mode
				~EndIf
			~EndIf
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Speeds should be reset going into and coming out of debug mode
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Chr_SetRange							;Stop the player's horizontal, vertical, and path motion
		:   0										;(Src) Sets Player_X_Vel, Player_Y_Vel, and Player_Path_Vel to 0
		:_Player_X_Vel									;(Start)
		:_Player_Path_Vel								;(End)

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Player Platform/Level collision settings should be reset so solid objects stop affecting him
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Chr_SetRange							;Clear player's Up, Down, Left, and Right collision flags
		:   0										;(Src)
		:_Player_Cant_Go_Up								;(Start)
		:_Player_Cant_Go_Right								;(End)

		#_Chr_SetRange							;Clear player's "on-path" settings
		:   0										;(Src)
		:_Player_On_Path								;(Start)
		:_Player_Was_On_Path								;(End)

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Other misc values should also be reset
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_FloatingFlag           = 0			;Disable the "floating" animation, in case it was on
		~Eval _Player_Skidding               = 0			;Force disable skidding in case it was on
		~Eval _Player_CarriedFlag            = 0			;Set player as not being carried
		~Flag _Player_Flags[_PFlag_VPCenter] = _On			;Re-enable the auto viewport, in case it was turned off in events such as death
		~Eval _Player_Solid_Plane            = _PriColPlane		;Reset player to primary collision plane
		~Eval _Player_TubeFlag               = 0			;Force disable forced-spin state in case it was on

		;;;;;;;;;;;;;;;;;;;;;;;;;
		;Toggle debug mode on/off
		;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Movement_Mode == _PMM_Debug				;If player is already in debug mode

			~Eval _Player_Movement_Mode = _PMM_Standing		;Go to standing mode
			~Eval _Player_Path_Mode     = _PPM_Floor		;Set path motion mode to vectored on floor

		~Else								;Player is not yet in debug mode

			~Eval _Player_Movement_Mode = _PMM_Debug		;Go to debug mode
			~Eval _Player_Path_Mode     = _PPM_Debug

			~Eval _OE_Property[1]       = _Player_Y_Dir << 1	;Set the orientation property in relation to the player's current orientation
			~Eval _OE_Property[1]      += _Player_X_Dir
		~EndIf

	~EndIf

# End	;Terminate Function

;===================================================================================
;=====================================
;Function 11 - Standing Mode Movement=
;=====================================

	.Alias Decel
	.Alias Vel
	.Alias Angle

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for demo recording/playback, and act accordingly
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_RunFunc_CurrPlayer_Const
	: 26 ;Demo Control

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Do player physics (gravity, slope, etc) handling
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0								;If player is not standing on a path or a solid object

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Player not on a path should be using the path mode corresponding with his gravity mode.
		;This should be done outside of the test for player leaving a path in case the player
		;falls onto the floor from freespace and hits an angle that changes his angle mode as
		;he slides across, and falls off the edge of the path, all in the same frame.
		;Also run before the test for striking a ceiling path, because if it were done
		;afterward, it would unconditionally pull him back off
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Path_Mode = _PPM_Floor						;In normal gravity, force player back to floor path mode when in freespace

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for player striking a ceiling (hitting near-wall curves causes player to grip them)
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Cant_Go_Up != 0							;If player has a solid ceiling above

			~Eval _Angle  = _Player_Motion_Angle - $20				;Determine whether the player is moving toward the ceiling, and more more vertically than horizontally (within the arc \/ )
			~Eval _Angle &= $C0							;"
			~If   _Angle == $80							;"

				~If _Player_SlipTimer != 0					;If the slip counter is on, the player probably just fell off the ceiling
					#_GoTo							;The player likely struck the ceiling again from some upward momentum from falling off an angled ceiling, don't let it make him re-grip
					: @Ceiling_NoGrip
				~EndIf								;Collision Angle Region Test

				~Eval _Angle  = _Player_Collision_Angle + $20			;Determine whether the angle of the ceiling section that the player has just struck is within proper range for grabbing on
				~Eval _Angle &= $40						;"
				~If   _Angle == 0						;"
					#_GoTo							;The angle is too similar to a horizontal ceiling, skip over this part and don't grip it
					: @Ceiling_NoGrip
				~EndIf

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;When taking a ceiling path, Path Velocity is set based on Y Velocity
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Path_Vel = _Player_Previous_Y_Vel			;Convert Player's Y Velocity to rightward Path Velocity
				~If   _Player_Collision_Angle >=$80				;If the angle that was struck is a right-side angle
					~Eval -_Player_Path_Vel					;Reverse converted Path Velocity
				~EndIf

				~If _Player_Path_Vel < 256
					~If _Player_Path_Vel > -256
						#_GoTo						;The player is moving too slowly to stay attached to the ceiling. Abort grip
						: @Ceiling_NoGrip
					~EndIf
				~EndIf

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Score bonus increment resets when player strikes a path
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_EnemyScoreCtr = 0					;Reset the successive enemy score increment

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Some flags are reset upon sriking a new path
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Skidding   = 0					;Force disable skidding when striking a new path
				~Eval _Player_JumpedFlag = 0					;After landing, reset the jump flag

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Use the angle of the ground to determine which path mode the player should enter
				;(player landing on tops of curves should change to wall path to keep fluid motion)
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Path_Mode   = 256 - _Player_Collision_Angle	;First, Mirror the angle, because left and right path mode IDs are reverse of those derived from the unmirrored path angle
				~Eval _Player_Path_Mode  += 32					;Rotate the player angle by 32 to obtain his orientation
				~Eval _Player_Path_Mode  &= 255					;Limit the angle to 255 units (anything higher wraps around)
				~Eval _Player_Path_Mode >>= 6					;Divide the angle by 64 (90 degrees) to determine which of the 4 possible path modes the player should use


				;;;;;;;;;;;;;;;;;;;;;;
				;Set Path ID and Angle
				;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_On_Path     = _Player_Cant_Go_Up			;Flag the player as being on the path by setting his Player_On_Path variable equal to the solid plane/sprite value in his Player_Cant_Go_Up
				~Eval _Player_Path_Angle  = _Player_Collision_Angle		;Set the player's Path angle to the angle he attatched to so he is oriented correctly

			~EndIf									;Test if player has just collided with the ceiling

		~EndIf										;Test if there is a ceiling above the player

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for player leaving a path and set velocity accordingly
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@Ceiling_NoGrip
		~If _Player_Was_On_Path != 0							;If player has just left a path (Was on a path during the last frame)
			~Eval _Player_Path_Vel = _Player_X_Vel					;In normal gravity, set the path velocity equal to the current X velocity for proper movement
		~EndIf										;Player just left a path test

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Freespace players with nonzero angles rotate toward "standing up straight"
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Path_Angle != 0							;If player in freespace is not at the appropriate "flat" angle

			~If _Player_Path_Angle >= 128						;If the angle is in the upper half of the 256-degree range (128 is middlepoint for downward gravity)

				~Eval _Player_Path_Angle++					;Rotate the angle clockwise
				~Eval _Player_Path_Angle &= 255					;This will cause a value of 256 to wrap back around to 0

			~Else									;Otherwise, the angle is in the lower half


				~Eval _Player_Path_Angle--					;Rotate the angle counter-clockwise

			~EndIf									;Angle semi-circle test

		~EndIf										;flat angle test

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Player doesn't "push" in freespace
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Pushing = 0							;Reset the pushing flag

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Apply Gravity and deceleration
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Y_Vel += _Player_Weight						;Apply Gravity

		~If _Player_Y_Vel < 0								;If player's Y Speed is between 0 and -1024
			~If _Player_Y_Vel >-1024						;If player's Y Speed is between 0 and -1024

				~Eval _Decel = _Player_Path_Vel >> 5				;Find current speed divided by 32, to be used as the deceleration value

				~If _Player_Path_Vel > 0					;If player Path velocity is positive
					~Eval _Player_Path_Vel -= _Decel			;Apply the deceleration. It will be positive in this case, so subtracting from current positive velocity will bring it closer to 0
					~If _Player_Path_Vel < 0				;If player Path velocity went below 0
						~Eval _Player_Path_Vel = 0			;Force velocity to 0
					~EndIf							;Path Velocity overflow test
				~Else								;If Path Velocity is Negative
					~Eval _Player_Path_Vel -= _Decel			;Apply the deceleration. It will be negative in this case, so subtracting from current negative velocity will bring it closer to 0
					~If _Player_Path_Vel > 0				;If player Path velocity went above 0
						~Eval _Player_Path_Vel = 0			;Force velocity to 0
					~EndIf							;Path Velocity overflow test
				~EndIf								;Path Velocity sign test

			~EndIf									;Y Speed Test
		~EndIf										;Y Speed Test

	~Else											;If player is on a path or a solid object [unangled solid objects should force player path angle to 0]

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for just striking a path
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Was_On_Path == 0							;If player has just struck a path (Was not on a path during the last frame)

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Player doesn't "push" until he's already standing on a flat surface
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Pushing = 0						;Reset the pushing flag

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Use the angle of the ground to determine which path mode the player should enter
			;(player landing on tops of curves should change to wall path to keep fluid motion)
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Path_Mode   = 256 - _Player_Collision_Angle		;First, Mirror the angle, because left and right path mode IDs are reverse of those derived from the unmirrored path angle
			~Eval _Player_Path_Mode  += 32						;Rotate the player angle by 32 to obtain his orientation
			~Eval _Player_Path_Mode  &= 255						;Limit the angle to 255 units (anything higher wraps around)
			~Eval _Player_Path_Mode >>= 6						;Divide the angle by 64 to determine which of the 4 possible path modes the player should use)

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;If player lands on a nonzero angle, his vertical velocity
			;should be converted to horizontal based on that angle
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 			~If _Player_Path_Angle != 0						;Test for nonzero angle

; 				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 				;Calculate the amount of extra path velocity the player should recieve from striking the
; 				;curve based on his current Y velocity and angle, and add it to his current X velocity
; 				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 				#_Chr=GetVectorY(ChrS,ChrA)					;Redistribute the Y velocity with which the player struck the ground onto the Path Velocity (the slope turns downward motion into left/right path motion)
; 				:_Player_Previous_Y_Vel								;(Chr0  Speed)
; 				:_Player_Collision_Angle							;(Chr1  Angle)
; 				:_Player_Path_Vel								;(Chr2  Y)

; 				~Eval _Player_Path_Vel += _Player_Previous_X_Vel		;Add whatever X velocity the player already had to the new path velocity

; 			~EndIf									;If the angle of the ground that was struck is nonzero

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Use the player's angle of motion to determine how to
			;apply X/Y freespace velocity to his Path Velocity
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Angle   = _Player_Motion_Angle - $20					;Determine which quadrant the player's angle of motion occupies within a circle divided by an X
			~Eval _Angle  &= $C0								;"
			~Eval _Angle >>= 6								;"

			#_StateSwitch(Reg)								;Apply X/Y axis velocity to Path Velocity based on the angle of motion
			:_Angle
			:  4												;Four quadrants
			: @TakePath_Floor										;Mostly downward movement
			: @TakePath_Wall										;Mostly leftward movement
			: @TakePath_Done										;Mostly upward movement (Never used when taking a path connected to the floor)
			: @TakePath_Wall										;Mostly rightward movement

@TakePath_Floor
				~Eval _Angle  = _Player_Collision_Angle + $20				;Determine whether the angle of the floor section that the player has just struck is within the lower quadrant
				~Eval _Angle &= $40							;"
				~If   _Angle == 0							;"
					~Eval _Angle  = _Player_Collision_Angle + $10			;Determine whether the angle that was struck is near-flat
					~Eval _Angle &= $20						;"
					~If   _Angle != 0						;"
						~Eval _Player_Previous_Y_Vel >>= 1			;The floor isn't near-flat. Use Y velocity but cut it in half
						#_GoTo							;Go to convert the Y velocity to Path Velocity
						: @TakePath_YToPath
					~EndIf
					#_GoTo								;The floor is near-flat. Use X Velocity instead of Y Velocity to create the Path Velocity
					: @TakePath_Wall
				~EndIf

				~If _Player_Previous_Y_Vel > $FC0					;If Y Velocity is greater than $FC0 downward
					~Eval _Player_Previous_Y_Vel = $FC0				;Cap the velocity to be converted to a maximum of $FC0
				~EndIf
@TakePath_YToPath
				~Eval _Player_Path_Vel = _Player_Previous_Y_Vel				;Convert Player's Y Velocity to rightward Path Velocity
				~If   _Player_Collision_Angle >=$80					;If the angle that was struck is a right-side angle
					~Eval -_Player_Path_Vel						;Change the converted Y velocity to leftward Path Velocity
				~EndIf

				#_GoTo
				: @TakePath_Done
@TakePath_Wall
				~Eval _Player_Path_Vel = _Player_Previous_X_Vel				;Apply Player's X velocity directly to his Path Velocity
@TakePath_Done
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Some flags are reset upon sriking a new path
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_EnemyScoreCtr = 0						;Reset the successive enemy score increment
			~Eval _Player_Skidding      = 0						;Force disable skidding when striking a new path
			~Eval _Player_JumpedFlag    = 0						;After landing, reset the jump flag

		~EndIf										;Testing if player was on a path last frame

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;If the player is standing on a sloped surface, apply downhill
		;acceleration based on the strength of the slope
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Reg=GetVectorY(ConstS,ChrA)							;Use the player's path angle to calculate the appropriate amount of downhill force to apply
		:   32												;(Const Speed)
		:_Player_Path_Angle										;(Chr0  Angle)
		:_Vel												;(Reg   Y)
		~Eval _Player_Path_Vel += _Vel							;Apply the force to the player's velocity

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Apply Deceleration if the player isn't making directional input
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_InputMotionFlag == 0						;If player is not making directional input

			~Eval _Player_Pushing  = 0						;Reset the pushing flag

			~If   _Player_Path_Vel > 0						;If player Path velocity is positive

				~Eval _Player_Path_Vel -= _Player_Decel				;Apply deceleration
				~If   _Player_Path_Vel  < 0					;If player Path velocity went below 0
					~Eval _Player_Path_Vel = 0				;Force velocity to 0
				~EndIf								;Path velocity overflow test

			~Else									;If Path velocity is Negative

				~Eval _Player_Path_Vel += _Player_Decel				;Apply deceleration
				~If   _Player_Path_Vel  > 0					;If player Path velocity went above 0
					~Eval _Player_Path_Vel = 0				;Force velocity to 0
				~EndIf								;Path velocity overflow test

			~EndIf									;Path velocity sign test

		~EndIf										;Directional input test

		~Eval _Player_InputMotionFlag = 0						;Reset the vector velocity input flag

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Handle slipping off steep curves
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_SlipTimer > 0							;If player slip counter is active
			~Eval _Player_SlipTimer--						;Decrease the timer
		~Else										;If player slip counter isn't on, check to see if it should be

; 			~If _Player_Was_On_Path != 0						;Don't perform the slip test if the player just landed on the path, skipping this test will prevent him from immediately detaching from walls and having his rotated sprite fall into them, giving the path routine the opportunity to keep that from happening

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Set "Animation Speed" here as absolute value of path speed and
				;test whether the player is moving fast enough to stay on the curve
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_AniSpeed = +_Player_Path_Vel			;When player is on a path (or solid object in path direction), set his animation speed to his path speed (if it's negative, it should be made positive)

				~If _Player_AniSpeed < 640					;If player is moving slow enough to slip

; 					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 					;Sonic 1 and 2 style slip test
; 					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 					~Eval _Angle  = _Player_Path_Angle + 32			;Rotate the player angle by 32 for testing
; 					~Eval _Angle &= 192					;Limit the angle to 255 units and keep only the part above 90 degrees (Will cause 0, 64, 128, or 192)
; 					~If   _Angle != 0					;If the angle value is significant enough

					;;;;;;;;;;;;;;;;;;;;;;;;
					;Sonic 3 style slip test
					;;;;;;;;;;;;;;;;;;;;;;;;

					~Eval _Angle  = _Player_Path_Angle + $18		;Rotate the player angle by 24 for testing
					~Eval _Angle &= $FF					;Limit the angle to 255 units
					~If   _Angle >= $30					;If the angle value is significant enough

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Slip has occurred, set timer and test for very steep angle
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~Eval _Player_SlipTimer = 30			;Set the slip timer, player loses input control
						~Eval _Angle  = _Player_Path_Angle + $30	;Rotate the player angle by 48 for testing
						~Eval _Angle &= $FF				;Limit the angle to 255 units
						~If   _Angle >= 60				;If the angle value is significant enough

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Steep angle, set player for leaving the path
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~Eval _Player_On_Path   = 0		;Player leaves the path
							~Eval _Player_Path_Vel  = _Player_X_Vel	;In normal gravity, set the path velocity equal to the current X velocity for proper movement when player leaves a path
							~Eval _Player_Path_Mode = 0

						~Else						;If the angle value is insufficient to cause a slip

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Not steep enough to fall off, just apply extra deceleration
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~If _Angle >= 48			;If the angle value was a left-side angle
								~Eval _Player_Path_Vel += 128	;Add 128 rightward motion to player
							~Else					;If the angle value was a right-side angle
								~Eval _Player_Path_Vel -= 128	;Add 128 leftward motion to player
							~EndIf					;Testing angle direction

						~EndIf						;If angle is significant enough for player to fall off path

					~EndIf							;If angle is significant enough to cause a slip

				~EndIf								;If speed is slow enough to slip off significant angles

; 			~EndIf									;If player is on a path

		~EndIf										;Player Slip Counter


	~EndIf											;On path test

	;;;;;;;;;;;;;;;;;;;;;
	;Handle Player Timers
	;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Flash_Timer > 0								;If player is in an invulnerable flash
		~Eval _Player_Flash_Timer--							;Decrease the timer
	~EndIf											;Player invulnerable timer

# End	;Terminate Function

;===================================================================================
;===================================
;Function 12 - Standing Mode Update=
;===================================

	.Alias PObjPtr
	.Alias ObjPtr
	.Alias Angle

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;All modes require these values to be reset at the beginning
	;of each animation update. They are turned on later if needed
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_InvertMirror = 0														;Reset the horizontal mirror flag
	~Eval _Player_InvertFlip   = 0														;Reset the vertical flip flag

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Remember the Pushing state. Do this first so no other states prevent this from occurring
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_PreviousPushState = _Player_Pushing										;Backup the pushing state so it can be properly tested by other functions during the next frame

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;This mode needs tripping reset also
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_TripFlag = 0														;Reset the "tripping" flag, turn it on only if player is detected as "tripping" this frame

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Set up the proper animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for "floating" state
	;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_FloatingFlag != 0														;If player is "floating"

		#_Player_SetAni_ByList														;Set player to use his current "floating" animation
		: l17																		;Player Current Floating Animation

		#_StopCurrentFunc														;Proper animation set, stop the update function

	~EndIf																	;Floating test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Allow the skidding animation even in midair
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Skidding != 0														;If player is skidding to slow down

		#_Player_SetAni_ByList														;Set player to use his skidding animation
		: l16																		;Player Skidding Animation

		#_StopCurrentFunc														;Proper animation set, stop the update function

	~EndIf																	;Skidding test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for other animations that require the player to be on a path
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path != 0														;If player is on the ground

		;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for "ducking" state
		;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Ducking != 0													;If player is ducking

			#_Player_SetAni_ByList													;Set player to use his ducking animation
			: l13																	;Player Current Ducking Animation

			#_StopCurrentFunc													;Proper animation set, stop the update function

		~EndIf																;Ducking test

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for "looking up" state
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_LookingUp != 0													;If player is looking up

			#_Player_SetAni_ByList													;Set player to use his looking up animation
			: l22																	;Player Current Looking Up Animation

			#_StopCurrentFunc													;Proper animation set, stop the update function

		~EndIf																;Looking up test

		;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for "pushing" state
		;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Pushing != 0													;If player is pushing something

			#_Player_SetAni_ByList													;Set Player to use his pushing animation
			: l10															;Player Pushing

			#_StopCurrentFunc													;Proper animation set, stop the update function

		~EndIf																;Pushing test

		;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for "tripping" state
		;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Path_Vel == 0													;If player is not moving along the path, see if he should be tripping over an edge

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Perform different trip tests for when the player is standing
			;on a level path, a tiled path object, or a solid object
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_On_Path > 0													;If player is standing on a level tile plane

				;;;;;;;;;;;;;;;;;;;;;;
				;Level Plane Trip Test
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Initialize variables for the drop test loop
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_Chr=Chr_MainBound												;Check at player's bottom boundary
				:_Bound_Y2															;(Side)
				:_Player_Temp															;(Dest) Trip Y Pos

				~Eval _Player_TripTest_SolidLineCounter      = 0								;Reset the line counter
				~Eval _Player_TripTest_SolidPixelAccumulator = 0								;Reset the temp value to 0 for an empty pixel accumulator

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Test a line 8 pixels tall to try and find solidity. If no solidity,
				;assume no slope, and set tripping animation. Player on steep slope
				;shouldn't get this far due to having an angle value
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Do 														;Start the test loop

					#_Chr=TileInfo(Chr)											;Read the solidity at this point in the line below the player at his horizontal center
					:_Player_X_Pos														;(Chr0 X)
					:_Player_Temp														;(Chr0 Y)       Trip Y Pos
					:_Player_Solid_Plane													;(Chr0 PlaneID)
					:_Player_TripTest_HotspotValue												;(Chr2  TileID) Throw Away
					:_Player_TripTest_HotspotValue												;(Chr2   Floor) Throw Away
					:_Player_Collision_Angle												;(Chr2   Angle) Used for angle of the last freespace tile
					:_Player_TripTest_HotspotValue												;(Chr2   Solid)

					~If _Player_Collision_Angle != 0									;If a nonzero angle was found

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Nonzero angle value found. Assume the "freespace"
						;area is actually part of a slope and do not trip
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~Eval _Player_TripTest_SolidPixelAccumulator++							;Flag "solidity" so the trip doesn't take effect
						~BreakDo											;Cancel this testing loop

					~EndIf

					~Eval _Player_TripTest_SolidPixelAccumulator += _Player_TripTest_HotspotValue				;Add this solidity value to the accumulator (If any pixel on this line is solid, the accumulator will read nonzero at the end)
					~Eval _Player_Temp++											;Move the testing point down
					~Eval _Player_TripTest_SolidLineCounter++								;Increase the counter

				~While _Player_TripTest_SolidLineCounter < 8									;Keep testing pixels until the counter reaches 8

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;If there is no solidity along an 8-pixel column at the
				;centerpoint, player should play tripping animation
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Player_TripTest_SolidPixelAccumulator == 0									;Test the solidity accumulator

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Since the player will be tripping, he must always face in the direction of the
					;cliff. Determine that direction by testing a horizontal line below the player
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					#_Chr=Chr_MainBound											;Check at player's bottom boundary
					:_Bound_Y2														;(Side)
					:_Player_Temp														;(Dest) Trip Y Pos

					~Eval _Player_VPXBackup                      = _Player_X_Pos						;Start check at player's center
					~Eval _Player_TripTest_SolidLineCounter      = 0							;Reset the line counter
					~Eval _Player_TripTest_SolidPixelAccumulator = 0							;Reset the temp value to 0 for an empty pixel accumulator

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Test a line 8 pixels wide [Player's half-width] to try and find
					;solidity on this side of the player. If solidity, the land he is
					;standing on is to his right. If not, it must be on his left
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~Do													;Start the test loop

						#_Chr=TileInfo(Chr)										;Read the solidity at this point in the line
						:_Player_VPXBackup												;(Chr0 X)       Used for Trip X Pos
						:_Player_Temp													;(Chr0 Y)       Trip Y Pos
						:_Player_Solid_Plane												;(Chr0 PlaneID)
						:_Player_TripTest_HotspotValue											;(Chr2  TileID) Throw Away
						:_Player_TripTest_HotspotValue											;(Chr2   Floor) Throw Away
						:_Player_TripTest_HotspotValue											;(Chr2   Angle) Throw Away
						:_Player_TripTest_HotspotValue											;(Chr2   Solid)

						~Eval _Player_TripTest_SolidPixelAccumulator += _Player_TripTest_HotspotValue			;Add this solidity value to the accumulator (If any pixel on this line is solid, the accumulator will read nonzero at the end)
						~Eval _Player_VPXBackup++									;Move the testing point down
						~Eval _Player_TripTest_SolidLineCounter++							;Increase the counter

					~While _Player_TripTest_SolidLineCounter < 9								;Keep testing pixels until the counter reaches 9  (Player's half-width)

					~If _Player_TripTest_SolidPixelAccumulator == 0								;If there is no solidity along a 9-pixel (Player's half-width) row from player center to player right, the ground must be to the player's left
						~Eval _Player_X_Dir = _Right									;Make player face right during tripping
					~Else													;Solidity was found on player's right
						~Eval _Player_X_Dir = _Left									;Make player face left during tripping
					~EndIf													;Which side of the player is standing on the surface?

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Now set the tripping animation to play
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					#_Player_SetAni_ByList											;Set Player to use his "tripping" animation
					: l21															;Player Tripping

					~Eval _Player_TripFlag = 1										;Set the "tripping" flag to signify that the player is tripping

					#_StopCurrentFunc											;Proper animation set, stop the update function

				~EndIf														;Is the column empty?

			~Else															;If player is standing on an object

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;The player is standing on an object. Obtain a
				;pointer, test whether or not it still exists,
				;and set up for collision testing
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_FetchObject_CurrChr												;Fetch the object the player is standing on
				:_Ref_All
				:_Player_On_Path

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;This object status can only be tested on the same frame on which it was destroyed,
				;as its RAM is freed at the end of the Object Movement Phase, but not before
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Object_Type != 0												;If the object was not destroyed earlier this frame

					#_Reg=Ptr(CurrObj)											;Get a pointer to the current object [The player movement object controlling this player] so that it can be restored below
					:_PObjPtr

					#_Reg=Ptr(FetchedObj)											;Get a pointer to the object that the player is standing on
					:_ObjPtr

					#_Set_CurrObj(Reg)											;Set CurrObj (the object regarded as "currently active") to the platform object, so that it will be the target of the collision tests below
					:_ObjPtr

					#_Player_SetCollisionArea_CurrObj									;Make sure the player's boundaries are set properly for collision with the object relative to its position in the looping (or non-looping) area
					:r_Curr_Player

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Perform different trip tests depending on whether the
					;object is a tiled path object, or a normal solid object
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~If _Object_Tag == 65535										;If the object is tagged as a tiled platform object

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Tiled Path Object Trip Test
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Initialize variables for the drop test loop
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						#_Chr=Chr_MainBound_Wrapped									;Check at player's bottom boundary, using the form made relative to the position of the target platform object
						:_Bound_Y2													;(Side)
						:_Player_Temp													;(Dest) Trip Y Pos

						#_Chr=Chr_MainBound_Wrapped									;Check at player's center, using the form made relative to the position of the target platform object
						:_Bound_HCenter													;(Side)
						:_Player_VPXBackup												;(Dest) Used as Trip X Pos

						~Eval _Player_TripTest_SolidLineCounter      = 0						;Reset the line counter
						~Eval _Player_TripTest_SolidPixelAccumulator = 0						;Reset the temp value to 0 for an empty pixel accumulator

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Test a line 8 pixels tall to try and find solidity. If no solidity,
						;assume no slope, and set tripping animation. Player on steep slope
						;shouldn't get this far due to having an angle value
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~Do 												;Start the test loop

							#_Chr=TiledObjInfo(Chr)									;Read the solidity at this point in the line below the player at his horizontal center
							:_Player_VPXBackup											;(Chr0 X) Used as Trip X Pos
							:_Player_Temp												;(Chr0 Y) Trip Y Pos
							:_Player_Solid_Plane											;(Chr0 PlaneID)
							:_Player_TripTest_HotspotValue										;(Chr2  TileID) Throw Away
							:_Player_TripTest_HotspotValue										;(Chr2   Floor) Throw Away
							:_Player_TripTest_HotspotValue										;(Chr2   Angle) Throw Away
							:_Player_TripTest_HotspotValue										;(Chr2   Solid)

							~Eval _Player_TripTest_SolidPixelAccumulator += _Player_TripTest_HotspotValue		;Add this solidity value to the accumulator (If any pixel on this line is solid, the accumulator will read nonzero at the end)
							~Eval _Player_Temp++									;Move the testing point down
							~Eval _Player_TripTest_SolidLineCounter++						;Increase the counter

						~While _Player_TripTest_SolidLineCounter < 8							;Keep testing pixels until the counter reaches 8

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;If there is no solidity along an 8-pixel column at the
						;centerpoint, player should play tripping animation
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~If _Player_TripTest_SolidPixelAccumulator == 0							;Test the solidity accumulator

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Since the player will be tripping, he must always face in the direction of the
							;cliff. Determine that direction by testing a horizontal line below the player
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_Chr=Chr_MainBound_Wrapped								;Check at player's bottom boundary, using the form made relative to the position of the target platform object
							:_Bound_Y2												;(Side)
							:_Player_Temp												;(Dest) Trip Y Pos

							#_Chr=Chr_MainBound_Wrapped								;Check at player's center, using the form made relative to the position of the target platform object
							:_Bound_HCenter												;(Side)
							:_Player_VPXBackup											;(Dest) Used as Trip X Pos

							~Eval _Player_TripTest_SolidLineCounter      = 0					;Reset the line counter
							~Eval _Player_TripTest_SolidPixelAccumulator = 0					;Reset the temp value to 0 for an empty pixel accumulator

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Test a line 8 pixels wide [Player's half-width] to try and find
							;solidity on this side of the player. If solidity, the land he is
							;standing on is to his right. If not, it must be on his left
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~Do											;Start the test loop

								#_Chr=TiledObjInfo(Chr)								;Read the solidity at this point in the line below the player at his horizontal center
								:_Player_VPXBackup										;(Chr0 X) Used as Trip X Pos
								:_Player_Temp											;(Chr0 Y) Trip Y Pos
								:_Player_Solid_Plane										;(Chr0 PlaneID)
								:_Player_TripTest_HotspotValue									;(Chr2  TileID) Throw Away
								:_Player_TripTest_HotspotValue									;(Chr2   Floor) Throw Away
								:_Player_TripTest_HotspotValue									;(Chr2   Angle) Throw Away
								:_Player_TripTest_HotspotValue									;(Chr2   Solid)

								~Eval _Player_TripTest_SolidPixelAccumulator += _Player_TripTest_HotspotValue	;Add this solidity value to the accumulator (If any pixel on this line is solid, the accumulator will read nonzero at the end)
								~Eval _Player_VPXBackup++							;Move the testing point to the right
								~Eval _Player_TripTest_SolidLineCounter++					;Increase the counter

							~While _Player_TripTest_SolidLineCounter < 9						;Keep testing pixels until the counter reaches 9  (Player's half-width)

							~If _Player_TripTest_SolidPixelAccumulator == 0						;If there is no solidity along a 9-pixel (Player's half-width) row from player center to player right, the ground must be to the player's left
								~Eval _Player_X_Dir = _Right							;Make player face right during tripping
							~Else											;Solidity was found on player's right
								~Eval _Player_X_Dir = _Left							;Make player face left during tripping
							~EndIf											;Which side of the player is standing on the surface?

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Now set the tripping animation to play
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_Player_SetAni_ByList									;Set Player to use his "tripping" animation
							: l21															;Player Tripping

							~Eval _Player_TripFlag = 1								;Set the "tripping" flag to signify that the player is tripping

							#_StopCurrentFunc									;Proper animation set, stop the update function

						~EndIf												;Is the column empty?

					~Else													;If the object is a normal sprite/boundbox object

						;;;;;;;;;;;;;;;;;;;;;;;
						;Solid Object Trip Test
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Initialize variables for the drop test
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						#_Chr=Chr_MainBound_Wrapped									;Get the form of the player's X position made relative to the position of the target solid object
						:_Bound_HCenter													;(Side)
						:_Player_Temp													;(Dest) Wrapped X Pos

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Determine which side of the object the player is standing on
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~If _Player_Temp <= _Object_X_Pos								;If player is standing on the left side of the object

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Find distance between player's center and object's left to determine tripping state
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_Chr=Object_Boundary_Offset								;Find out where the plaer's center is relative to the object's left side
							:_BoxOff_HCenter											;(Chr)
							:_BoundTest_Left											;(Obj)
							:_Player_TripTest_HotspotValue										;(Dest)

							~If _Player_TripTest_HotspotValue < 0							;If the player's center is farther left than the left side of the object, it is in freespace. Do a trip

								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
								;Set player's direction and tripping state
								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


								#_Player_SetAni_ByList								;Set Player to use his "tripping" animation
								: l21 ;Player Tripping

								~Eval _Player_TripFlag = 1							;Set the "tripping" flag to signify that the player is tripping
								~Eval _Player_X_Dir    = _Left							;Make player face left during tripping

								#_StopCurrentFunc								;Proper animation set, stop the update function

							~EndIf											;Is player's center not on the object?

						~Else												;If player is standing on the right side of the object

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Find distance between player's center and object's right to determine tripping state
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_Chr=Object_Boundary_Offset								;Find out where the player's center is relative to the object's right side
							:_BoxOff_HCenter											;(Chr)  X Position
							:_BoundTest_Right											;(Obj)  Right Boundary
							:_Player_TripTest_HotspotValue										;(Dest)

							~If _Player_TripTest_HotspotValue > 0							;If the player's center is farther right than the right side of the object, it is in freespace. Do a trip (0 means the two positions are equal)

								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
								;Set player's direction and tripping state
								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


								#_Player_SetAni_ByList								;Set Player to use his "tripping" animation
								: l21												;Player Tripping

								~Eval _Player_TripFlag = 1							;Set the "tripping" flag to signify that the player is tripping
								~Eval _Player_X_Dir    = _Right							;Make player face right during tripping

								#_StopCurrentFunc								;Proper animation set, stop the update function

							~EndIf											;Is player's center not on the object?

						~EndIf												;On which half of the object is the player standing?

					~EndIf													;Is the object tiled or a sprite?

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;No tripping, restore the "Current Object" pointer
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					#_Set_CurrObj(Reg)											;Restore CurrObj (the object regarded as "currently active") to the Player Movement object controlling this player
					:_PObjPtr														;Player Move Ptr

				~EndIf														;Was the object not destroyed earlier this frame?
			~EndIf															;Is player standing on level or object?

		~EndIf																;Is player moving along the path?

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Set Player_AniSpeed again here to ensure that it is
		;correct after any speed modification such as deceleration
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_AniSpeed = +_Player_Path_Vel
@AniSpeed_WallReset
		~Eval _Player_AniDelay   = $900 - _Player_AniSpeed										;Create the delay value
		~Eval _Player_AniDelay >>= 8													;Use only the full pixel portion of the speed
		~If   _Player_AniDelay   < 1													;If the delay value is less than 1
			~Eval _Player_AniDelay = 1												;Set minimum delay
		~EndIf																;If the delay value is less than 1

		#_GoTo																;Skip over the test for walls. It is unnecessary now, and it may in fact be what triggered this code
		: @AniSpeed_DontTestWalls

	~EndIf																	;On ground test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the player is not on a path, test for collision with walls. Midair
	;collision with walls should set the animation speed back to minimum
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Cant_Go_Left != 0														;If the player has struck a wall to the left
		~Eval _Player_AniSpeed = 1													;Minimize his animation speed
		#_GoTo																;Set the animation delay according to the minimized speed
		: @AniSpeed_WallReset
	~EndIf

	~If _Player_Cant_Go_Right != 0														;If the player has struck a wall to the left
		~Eval _Player_AniSpeed = 1													;Minimize his animation speed
		#_GoTo																;Set the animation delay according to the minimized speed
		: @AniSpeed_WallReset
	~EndIf
@AniSpeed_DontTestWalls
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 	;Try the walking/running animations if the player is moving
	;along a path, or was right before he was sent into freespace
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_AniSpeed != 0														;If player vector velocity was nonzero when he was last touching the ground

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Determine which version of the walking/running animation to use based on the player's orientation
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Angle   = _Player_Path_Angle + 16											;Rotate the player angle by 16 (half of 45 degrees) to obtain his orientation
		~Eval _Angle  &= 255														;Limit the angle to 255 units (anything higher wraps around)
		~Eval _Angle >>= 5														;Divide the angle by 32 to determine which of the 8 possible display angles the player should use

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Determine whether player is walking or running
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_AniSpeed >= _Player_RunVel1												;If player speed is enough to be running instead of walking

			~If _Player_AniSpeed >= _Player_RunVel2											;If player is moving fast enough to use the secondary animation

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Set running animation according to the player's orientation
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_StateSwitch(Reg)												;Test player's orientation
				:_Angle																;(Reg) Player Orientation
				:   8																;There are 8 possible orientations
				: @Run2_Floor															;(State 0) Flat Floor
				: @Run2_BottomLeft														;(State 1) Bottom-left Curve
				: @Run2_LeftWall														;(State 2) Left Wall
				: @Run2_TopLeft															;(State 3) Top-Left Curve
				: @Run2_Ceiling															;(State 4) Ceiling
				: @Run2_TopRight														;(State 5) Top-Right Curve
				: @Run2_RightWall														;(State 6) Right Wall
				: @Run2_BottomRight														;(State 7) Bottom-Right Curve

					;;;
					;Floor Running
					;;;
@Run2_Floor
					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					: l26															;(New ID) Running Floor
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Bottom-Left Curve Running
					;;;
@Run2_BottomLeft
					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1															;1 Animation
						:  l1															;(Ref ID) Running Animation Slot
						: l29															;(New ID) Running Angle Down
						:_AniChange_Smooth													;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1															;1 Animation
						:  l1															;(Ref ID) Running Animation Slot
						: l27															;(New ID) Running Angle Up
						:_AniChange_Smooth													;(Smooth)

					~EndIf													;Player Direction Test

					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Left Wall Running
					;;;
@Run2_LeftWall
					~If _Player_X_Dir == _Right										;If player is facing right

						~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
						~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					~EndIf													;Player Direction Test

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					: l28															;(New ID) Running Wall
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Top-Left Curve Running
					;;;
@Run2_TopLeft
					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						: l27														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						: l29														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Ceiling Running
					;;;
@Run2_Ceiling
					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					: l26															;(New ID) Running Floor
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Top-Right Curve Running
					;;;
@Run2_TopRight
					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						: l29														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						: l27														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Right Wall Running
					;;;
@Run2_RightWall
					~If _Player_X_Dir == _Left										;If player is facing left

						~Eval _Player_InvertMirror = 1									;This orientation mirrors the player
						~Eval _Player_InvertFlip   = 1									;This orientation flips the player

					~EndIf													;Player Direction Test

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					: l28															;(New ID) Running Wall
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Bottom-Right Curve Running
					;;;
@Run2_BottomRight
					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						: l27														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						: l29														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

			~Else															;If player is NOT running fast enough for the secondary animation

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Set running animation according to the player's orientation
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_StateSwitch(Reg)												;Test player's orientation
				:_Angle																;(Reg) Player Orientation
				:   8																;There are 8 possible orientations
				: @Run_Floor															;(State 0) Flat Floor
				: @Run_BottomLeft														;(State 1) Bottom-left Curve
				: @Run_LeftWall															;(State 2) Left Wall
				: @Run_TopLeft															;(State 3) Top-Left Curve
				: @Run_Ceiling															;(State 4) Ceiling
				: @Run_TopRight															;(State 5) Top-Right Curve
				: @Run_RightWall														;(State 6) Right Wall
				: @Run_BottomRight														;(State 7) Bottom-Right Curve

					;;;
					;Floor Running
					;;;
@Run_Floor
					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					:  l6															;(New ID) Running Floor
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Bottom-Left Curve Running
					;;;
@Run_BottomLeft
					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l9														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l7														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Left Wall Running
					;;;
@Run_LeftWall
					~If _Player_X_Dir == _Right										;If player is facing right

						~Eval _Player_InvertMirror = 1									;This orientation mirrors the player
						~Eval _Player_InvertFlip   = 1									;This orientation flips the player

					~EndIf													;Player Direction Test

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					:  l8															;(New ID) Running Wall
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Top-Left Curve Running
					;;;
@Run_TopLeft
					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l7														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l9														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Ceiling Running
					;;;
@Run_Ceiling
					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					:  l6															;(New ID) Running Floor
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Top-Right Curve Running
					;;;
@Run_TopRight
					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l9														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l7														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Right Wall Running
					;;;
@Run_RightWall
					~If _Player_X_Dir == _Left										;If player is facing left

						~Eval _Player_InvertMirror = 1									;This orientation mirrors the player
						~Eval _Player_InvertFlip   = 1									;This orientation flips the player

					~EndIf													;Player Direction Test

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l1															;(Ref ID) Running Animation Slot
					:  l8															;(New ID) Running Wall
					:_AniChange_Smooth													;(Smooth)
					#_GoTo													;End this state and set the player's animation
					: @Run_SetAni

					;;;
					;Bottom-Right Curve Running
					;;;
@Run_BottomRight
					~If _Player_X_Dir == _Right										;If player is facing right

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l7														;(New ID) Running Angle Up
						:_AniChange_Smooth												;(Smooth)

					~Else													;If player is facing left

						#_Player_ChangeAnimations									;Set the running animation slot to the animation for this orientation
						:   1														;1 Animation
						:  l1														;(Ref ID) Running Animation Slot
						:  l9														;(New ID) Running Angle Down
						:_AniChange_Smooth												;(Smooth)

					~EndIf													;Player Direction Test

			~EndIf
@Run_SetAni
			#_Player_SetAni_ByList													;Make sure the currently-set walk/run animation is playing
			:  l1																	;Current Player Running Animation

		~Else																;If player is NOT moving fast enough to use the secondary animation

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Set running animation according to the player's orientation
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_StateSwitch(Reg)													;Test player's orientation
			:_Angle																;(Reg) Player Orientation
			:   8																	;There are 8 possible orientations
			: @Walk_Floor																;(State 0) Flat Floor
			: @Walk_BottomLeft															;(State 1) Bottom-left Curve
			: @Walk_LeftWall															;(State 2) Left Wall
			: @Walk_TopLeft																;(State 3) Top-Left Curve
			: @Walk_Ceiling																;(State 4) Ceiling
			: @Walk_TopRight															;(State 5) Top-Right Curve
			: @Walk_RightWall															;(State 6) Right Wall
			: @Walk_BottomRight															;(State 7) Bottom-Right Curve

				;;;
				;Floor Walking
				;;;
@Walk_Floor
				#_Player_ChangeAnimations											;Set the running animation slot to the animation for this orientation
				:   1																;1 Animation
				:  l0																;(Ref ID) Walking Animation Slot
				:  l2																;(New ID) Walking Floor
				:_AniChange_Smooth														;(Smooth)
				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Bottom-Left Curve Walking
				;;;
@Walk_BottomLeft
				~If _Player_X_Dir == _Right											;If player is facing right

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l5															;(New ID) Walking Angle Down
					:_AniChange_Smooth													;(Smooth)

				~Else														;If player is facing left

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l3															;(New ID) Walking Angle Up
					:_AniChange_Smooth													;(Smooth)

				~EndIf														;Player Direction Test

				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Left Wall Walking
				;;;
@Walk_LeftWall
				~If _Player_X_Dir == _Right											;If player is facing right

					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

				~EndIf														;Player Direction Test

				#_Player_ChangeAnimations											;Set the running animation slot to the animation for this orientation
				:   1																;1 Animation
				:  l0																;(Ref ID) Walking Animation Slot
				:  l4																;(New ID) Walking Wall
				:_AniChange_Smooth														;(Smooth)
				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Top-Left Curve Walking
				;;;
@Walk_TopLeft
				~Eval _Player_InvertMirror = 1											;This orientation mirrors the player
				~Eval _Player_InvertFlip   = 1											;This orientation flips the player

				~If _Player_X_Dir == _Right											;If player is facing right

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l3															;(New ID) Walking Angle Up
					:_AniChange_Smooth													;(Smooth)

				~Else														;If player is facing left

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l5															;(New ID) Walking Angle Down
					:_AniChange_Smooth													;(Smooth)

				~EndIf														;Player Direction Test

				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Ceiling Walking
				;;;
@Walk_Ceiling
				~Eval _Player_InvertMirror = 1											;This orientation mirrors the player
				~Eval _Player_InvertFlip   = 1											;This orientation flips the player

				#_Player_ChangeAnimations											;Set the running animation slot to the animation for this orientation
				:   1																;1 Animation
				:  l0																;(Ref ID) Walking Animation Slot
				:  l2																;(New ID) Walking Floor
				:_AniChange_Smooth														;(Smooth)
				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Top-Right Curve Walking
				;;;
@Walk_TopRight
				~Eval _Player_InvertMirror = 1											;This orientation mirrors the player
				~Eval _Player_InvertFlip   = 1											;This orientation flips the player

				~If _Player_X_Dir == _Right											;If player is facing right

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l5															;(New ID) Walking Angle Down
					:_AniChange_Smooth													;(Smooth)

				~Else														;If player is facing left

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l3															;(New ID) Walking Angle Up
					:_AniChange_Smooth													;(Smooth)

				~EndIf														;Player Direction Test

				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Right Wall Walking
				;;;
@Walk_RightWall
				~If _Player_X_Dir == _Left											;If player is facing left

					~Eval _Player_InvertMirror = 1										;This orientation mirrors the player
					~Eval _Player_InvertFlip   = 1										;This orientation flips the player

				~EndIf														;Player Direction Test

				#_Player_ChangeAnimations											;Set the running animation slot to the animation for this orientation
				:   1																;1 Animation
				:  l0																;(Ref ID) Walking Animation Slot
				:  l4																;(New ID) Walking Wall
				:_AniChange_Smooth														;(Smooth)
				#_GoTo														;End this state and set the player's animation
				: @Walk_SetAni

				;;;
				;Bottom-Right Curve Walking
				;;;
@Walk_BottomRight
				~If _Player_X_Dir == _Right											;If player is facing right

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l3															;(New ID) Walking Angle Up
					:_AniChange_Smooth													;(Smooth)

				~Else														;If player is facing left

					#_Player_ChangeAnimations										;Set the running animation slot to the animation for this orientation
					:   1															;1 Animation
					:  l0															;(Ref ID) Walking Animation Slot
					:  l5															;(New ID) Walking Angle Down
					:_AniChange_Smooth													;(Smooth)

				~EndIf														;Player Direction Test

@Walk_SetAni
			#_Player_SetAni_ByList													;Make sure the currently-set walk/run animation is playing
			:  l0																	;Current Player Walking Animation

		~EndIf																;Running Speed test

	~Else																	;If player is standing still

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Not moving or in a special state, play the animation for standing still
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Player_SetAni_ByList														;Set Player to use his "Standing" animation
		:   0																		;Standing Animation

	~EndIf																	;Player Animation Speed test

# End	;Terminate Function

;===================================================================================
;=====================================
;Function 13 - Spinning Mode Movement=
;=====================================

	.Alias Angle
	.Alias Keys
	.Alias Vel

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for demo recording/playback, and act accordingly
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_RunFunc_CurrPlayer_Const
	: 26 ;Demo Control

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Do player physics (gravity, slope, etc) handling
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0									;If player is not standing on a path or a solid object

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Player not on a path should be using the path mode corresponding with his gravity mode.
		;This should be done outside of the test for player leaving a path in case the player
		;falls onto the floor from freespace and hits an angle that changes his angle mode as
		;he slides across, and falls off the edge of the path, all in the same frame.
		;Also run before the test for striking a ceiling path, because if it were done
		;afterward, it would unconditionally pull him back off
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Path_Mode = _PPM_Floor							;In normal gravity, force player back to floor path mode when in freespace

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for player striking a ceiling (hitting near-wall curves causes player to grip them)
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Cant_Go_Up != 0								;If player has a solid ceiling above

			~Eval _Angle  = _Player_Motion_Angle - $20					;Determine whether the player is moving toward the ceiling, and more more vertically than horizontally (within the arc \/ )
			~Eval _Angle &= $C0								;"
			~If   _Angle == $80								;"

				~If _Player_SlipTimer != 0						;If the slip counter is on, the player probably just fell off the ceiling
					#_GoTo								;The player likely struck the ceiling again from some upward momentum from falling off an angled ceiling, don't let it make him re-grip
					: @Ceiling_NoGrip
				~EndIf									;Collision Angle Region Test

				~Eval _Angle  = _Player_Collision_Angle + $20				;Determine whether the angle of the ceiling section that the player has just struck is within proper range for grabbing on
				~Eval _Angle &= $40							;"
				~If   _Angle == 0							;"
					#_GoTo								;The angle is too similar to a horizontal ceiling, skip over this part and don't grip it
					: @Ceiling_NoGrip
				~EndIf

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;When taking a ceiling path, Path Velocity is set based on Y Velocity
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Path_Vel = _Player_Previous_Y_Vel				;Convert Player's Y Velocity to rightward Path Velocity
				~If   _Player_Collision_Angle >=$80					;If the angle that was struck is a right-side angle
					~Eval -_Player_Path_Vel						;Reverse converted Path Velocity
				~EndIf

				~If _Player_Path_Vel < 256
					~If _Player_Path_Vel > -256
						#_GoTo							;The player is moving too slowly to stay attached to the ceiling. Abort grip
						: @Ceiling_NoGrip
					~EndIf
				~EndIf

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Score bonus increment resets when player strikes a path
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_EnemyScoreCtr = 0						;Reset the successive enemy score increment

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Some flags are reset upon sriking a new path
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Skidding   = 0						;Force disable skidding when striking a new path
				~Eval _Player_JumpedFlag = 0						;After landing, reset the jump flag

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Use the angle of the ground to determine which path mode the player should enter
				;(player landing on tops of curves should change to wall path to keep fluid motion)
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Path_Mode   = 256 - _Player_Collision_Angle		;First, Mirror the angle, because left and right path mode IDs are reverse of those derived from the unmirrored path angle
				~Eval _Player_Path_Mode  += 32						;Rotate the player angle by 32 to obtain his orientation
				~Eval _Player_Path_Mode  &= 255						;Limit the angle to 255 units (anything higher wraps around)
				~Eval _Player_Path_Mode >>= 6						;Divide the angle by 64 to determine which of the 4 possible path modes the player should use

				;;;;;;;;;;;;;;;;;;;;;;
				;Set Path ID and Angle
				;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_On_Path    = _Player_Cant_Go_Up				;Flag the player as being on the path by setting his Player_On_Path variable equal to the solid plane/sprite value in his Player_Cant_Go_Up
				~Eval _Player_Path_Angle = _Player_Collision_Angle			;Set the player's Path angle to the angle he attatched to so he is oriented correctly

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;When not in forced-spin mode, player should stand up when taking the path
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Player_TubeFlag == 0						;Test if Player is in forced-spin mode

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Force the spinning player to stand
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~Eval _Player_Movement_Mode = 0					;Send the player back to standing mode

					.Include macro\unspin.def					;Adjust the player's Y position to compensate

				~EndIf

				#_GoTo									;Player has gripped the ceiling and thus is on a path, behave as if on a path
				: @ApplySlopeAccel

			~EndIf										;Test if player has just collided with the ceiling

		~EndIf											;Test if there is a ceiling above the player
@Ceiling_NoGrip
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for player leaving a path and set velocity accordingly
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Was_On_Path != 0								;If player has just left a path (Was on a path during the last frame)
			~Eval _Player_Path_Vel = _Player_X_Vel						;In normal gravity, set the path velocity equal to the current X velocity for proper movement
		~EndIf											;Player just left a path test

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Apply jump height control (if in a normal jump and moving up)
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_ForceJumpFlag != 0								;Is this a forced jump?
			#_GoTo										;Skip manual height control to sustain the forced jump
			: @ForcedJump
		~EndIf

		~If _Player_JumpedFlag == 1								;Only apply height control for normal jump and during a bubble-shield bounce

			#_Keys_Pack									;Get Player's key states
			:r_Curr_Player											;Current Player's Map
			:_Keys												;Player KeyState

			~Eval _Keys &= $70								;Keep only the jump keys

			~If   _Keys == 0								;If the player is holding a jump button

				~If _Player_WaterFlag == 0						;If player isn't under water
					~Eval _Vel = -$400						;Set the upward velocity cap
				~Else									;If Player Under Water
					~Eval _Vel = -$200						;Set the upward velocity cap
				~EndIf

				~If _Vel > _Player_Y_Vel						;If the player's upward velocity is beyond the maximum
					~Eval _Player_Y_Vel = _Vel					;Cap upward velocity at the maximum
				~EndIf

			~EndIf										;Test for jump height control input

		~EndIf											;Test jumping mode
@ForcedJump
		~Eval _Player_Pushing       = 0								;Reset the pushing flag

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Apply Gravity and deceleration
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Y_Vel += _Player_Weight							;Apply Gravity Force

		~If   _Player_Y_Vel < 0									;If player's Y Speed is between 0 and -1024

			~If _Player_Y_Vel > -1024							;If player's Y Speed is between 0 and -1024

				~Eval _Vel = _Player_Path_Vel >> 5					;Find current speed divided by 32, to be used as the deceleration value

				~If   _Player_Path_Vel > 0						;If player Path velocity is positive

					~Eval _Player_Path_Vel -= _Vel					;Apply the deceleration. It will be positive in this case, so subtracting from current positive velocity will bring it closer to 0

					~If   _Player_Path_Vel < 0					;If player Path velocity went below 0
						~Eval _Player_Path_Vel = 0				;Force velocity to 0
					~EndIf								;Path Velocity overflow test

				~Else									;If Path Velocity is Negative

					~Eval _Player_Path_Vel -= _Vel					;Apply the deceleration. It will be negative in this case, so subtracting from current negative velocity will bring it closer to 0

					~If _Player_Path_Vel > 0					;If player Path velocity went above 0
						~Eval _Player_Path_Vel = 0				;Force velocity to 0
					~EndIf								;Path Velocity overflow test

				~EndIf									;Path Velocity sign test

			~EndIf										;Y Speed Test
		~EndIf											;Y Speed Test

	~Else												;If player is on a path or a solid object [unangled solid objects should force player path angle to 0]

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for just striking a path
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Was_On_Path == 0								;If player has just struck a path (Was not on a path during the last frame)

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Score bonus increment resets when player strikes a path
			;(Doing this now allows it to happen for bubble bounce)
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_EnemyScoreCtr = 0							;Reset the successive enemy score increment

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Manage Bubble Shield Bounce for Sonic
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_Type == _Sonic							;If player is Sonic

				~If _Player_ShieldType == _Shield_Water					;If player has a water shield

					~If _Player_JumpedFlag == 2					;If player used the bubble bounce

						~If _Player_InvincTimer != 0				;If player has invincibility
							#_GoTo						;Don't allow the bubble bounce
							: @NoBubbleBounce
						~EndIf

						~If _Player_WaterFlag == 0				;If player isn't under water
							#_Chr=SplitVector(ConstS,ChrA)			;Get X and Y velocities based on bounce speed and the angle of the floor that was struck
							:-$780								;(Const Speed)
							:_Player_Path_Angle						;(Chr0 Angle)
							:_Player_Y_Vel							;(Chr2 X)
							:_Player_Temp							;(Chr2 Y)
						~Else							;If Player Under Water
							#_Chr=SplitVector(ConstS,ChrA)			;Get X and Y velocities based on bounce speed and the angle of the floor that was struck
							:-$400								;(Const Speed)
							:_Player_Path_Angle						;(Chr0 Angle)
							:_Player_Y_Vel							;(Chr2 X)
							:_Player_Temp							;(Chr2 Y)
						~EndIf

						~Eval -_Player_Temp					;X Velocity comes out reverse of the desired result, so correct it

						~Eval  _Player_Path_Vel        += _Player_Temp		;Add the calculated horizontal velocity value to the current horizontal velocity
						~Eval  _Player_On_Path          = 0			;Detach the player from the path so he can bounce off
						~Eval  _Player_JumpedFlag       = 1			;Allow height control with Jump button by flagging a normal jump, also allows another bounce
						~Eval  _Player_ShieldAniTrigger = 3			;Trigger the shield's bouncing up animation

						#_PlaySound						;Play the bubble bounce sound
						:_SFX_BubbleBounce							;(ID)
						:_Snd_Once								;(Loop)

						#_GoTo							;Player bounced off the path, don't perform the rest of the collision handling
						: @SetAniSpeed

					~EndIf

				~EndIf

			~EndIf
@NoBubbleBounce
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Use the angle of the ground to determine which path mode the player should enter
			;(player landing on tops of curves should change to wall path to keep fluid motion)
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Path_Mode   = 256 - _Player_Collision_Angle			;First, Mirror the angle, because left and right path mode IDs are reverse of those derived from the unmirrored path angle
			~Eval _Player_Path_Mode  += 32							;Rotate the player angle by 32 to obtain his orientation
			~Eval _Player_Path_Mode  &= 255							;Limit the angle to 255 units (anything higher wraps around)
			~Eval _Player_Path_Mode >>= 6							;Divide the angle by 64 to determine which of the 4 possible path modes the player should use

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Use the player's angle of motion to determine how to
			;apply X/Y freespace velocity to his Path Velocity
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Angle   = _Player_Motion_Angle - $20					;Determine which quadrant the player's angle of motion occupies within a circle divided by an X
			~Eval _Angle  &= $C0								;"
			~Eval _Angle >>= 6								;"

			#_StateSwitch(Reg)								;Apply X/Y axis velocity to Path Velocity based on the angle of motion
			:_Angle
			:  4												;Four quadrants
			: @TakePath_Floor										;Mostly downward movement
			: @TakePath_Wall										;Mostly leftward movement
			: @TakePath_Done										;Mostly upward movement (Never used when taking a path connected to the floor)
			: @TakePath_Wall										;Mostly rightward movement

@TakePath_Floor
				~Eval _Angle  = _Player_Collision_Angle + $20				;Determine whether the angle of the floor section that the player has just struck is within the lower quadrant
				~Eval _Angle &= $40							;"
				~If   _Angle == 0							;"
					~Eval _Angle  = _Player_Collision_Angle + $10			;Determine whether the angle that was struck is near-flat
					~Eval _Angle &= $20						;"
					~If   _Angle != 0						;"
						~Eval _Player_Previous_Y_Vel >>= 1			;The floor isn't near-flat. Use Y velocity but cut it in half
						#_GoTo							;Go to convert the Y velocity to Path Velocity
						: @TakePath_YToPath
					~EndIf
					#_GoTo								;The floor is near-flat. Use X Velocity instead of Y Velocity to create the Path Velocity
					: @TakePath_Wall
				~EndIf

				~If _Player_Previous_Y_Vel > $FC0					;If Y Velocity is greater than $FC0 downward
					~Eval _Player_Previous_Y_Vel = $FC0				;Cap the velocity to be converted to a maximum of $FC0
				~EndIf
@TakePath_YToPath
				~Eval _Player_Path_Vel = _Player_Previous_Y_Vel				;Convert Player's Y Velocity to rightward Path Velocity
				~If   _Player_Collision_Angle >=$80					;If the angle that was struck is a right-side angle
					~Eval -_Player_Path_Vel						;Change the converted Y velocity to leftward Path Velocity
				~EndIf

				#_GoTo
				: @TakePath_Done
@TakePath_Wall
				~Eval _Player_Path_Vel = _Player_Previous_X_Vel				;Apply Player's X velocity directly to his Path Velocity
@TakePath_Done
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Some flags are reset upon sriking a new path
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Skidding   = 0							;Force disable skidding when striking a new path
			~Eval _Player_JumpedFlag = 0							;After landing, reset the jump flag

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;When not in forced-spin mode, player should stand up when taking the path
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_TubeFlag == 0							;If Player is not in forced-spin mode

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Force the spinning player to stand
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_Movement_Mode = 0						;Send the player back to standing mode

				.Include macro\unspin.def						;Adjust the player's Y position to compensate

			~EndIf

		~EndIf											;Testing if player was on a path last frame
@ApplySlopeAccel
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Apply acceleration to the player based on the slope of the path
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Reg=GetVectorY(ConstS,ChrA)								;Use the path angle and a vector force of 80 to obtain a path velocity to apply to the player
		: 80													;(Const Speed)
		:_Player_Path_Angle											;(Chr0  Angle)
		:_Vel													;(Reg   Y)     Gravity Effect

		~If _Player_Path_Vel > 0								;If player Path velocity is positive
			~If _Vel < 0									;If the speed change is negative
				~Eval _Vel >>= 2							;Divide the speed change by 4 when it is in the opposite direction of the current path velocity
			~EndIf										;Testing whether the speed change is negative
		~Else											;If player Path velocity is negative
			~If _Vel > 0									;If the speed change is positive
				~Eval _Vel >>= 2							;Divide the speed change by 4 when it is in the opposite direction of the current path velocity
			~EndIf										;Testing whether the speed change is positive
		~EndIf											;Testing the direction of player path velocity

		~Eval _Player_Path_Vel += _Vel								;Apply the acceleration to the player's path velocity

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Apply Deceleration if the player is on a path (even if they are attempting left/right input)
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Pushing  = 0								;Reset the pushing flag

		~If   _Player_Path_Vel > 0								;If player Path velocity is positive

			~Eval _Player_Path_Vel -= _Player_SpinDecel					;Apply spinning deceleration

			~If _Player_Path_Vel < 0							;If player Path velocity went below 0
				~Eval _Player_Path_Vel = 0						;Force velocity to 0
			~EndIf										;Path velocity overflow test

		~Else											;If Path velocity is Negative

			~Eval _Player_Path_Vel += _Player_SpinDecel					;Apply spinning deceleration

			~If _Player_Path_Vel > 0							;If player Path velocity went above 0
				~Eval _Player_Path_Vel = 0						;Force velocity to 0
			~EndIf										;Path velocity overflow test

		~EndIf											;Path velocity sign test

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Handle slipping off steep curves
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_SlipTimer > 0								;If player slip counter is active
			~Eval _Player_SlipTimer--							;Decrease the timer
		~Else											;If player slip counter isn't on, check to see if it should be

			~If _Player_Was_On_Path != 0							;Don't perform the slip test if the player just landed on the path, skipping this test will prevent him from immediately detaching from walls and having his rotated sprite fall into them, giving the path routine the opportunity to keep that from happening

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Set "Animation Speed" here as absolute value of path speed and
				;test whether the player is moving fast enough to stay on the curve
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_AniSpeed = +_Player_Path_Vel				;When player is on a path (or solid object in path direction), set his animation speed to his path speed (if it's negative, it should be made positive)

				~If _Player_AniSpeed < 640						;If player is moving slow enough to slip

; 					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 					;Sonic 1 and 2 style slip test
; 					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 					~Eval _Angle  = _Player_Path_Angle + 32				;Rotate the player angle by 32 for testing
; 					~Eval _Angle &= 192						;Limit the angle to 255 units and keep only the part above 90 degrees (Will cause 0, 64, 128, or 192)
; 					~If   _Angle != 0						;If the angle value is significant enough

					;;;;;;;;;;;;;;;;;;;;;;;;
					;Sonic 3 style slip test
					;;;;;;;;;;;;;;;;;;;;;;;;

					~Eval _Angle  = _Player_Path_Angle + $18		;Rotate the player angle by 24 for testing
					~Eval _Angle &= $FF					;Limit the angle to 255 units
					~If   _Angle >= $30					;If the angle value is significant enough

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Slip has occurred, set timer and test for very steep angle
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~Eval _Player_SlipTimer = 30			;Set the slip timer, player loses input control
						~Eval _Angle  = _Player_Path_Angle + $30	;Rotate the player angle by 48 for testing
						~Eval _Angle &= $FF				;Limit the angle to 255 units
						~If   _Angle >= 60				;If the angle value is significant enough

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Steep angle, set player for leaving the path
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~Eval _Player_On_Path   = 0		;Player leaves the path
							~Eval _Player_Path_Vel  = _Player_X_Vel	;In normal gravity, set the path velocity equal to the current X velocity for proper movement when player leaves a path
							~Eval _Player_Path_Mode = 0
							#_GoTo					;Skip the "stand up on zero velocity" check so that slipping off while spinning doesn't cause the player to stand
							: @SetAniSpeed

						~Else						;If the angle value is insufficient to cause a slip

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Not steep enough to fall off, just apply extra deceleration
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~If _Angle >= 48			;If the angle value was a left-side angle
								~Eval _Player_Path_Vel += 128	;Add 128 rightward motion to player
							~Else					;If the angle value was a right-side angle
								~Eval _Player_Path_Vel -= 128	;Add 128 leftward motion to player
							~EndIf					;Testing angle direction

						~EndIf						;If angle is significant enough for player to fall off path

					~EndIf							;If angle is significant enough to cause a slip

				~EndIf								;If speed is slow enough to slip off significant angles

 			~EndIf									;If player is on a path

		~EndIf										;Player Slip Counter


		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Player stops if at 0 velocity and on surface (Do it before angle slipping,
		;because falling off a wall would reset "Path_Velocity" to 0, and since this
		;code is still assuming the player is connected to a path, even though he is
		;not, it would cause him to stand up when he should not)
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Path_Vel == 0							;If player is no longer moving

			~If _Player_Was_On_Path != 0 						;If player was already on the path (If player just struck the path, he would already have stood up and had his offsets fixed)

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;When not in forced-spin mode, player should stand up
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Player_TubeFlag == 0					;If the player is not in forced-spin mode

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Force the spinning player to stand
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~Eval _Player_Movement_Mode = 0				;Send the player back to standing mode

					.Include macro\unspin.def				;Adjust the player's Y position to compensate

				~Else								;If player is in forced-spin

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Forceed-spin mode forces path velocity onto the player to maintain the spin
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~Eval _Player_Path_Vel = $200				;Apply spin force velocity

					~If _Player_X_Dir == _Left				;If player is facing left
						~Eval -_Player_Path_Vel				;Reverse the velocity to move left
					~EndIf
				~EndIf

			~EndIf									;Was On Path Test

		~EndIf										;Path Velocity test
@SetAniSpeed
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Set Player_Animation_Speed again here to ensure that it is
		;correct after any speed modification such as deceleration
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_AniSpeed = +_Player_Path_Vel					;When player is on a path (or solid object in path direction), set his animation speed to his path speed (if it's negative, it should be made positive)
@AniSpeed_WallReset
		~Eval _Player_AniDelay   = $500 - _Player_AniSpeed				;Create the delay value
		~Eval _Player_AniDelay >>= 8							;Use only the full pixel portion of the speed
		~If   _Player_AniDelay   < 1							;If the delay value is less than 1
			~Eval _Player_AniDelay = 1						;Set minimum delay
		~EndIf										;If the delay value is less than 1

		#_GoTo										;Skip over the test for walls. It is unnecessary now, and it may in fact be what triggered this code
		: @AniSpeed_DontTestWalls

	~EndIf											;If player is on a path

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the player is not on a path, test for collision with walls. Midair
	;collision with walls should set the animation speed back to minimum
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Cant_Go_Left != 0								;If the player has struck a wall to the left
		~Eval _Player_AniSpeed = 1							;Minimize his animation speed
		#_GoTo										;Set the animation delay according to the minimized speed
		: @AniSpeed_WallReset
	~EndIf

	~If _Player_Cant_Go_Right != 0								;If the player has struck a wall to the left
		~Eval _Player_AniSpeed = 1							;Minimize his animation speed
		#_GoTo										;Set the animation delay according to the minimized speed
		: @AniSpeed_WallReset
	~EndIf
@AniSpeed_DontTestWalls
	;;;;;;;;;;;;;;;;;;;;;
	;Handle Player Timers
	;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Flash_Timer > 0								;If player is in an invulnerable flash
		~Eval _Player_Flash_Timer--							;Decrease the timer
	~EndIf											;Player invulnerable timer

# End	;Terminate Function

;===================================================================================
;===================================
;Function 14 - Spinning Mode Update=
;===================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;All modes require these values to be reset at the beginning
	;of each animation update. They are turned on later if needed
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_InvertMirror = 0								;Reset the horizontal mirror flag
	~Eval _Player_InvertFlip   = 0								;Reset the vertical flip flag

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Set up the proper animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_Player_SetAni_ByList									;Make sure the normal spinning animation is playing
	: l12 ;Spinning Animation

# End	;Terminate Function

;===================================================================================
;====================================
;Function 15 - Hurting Mode Movement=
;====================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for demo recording/playback, and act accordingly
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_RunFunc_CurrPlayer_Const
	: 26 ;Demo Control

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for player standing on a path or solid object
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0							;If player is not standing on a path or a solid object
@Disconnect
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Player is still in freespace. Apply gravity and allow the
		;player to connect to a path the next time he strikes one
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_Y_Vel                  += _Player_Weight			;Apply Gravity Force
		~Eval _Player_PlatformDisconnectFlag  = 0				;Unset the flag so the next time the player touches a path, he will land on it

	~Else										;If player has struck a path or a solid object

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;If the player has just been bounced, set him to freespace. Doing this here prevents
		;ShovePlayer from locking the player to the ground after hurt collisions
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_PlatformDisconnectFlag == 1					;Test whether the player should be forced to disconnect from the path

			~Eval _Player_On_Path = 0					;Detach player from ground so he doesn't immediately exit "hurting" mode (Prevents horizontal movement from being immediately reset)

			#_GoTo								;Behave as if in freespace
			: @Disconnect

		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Striking a path ends hurt mode. Stop the player and go back to standing mode
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Chr_SetRange								;Stop player's movement
		:   0											;(Src)
		:_Player_X_Vel										;(Start)
		:_Player_Path_Vel									;(End)

		~Eval _Player_Movement_Mode = _PMM_Standing				;Send the player back to standing mode

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Some flags are reset upon sriking a new path
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_JumpedFlag    = 0						;After landing, reset the jump flag
		~Eval _Player_Skidding      = 0						;Force disable skidding when striking a new path
		~Eval _Player_FloatingFlag  = 0						;Disable the player "floating" animation
		~Eval _Player_EnemyScoreCtr = 0						;Reset the successive enemy score increment

	~EndIf										;If player has struck a path while hurting

# End	;Terminate Function

;===================================================================================
;==================================
;Function 16 - Hurting Mode Update=
;==================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;All modes require these values to be reset at the beginning
	;of each animation update. They are turned on later if needed
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_InvertMirror = 0							;Reset the horizontal mirror flag
	~Eval _Player_InvertFlip   = 0							;Reset the vertical flip flag

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Set up the proper animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_Player_SetAni_ByList								;Make sure the hurting animation is playing
	: l11 ;Hurting Animation

# End	;Terminate Function

;===================================================================================
;=====================================
;Function 17 - Debug Movement Handler=
;=====================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for demo recording/playback, and act accordingly
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_RunFunc_CurrPlayer_Const
	: 26 ;Demo Control

	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Check Control Value Input
	;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Curr_Player == _Player1						;Don't allow the input to double-up by player 2

		#_If(KeyState)							;If tapped Debug Forward key
		:M2T5										;Debug Num Forward (Control Map Entry 5)
		:   1										;On
			~Eval _OE_Property[0]++					;Increase Debug Number
		#_EndIf								;If tapped Debug Forward key

		#_If(KeyState)							;If tapped Debug Backward key
		:M2T4										;Debug Num Backward (Control Map Entry 4)
		:   1										;On
			~Eval _OE_Property[0]--					;Decrease Debug Number
		#_EndIf								;If tapped Debug Backward key

	~EndIf									;Don't allow the input to double-up by player 2

	;;;;;;;;;;;;;;;;;;;;;
	;Check Leftward Input
	;;;;;;;;;;;;;;;;;;;;;

	#_If(KeyState)								;If not holding the Left direction
	: PH2											;Holding Left (Current Player)
	:_Off
		#_GoTo								;If not holding left, check for holding right
		: @CheckRight
	#_EndIf									;If Not Holding Left

	;;;;;;;;;;;;;;;;;;;
	;Do Leftward Motion
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;On first tap, move one pixel for precision placement
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_X_Vel >= 0							;If velocity is not leftward, zero velocity and nudge once to the left
		~Eval _Player_X_Vel = 0						;Stop horizontal motion
		~Eval _Player_X_Pos--						;Move one pixel
	~EndIf									;Leftward Movement Test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Process leftward acceleration
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_X_Vel <= -4096						;Don't let player excede the maximum
		#_GoTo								;Can't speed up, but Leftward input was made. Skip ahead to test for vertical input
		: @CheckUp
	~EndIf									;Maximum Velocity Test

	~Eval _Player_X_Vel -= 12						;Increase leftward velocity

	#_GoTo									;Leftward input was made. Skip ahead to test for vertical input
	: @CheckUp

	;;;;;;;;;;;;;;;;;;;;;;
	;Check Rightward Input
	;;;;;;;;;;;;;;;;;;;;;;
@CheckRight
	#_If(KeyState)								;If not holding the Right direction
	: PH3											;Holding Right (Current Player)
	:_Off
		~Eval _Player_X_Vel = 0						;If not holding left or right, stop horizontal motion
		#_GoTo								;Go to the vertical input test
		: @CheckUp
	#_EndIf									;If Not Holding Right

	;;;;;;;;;;;;;;;;;;;;
	;Do Rightward Motion
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;On first tap, move one pixel for precision placement
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_X_Vel <= 0							;If velocity is not rightward, zero velocity and nudge once to the right
		~Eval _Player_X_Vel = 0						;Stop horizontal motion
		~Eval _Player_X_Pos++						;Move one pixel
	~EndIf 									;Rightward Movement Test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Process rightward acceleration
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_X_Vel >= 4096						;Don't let player excede the maximum
		#_GoTo								;Can't speed up, but Rightward input was made. Skip ahead to test for vertical input
		: @CheckUp
	~EndIf									;Maximum Velocity Test

	~Eval _Player_X_Vel += 12						;Increase rightward velocity

	;;;;;;;;;;;;;;;;;;;
	;Check Upward Input
	;;;;;;;;;;;;;;;;;;;
@CheckUp
	#_If(KeyState)								;If not holding the Up direction
	: PH0											;Holding Up (Current Player)
	:_Off
		#_GoTo								;If not holding up, check for holding down
		: @CheckDown
	#_EndIf									;If Not Holding Left

	;;;;;;;;;;;;;;;;;
	;Do Upward Motion
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;first tap, move one pixel for precision placement
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Y_Vel >= 0							;If velocity is not upward, zero velocity and nudge once upward
		~Eval _Player_Y_Vel = 0						;Stop vertical motion
		~Eval _Player_Y_Pos--						;Move one pixel
	~EndIf									;Upward Movement Test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Process Upward acceleration
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Y_Vel <= -4096						;Don't let player excede the maximum
		#_StopCurrentFunc						;Can't speed up, but Upward input was made. Stop the funtion, nothing left to test
	~EndIf									;Maximum Velocity Test

	~Eval _Player_Y_Vel -= 12						;Increase upward velocity

	#_StopCurrentFunc							;Upward input was made. End function so as not to check downward

	;;;;;;;;;;;;;;;;;;;;;
	;Check Downward Input
	;;;;;;;;;;;;;;;;;;;;;
@CheckDown
	#_If(KeyState)								;If not holding the Down direction
	: PH1											;Holding Down (Current Player)
	:_Off
		~Eval _Player_Y_Vel = 0						;If not holding up or down, stop vertical motion
		#_StopCurrentFunc						;Nothing left to check for, stop function
	#_EndIf									;If Not Holding Down

	;;;;;;;;;;;;;;;;;;;
	;Do Downward Motion
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;On first tap, move one pixel for precision placement
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Y_Vel <= 0							;If velocity is not downward, zero velocity and nudge once downward
		~Eval _Player_Y_Vel = 0						;If not holding left or right, stop horizontal motion
		~Eval _Player_Y_Pos++						;Move one pixel
	~EndIf									;Downward Movement Test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Process downward acceleration
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Y_Vel >= 4096						;Don't let player excede the maximum
		#_StopCurrentFunc						;Can't do left and right at the same time, stop
	~EndIf									;Maximum Velocity Test

	~Eval _Player_Y_Vel += 12						;Increase downward velocity

# End	;Terminate Function

;===================================================================================
;===================================
;Function 18 - Debug Scroll Forward=
;===================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	~Eval _Player_DebugNum++						;Scroll forward in the debug list (The program will wrap this value around the list boundaries)

# End	;Terminate Function

;===================================================================================
;====================================================
;Function 19 - Debug Scroll Backward or Place Object=
;====================================================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	#_If(KeyState)								;If holding the A button
	: PH4											;Holding A (Current Player)
	:_On
		~Eval _Player_DebugNum--					;Scroll backward in the debug list (The program will wrap this value around the list boundaries)
		#_StopCurrentFunc						;Nothing else to do
	#_Else									;If not holding A, this key places the currently-selected object
		#_Debug_PlaceObject
	#_EndIf 								;Holding A test

# End	;Terminate Function

;===================================================================================
;=================================
;Function 20 - Debug Save Objects=
;=================================

	#_SaveObjects								;Save the current Object layout to a file
	: Current										;Instructs the command to save objects back to the file that was last loaded

# End	;Terminate Function

;===================================================================================
;==================================
;Function 21 - Dying Mode Movement=
;==================================

	.Alias YVel
	.Alias DeathPos

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for demo recording/playback, and act accordingly
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_RunFunc_CurrPlayer_Const
	: 26 ;Demo Control

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;On the first frame of death, kill powerups by setting them to end the next
	;time they are processed (Most importantly, keeps player powerup and music
	;states working properly when a player dies and respawns in 2p mode)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_InvincTimer != 0						;If player has invincibility
		~Eval _Player_InvincTimer = 1					;Set timer to end next frame
	~EndIf

	~If _Player_SneakerTimer != 0						;If player has Super or Lead Sneakers
		~If _Player_SneakerTimer < 0					;If player has Lead Sneakers
			~Eval _Player_SneakerTimer = -1				;Set timer to end next frame
		~Else 								;If player has Super Sneakers
			~Eval _Player_SneakerTimer =  1				;Set timer to end next frame
		~EndIf
	~EndIf

	~If _Player_SuperFlag != 0						;If player is "Super"
		~Eval _Player_SuperFlag = -1					;Set player to de-transform
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Manage a non-Y-looped version of player Y position during death so that
	;the position will not wrap around the Y axis and mistakenly report that
	;he is above or below the death threshold when this is not actually true
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _YVel = _Player_Y_Vel >> 8					;Get the whole-pixel count of the current Y velocity
	~Eval _Player_Temp +=_YVel						;Move the player's death position (Version of player's Y position that is not Y-loop-wrapped) to follow the player

	~Eval _Player_Y_Vel += _Player_Weight					;Apply Gravity Force

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Calculate death threshold and check player death Y position against it
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Option_PlayMode == _Screen_Single					;If the mode is single-player
		~Eval _DeathPos   = _vpyr[0] + 256				;Set the death threshold to 32 pixels below the bottom of the Player 1 screen's current position (224+32)
	~Else 									;If the mode is Split-Screen
		~Array _DeathPos  = _vpyr[_Curr_Player]				;Retrieve the current player's Y Viewport offset
		~Eval  _DeathPos += 256						;Set the death threshold to 32 pixels below the bottom of the Current Player screen's current position (224+32)
	~EndIf

	~If _DeathPos <= _Player_Temp						;If player has gone below this point, end the death sequence (do life minus,pause,restart/respawn/gameover)

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Position has fallen below threshold, complete death sequence
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~Eval _Player_X_Vel      = 0					;Stop the player's horizontal motion
		~Eval _Player_Y_Vel      = 0					;Stop the player's vertical motion
		~Eval _Player_Path_Vel   = 0					;Stop the player's Path motion

		~If   _Player_WaterFlag != 0					;If the player was underwater

			~If _Option_PlayMode == _Screen_Split			;If the mode is split-screen

				~If _Player_WaterFlag < 720			;If the player had triggered the drowning timer

					~Eval _Drowning_Ctr--			;Remove this player from counter

					#_RunFunc_Game				;If no player is drowning, switch back to the appropriate music
					: G20 ;Switch Music

				~EndIf

			~EndIf
@Music_Done
			~Eval _Player_WaterFlag = 1800				;Reset underwater timer so it doesn't stay stuck at 1 if he had just drowned; character above water will take care of unsetting it and fixing velocities if necessary

		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		; P2 following P1 in 1P mode doesn't actually die
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Option_PlayMode == _Screen_Single				;If the mode is single-player
			~If _Curr_Player == _Player2				;If this is player 2
				~Eval _Player_AIMode = _AI_SetFlyBack		;Set Tails to fly back to P1
				#_StopCurrentFunc				;Stop function to prevent the death sequence from resetting the level
			~EndIf
		~EndIf

		~Eval _Player_Lives--						;Subtract a life

		~Eval _Player_Temp  = 0						;Clear the temp var so it can be used for the timer below ("Loop" doesn't auto-init to 0, but does reset to 0 when finished)

		~If  _Player_Lives <= 0						;If player has no more lives

			~Flag   _GameState[_GSF_TimeOver]  = _Off		;Make sure that if a character lost his final life while a "Time Over" was already playing, that the "Game Over" object acts in the end as "Game Over" rather than "Time Over"
@Spawn_TimeOver
			~IfFlag _GameState[_GSF_GameOver] == _Off		;If "Game Over" is not already active

				#_PlayMusic
				:  G3								;Game Over Music

				~If _Curr_Player == _Player1			;Figure out the appropriate screen to spawn for (spawn parameters can't use register values)
					#_SpawnObject
					:_OType_PlayerScript					;(Type)
					:   0							;(X)
					:   0							;(Y)
					:   2							;(Pri)
					:   0							;(Tag)
					:   0							;(Flags)
					:   2							;1 Parameter
					:  G2							;(Param 0) "Game/Time Over" Object
					:_Player1						;(Param 1)
				~Else
					#_SpawnObject
					:_OType_PlayerScript					;(Type)
					:   0							;(X)
					:   0							;(Y)
					:   2							;(Pri)
					:   0							;(Tag)
					:   0							;(Flags)
					:   2							;1 Parameter
					:  G2							;(Param 0) "Game/Time Over" Object
					:_Player2						;(Param 1)
				~EndIf

				~Flag _GameState[_GSF_GameOver] = _On		;Flag that "Game Over" is active

			~EndIf							;Is "Game Over" already active?

			#_Loop							;Wait indefinitely, until "Game/Time Over" object ends the level
			:  -0									;(Location)
			:   0									;(Counter)
			:  -1									;(Repeats) Forever

		~EndIf								;Does player still have more lives?

		~IfFlag _GameState[_GSF_TimeOver] == _On			;If player still has lives, test the "Time Over" flag)
			#_GoTo							;If "Time Over" has been flagged, spawn a "Game Over" object to show "Time Over", if one is not already active
			: @Spawn_TimeOver
		~EndIf								;Does player still have more lives?

		#_FetchPlayer
		:_Ref_All
		:r_Curr_Player

		#_Loop								;Wait 1 second longer
		:  -1										;(Location)
		:  a2										;(Counter) Player Temp
		:  60										;(Repeats) Forever

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;1P/2P Death resets the level, Competition death resets the dead player's position
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Screen_Mode == _Screen_Split				;If the mode is Split-Screen

			;;;;;;;;;;;;;;;;;;
			;Competition Death
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Remove player's rings and reset ring bonus and enemy score increment
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Rings         = 0				;Remove Rings
			~Eval _Player_NextRingBonus = 100			;First extra life for rings at 100
			~Eval _Player_EnemyScoreCtr = 0				;Reset the successive enemy score increment

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Reset the player's position to his start/restart position
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Param1 = _Player_RestartX			;Get player's Restart position
			~Eval _Param2 = _Player_RestartY

			#_Player_SetPos
			:r_Curr_Player								;(ChrID) Current Player
			:R_Param1								;(X)     Restart X
			:R_Param2								;(Y)     Restart Y
			:_PAlign_Floor								;(Align)
			:_PAlign_VPScroll							;(VPAdj)

			~Array _Player_VPX = _vpxr[_Curr_Player]		;Make sure the player's Viewport matches the screen when auto-adjust is turned back on to cause a pan (Player's viewport moved below the screen during death)
			~Array _Player_VPY = _vpyr[_Curr_Player]		;Retrieve these values from the current screen settings based on the current player

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Reset the player's movement and viewport modes, and collision flags
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Movement_Mode = _PMM_Standing		;Reset player mode so he doesn't immediately re-trigger death on the first frame after level restart
			~Eval _Player_Path_Mode     = _PPM_Floor		;Reset path mode to floor path mode

			#_Chr_SetRange						;Reset side solidity collision flags
			:   0									;(Src)
			:_Player_Cant_Go_Up							;(Start)
			:_Player_Cant_Go_Right							;(End)

			~Flag _Player_Flags[_PFlag_VPCenter] = _On		;Re-enable the auto viewport, which was disabled to lock the screen during death

	~Else									;If single-player mode

			;;;;;;;;;;;;
			;1P/2P Death
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Fade out and reset the level
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_LevelActivation					;Disable palette rotation so colors don't un-fade themselves (also disables object movement, player movement, and tile animation
			:_Off

			~Eval _Player_Freeze = 0				;Un-freeze this player. Otherwise, this routine will stop running and the game will freeze at fade-out

			~Eval _NextZone = _zone					;Restart the level
			~Eval _NextAct  = _act
			#_CallFunc_Game
			: G19 ;Fade To Next Level

			#_Chr_SetRange						;Reset Player's position to avoid level boundary during level restart
			:   0									;(Src)
			:_Player_X_Pos								;(Start)
			:_Player_Y_Pos								;(End)

		~EndIf								;Is this split-screen mode?

	~EndIf									;If player has gone below the lower death boundary

# End	;Terminate Function

;===================================================================================
;=================================
;Function 22 - Player Init/Sentry=
;=================================

	.Alias OtherPlayer
	.Alias KeyState
	.Alias KeyMask
	.Alias Offset
	.Alias Timer
	.Alias SpawnX
	.Alias SpawnY
	.Alias WaterFlag
	.Alias LoopFlag
	.Alias Bound
	.Alias TBound

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Initialize general Player settings and sentries
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchPlayer								;Get a pointer to the current player
	:_Ref_All
	:r_Curr_Player

	~Eval _Player_Flags = %11101011100					;Initialize the player's flags

	~If _Player_Type == _Tails						;If the player is Tails
		#_RunFunc_CharList 						;Call Tails' Tail Setup routine. Call from here so it doesn't leave an empty function slot before this sentry routine when it exits
		: 0 ;Tails' Tail setup
	~EndIf

	~If _Option_PlayMode == _Screen_Single					;If the mode is single-player

		~If _Curr_Player == _Player1					;If this is player 1
			#_CallFunc_CurrPlayer_Const				;Record Sonic input and generate Tails input at the end of Sonic's movement
			: 24 ;P2 AI
		~Else 								;If this is player 2
			~Eval _Player_AIMode = _AI_Follow			;Reset Tails AI Mode
		~EndIf

	~EndIf

	#_FuncBreak								;Wait for the next Game Frame to begin sentry
@Repeat
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;From here on, this code will be run once every Game Frame for this player
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchPlayer								;Get a pointer to the current player
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Check for player being forced to spin (The spin must take
        ;place after Movement has finished for the frame because the
        ;MainBound change will not take effect until Animation time)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_TubeFlag == 1						;If player has been set for forced-spin

		~Eval _Player_TubeFlag = 2					;Set the forced spinning flag

		~If _Player_Movement_Mode != _PMM_Spinning			;If player isn't spinning

			~Eval _Player_Skidding = 0				;Force disable skidding in case it was on
; 			~Eval _Player_X_Count  = 256				;
			~Eval _Player_Ducking  = 0				;Make sure ducking isn't triggered when entering spin mode

			#_PlaySound
			:_SFX_Spin								;(ID)
			:_Snd_Once								;(Loop)

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Put the player into spinning mode, and adjust his VP and Y position to compensate
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			.Include macro\spin.def

		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for this player being carried by another
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_CarriedFlag	!= 0						;If player reads as being carried

		~Eval _OtherPlayer = _Curr_Player ^ 1				;Get the ID of the opposite player (Note: It has not been Fetched yet)

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Anything changing the player's mode (such as death or hurting) disables being carried
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Movement_Mode != _PMM_Animate			;If player is no longer in animate-only mode
			~If _Player_Movement_Mode == _PMM_Dying			;If player is dying
				#_GoTo						;Only disable the carrying flag
				: @DisableCarry
			~EndIf
			#_GoTo							;Disable carrying and re-enable the auto viewport
			: @DisableCarryAndSetVPFollow
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Down+jump allows carried player to jump away
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_If(KeyState)							;Down key allows jump
		: PH1										;Holding Down (Current Player)
		:   1										;On
			#_If(KeyState)						;Take Jumping input
			: PT4									;Tapped A (Current Player)
			:   1									;On
				#_GoTo						;Perform the jump
				: @JumpFromCarry
			#_EndIf
			#_If(KeyState)	 					;Take Jumping input
			: PT5									;Tapped B (Current Player)
			:   1									;On
				#_GoTo						;Perform the jump
				: @JumpFromCarry
			#_EndIf
			#_If(KeyState)	 					;Take Jumping input
			: PT6									;Tapped C (Current Player)
			:   1									;On
@JumpFromCarry
				~If _Player_WaterFlag == 0			;If player isn't under water
					~Eval _Player_Y_Vel = -$400		;Apply Jump Velocity
				~Else 						;If player is under water
					~Eval _Player_Y_Vel = -$200		;Apply Jump Velocity
				~EndIf

				~Eval _Player_VPY   += _Player_SpinOffset	;Perform the vp offset immediately also so that it takes effect this frame, because input was taken after the sentry combines the two offsets
				~Eval _Player_Y_Pos += _Player_SpinOffset	;Adjust for player's spinning height

				~Eval _Player_Movement_Mode = _PMM_Spinning	;Set player to spinning mode
				~Eval _Player_JumpedFlag    = 1			;Set jump state so player can use special move

				#_Keys_Pack	 				;Get player's key states for tap
				:r_Curr_Player
				:_KeyState							;P1 KeyState

				#_Keys_Pack 					;Get player's key states for held
				:r_Curr_Player
				:_KeyMask

				~Eval _KeyState &= $F				;Keep only the directional button states

				#_Keys_Force	 				;Disable the jump buttons so player doesn't immediately use his special move this frame
				:r_Curr_Player							;(Map)
				:_KeyState							;(State)
				:_KeyMask							;(Mask)

				#_FetchPlayer					;Obtain the opposite player's pointer
				:_Ref_Src0
				:r_OtherPlayer							;Other Player ID

				~Eval _Player_X_Vel    = _Player_X_Vel		;Set player velocity to Tails' velocity
				~Eval _Player_Path_Vel = _Player_Path_Vel	;Set player velocity to Tails' velocity

				#_GoTo						;Disable carrying and re-enable the auto viewport
				: @DisableCarryAndSetVPFollow

			#_EndIf
		#_EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for anything that should cause Tails to drop this player
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_FetchPlayer							;Obtain the opposite player's pointer
		:_Ref_Src0
		:r_OtherPlayer									;Other Player ID

		~If _Player_Movement_Mode != _PMM_Flying			;If Tails is no longer flying

			~Eval _Player_Movement_Mode = _PMM_Standing		;Reset player's movement mode

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Dropped player copies Tails' AutoVPUpdate state
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@DisableCarryAndSetVPFollow
			#_FetchPlayer						;Obtain the opposite player's pointer, it may not already be here depending on which part of the script jumped to this location
			:_Ref_Src0
			:r_OtherPlayer								;Other Player ID

			~IfFlag _Player_Flags[_PFlag_VPCenter] == _On		;If Tails' AutoVPUpdate Flag is on
				~Flag _Player_Flags[_PFlag_VPCenter] = _On	;Re-enable the auto viewport
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Unflag this player and Tails to disable the carry
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@DisableCarry
			~Eval _Player_CarriedFlag = 0				;Set player as not being carried

			#_FetchPlayer						;Obtain the opposite player's pointer
			:_Ref_Dest
			:r_OtherPlayer								;Other Player ID

			~Eval _Tails_CarriedPlayerPtr = 0			;Tails is no longer carrying the player

		~EndIf

		#_FetchPlayer							;Re-obtain the current player's pointer
		:_Ref_All
		:r_Curr_Player

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;If the player is still being carried, count his invulnerability timer, because it won't otherwise happen
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_CarriedFlag != 0					;If player reads as being carried
			~If _Player_Flash_Timer > 0				;If player is in an invulnerable flash
				~Eval _Player_Flash_Timer--			;Decrease the timer
			~EndIf 							;Player Invulnerable flash
		~EndIf

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Update the viewport positioning style depending on path state
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path != 0						;If player is on a solid path

		~If _Player_Was_On_Path == 0					;If player has just struck the path

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Non-movement mode uses freespace camera
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_Movement_Mode == _PMM_Animate		;If player is in non-movement mode

				~Eval _Player_On_Path     = 0			;Set player back into freespace so that when movement resumes, he will have just struck the path
				~Eval _Player_Was_On_Path = 0			;Set player back into freespace so that when movement resumes, he will have just struck the path

				#_GoTo						;Go to freespace camera mode
				: @FreespaceCamera
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Account for Knuckles' gliding fall
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_FloatingFlag == 2				;"Floating" in mode 2 means that Knuckles is falling from a glide
				~Eval _Player_VPCenterOffset += 9		;Adjust the VP centering offset to follow the player and avoid jittering upon landing
				~Eval _Player_FloatingFlag    = 1		;Set "floating" to mode 1 to remove the vp offset applied by mode 2
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Disable float "3" on landing
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_FloatingFlag == 3				;"Floating" in mode 3 means that the player exits "floating" as soon as he collides with the floor
				~Eval _Player_FloatingFlag = 0			;Disable the "Floating" flag
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Before changing vp positioning style, force the vp to the new positon for
			;the player's current position so that his offset can be can be calculated.
			;The automatic update doesn't happen until later
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Offset = _Player_VPY + _Player_VPScroll_Up	;Obtain upper tolerance of the viewport

			~If   _Offset > _Player_Y_Pos				;If the player has moved above the viewport upper tolerance

				~Eval _Offset -= _Player_VPY_Speed		;Find the maximum position to which the viewport can move in this direction

				~If   _Offset  > _Player_Y_Pos			;If the player has moved beyond that point

					~Eval _Player_VPY -= _Player_VPY_Speed	;Move vp to folow the player up to maximum speed

				~Else 						;If the player hasn't moved beyond that point

					~Eval _Player_VPY = _Player_Y_Pos - _Player_VPScroll_Up	;Move vp to follow the player

				~EndIf

			~EndIf

			~Eval _Offset = _Player_VPY + _Player_VPScroll_Down	;Obtain lower tolerance of the viewport

			~If   _Offset < _Player_Y_Pos				;If the player has moved below the viewport lower tolerance

				~Eval _Offset += _Player_VPY_Speed		;Find the maximum position to which the viewport can move in this direction

				~If   _Offset  < _Player_Y_Pos			;If the player has moved beyond that point

					~Eval _Player_VPY += _Player_VPY_Speed	;Move vp to folow the player up to maximum speed

				~Else 						;If the player hasn't moved beyond that point

					~Eval _Player_VPY = _Player_Y_Pos - _Player_VPScroll_Down	;Move vp to follow the player

				~EndIf

			~EndIf 							;Player was on path test

			~If _Screen_Mode == _Screen_Large			;If Double-resolution

				;;;;;;;;;;;;;;;;;
				;On-path style VP
				;;;;;;;;;;;;;;;;;

				~Eval _Player_VPScroll_Up   = 192		;Veiwport moves up directly with player

				~Eval _Player_VPScroll_Down = 192		;Veiwport moves Down directly with player

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Find player's current Y offset from viewport center, and set as the
				;viewport's offset (vp will slowly scroll to center onto the player)
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_VPCenterOffset += 180		;Subtract 12 to avoid jerky scrollback on the first frame
				~Eval _Player_VPCenterOffset += _Player_VPY
				~Eval _Player_VPCenterOffset -= _Player_Y_Pos

				~Eval _Player_VPY = _Player_Y_Pos - 192

			~Else							;If Normal-resolution

				;;;;;;;;;;;;;;;;;
				;On-path style VP
				;;;;;;;;;;;;;;;;;

				~Eval _Player_VPScroll_Up   = 96		;Veiwport moves up directly with player

				~Eval _Player_VPScroll_Down = 96		;Veiwport moves Down directly with player

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Find player's current Y offset from viewport center, and set as the
				;viewport's offset (vp will slowly scroll to center onto the player)
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~Eval _Player_VPCenterOffset += 90		;Subtract 6 to avoid jerky scrollback on the first frame
				~Eval _Player_VPCenterOffset += _Player_VPY
				~Eval _Player_VPCenterOffset -= _Player_Y_Pos

				~Eval _Player_VPY = _Player_Y_Pos - 96

			~EndIf
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;If the viewport center offset is nonzero, "pan" the offset toward 0
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_VPCenterOffset > 0					;If the viewport is off-center downward

			~Eval _Player_VPCenterOffset -= 6

			~If _Player_VPCenterOffset < 0				;If the offset has moved beyond 0
				~Eval _Player_VPCenterOffset = 0		;Set the offset to 0
			~EndIf

		~EndIf

		~If _Player_VPCenterOffset < 0					;If the viewport is off-center Upward

			~Eval _Player_VPCenterOffset += 6

			~If _Player_VPCenterOffset > 0				;If the offset has moved beyond 0
				~Eval _Player_VPCenterOffset = 0		;Set the offset to 0
			~EndIf

		~EndIf

	~Else									;If player is not on a solid path

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Freespace viewport has lenience on player position before scrolling
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@FreespaceCamera
		~If _Screen_Mode == _Screen_Large				;If Double-resolution
			~Eval _Player_VPScroll_Up    = 138			;Veiwport has lenience before moving up with player
			~Eval _Player_VPScroll_Down  = 266			;Veiwport has lenience before moving down with player
		~Else								;If Normal-resolution
			~Eval _Player_VPScroll_Up    =  69			;Veiwport has lenience before moving up with player
			~Eval _Player_VPScroll_Down  = 133			;Veiwport has lenience before moving down with player
		~EndIf

		~Eval _Player_VPY           += _Player_VPCenterOffset		;Upon disconnection, give any existing offset directly to the VP
		~Eval _Player_VPCenterOffset = 0				;Clear the offset value

	~EndIf									;Player on path test

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the viewport panning offset is nonzero and the player isn't holding "duck"/"look up", "pan" the offset toward 0
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_VPPanTimer < 120						;If the viewport scroll timer isn't at full time
		~If _Player_VPPanOffset > 0					;If the viewport is panned downward
			~Eval _Player_VPPanOffset -= 2
		~EndIf
		~If _Player_VPPanOffset < 0					;If the viewport is panned upward
			~Eval _Player_VPPanOffset += 2
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the vp panning timer is active, count it down if moving, spindashing, or not "ducking"/"looking up"
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_VPPanTimer > 0						;If the viewport scroll timer is active

		~If _Player_Path_Vel != 0					;If the player is moving
			#_GoTo							;Count down the timer
			: @DecreasePanTimer
		~EndIf

		~If _Player_SpindashCtr > 0					;If the player is charging a spindash
			#_GoTo							;Count down the timer
			: @DecreasePanTimer
		~EndIf

		#_If(KeyState)							;Timer should only count down if not holding Up
		: PH0										;Holding Up (Current Player)
		:   0										;Off
			#_If(KeyState)						;Timer should only count down if not holding down
			: PH1									;Holding Down (Current Player)
			:   0									;Off
@DecreasePanTimer
				~Eval _Player_VPPanTimer--			;Decrease the timer
			~EndIf
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Apply "Center", "Pan", and special state offsets to HCGE's VP panning variable so that they take effect
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_VP_YPan = _Player_VPCenterOffset + _Player_VPPanOffset	;Apply "Center" and "Pan" Offsets

	~If _Player_Movement_Mode == _PMM_Gliding				;If player is Flying/Gliding
		~If _Player_On_Path != 0					;If player is on a solid path
			~If _Player_Was_On_Path != 0				;If player was on a solid path last frame
				~Eval _Player_VP_YPan -= 9			;Knuckles is sliding on the ground, use a different offset
			~Else							;If player was not on a solid path last frame
				#_GoTo						;Use offset
				: @UseSpinOffset
			~EndIf
		~Else								;If player is not on a solid path
			#_GoTo							;Use offset
			: @UseSpinOffset
		~EndIf
	~EndIf

	~If _Player_Movement_Mode == _PMM_Climbing				;If player is Flying Back/Climbing
		#_GoTo								;Use offset
		: @UseSpinOffset
	~EndIf

	~If _Player_FloatingFlag == 2						;If player is "Floating" in mode 2 (not mode 1)
		#_GoTo								;Use offset
		: @UseSpinOffset
	~EndIf

	~If _Player_Movement_Mode == _PMM_Spinning				;If player is spinning
@UseSpinOffset
		~Eval _Player_VP_YPan -= _Player_SpinOffset			;Use an offset so that entering spinning mode doesn't jerk the screen down
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Handle Entering/Exiting Water
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_WaterFlag != 0						;If player is under water

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Fire and Lightning shields fizzle out in water
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_ShieldType == _Shield_Fire				;If player is wearing a fire shield
			~Eval _Player_ShieldType            = _Shield_None	;Disable player's shield
			~Eval _Player_FrontShield_ObjectPtr = 0			;Clear the shield pointers
			~Eval _Player_BackShield_ObjectPtr  = 0
		~EndIf

		~If _Player_ShieldType == _Shield_Lightning			;If player is wearing a lightning shield

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;If another Lightning Shield Water Flash is not in progress, flash the water palette
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~IfFlag _GameState[_GSF_LightningFlash] == _Off		;If the palette is not already being flashed

				#_Pal_DimRange					;Brighten the water palette for the flash effect
				:_PalSet_Current						;(Type) Current W/O Water
				: 128								;(Start)
				: 128								;128 Indexes
				: 256								;(Percentage) Twice brightness

				~Flag _GameState[_GSF_LightningFlash] = _On	;Flag that Lightning Shield Water Flash is active

				#_CallFunc_Game					;Start a function to wait four frames and then the flash
				: G12 ;Lightning Shield Water Flash Manager

			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Remove the shield from the player
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_ShieldType            = _Shield_None	;Disable player's shield status
			~Eval _Player_FrontShield_ObjectPtr = 0			;Clear the shield pointers
			~Eval _Player_BackShield_ObjectPtr  = 0

		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Tails swimming back to Sonic doesn't use air timer
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Curr_Player == _Player2					;If this is player 2
			~If _Player_Type == _Tails				;If player is Tails
				~If _Player_AIMode == _AI_FlyBack		;If Tails is flying back to P1
					#_GoTo					;Don't use the air timer
					: @SkipAirTimer
				~EndIf
			~EndIf
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Bubble and Bubble Shield disable air timer
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_WaterFlag == 1801					;If the player got a bubble
			~Eval _Player_WaterFlag--				;Count down the air timer to remove bubble get flag
			#_GoTo							;Restore the level/powerup music
			: @Drowning_RestoreMusic
		~EndIf

		;Bubble Shield disables air timer
		~If _Player_ShieldType == _Shield_Water				;If player has a water shield
			#_GoTo							;Don't use the air timer
			: @SkipAirTimer
		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Handle air countdown timer and player-spawned bubbles
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_WaterFlag != 0					;If player hasn't just drowned

			~If _Player_WaterFlag > 0)
				~Eval _Player_WaterFlag--			;Count down the air timer
			~EndIf

			~If _Player_WaterFlag == 0				;If air timer has ended

				;;;;;;;;;;;;
				;Do Drowning
				;;;;;;;;;;;;

				#_PlaySound					;Play Drown Sound
				:_SFX_Drown
				:_Snd_Once

				~Eval _Player_WaterFlag              = -1		;Set player as having just drowned
				~Eval _Player_Movement_Mode          =  _PMM_Dying	;Set player to Dying mode
				~Eval _Player_Path_Mode              =  4		;Go to the no collision "path" mode
				~Eval _Player_X_Vel                  =  0		;Stop player's movement
				~Eval _Player_Y_Vel                  =  0
				~Eval _Player_Path_Vel               =  0
				~Eval _Player_Weight                 =  $10		;Drowning player falls more slowly than normal death
				~Eval _Player_On_Path                =  0		;Remove player from path
				~Eval _Player_Was_On_Path            =  0
				~Eval _Player_ShieldType             =  _Shield_None	;Remove shield status
				~Eval _Player_FrontShield_ObjectPtr  =  0		;Remove shield status
				~Eval _Player_BackShield_ObjectPtr   =  0		;Remove shield status

				~Eval _Player_BreathBubbles_Count    = 10		;Drowning player spawns 10 bubbles
				~Flag _Player_BreathBubbles_Flag[0]  =  _On		;Flag that bubbles should be spawned

				~If   _Option_PlayMode == _Screen_Single	;If the mode is single-player
					~If _Curr_Player == _Player2		;If this is player 2
						#_GoTo				;don't unset this flag for single-player mode. Bit must stay on unless turned off by another event, or the VP will stop following Sonic while Tails is carrying him
						: @KeepVPFlag
					~EndIf
				~EndIf

				~Flag _Player_Flags[_PFlag_VPCenter] = _Off	;Disable the auto viewport to lock it in place while player dies
@KeepVPFlag
				~Eval _Player_Temp = _Player_Y_Pos		;Set player's death Y position to current Y position

				#_Player_SetAni_ByList				;Set the player's death animation
				: l24								;Player Drowning Animation

				~Eval _Player_CarriedFlag = 0			;Set player as not being carried

				~Eval _OtherPlayer        = _Curr_Player ^ 1	;Get the ID of the opposite player

				#_FetchPlayer					;Obtain the opposite player's pointer
				:_Ref_Dest
				:r_OtherPlayer							;Other Player ID

				~Eval _Tails_CarriedPlayerPtr = 0		;Tails is no longer carrying the player

			~Else							;If air timer is not over

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Manage air timer warning chime and drowning music
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Option_PlayMode == _Screen_Single		;If the mode is single-player
					~If _Curr_Player == _Player2		;If this is player 2
						#_GoTo				;P2 doesn't play warning sounds/music in 1P mode
						: @SkipAirWarning
					~EndIf
				~EndIf

				~If _Player_WaterFlag >= 900			;If air timer is not nearing end

					~Eval _Timer = _Player_WaterFlag % 300	;Test for every 5 seconds

					~If   _Timer == 0			;If this is a 5th second
						#_PlaySound			;Play air warning sound
						:_SFX_AirWarn
						:_Snd_Once
					~EndIf

				~Else						;If air timer is nearing end

					~If _Player_WaterFlag == 720		;Check for 12 seconds remaining
						~Eval _Drowning_Ctr++		;Add this player to counter
						~Flag _GameState[_GSF_Drowning] = _On	;Flag that drowning music is playing
						#_PlayMusic			;Play Drowning Music
						:  G4						;Drowning Music
					~EndIf

				~EndIf
@SkipAirWarning
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Randomly spawn bubbles from player's mouth
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Only if the player isn't wearing a bubble shield
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Player_ShieldType != _Shield_Water					;If the player doesn't have a bubble shield


					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Every two seconds for the last 12 seconds of air, a countdown number should be spawned
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					#_Switch(Chr)							;Check the timer for two-second intervals at 12 seconds or less
					:_Player_WaterFlag

						#_Case									;12 seconds remaining (number 5)
						: 720
						#_Case									;10 seconds remaining (number 4)
						: 600
						#_Case									; 8 seconds remaining (number 3)
						: 480
						#_Case									; 6 seconds remaining (number 2)
						: 360
						#_Case									; 4 seconds remaining (number 1)
						: 240
						#_Case									; 2 seconds remaining (number 0)
						: 120
							~Flag _Player_BreathBubbles_Flag[7] = _On	;Flag the bubble to turn into a number
						#_BreakCase

					#_EndSwitch

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Every 1 second, flag for a bubble set to spawn, with a 1/2 chance of two bubbles
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~Eval _Timer = _Player_WaterFlag % 60						;60 frames = 1 second

					~If   _Timer == 0								;If this is a 1 second mark

						~Eval _Player_BreathBubbles_Delay = 0					;Cause the first bubble to be spawned immediately
						~Flag _Player_BreathBubbles_Flag[0] = _On				;Flag that a bubble set should be spawned
						#_Chr=Rand(Const)							;Random chance for extra bubble spawn
						:   1											;(Lower)
						:   2											;(Upper)
						:_Player_BreathBubbles_Count								;(Dest)

					~EndIf

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;Test whether a bubble set is in progress, and spawn the bubble(s) when appropriate
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~IfFlag _Player_BreathBubbles_Flag[0] == _On

						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
						;Count down the spawn delay timer and spawn a new bubble if it has expired
						;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

						~Eval _Player_BreathBubbles_Delay--
						~If   _Player_BreathBubbles_Delay < 0

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Set a random delay before the spawning of the next bubble in the set
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_Chr=Rand(Const)
							:   0										;(Lower)
							:  $F										;(Upper)
							:_Player_BreathBubbles_Delay							;(Dest)

							~Eval _Player_BreathBubbles_Delay += 8

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Set the bubble to spawn at the player's position
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_Reg=Chr_Hotspot
							:   0										;(Hotspot ID) Center Spot
							:_SpawnX									;(Obj2 X)
							:_SpawnY									;(Obj2 Y)

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Offset the bubble's spawn position toward the player's
							;mouth, and cause it to start its wobble away from him
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~If _Player_X_Dir == _Right					;If the Player is facing right
								~Eval _SpawnX += 6					;Position horizontal spawn to correspond with right-facing frame
								~Eval _Param1  = 0					;Start at the beginning of the wobble table for a rightward wobble
							~Else								;If the Player is facing left
								~Eval _SpawnX -= 6					;Position horizontal spawn to correspond with left-facing frame
								~Eval _Param1  = $40					;Start at the middle of the wobble table for a leftward wobble
							~EndIf

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;When the player is dying due to drowning, the extra
							;bubbles spawn twice as fast and a little higher
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~If _Player_WaterFlag == -1					;If the air timer has expired and killed the Player
								~Eval _Player_BreathBubbles_Delay &= 7			;Remove the added 8-frame delay from the random bubble spawn delay
								~Eval _SpawnY -= $C					;Move the bubble spawn higher to account for positioning with the drowning frame
							~EndIf

							;;;;;;;;;;;;;;;;;
							;Spawn the bubble
							;;;;;;;;;;;;;;;;;

							#_SpawnObject
							:  G36										;(Type) Bubble
							:r_SpawnX									;(X)
							:r_SpawnY									;(Y)
							:_UseDefPri									;(Pri)
							:    0										;(Tag)
							:    0										;(Flag) No special flags
							:    1										;1 Paramater
							:-$100										;(Y Vel)

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Breath bubbles move faster than normal bubbles
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							#_FetchObject_LastSpawned
							:_Ref_All

							~Eval _Object_YVel = -$100

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Test whether one of the bubbles in this set should be made a number
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~IfFlag _Player_BreathBubbles_Flag[7] == _On

								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
								;If this is the last bubble in the set,
								;it should be made into a number
								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

								~If _Player_BreathBubbles_Count == 1

									#_GoTo
									: @DoNumber

								~EndIf


								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
								;There is a 1 of 4 chance that the bubble
								;should be turned into a number
								;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

								#_Reg=Rand(Const)
								:   0									;(Lower)
								:   3									;(Upper)
								:_Timer									;(Dest)

								~If _Timer == 0
@DoNumber
									;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
									;Turn the bubble into a number
									;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

									#_Object_SetAni_ByList
									:   2 ;Drowning Countdown Number

									;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
									;Flag that a number has already been created
									;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

									~Flag _Player_BreathBubbles_Flag[7] = _Off

									;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
									;Use the remaining time on the water counter
									;to calculate the appropriate countdown number
									;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

									~Eval _Number_Frame  = _Player_WaterFlag - 60	;Knock the first second off
									~Eval _Number_Frame /= 60			;Every 60 frames is one second
									~Eval _Number_Frame &= $FE			;One number for every two seconds
									~Eval _Number_Frame += 13			;Numbers start at frame 13
									~If   _Option_PlayMode == _Screen_Single	;If this is single-player or cooperative mode
										~Eval _Number_Screen = _Player1		;Set the number to follow Player 1's screen
									~Else						;If this is split-screen competition mode
										~Eval _Number_Screen = _Curr_Player	;Set the number to follow the screen of the Player that spawned it
									~EndIf
								~EndIf

							~EndIf

							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
							;Remove this bubble from the counter
							;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

							~Eval _Player_BreathBubbles_Count--				;Remove one bubble from the counter
							~If   _Player_BreathBubbles_Count <= 0				;If no bubbles are left
								~Eval _Player_BreathBubbles_Flag = 0			;Flag that no bubbles are to be spawned
							~EndIf

						~EndIf

					~EndIf

				~EndIf

			~EndIf

		~EndIf

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for conditions that disable water physics
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_Movement_Mode == _PMM_Dying				;If player is dying
			~If _Player_WaterFlag != -1				;If player hasn't just drowned
				#_GoTo						;Exit Water without splash
				: @ExitWater
			~EndIf
		~EndIf

		~If _Player_Movement_Mode == _PMM_Debug				;If player is in debug mode
			#_GoTo							;Exit Water without splash
			: @ExitWater
		~EndIf
@SkipAirTimer
		;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for leaving the water
		;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Water_Level > _Player_Y_Pos				;If the player has moved above the water

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Tails swimming back to Sonic doesn't cause splash
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Curr_Player == _Player2				;If this is player 2
				~If _Player_Type == _Tails			;If player is Tails
					~If _Player_AIMode == _AI_FlyBack	;If Tails is flying back to P1
						#_GoTo				;Don't cause splash
						:  @ExitWater
					~EndIf
				~EndIf
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Splash only occurs if the Player leaves the water under his own
			;power, not, for example, when the water level itself is lowered
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_Y_Vel >= 0					;If the Player is not moving upward
				#_GoTo						;Don't cause splash
				:  @ExitWater
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Player's Y velocity doubles upon exiting the water (due to loss of resistence)
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Y_Vel <<= 1

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Prevent the player from moving too fast upward when exiting the water after
			;hitting a spring. For S1-3K method of not reducing the spring force
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~IfFlag _GameState[_GSF_SpringType] == _Off		;If not flagged for Mettrix style spring reaction
				~If _Player_Y_Vel < -$1000			;If player moving up too fast
					~Eval _Player_Y_Vel = -$1000		;Cap the upward velocity
				~EndIf
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Create a splash on top of the water at the player's X position
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


			~Eval _SpawnX = _Player_X_Pos				;Get the player's X position for spawning the splash

			#_SpawnObject
			: G21									;(Type)  Water Splash Object
			:r_SpawnX								;(X)     Player X Pos
			:r_Water_Level								;(Y)
			:_UseDefPri								;(Pri)   Use Def Entry
			:   0									;(Tag)
			:   0									;(Flags)
			:   0									;0 Paramaters

			#_PlaySound
			:_SFX_Splash
			:_Snd_Once
@ExitWater
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Set player as not under water, but remember the timer value so that
			;it can be used to determine whether or not the player is drowning
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _WaterFlag        = _Player_WaterFlag		;Remember the value of "Water Flag" for the test
			~Eval _Player_WaterFlag = 0				;Set player as not under water

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Set velocities and accelerations/decelerations for above water
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_RunFunc_CurrPlayer_Const				;Call the Player Speed Stat handling Function to set appropriate speeds
			: 28 ;Set Speed Values

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Test for drowning and fix music and drowning player counter
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _WaterFlag <= 720					;If this player is drowning
@Drowning_RestoreMusic
				~If _Option_PlayMode == _Screen_Single		;If the mode is single-player
					~If _Curr_Player == _Player2		;If this is player 2
						#_GoTo				;P2 doesn't play warning sounds/music in 1P mode
						: @Music_Done
					~EndIf
				~EndIf

				~Eval _Drowning_Ctr--				;Remove this player from counter

				#_RunFunc_Game					;If no player is drowning, switch back to the appropriate music
				: G20 ;Switch Music

			~EndIf
@Music_Done
		~EndIf

	~Else 									;If player is not under water

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for entering the water
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Water_Level < _Player_Y_Pos				;If the player has moved below the water

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Test for conditions that disable water physics
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_Movement_Mode == _PMM_Dying			;If player is dying
				#_GoTo						;Don't Enter Water
				: @IgnoreWater
			~EndIf
			~If _Player_Movement_Mode == _PMM_Debug			;If player is in debug mode
				#_GoTo						;Don't Enter Water
				: @IgnoreWater
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Tails flying back to Sonic doesn't cause splash
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Curr_Player == _Player2				;If this is player 2
				~If _Player_Type == _Tails			;If player is Tails
					~If _Player_AIMode == _AI_FlyBack	;If Tails is flying back to P1
						#_GoTo				;Don't cause splash
						: @NoSplash
					~EndIf
				~EndIf
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Splash only occurs if the Player leaves the water under his own
			;power, not, for example, when the water level itself is lowered
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~If _Player_Y_Vel <= 0					;If the Player is not moving downward
				#_GoTo						;Don't cause splash
				:  @NoSplash
			~EndIf

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Create a splash on top of the water at the player's X position
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _SpawnX = _Player_X_Pos				;Get the player's X position for spawning the splash

			#_SpawnObject
			: G21									;(Type)  Water Splash Object
			:r_SpawnX								;(X)     Player X Pos
			:r_Water_Level								;(Y)
			:_UseDefPri								;(Pri)   Use Def Entry
			:   0									;(Tag)
			:   0									;(Flags)
			:   0									;0 Paramaters

			#_PlaySound
			:_SFX_Splash
			:_Snd_Once

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Set velocities and accelerations/decelerations for under water
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@NoSplash
			~Eval _Player_WaterFlag = 1800				;Set player under water (30 second timer)

			#_RunFunc_CurrPlayer_Const				;Call the Player Speed Stat handling Function to set appropriate speeds
			: 28 ;Set Speed Values

			~Eval _Player_X_Vel    >>= 1
			~Eval _Player_Y_Vel    >>= 1
			~Eval _Player_Path_Vel >>= 1

		~EndIf

	~EndIf
@IgnoreWater
	;;;;;;;;;;;;;;;;;;;;
	;Invincibility Timer
	;;;;;;;;;;;;;;;;;;;;

	~If _Player_SuperFlag == 0						;If player is not "Super"

		~If _Player_InvincTimer != 0					;If player is invincible

			~Eval _Player_InvincTimer--				;Count down the timer

			~If _Player_InvincTimer == 0				;If the timer has reached 0

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Disable the invincibility music, ONLY if the Insta-Shield flag isn't active
				;(meaning that this is Insta-Shield invincibility, not real Invincibility)
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				~If _Player_Type != _Sonic			;If the player isn't Sonic
					#_GoTo					;Player doesn't use insta-shield
					: @NoInstaShield
				~EndIf

				~If _Sonic_InstaShieldFlag == 0			;If this wasn't insta-shield invincibility
@NoInstaShield
					~Eval _InvincCtr--			;Remove this player from counter

					#_RunFunc_Game				;If no player is invincible, switch back to the appropriate music
					: G20 ;Switch Music

				~Else						;If this was insta-shield invincibility
					~Eval _Sonic_InstaShieldFlag = 0	;Disable the insta-shield
				~EndIf

			~EndIf

		~EndIf

	~Else									;If player is "Super"

		~If _Player_SuperFlag == -1					;If set for de-transformation
			~Eval _Player_SuperFlag = 0				;Remove Super state
			#_GoTo							;Do Detransformation
			: @Super_Detransform
		~EndIf

		~Eval _Player_SuperFlag--					;Count off 60 frames to decrease rings only once per second

		~If _Player_SuperFlag == 0					;If the timer has reached 0

			~Eval _Player_Rings--					;Count down the ring count as the timer
			~Eval _Player_InvincTimer = _Player_Rings		;Player's invincibility time should correspond with the number of rings

			~If _Player_Rings == 0					;If the ring count has reached 0
@Super_Detransform
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Fade the player's palette back from super to normal, and
				;restore the normal level music and player stats
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_RunFunc_CurrPlayer_Const			;Call the Player Speed Stat handling Function to set appropriate speeds
				: 28 ;Set Speed Values

				#_StateSwitch(Chr)				;Determine how to proceede based on which Character is being played
				:_Player_Type
				: 3
				: @Detransform_Sonic
				: @Detransform_Tails
				: @Detransform_Knuckles

@Detransform_Sonic
					#_RunFunc_CurrPlayer_PList		;Super Sonic uses different animations than normal Sonic, so switch back to normal animations. (The palette cycle routine will trap de-transformation and fade back to normal blue colors)
					: 46 ;Set Normal Anis
					#_GoTo					;Skip past other Character code
					: @Detransform_Finish

@Detransform_Tails
					#_StopFunc_Game				;Stop Tails' Super palette cycle
					: 17 ;Super Tails Palette Cycle
					#_Pal_SetRange				;Restore Tails' normal colors
					:_PalSet_Current_Target_Water				;(Type)
					: 120							;(Start)
					:   2							;(Count)
					: 224							;(Red 120)
					: 128							;(Green)
					:   0							;(Blue)
					: 224							;(Red 121)
					: 160							;(Green)
					:   0							;(Blue)
					#_Pal_SetRange				;Restore Tails' normal colors
					:_PalSet_Current_Target_Water				;(Type)
					: 108							;(Start)
					:   1							;(Count)
					: 160							;(Red 108)
					:  96							;(Green)
					:  64							;(Blue)
					#_GoTo					;Skip past other Character code
					: @Detransform_Finish

@Detransform_Knuckles
					#_StopFunc_Game				;Stop Knuckles' Super palette cycle
					: 18 ;Super Knuckles Palette Cycle
					#_Pal_SetRange				;Restore Knuckles' normal colors
					:_PalSet_Current_Target_Water				;(Type)
					: 125							;(Start)
					:   2							;(Count)
					: 224							;(Red 125)
					:  32							;(Green)
					:  32							;(Blue)
					: 192							;(Red 126)
					:   0							;(Green)
					:  32							;(Blue)
					#_Pal_SetRange				;Restore Knuckles' normal colors
					:_PalSet_Current_Target_Water				;(Type)
					: 122							;(Start)
					:   1							;(Count)
					:  96							;(Red 122)
					:   0							;(Green)
					:  32							;(Blue)
; 					#_GoTo					;Skip past other Character code
; 					: @Detransform_Finish

@Detransform_Finish
				~Eval _Player_InvincTimer = 0			;Player should no longer be considered "invincible"

				#_RunFunc_Game					;Switch back to the appropriate music
				: G20 ;Switch Music

			~Else							;If the player still has rings

				~Eval _Player_SuperFlag = 60			;Reset the frame timer for the next second countdown

			~EndIf

		~EndIf

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;
	;Super/Lead Sneaker Timer
	;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_SneakerTimer != 0						;If player has Super or Lead Sneakers

		~If _Player_SneakerTimer > 0					;If player has Super Sneakers

			;;;;;;;;;;;;;;;
			;Super Sneakers
			;;;;;;;;;;;;;;;

			~Eval _Player_SneakerTimer--				;Count down the timer

			~If _Player_SneakerTimer == 0				;If the timer has reached 0

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Restore level music and reduce player speed to remove the effect
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_RunFunc
				: 28 ;Set Speed Values

				~Eval _SuperSneakerCtr--			;Remove this player from counter
@Sneakers_FixMusic
				~IfFlag _GameState[_GSF_SneakerSound] == _Sneaker_NewSong		;If Speed Shoes play a new song

					#_RunFunc_Game				;If no player has Super Sneakers, switch back to the appropriate music
					: G20 ;Switch Music

				~Else							;If the current music is XM
					#_XM_SetSpeedMod				;Reset music speed
					:   0								;(Speed) Normal Speed
				~EndIf

			~EndIf

		~Else									;If player has Lead Sneakers

			;;;;;;;;;;;;;;;
			;Lead Sneakers
			;;;;;;;;;;;;;;;

			~Eval _Player_SneakerTimer++					;Count down the timer

			~If _Player_SneakerTimer == 0					;If the timer has reached 0

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;Restore level music and increase player speed to remove the effect
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_RunFunc
				: 28 ;Set Speed Values

				~Eval _SuperSneakerCtr--				;Remove this player from counter

				#_GoTo							;Manage the sound the same as with Super Sneakers
				: @Sneakers_FixMusic

			~EndIf

		~EndIf

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Don't allow Tails to die when following dead Sonic
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Curr_Player == _Player2							;If this is player 2
		~If _Player_Type == _Tails						;If player is Tails
			~If _Player_AIMode == _AI_FlyBack				;If Tails is flying back to P1
				#_GoTo							;Don't kill Tails
				: @NoDeath
			~EndIf
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;
	;Death Handling
	;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode != _PMM_Dying						;If player isn't already dying

		~If _Player_Movement_Mode != _PMM_Debug					;If player isn't in debug mode

			;;;;;;;;;;;;
			;Crush Death
			;;;;;;;;;;;;

			~If _Player_Cant_Go_Left != 0					;If player is flagged for solidity on the left
				~If _Player_Cant_Go_Right != 0				;If player is flagged for solidity on the Right
					~If _Player_Cant_Go_Left != _Player_Cant_Go_Right	;If two different planes/objects are involved (player within a single plane/object should not cause a death)
						#_GoTo					;Run the death code
						: @DoDeath
					~EndIf						;Are both sides touching the same plane/object?
				~EndIf							;Is there solidity on player's right?
			~EndIf								;Is there solidity on player's left?

			~If _Player_Cant_Go_Up != 0					;If player is flagged for solidity on the top
				~If _Player_Cant_Go_Down != 0				;If player is flagged for solidity on the bottom
					~If _Player_Cant_Go_Up != _Player_Cant_Go_Down	;If two different planes/objects are involved (player within a single plane/object should not cause a death)
						#_GoTo					;Run the death code
						: @DoDeath
					~EndIf						;Are both sides touching the same plane/object?
				~EndIf							;Is there solidity on player's bottom?
			~EndIf								;Is there solidity on player's top?

			;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;Bottom-of-the-screen Death
			;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Array _LoopFlag = _YLoop_Active[_Curr_Player]			;Retrieve the current player's YLoop state

			~If    _LoopFlag == 0						;If YLoop isn't active, check for death at the bottom of the screen

				~Array _Bound  = _LevelBound_Y2[_Curr_Player]		;Retrieve the current player's lower level boundary
				~Array _TBound = _Target_LevelBound_Y2[_Curr_Player]	;Retrieve the current player's lower target level boundary

				~If _TBound > _Bound					;If the target boundary is lower than the current boundary (screen is panning down)
					~Eval _Bound = _TBound				;Use the target for testing instead of the current
				~EndIf							;If target boundary is lower than current

				~IfFlag _GameState[_GSF_ReturnFromSpecial] == _False	;If in a normal gameplay level (as opposed to a Special or Bonus Stage, which manage their own bottom-of-the-screen events)

					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;This is a normal stage. Reaching he bottom of the screen causes death
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					~If _Bound <= _Player_Y_Pos			;If player has gone below this point, create a death sequence
						#_GoTo					;Run the death code
						: @DoDeath
					~EndIf

				~EndIf

			~EndIf

			#_GoTo								;No death conditions met, skip the death code
			: @NoDeath
@DoDeath
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;This code causes player death if any of the conditions above are met
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			~Eval _Player_Movement_Mode          = _PMM_Dying	;Set player to Dying mode
			~Eval _Player_Path_Mode              = 4		;Go to the no collision "path" mode
			~Eval _Player_X_Vel                  = 0		;Stop the player's horizontal motion
			~Eval _Player_Y_Vel                  = _Player_DeathBounce	;Set Death Bounce speed
			~Eval _Player_Path_Vel               = 0		;Stop the player's Path motion
			~Eval _Player_On_Path                = 0		;Remove player from path
			~Eval _Player_Was_On_Path            = 0		;Remove player from path
			~Eval _Player_ShieldType             = _Shield_None	;Remove shield status
			~Eval _Player_FrontShield_ObjectPtr  = 0		;Remove shield status
			~Eval _Player_BackShield_ObjectPtr   = 0		;Remove shield status

			~If _Option_PlayMode != _Screen_Split			;If the mode is single-player
				~If _Curr_Player == _Player2			;If this is player 2
					#_GoTo					;Don't unset this flag for single-player mode. Bit must stay on unless turned off by another event, or the VP will stop following Sonic while Tails is carrying him
					: @KeepVPFlag2
				~EndIf
			~EndIf

			~Flag _Player_Flags[_PFlag_VPCenter] = _Off		;Disable the auto viewport to lock it in place while player dies
@KeepVPFlag2
			~Eval _Player_Temp = _Player_Y_Pos			;Set player's death Y position to current Y position

			#_Player_SetAni_ByList					;Set the player's death animation
			: l20 ;Player Dying Animation

			#_PlaySound
			:_SFX_Hurt
			:_Snd_Once

			;;;;;;;;;;;;;;;
			;End Death Code
			;;;;;;;;;;;;;;;

		~EndIf

	~EndIf
@NoDeath
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the player is allowed to move off the right side of the screen, prevent
	;him from moving any farther than 32 pixels beyond the screen's edge
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Array _Offset  = _LevelBound_X2[_Curr_Player]		;Obtain the right-side screen boundary position for the current player
	~Eval  _Offset +=  32					;Find the position 32 pixels outside of the right-side boundary

	~If _Player_X_Pos > _Offset				;If the Player has travelled more than 32 pixels beyond the right-side boundary

		~Eval _Player_X_Pos = _Offset			;Force him back to exactly 32 pixels beyond the boundary

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Keep Player's Spindash Dust Object Aligned with him
	;   (**THIS OBJECT MUST EXIST FOR THIS PLAYER**)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchObject_FetchedChr						;Fetch the Spindash Dust Object assigned to this player
	:_Ref_All
	:_Player_DashDust_ObjectPtr

	~If _Object_Type != OG18						;Double-check the object to make sure this isn't somehow an invalid pointer
		#_ShowMessage							;This pointer does not point to a spindash dust object. Throw an error message
		: G2
	~EndIf

	#_Obj=Chr_Hotspot							;Dust object follows player
	:   0											;(Spot ID) Player Center Spot
	:_Object_X_Pos										;(Obj2 X)  Object X
	:_Object_Y_Pos										;(Obj2 Y)  Object Y

	~If _Player_SpindashCtr <= 0						;If player is not charging a spindash
		~If _Player_SuperFlag != 0					;If Player is Super
			~If _Player_AniSpeed >= _Player_RunVel1			;If Player is at maximum running speed
				#_Object_SetAni_ByList				;Start the spark manager animation
				:   2 ;Super Spark Main Animation
				#_GoTo						;Do not disable the animation
				: @Super_KeepSpark
			~EndIf
		~EndIf
		#_Object_SetAni_ByList						;Make sure spindash dust is off
		:   0 ;Blank
	~Else									;If player is charging a spindash
		~If _Player_Type == _Tails					;Tails is smaller than Sonic and Knuckles, his dust should be offset
			~Eval _Object_Y_Pos -= 4				;Shift the dust position for proper alignment
		~EndIf								;Test player mirror

		#_Object_SetAni_ByList						;Make sure spindash dust is on
		:   1 ;Dash Dust

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Set the direction of the dust equal to the direction of the player
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		~If _Player_X_Dir == _Right					;If player is facing right
			~If _Player_InvertMirror == 0				;If player mirror is not active
				~Flag _Object_Flags[_OF_HFlip] = _Off		;Make sure the sprite is unmirrored
			~Else							;If player mirror is active
				~Flag _Object_Flags[_OF_HFlip] = _On		;Make sure the sprite is mirrored
			~EndIf							;Test player mirror
		~Else								;If player is facing left
			~If _Player_InvertMirror == 0				;If player mirror is not active
				~Flag _Object_Flags[_OF_HFlip] = _On		;Make sure the sprite is mirrored
			~Else							;If player mirror is active
				~Flag _Object_Flags[_OF_HFlip] = _Off		;Make sure the sprite is unmirrored
			~EndIf							;Test player mirror
		~EndIf								;Test player X Direction

		~If _Player_Y_Dir == 0					;If player is normal Y orientation
			~If _Player_InvertFlip == 0				;If player flip is not active
				~Flag _Object_Flags[_OF_VFlip] = _Off		;Make sure the sprite is unfliped
			~Else							;If player flip is active
				~Flag _Object_Flags[_OF_VFlip] = _On		;Make sure the sprite is fliped
			~EndIf							;Test player flip
		~Else								;If player is opposite Y orientation
			~If _Player_InvertFlip == 0				;If player flip is not active
				~Flag _Object_Flags[_OF_VFlip] = _On		;Make sure the sprite is fliped
			~Else							;If player flip is active
				~Flag _Object_Flags[_OF_VFlip] = _Off		;Make sure the sprite is unfliped
			~EndIf							;Test player flip
		~EndIf								;Test player Y Direction

	~EndIf
@Super_KeepSpark
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Keep Player's Shield Objects Aligned with him
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_InvincTimer == 0						;If player isn't invincible
@InstaShield_Follow
		~If _Player_FrontShield_ObjectPtr != 0				;If player has a front shield object

			#_FetchObject_FetchedChr				;Fetch the player's front shield object
			:_Ref_All
			:_Player_FrontShield_ObjectPtr
			~If _Object_Type != OG11				;Double-check the object to make sure this isn't somehow an invalid pointer
				#_ShowMessage					;This pointer does not point to a shield object. Throw an error message
				: G3
			~EndIf

			#_Obj=Chr_Hotspot					;Shield follows player
			:   0									;(Spot ID) Player Center Spot
			:_Object_X_Pos								;(Obj2 X)  Object X
			:_Object_Y_Pos								;(Obj2 Y)  Object Y

		~EndIf

		~If _Player_BackShield_ObjectPtr != 0				;If player has a back shield object

			#_FetchObject_FetchedChr				;Fetch the player's back shield object
			:_Ref_All
			:_Player_BackShield_ObjectPtr
			~If _Object_Type != OG11				;Double-check the object to make sure this isn't somehow an invalid pointer
				#_ShowMessage					;This pointer does not point to a shield object. Throw an error message
				: G4
			~EndIf

			#_Obj=Chr_Hotspot					;Shield follows player
			:   0									;(Spot ID) Player Center Spot
			:_Object_X_Pos								;(Obj2 X)  Object X
			:_Object_Y_Pos								;(Obj2 Y)  Object Y

		~EndIf

	~Else									;If player is invincible

		~If _Player_Type == _Sonic					;If the player is Sonic
			~If _Sonic_InstaShieldFlag != 0				;If player is using insta-shield
				#_GoTo						;Insta-shield causes "invincibility" but should still follow player
				: @InstaShield_Follow
			~EndIf
		~EndIf

		~If _Player_FrontShield_ObjectPtr != 0				;If player has a front shield object

			#_FetchObject_FetchedChr				;Fetch the player's front shield object
			:_Ref_All
			:_Player_FrontShield_ObjectPtr
			~If _Object_Type != OG11				;Double-check the object to make sure this isn't somehow an invalid pointer
				#_ShowMessage					;This pointer does not point to a shield object. Throw an error message
				: G3
			~EndIf

			~Eval _Object_X_Pos = -128				;Hide shield off-screen

		~EndIf

		~If _Player_BackShield_ObjectPtr != 0				;If player has a back shield object

			#_FetchObject_FetchedChr				;Fetch the player's back shield object
			:_Ref_All
			:_Player_BackShield_ObjectPtr
			~If _Object_Type != OG11				;Double-check the object to make sure this isn't somehow an invalid pointer
				#_ShowMessage					;This pointer does not point to a shield object. Throw an error message
				: G4
			~EndIf

			~Eval _Object_X_Pos = -128				;Hide shield off-screen

		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Reset certain control variables after movement, before input (Sentry phase happens right before input phase)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_Skidding  &= 1						;Bit 2 signifies that the player is actively skidding (making this value 3), and should be reset every frame (making this value 1 if the animation is still playing). The animation must terminate itself either when it is finished or when the player velocity reaches 0
	~Eval _Player_Ducking    = 0						;Release ducking state
	~Eval _Player_LookingUp  = 0						;Release looking up state

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Run this sentry code again on the next frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_BreakTo
	: @Repeat

# End	;Terminate Function

;===================================================================================
;==================================
;Function 23 - Spawn Skidding Puff=
;==================================

	.Alias SpawnX
	.Alias SpawnY

	#_FetchPlayer			;Get a pointer to the current player
	:_Ref_All
	:r_Curr_Player

	#_Reg=Chr_MainBound		;Get the Y position of the bottom of the Player's feet
	:_Bound_Y2					;(Side)
	:_SpawnY					;(Reg)

	~If _SpawnY > _Water_Level	;If the position is below the water
		#_StopCurrentFunc	;Don't spawn the puff underwater
	~EndIf

	~Eval _SpawnX = _Player_X_Pos	;Position the object centered horizontally on the player and at his feet

	#_SpawnObject			;Spawn the puff object
	: G19						;(Type) Skidding Puff
	:r_SpawnX					;(X)
	:r_SpawnY					;(Y)
	:  59						;(Pri)  Right in front of players
	:   0						;(Tag)
	:   0						;(Flags)
	:   0						;0 Paramaters

# End	;Terminate Function

;===================================================================================
;====================
;Function 24 - P2 AI=
;====================

	.Alias Pos
	.Alias Keys
	.Alias Index
	.Alias XDist
	.Alias XDist2
	.Alias YDist
	.Alias Frame
@Repeat
	#_FetchPlayer							;Get a pointer to player 2
	:_Ref_All
	:_Player1

	~If _Player_Movement_Mode == _PMM_Debug				;If P1 is in debug mode
		#_GoTo							;Skip position and key logging
		: @SkipKeyLog
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Log position and keystates for Player 1
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Pos  = _Player_X_Pos << 16				;Get Player's X position and move it to the upper word
	~Eval _Pos += _Player_Y_Pos					;Add on the player's Y position

	~Array _P1_PosLog[_PosLog_Ctr] = _Pos				;Store the values into the position/key log

	#_Keys_Pack							;Get P1's key states
	:   0
	:_Keys

	~If _Player_PreviousPushState != 0				;If P1 is pushing
		~Eval _Keys |= $8000					;Flag that P1 was pushing during this frame (use the highest bit as the flag)
	~EndIf

	~Array _P1_KeyLog[_PosLog_Ctr] = _Keys				;Store the state flags into the position/key log

	~Eval _PosLog_Ctr++						;Move to the next index
	~Eval _PosLog_Ctr &= $1F					;Keep the index within bounds
@SkipKeyLog
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for P1 Death or P2 offscreen
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode == _PMM_Dying				;If P1 is dying

		#_FetchPlayer						;Get a pointer to player 2
		:_Ref_All
		:_Player2

		~If _Player_Type == _Tails				;Only Tails performs this action

			~If _Player_AIMode < _AI_SetFlyBack		;If Tails isn't already flying toward P1
				#_GoTo					;Set Tails to fly toward P1
				: @DeathFlyBack
			~EndIf
		~EndIf
	~Else 								;If P1 isn't dying

		#_FetchPlayer						;Get a pointer to player 2
		:_Ref_All
		:_Player2

		~Eval _Pos = _Player_X_Pos - _vpxr[0]			;Get P2's offset from P1's screen left

		~If   _Pos > -32					;If P2 isn't offscreen to the left
			~If   _Pos < 352				;If P2 isn't offscreen to the right

				~Eval _Pos = _Player_Y_Pos - _vpyr[0]	;Get P2's offset from P1's screen top

				~If _Pos > -32				;If P2 isn't offscreen to the top
					~If _Pos < 256			;If P2 isn't offscreen to the bottom

						;;;;;;;;;;;;;;;
						;P2 is Onscreen
						;;;;;;;;;;;;;;;

						~Eval _P2_OffscreenTimer = 0	;Reset P2's Offscreen Timer
						#_GoTo			;Move on to normal following AI
						: @NotOffScreen

					~EndIf
				~EndIf
			~EndIf
		~EndIf

		;;;;;;;;;;;;;;;;
		;P2 is Offscreen
		;;;;;;;;;;;;;;;;

		~Eval _P2_OffscreenTimer++				;Increase P2's off-screen timer
		~If _P2_OffscreenTimer == 300				;If P2 has been off-screen for 5 seconds
			~Eval _Player_AIMode = _AI_FlyBack		;Set to "Set P2 to fly back to P1" mode
			#_GoTo						;Jump to "Set P2 to fly back to P1"
			: @SetFlyBack
		~EndIf
	~EndIf
@NotOffScreen
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for P2 input (temporarily cancels AI)
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_AIMode < _AI_SetFlyBack				;If P2 is in any mode other than flying back or being set to

		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;Test for Player Input, which temporarily cancels AI
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_Keys_Pack 						;Get P2's key states
		:   1
		:_Keys

		~If _Keys != 0						;If Player 2 has entered input this frame
			~Eval _P2_InputTimer = 600			;Set the Timer
		~EndIf
		~If _P2_InputTimer != 0					;If Player 2 has entered input recently
			~Eval _P2_InputTimer--				;Count down the timer
			#_GoTo 						;Skip the AI
			: @EndAI
		~EndIf

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Perform AI based on mode setting
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_StateSwitch(Chr) 						;Perform AI based on P2's state
	: _Player_AIMode								;(Chr0)    AI Mode
	:  5										;There are 5 possible AI states
	: @Following									;(State 0) Following P1
	: @Spindashing									;(State 1) Spindashing toward P1
	: @SetFlyBack									;(State 2) Setting to fly back
	: @FlyBack									;(State 3) Flying back to P1
	: @EndAI									;(State 4) Do Nothing

;***
;State 0 - Following P1
;***

@Following

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If Tails is carrying P1, P1 has direct control of Tails
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Type == _Tails					;Only Tails performs this action
		~If _Tails_CarriedPlayerPtr != 0			;If Tails is carrying another player

			~Eval _Index  = _PosLog_Ctr - 1			;Move back one entry in the log, for already having moved ahead
			~Eval _Index &= $1F				;Keep the index within bounds

			~Array _Keys = _P1_KeyLog[_Index]		;Retrieve Sonic's button state from this frame

			#_Keys_Force 					;Set P2's key states
			:   1								;(Map)
			:_Keys								;(State)
			:_P2_KeyControl							;(Mask)

			#_GoTo 						;End this AI mode
			:  @EndAI

		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If P2 is getting hurt, no AI
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode == _PMM_Hurting			;If the player is getting hurt
		#_GoTo 							;No AI until it's over
		:  @EndAI
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If not Tails carrying P1...
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Check for curves preventing P2 from reaching P1
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_SlipTimer != 0					;If the slip timer is active
		~If _Player_AniSpeed < 128				;If the player isn't moving fast enough to spin
			~Eval _Player_AIMode = _AI_Spindash		;Set to "Spindashing toward P1" mode
			#_GoTo						;Jump to "Spindashing toward P1"
			: @Spindashing
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Obtain P1's position and input state from 16 frames ago
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval  _Index  = _PosLog_Ctr - 17				;Move back 16 (+ 1 for already having moved ahead) entries in the log
	~Eval  _Index &= $1F						;Keep the index within bounds

	~Array _Keys   = _P1_KeyLog[_Index]				;Retrieve P1's button state 16 frames ago
	~Array _Pos    = _P1_PosLog[_Index]				;Retrieve P1's position 16 frames ago

	~Eval _XDist   = _Pos >> 16					;Unpack the X position
	~Eval _YDist   = _Pos  & $FFFF					;Unpack the Y position

	#_FetchPlayer							;Get a pointer to player 1
	:_Ref_Src0
	:_Player1

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;When P2 is not on a solid object, put P2 "behind" P1. Otherwise, use P1 exact position
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path >= 0					;If P1 is not standing on a solid object (negative value), but is on a level path, or in freespace)
		~If _Player_AniSpeed < $400				;If P1 is moving slowly
			~If _Player_X_Dir == _Right			;If P1 is facing right
				~Eval _XDist -= $20			;Offset the target position by $20 to put P2 behind P1
			~Else 						;If P1 is facing left
				~Eval _XDist += $20			;Offset the target position by $20 to put P2 behind P1
			~EndIf
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Determine P2 distance from target
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchPlayer							;Get a pointer to player 2
	:_Ref_Src0
	:_Player2

	~Eval _XDist -= _Player_X_Pos					;Find P2' offset from the target X Position
	~If   _XDist == 0						;If P2 is horizontally aligned with P1
		#_GoTo 							;P2 doesn't need to move
		: @AlreadyInRange
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Force P2 movement toward P1 if not within reasonable range
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _XDist < 0							;If P2 is to the right of the target
		~If _XDist < -$30					;If P2 is not within $30 x of the target
			~Eval _Keys &= $FFF3				;Remove Horizontal Buttonpress
			~Eval _Keys |= $0004				;Force left buttonpress
		~EndIf
		~If _Player_Path_Vel != 0				;If P2 is moving
			~If _Player_X_Dir == _Left			;If P2 is facing left
				~Eval _Player_X_Count -= $100		;Speed P2 up slightly to catch up (add one pixel's-worth of movement)
			~EndIf
		~EndIf
	~Else 								;If P2 is to the left of the target
		~If _XDist > $30					;If P2 is not within $30 x of the target
			~Eval _Keys &= $FFF3				;Remove Horizontal Buttonpress
			~Eval _Keys |= $0008				;Force right buttonpress
		~EndIf
		~If _Player_Path_Vel != 0				;If P2 is moving
			~If _Player_X_Dir == _Right			;If P2 is facing right
				~Eval _Player_X_Count += $100		;Speed P2 up slightly to catch up (add one pixel's-worth of movement)
			~EndIf
		~EndIf
	~EndIf
@AlreadyInRange
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Force P2 to jump if an obstacle prevents him from reaching the
	;target position horizontally, or if he is too far below P1
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _P2_JumpControl != 0					;Test if P2 should sustain a jump
		~Eval _Keys |= $10					;Force jump buttonpress
		~If _Player_On_Path != 0				;If P2 has struck a path
			~Eval _P2_JumpControl--				;Release jump
		~EndIf
	~EndIf

	~If _Player_PreviousPushState != 0				;If P2 is pushing
		~IfFlag _Keys[15] == _Off				;If P1 was not pushing on the target frame
			#_GoTo 						;An obstacle is keeping P2 from getting to P1, make him jump over it
			: @CauseJump
		~EndIf
	~EndIf

	~Eval _YDist -= _Player_Y_Pos					;Find P2' offset from the target Y Position

	~If   _YDist  < -20						;If P2 is not within $20 y of the target
@CauseJump
		~Eval _Frame  = _FrameCounter & $3F			;Test the frame counter for every 64th frame
		~If   _Frame == 0					;If this is a 64th frame
			~IfFlag _Keys[1] == _Off			;Only jump if not ducking, so as to not mistakenly spindash
				~Eval _Keys           |= $10		;Force jump buttonpress
				~Eval _P2_JumpControl  = 2		;Flag for jump sustain (Because of the jump control function break, player still reads as on a path for the first frame, so allow the test to fail once)
			~EndIf
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Use the generated keystate bitfield to force P2's keypresses for this frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_Keys_Force 							;Set P2's key states
	:   1									;(Map)
	:_Keys									;(State)
	:_P2_KeyControl								;(Mask)

	#_GoTo 								;End this AI mode
	: @EndAI

;***
;State 1 - Spindash around a loop/up a curve to keep up
;***

@Spindashing
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Don't allow other copied input or AI to take effect
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Keys          = 0					;Clear the forced buttonpress
	~Eval _P2_KeyControl = 0					;Clear the forced buttonpress

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Test for situations that should not allow Spindash
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_SlipTimer != 0					;If the slip timer is still active
		#_GoTo 							;Don't spindash yet
		:  @EndAI
	~EndIf

	~If _Player_SpindashCtr > 0					;If the player is already charging a Spindash
		#_GoTo 							;Sustain the charge
		: @SustainSpindash
	~EndIf

	~Eval _Player_AniSpeed = +_Player_Path_Vel			;Get proper absolute value of path velocity now, in case player just landed on a curve this frame

	~If _Player_AniSpeed >= 128					;If the player is moving fast enough to spin
		#_GoTo 							;Don't spindash yet
		:  @EndAI
	~EndIf

	~If _Player_On_Path == 0					;If the player is not on a path or a solid object
		#_GoTo 							;Don't spindash yet
		:  @EndAI
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Determine spindash direction and force P2's orientation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchPlayer							;Get a pointer to player 1
	:_Ref_Src1
	:_Player1

	~Eval _XDist = _Player_X_Pos - _Player_X_Pos			;Find the distance between P2 and the target

	~If _XDist < 0							;If P2 is to the left of the target
		~Eval _Player_X_Dir = _Right				;Make player face right
	~Else 								;If P2 is to the right of the target
		~Eval _Player_X_Dir = _Left				;Make player face left
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;
	;Force spindash charge
	;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Keys = $12						;Force down for ducking and A for Charge Spindash

	#_GoTo								;Force the keypresses
	: @SpindashFinish

@SustainSpindash
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Sustain an in-progress spindash until time has expired
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Keys   = $2						;Force down key for ducking
	~Eval _Frame  = _FrameCounter & $7F				;Test the frame counter for every 128th frame
	~If   _Frame != 0						;If this isn't a 128th frame
		#_GoTo							;Test for revving spindash
		: @Spindash_DoRev
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;
	;Release the Spindash
	;;;;;;;;;;;;;;;;;;;;;

	~Eval _Keys          = 0					;Clear buttonpresses to release spindash
	~Eval _Player_AIMode = _AI_Follow				;Go back to following P1

	#_GoTo								;Force the keypresses
	: @SpindashFinish

@Spindash_DoRev
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Rev an in-progress Spindash
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Frame &= $1F						;Test for every 32nd frame
	~If   _Frame == 0						;If this is a 32nd frame
		~Eval _Keys |= $40					;Force C for Charge Spindash
	~EndIf

@SpindashFinish
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Use the generated keystate bitfield to force P2's keypresses for this frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_Keys_Force 							;Set P2's key states
	:   1										;(Map)
	:_Keys										;(State)
	:_P2_KeyControl									;(Mask)

	#_GoTo 								;End this AI mode
	: @EndAI

;***
;State 2 - Setup for flying/swimming back
;***

@SetFlyBack
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Initialize P2 position to just above screen, centered horizontally with P1
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchPlayer
	:_Ref_Src0
	:_Player1

	~Eval _Player_X_Pos  = _Player_X_Pos				;Align P2 horizontally with P1
	~Eval _Player_Y_Pos  = _vpyr[0]					;Move P2 vertically to the top of the screen
	~Eval _Player_Y_Pos -= $C0					;Adjust P2 vertically to be above the screen

	#_FetchPlayer							;P1 was Fetched here.. Must Fetch player 2 again
	:_Ref_Src0
	:_Player2

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Reset offscreen timer and initialize velocities and angle to 0
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@DeathFlyBack
	~Eval _P2_OffscreenTimer = 0					;Reset P2's Offscreen Timer
	~Eval _Player_X_Vel      = 0					;Reset X Velocity so that it doesn't affect P2 during the flight back
	~Eval _Player_Y_Vel      = 0					;Reset Y Velocity so that it doesn't affect P2 during the flight back
	~Eval _Player_Path_Vel   = 0					;Reset Path Velocity so that it doesn't affect P2 during the flight back
	~Eval _Player_Path_Angle = 0					;Reset Path Angle so that it doesn't display P2 at some odd angle if flight ends in freespace

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Any characters other than Tails should reappear directly on top of P1, invulnerable and spinning
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Type != _Tails					;If the player isn't Tails

		#_FetchPlayer						;Get a pointer to player 1
		:_Ref_Src0
		:_Player1

		~Eval _Player_Y_Pos    = _Player_Y_Pos			;Align P2 Vertically with P1
		~Eval _Player_X_Vel    = _Player_X_Vel			;Align P2 horizontally with P1
		~Eval _Player_Y_Vel    = _Player_Y_Vel			;Apply P1's vertical speed to P2
		~Eval _Player_Path_Vel = _Player_Path_Vel		;Apply P1's horizontal speed to P2

		~Eval _Player_Movement_Mode = _PMM_Spinning		;Set P2 to the same Movement Mode as P1
		~Eval _Player_Flash_Timer   = 120			;Give P2 2 seconds of invulnerability

		#_GoTo 							;Skip Tails-specific code and set relevant player states equal to P1's, going back to follow mode
		: @NotTails
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Initialize Tails' flying mode
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Tails_FlightTimer    = 480				;Set Tails's flight timer so he won't be playing the "tired" animation
	~Eval _Player_Movement_Mode = _PMM_FlyBack			;Set Tails to flying back in mode
	~Eval _Player_Path_Mode     = 4					;Go to the no collision "path" mode
	~Eval _Player_AIMode        = _AI_FlyBack			;Go to flying back to P1

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Continue processing into the flying state
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;***
;State 3 - Fly/Swim back toward P1
;***

@FlyBack
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Obtain P1's position from 16 frames ago
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval  _Index  = _PosLog_Ctr - 17				;Move back 16 (+ 1 for already having moved ahead) entries in the log
	~Eval  _Index &= $1F						;Keep the index within bounds

	~Array _Pos    = _P1_PosLog[_Index]				;Retrieve P1's position 16 frames ago

	~Eval  _XDist  = _Pos >> 16					;Unpack the X position
	~Eval  _YDist  = _Pos  & $FFFF					;Unpack the Y position

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Determine P2 horizontal distance from target
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _XDist  -= _Player_X_Pos					;Find Tails' offset from the target X Position

	~Eval _XDist2  = _XDist						;Copy the X offset for testing later

	~If   _XDist  == 0						;If Tails is horizontally aligned with P1
		#_GoTo 							;Tails doesn't need to move
		: @XPosEqual
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Calculate base horizontal speed as 1/16 of the distance,
	;but do not allow it to excede a value of $C. Add P1's
	;velocity to obtain final horizontal velocity
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _XDist >>= 4						;Divide by 16

	#_FetchPlayer							;Get a pointer to player 1
	:_Ref_Src0
	:_Player1

	~If _XDist2 < 0							;If Tails is to the right of the target
		~If _XDist < -$C					;If the modified distance is greater than $C
			~Eval _XDist = -$C				;Cap the value at $C
		~EndIf
		~If _Player_X_Vel < 0					;If P1 is also moving to the left
			~Eval _XDist += _Player_X_Vel			;Add P1's velocity to the modified offset to create Tails's Velocity
		~EndIf
		~Eval _XDist--						;Add one to the new velocity
		~Eval _Player_X_Dir = _Left				;Make Tails face left

		~If _XDist < _XDist2					;If the velocity is greater than the remaining distance
			~Eval _XDist  = _XDist2				;Set the velocity to the remaining distance
			~Eval _XDist2 = 0				;Clear the distance value to indicate that Tails has caught up during this frame
		~EndIf
	~Else 								;If Tails is to the left of the target
		~If _XDist > $C						;If the modified distance is greater than $C
			~Eval _XDist = $C				;Cap the value at $C
		~EndIf
		~If _Player_X_Vel > 0					;If P1 is also moving to the left
			~Eval _XDist += _Player_X_Vel			;Add P1's velocity to the modified offset to create Tails's Velocity
		~EndIf
		~Eval _XDist++						;Add one to the new velocity
		~Eval _Player_X_Dir = _Right				;Make Tails face right
		~If _XDist > _XDist2					;If the velocity is greater than the remaining distance
			~Eval _XDist  = _XDist2				;Set the velocity to the remaining distance
			~Eval _XDist2 = 0				;Clear the distance value to indicate that Tails has caught up during this frame
		~EndIf
	~EndIf

	~Eval _Player_X_Pos += _XDist					;Move Tails horizontally toward the target by the calculated velocity

	#_FetchPlayer
	:_Ref_Src0
	:_Player2
@XPosEqual
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Determine P2 vertical distance from target, and move toward target 1 pixel at a time
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _YDist -= _Player_Y_Pos					;Find Tails' offset from the target Y Position

	~If   _YDist == 0						;If Tails is vertically aligned with P1
		#_GoTo 							;Tails doesn't need to move
		: @YPosEqual
	~EndIf

	~If _YDist < 0							;If Tails is below the target
		~Eval _Player_Y_Pos--					;Move Tails vertically toward the target by 1
	~Else 								;If Tails is above the target
		~Eval _Player_Y_Pos++					;Move Tails vertically toward the target by 1
	~EndIf
@YPosEqual
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If P2 has reached the target position both horizontally and vertically,
	;return to normal AI mode, setting Path Mode, Path ID, and forced spin
	;states equal to those of P1
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _YDist |= _XDist2						;Combine the values to test for nonzero

	~If   _YDist == 0						;If Tails has caught up with the target position

		#_FetchPlayer
		:_Ref_Src0
		:_Player1

		~If _Player_Movement_Mode != _PMM_Dying			;If P1 isn't dying

			~Eval _Player_Movement_Mode = _PMM_Standing	;Set Tails to standing movement mode
			~Eval _Player_AniSpeed      = 1			;Set minimum animation speed for walking (as opposed to standing straight)
@NotTails
			~Eval _Player_Path_Mode     = _Player_Path_Mode	;Set Tails to use the same path mode as P1
			~Eval _Player_Solid_Plane   = _Player_Solid_Plane	;Set Tails to stand on the same path as P1
			~Eval _Player_TubeFlag      = _Player_TubeFlag	;Set Tails' forced-spin state to the same as P1's
			~Eval _Player_AIMode        = _AI_Follow	;Go back to following P1

		~EndIf

	~EndIf

;***
;End states / Repeat next frame
;***

@EndAI
	#_BreakTo
	: @Repeat

# End	;Terminate Function

;===================================================================================
;===============================
;Function 25 - Input Looking Up=
;===============================

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	~If _Player_On_Path != 0						;If player is on the ground

		~If _Player_AniSpeed == 0					;If player isn't moving

			~Eval _Player_LookingUp = 1				;Set Looking Up Animation

			~If _Player_VPPanTimer < 120				;If the viewport scroll timer isn't at full time
				~Eval _Player_VPPanTimer++			;Increase the timer
			~Else 							;If the timer has reached its limit
				~If _Player_VPPanOffset > -104			;If the viewport hasn't panned to its maximum
					~Eval _Player_VPPanOffset -= 2		;Pan the screen down
				~EndIf
			~EndIf

		~EndIf

	~EndIf

# End	;Terminate Function

;===================================================================================
;===========================
;Function 26 - Demo Control=
;===========================

	.Alias Data
	.Alias Data2

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;The demo-end fade-out timer is handled first, if it's active,
	;but only when this Function is run for Player 1
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Curr_Player == _Player1					;If Player 1 called this Function

		~If _DemoFade > 0					;If demo-end fadeout has been activated

			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			;The demo-end fadeout is active. Fade toward black and
			;count down the timer to determine when to cut the demo
			;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

			#_Pal_FadeEffect				;Fade the palette toward complete blackness by one step
			:_PalFade_ToBlack_Once

			~Eval _DemoFade--				;Decrease the demo-end fadeout timer
			~If   _DemoFade == 0				;If the timer has expired

				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
				;The demo-end timer has expired. It is now time
				;to stop playback read or recording write and
				;return to the title screen
				;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

				#_StateSwitch(Reg)			;Use demo system state to determine what to do after fade-out
				:_DemoFlag
				: 3
				: @DemoEnd_None				;Dummy state- This is not a demo
				: @DemoEnd_Read				;A demo is being played back
				: @DemoEnd_Write			;A demo is being recorded
@DemoEnd_Read
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;A demo is being played back. Close the read file
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					#_File_Close			;Close the read file
					:_FileType_Read
					#_GoTo				;Skip other demo state code
					: @DemoEnd_Reset
@DemoEnd_Write
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
					;A demo is being written. Write one more byte
					;to prevent playback from reading beyond the
					;end of the file, and close the write file
					;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

					#_File_WriteBYTEs_Reg		;Write a terminating byte
					:_Data2
					: 1
					#_File_Close			;Close the write file
					:_FileType_Write
@DemoEnd_Reset
				#_SetLevel				;Return to the Title Screen
				:_ZoneID_Reset						;(Zone)
				:  0							;(Act)
@DemoEnd_None
				#_StopCurrentFunc			;The demo is over, stop demo processing

			~EndIf
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;A demo is still being processed. Perform playback or recording
	;for the current player depending on demo state
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_StateSwitch(Reg)						;Use demo system state to determine whether to read or write
	:_DemoFlag
	: 3
	: @DemoDo_None									;Dummy state- This is not a demo
	: @DemoDo_Read									;A demo is being played back
	: @DemoDo_Write									;A demo is being recorded
@DemoDo_Read
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;A demo is being played back. Read and apply the Player key state for this frame
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_File_ReadBYTEs_Reg					;Read the next key state byte from the demo file
		:_Data
		: 1

		~If _Data == $FF					;Check whether the byte is a demo fade-out flag

			~If _DemoFade == 0				;If demo fade-out has not already been activated
				~Eval _DemoFade = 60			;Set demo fade-out to process over the course of 60 frames (1 second)
				~Flag _FreezeObjects[_Freeze_PalAni] = _On 	;Disable palette animation so that it doesn't interfere with the fade
			~EndIf

			~Flag _Player_Flags[_PFlag_VPCenter] = _Off	;Stop the camera from following the player during the fade-out

			#_File_ReadBYTEs_Reg				;Read the next byte to actually obtain the key state for this frame (as the one byte read for this frame was just the demo fade-out flag)
			:_Data
			: 1

		~EndIf

		~If _Data != $0F					;If this is not an AI identifier (marks "no keys pressed" for Player 2's AI mode)

			~Array _Data2 = _DemoKeys_P1[_Curr_Player]	;Get the demo keys held state for the current Player

				#_Keys_Force 				;Apply Player's key states
				:r_Curr_Player						;(Map)
				:_Data							;(State) Player KeyState
				:_Data2							;(Mask)  Player KeyControl

			~Array _DemoKeys_P1[_Curr_Player] = _Data2	;Remember the newest set of keys as the "held" state

		~EndIf

		#_GoTo							;Skip other demo state code
		: @DemoDo_None
@DemoDo_Write
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;A demo is being written. Pack and write the Player key state for this frame
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		#_If(KeyState)						;If the Player has pressed "Start" on this frame
		: M2T0
		:_On

			~Eval _Data2 = $FF				;Prepare the demo fade-out flag for write

			#_File_WriteBYTEs_Reg				;Write the demo fade-out flag to the file
			:_Data2
			: 1

			~Eval _DemoFade = 62				;Set the demo fade-out timer to fade during demo recording, and continue to record until the fade is complete (Add two frames to write fade-out so the playback doesn't end up reading past the end of the file during the read fade)
			~Flag _FreezeObjects[_Freeze_PalAni] = _On 	;Disable palette animation so that it doesn't interfere with the fade

		~EndIf

		#_Keys_Pack						;Get Player's key state for this frame
		:r_Curr_Player
		:_Data

		~If _Option_PlayMode == 0				;If the mode is single-player
			~If _Curr_Player == _Player2			;If Player 2 is using AI
				~If _P2_InputTimer == 0
					~Eval _Data = $0F		;Output an AI identifier in place of Player 2's input to signify no actual input on this frame
				~EndIf
			~EndIf
		~EndIf

		#_File_WriteBYTEs_Reg					;Write the Player's key state for this frame to the demo file
		:_Data
		: 1
@DemoDo_None

# End	;Terminate Function

;===================================================================================
;===================================
;Function 27 - Forced Rightward Run=
;===================================

	.Alias Data
	.Alias Data2

@WaitToLand
	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the Player is in Debug Mode, do not take automatic control
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Movement_Mode == _PMM_Debug			;If the Player is in Debug Mode
		#_StopCurrentFunc				;Terminate this Function to allow user to retain Player control
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the player is still in midair, wait for him to land before enforcing rightward motion
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_On_Path == 0				;If the Player is not on a path or a solid object

		#_BreakTo					;Go back to perform the test again on the next frame
		: @WaitToLand

	~EndIf
@Repeat
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;The Player has landed on the ground without having entered Debug Mode. Disable
	;user control of the Player and force him to run off-screen to the right,
	;jumping over any obstacle that blocks his path
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	#_Keys_Pack						;Get the Player's key states
	:r_Curr_Player
	:_Data

	~Eval _Data &= _Button_B				;Test whether the Player has pressed the "B" Button (and is attempting to enter Debug Mode)

	~If   _Data != 0					;If the "B" button was pressed (the player attempted to enter Debug Mode)
		#_StopCurrentFunc				;Terminate this Function to restore control of the Player to the user
	~EndIf

	~Eval _Player_ForceJumpFlag  = 0			;Initialized the forced jump flag to off state
	~If   _Player_Cant_Go_Right != 0			;If the Player is blocked by a solid object or level layout to the right
		~Eval _Player_ForceJumpFlag  = 1		;Flag that jump height control should not be applied (the Player should reach the top of his jump as if the button was held)
		~Eval _Data                 |= _Button_C	;Force a jump button press
	~EndIf

	~Eval _Data |= _Button_Right				;Force a right button press

	~Array _Data2 = _DemoKeys_P1[_Curr_Player]		;Get the demo keys held state for the current Player

	#_Keys_Force 						;Force the Player's key state for rightward motion, and if applicable, jumping
	:r_Curr_Player								;(Map)
	:_Data									;(State) Player KeyState
	:_Data2									;(Mask)  Player KeyControl

	~Array _DemoKeys_P1[_Curr_Player] = _Data2		;Remember this set of keys as the "held" state

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Run this sentry code again on the next frame
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_BreakTo
	: @Repeat

# End	;Terminate Function

;===================================================================================
;===============================
;Function 28 - Set Speed Values=
;===============================

	.Alias CharTable
	.Alias Index

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Use the Player's state variables to determine which set of table
	;entries should be read to obtain the appropriate speed values
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _CharTable = _Table_SonicSpeeds + _Player_Type			;Calculate the appropriate Table ID based on the current Player Character type

	~Eval _Index = 0							;Initialize the read index

	~If _Player_WaterFlag != 0						;If the Player is underwater
		~Flag _Index[3] = _On						;Flag to choose from the "underwater"-based speed values from the table
	~EndIf

	~If _Player_SuperFlag != 0						;If the Player is in Super state
		~Flag _Index[4] = _On						;Flag to choose from the "Super"-based speed values from the table
	~EndIf

	~If _Player_SneakerTimer != 0						;If the Player has Super or Lead Sneakers
		~If _Player_SneakerTimer > 0					;If the Player has Super Sneakers
			~Flag _Index[5] = _On					;Flag to choose the from the "Super Sneakers"-based speed values
		~Else								;If the Player has Lead Sneakers
			~Flag _Index[6] = _On					;Flag to choose the from the "Lead Sneakers"-based speed values
		~EndIf
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;The appropriate table starting index has now been created based
	;on the Player's state variables. Read the entries from this
	;section of the table and apply them to the Player
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~TableW _Player_MaxVel      = _CharTable[_Index]			;Set the Player's Maximum input velocity value
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_Accel       = _CharTable[_Index]			;Set the Player's input acceleration value
	~TableW _Player_Decel       = _CharTable[_Index]			;Set the Player's no-input standing deceleration value
	~If _Player_SuperFlag > 0						;If the Player is in Super state
		~If _Player_SneakerTimer > 0					;If the Player has Super Sneakers
			~Eval _Player_Decel >>= 1				;No-input deceleration should be half of normal (Super with Super Sneakers)
		~Else								;If the Player does not have Super Sneakers
			~Eval _Player_Decel >>= 2				;No-input deceleration should be one quarter of normal (Super without Super Sneakers)
		~EndIf
	~EndIf
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_SkidDecel   = _CharTable[_Index]			;Set the Player's input "skidding" deceleration value
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_SpinDecel   = _CharTable[_Index]			;Set the Player's no-input spinning deceleration value
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_HurtBounceX = _CharTable[_Index]			;Set the Player's horizontal bounce velocity value for when hurt
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_HurtBounceY = _CharTable[_Index]			;Set the Player's vertical bounce velocity value for when hurt
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_JumpForce   = _CharTable[_Index]			;Set the Player's "jump force" velocity value (vectored with the angle of the ground he's standing on to create horizontal and vertical velocities)
	~Eval _Index++								;Move to the next table entry
	~TableW _Player_Weight      = _CharTable[_Index]			;Set the Player's "weight" value (the velocity applied on every frame as force of gravity)

# End	;Terminate Function

;===================================================================================
;===================================
;Function 29 - Super Transformation=
;===================================

	#_PlaySound
	:_SFX_Transform2
	:_Snd_Once

	;;;;;;;;;;;;;;;;;;;;;;;;
	;Start the "Super" music
	;;;;;;;;;;;;;;;;;;;;;;;;

	#_PlayMusic
	: G10										;Super Music

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Cause the Player to pause in mid-air and play his "transformation" animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_Player_ChangeAnimations					;Set the floating animation slot to the glide falling animation
	:   1										;1 Animation
	: l17										;(Ref ID) Floating Slot
	: l30										;(New ID) Super Transformation Slot
	:_AniChange_Restart								;(Smooth)

	#_Player_SetAni_ByList						;Set player to use his current "floating" animation
	: l17 ;Player Current Floating Animation

	~Eval _Player_FloatingFlag  = 3					;Enable the "floating" animation

	~If _Player_Movement_Mode  == _PMM_Spinning			;If the Player is spinning

		~Eval _Player_Movement_Mode = _PMM_Standing		;Abort spin by switching to standing mode
		~Eval _Player_VPPanOffset  -= _Player_SpinOffset	;Adjust the VPY offset so the sudden transition from spinning to standing doesn't cause the screen to jerk

	~EndIf

	~Eval _Player_On_Path     = 0					;In case the player is already on a path, cause a disconnect so that the "floating" state will be cancelled as soon as control is restored

	~Eval _Player_Freeze      = _PFreeze_Movement			;Disallow player movement during the transformation animation (the animation itself will restore movement when appropriate)

	;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Setup "Super" timer values
	;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_SuperFlag   = 60					;Initialize the frame counter for 60 frames (1 second), the time it takes for one ring to be removed from the Player's ring counter
	~Eval _Player_InvincTimer = _Player_Rings			;Set the Player's invincibility timer, so invincibility behavior will be in effect

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Call the speed value handler to apply Super state to the Player's speed values
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	#_RunFunc_CurrPlayer_Const
	: 28 ;Set Speed Values

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Sonic uses different animations when Super than when not
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_Type == _Sonic					;If the Player is Sonic
		#_RunFunc_CurrPlayer_PList				;Set Sonic to use his Super animations
		: 45 ;Set Super Anis
	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;If the player currently has a shield, move it off-screen now since the code to hide
	;the shield during invincibility will not run until Player_Freeze is cleared
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~If _Player_FrontShield_ObjectPtr != 0				;If player has a front shield object

		#_FetchObject_FetchedChr				;Fetch the player's front shield object
		:_Ref_All
		:_Player_FrontShield_ObjectPtr

		~Eval _Object_X_Pos = -128				;Hide shield off-screen

	~EndIf

	~If _Player_BackShield_ObjectPtr != 0				;If player has a back shield object

		#_FetchObject_FetchedChr				;Fetch the player's back shield object
		:_Ref_All
		:_Player_BackShield_ObjectPtr

		~Eval _Object_X_Pos = -128				;Hide shield off-screen

	~EndIf

# End	;Terminate Function

;===================================================================================
;===================================================
;Function 30 - Lock Controls And Pose After Landing=
;===================================================

	.Alias KeyState
	.Alias KeyMask

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Wait until the Player lands on a surface before
	;locking control and beginning the animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@WaitToLand
	#_FetchPlayer
	:_Ref_All
	:r_Curr_Player

	~If _Player_On_Path == 0			;If the Player is not on a path or a solid object

		#_BreakTo				;Go back to test again on the next frame
		: @WaitToLand

	~EndIf

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;Player has landed on a surface. Stop motion,
	;lock controls, and play the victory animation
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	~Eval _Player_X_Vel         = 0			;Stop Player horizontal motion
	~Eval _Player_Y_Vel         = 0			;Stop player vertical motion
	~Eval _Player_Path_Vel      = 0			;Stop player path motion
	~Eval _Player_Movement_Mode = _PMM_Animate	;Set the Player to animation-only mode (no movement, no input handling)
	#_Player_SetAni_ByList				;Set Player to play his victory animation
	: l31

# End	;Terminate Function

-EOF-