freeslot("S_PLAY_PEEL1", "S_PLAY_PEEL2", "S_PLAY_PEEL3", "S_PLAY_PEEL4", "S_PLAY_PWALK1", "S_PLAY_PWALK2", "S_PLAY_PWALK3", "S_PLAY_PWALK4", "S_PLAY_PWALK5", "S_PLAY_PWALK6", "S_PLAY_PWALK7", "S_PLAY_PWALK8", "S_PLAY_PWALK9", "S_PLAY_PWALK10", "S_PLAY_PWALK11", "S_PLAY_PWALK12", "S_PLAY_PWALK13", "S_PLAY_PWALK14", "S_PLAY_PWALK15", "S_PLAY_PWALK16", "S_PLAY_MPEEL1", "S_PLAY_MPEEL2", "S_PLAY_MPEEL3", "S_PLAY_MPEEL4", "S_PLAY_SPEEL1", "S_PLAY_SPEEL2")
states[S_PLAY_PWALK1] = {SPR_PLAY, SPR2_WALK1, 4, nil, 0, 0, S_PLAY_PWALK2}
states[S_PLAY_PWALK2] = {SPR_PLAY, SPR2_WALK2, 4, nil, 0, 0, S_PLAY_PWALK3}
states[S_PLAY_PWALK3] = {SPR_PLAY, SPR2_WALK3, 4, nil, 0, 0, S_PLAY_PWALK4}
states[S_PLAY_PWALK4] = {SPR_PLAY, SPR2_WALK4, 3, nil, 0, 0, S_PLAY_PWALK5}
states[S_PLAY_PWALK5] = {SPR_PLAY, SPR2_WALK5, 3, nil, 0, 0, S_PLAY_PWALK6}
states[S_PLAY_PWALK6] = {SPR_PLAY, SPR2_WALK6, 3, nil, 0, 0, S_PLAY_PWALK7}
states[S_PLAY_PWALK7] = {SPR_PLAY, SPR2_WALK7, 2, nil, 0, 0, S_PLAY_PWALK8}
states[S_PLAY_PWALK8] = {SPR_PLAY, SPR2_WALK8, 2, nil, 0, 0, S_PLAY_PWALK9}
states[S_PLAY_PWALK9] = {SPR_PLAY, SPR2_WALK1, 2, nil, 0, 0, S_PLAY_PWALK10}
states[S_PLAY_PWALK10] = {SPR_PLAY, SPR2_WALK2, 1, nil, 0, 0, S_PLAY_PWALK11}
states[S_PLAY_PWALK11] = {SPR_PLAY, SPR2_WALK3, 1, nil, 0, 0, S_PLAY_PWALK12}
states[S_PLAY_PWALK12] = {SPR_PLAY, SPR2_WALK4, 1, nil, 0, 0, S_PLAY_PWALK13}
states[S_PLAY_PWALK13] = {SPR_PLAY, SPR2_WALK5, 1, nil, 0, 0, S_PLAY_PWALK14}
states[S_PLAY_PWALK14] = {SPR_PLAY, SPR2_WALK6, 1, nil, 0, 0, S_PLAY_PWALK15}
states[S_PLAY_PWALK15] = {SPR_PLAY, SPR2_WALK7, 1, nil, 0, 0, S_PLAY_PWALK16}
states[S_PLAY_PWALK16] = {SPR_PLAY, SPR2_WALK8, 1, nil, 0, 0, S_PLAY_PEEL1}
states[S_PLAY_PEEL1] = {SPR_PLAY, SPR2_RUN_1, 1, nil, 0, 0, S_PLAY_PEEL2}
states[S_PLAY_PEEL2] = {SPR_PLAY, SPR2_RUN_2, 1, nil, 0, 0, S_PLAY_PEEL3}
states[S_PLAY_PEEL3] = {SPR_PLAY, SPR2_RUN_3, 1, nil, 0, 0, S_PLAY_PEEL4}
states[S_PLAY_PEEL4] = {SPR_PLAY, SPR2_RUN_4, 1, nil, 0, 0, S_PLAY_PEEL1}
states[S_PLAY_MPEEL1] = {SPR_PLAY, SPR2_DASH1, 1, nil, 0, 0, S_PLAY_MPEEL2}
states[S_PLAY_MPEEL2] = {SPR_PLAY, SPR2_DASH2, 1, nil, 0, 0, S_PLAY_MPEEL3}
states[S_PLAY_MPEEL3] = {SPR_PLAY, SPR2_DASH3, 1, nil, 0, 0, S_PLAY_MPEEL4}
states[S_PLAY_MPEEL4] = {SPR_PLAY, SPR2_DASH4, 1, nil, 0, 0, S_PLAY_MPEEL1}
states[S_PLAY_SPEEL1] = {SPR_PLAY, SPR2_RUN_1, 1, nil, 0, 0, S_PLAY_SPEEL2}
states[S_PLAY_SPEEL2] = {SPR_PLAY, SPR2_RUN_2, 1, nil, 0, 0, S_PLAY_SPEEL1}

freeslot(
"S_DROPDASH",
"SPR_DRPD",
"sfx_drpdsh",
"sfx_pelchr",
"sfx_pelrel",
"sfx_airrol",
"sfx_mcbnce",
"MT_INSTASHIELD",
"S_INSTASHIELD1",
"S_INSTASHIELD1B",
"S_INSTASHIELD2",
"S_INSTASHIELD2B",
"S_INSTASHIELD3",
"S_INSTASHIELD3B",
"S_INSTASHIELD4",
"S_INSTASHIELD4B",
"S_INSTASHIELD5",
"S_INSTASHIELD5B",
"S_INSTASHIELD6",
"S_INSTASHIELD6B",
"S_INSTASHIELD7",
"S_INSTASHIELD7B",
"SPR_TWSP",
"S_POSE1",
"S_POSE2",
"S_POSE3",
"SPR_POSE",
"S_SPRINGTWIRL",
"SPR_SPTW",
"S_BURN",
"SPR_BURN",
"S_ELECTRO1",
"S_ELECTRO2",
"SPR_ELED",
"S_PLAY_LDSH", 
"S_PLAY_LDSH2",
"S_DAB",
"SPR_DABM",
"S_BOUNCE1",
"S_BOUNCE2",
"SPR_BUCE",
"S_MCALTFALL1",
"S_MCALTFALL2",
"SPR2_MCFL",
"SPR2_MCTR",
"S_HELLFIRE",
"SPR_HEFI",
"sfx_hefire"
)

states[S_DROPDASH] = {
	sprite = SPR_DRPD,
	frame = FF_ANIMATE,
	tics = -1,
	var1 = 5,
	var2 = 1,
	nextstate = S_PLAY_ROLL
}

sfxinfo[sfx_drpdsh] = {
	singular = false,
	caption = "Dropdash"
}

sfxinfo[sfx_pelchr] = {
	singular = false,
	caption = "Peelout charge"
}

sfxinfo[sfx_pelrel] = {
	singular = false,
	caption = "Peelout release"
}

sfxinfo[sfx_airrol] = {
	singular = false,
	caption = "Air roll"
}

sfxinfo[sfx_hefire] = {
	singular = false,
	caption = "I WILL RAIN HELLFIRE UPON YOU"
}

sfxinfo[sfx_mcbnce] = {
	singular = false,
	caption = "Bounce"
}

states[S_POSE1] = {
	sprite = SPR_POSE,
	frame = A,
	tics = 5,
	nextstate = S_POSE2
}

states[S_POSE2] = {
	sprite = SPR_POSE,
	frame = B,
	tics = 5,
	nextstate = S_POSE3
}

states[S_POSE3] = {
	sprite = SPR_POSE,
	frame = C,
	tics = -1,
	nextstate = S_PLAY_STND
}

states[S_SPRINGTWIRL] = {
	sprite = SPR_SPTW,
	frame = FF_ANIMATE,
	tics = -1,
	var1 = 7,
	var2 = 2,
	nextstate = S_PLAY_SPRING
}

mobjinfo[MT_INSTASHIELD] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_INSTASHIELD1,
	radius = 32*FRACUNIT,
	height = 32*FRACUNIT,
	flags = MF_NOGRAVITY|MF_NOCLIPHEIGHT
}

states[S_BURN] = {
	sprite = SPR_BURN,
	frame = A,
	tics = -1,
	nextstate = S_BURN
}

states[S_ELECTRO1] = {
	sprite = SPR_ELED,
	frame = A,
	tics = -1,
	nextstate = S_ELECTRO2
}

states[S_ELECTRO2] = {
	sprite = SPR_ELED,
	frame = B,
	tics = -1,
	nextstate = S_ELECTRO1
}

states[S_PLAY_LDSH] = {
	sprite = SPR_LDSH,
	frame = A,
	tics = 0,
	nextstate = S_PLAY_LDSH2
}

states[S_PLAY_LDSH2] = {
	sprite = SPR_LDSH,
	frame = A,
	tics = 1,
	nextstate = S_PLAY_RUN
}

states[S_DAB] = {
	sprite = SPR_DABM,
	frame = A,
	tics = -1,
	nextstate = S_PLAY_STND
}

states[S_BOUNCE1] = {
	sprite = SPR_BUCE,
	frame = A,
	tics = 3,
	nextstate = S_BOUNCE2
}

states[S_BOUNCE2] = {
	sprite = SPR_BUCE,
	frame = B,
	tics = -1,
	nextstate = S_BOUNCE2
}

states[S_MCALTFALL1] = {
	sprite = SPR_PLAY,
	frame = SPR2_MCTR|A,
	tics = 2,
	nextstate = S_MCALTFALL2
}

states[S_MCALTFALL2] = {
	sprite = SPR_PLAY,
	frame = SPR2_MCFL|FF_ANIMATE,
	tics = 1,
	var1 = 1,
	var2 = 1,
	nextstate = S_PLAY_ROLL
}

states[S_HELLFIRE] = {
	sprite = SPR_HEFI,
	frame = A,
	tics = -1,
	nextstate = S_PLAY_WAIT
}

states[S_INSTASHIELD1] = {SPR_TWSP, 0|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD1B}
states[S_INSTASHIELD1B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD2}
states[S_INSTASHIELD2] = {SPR_TWSP, 1|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD2B}
states[S_INSTASHIELD2B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD3}
states[S_INSTASHIELD3] = {SPR_TWSP, 2|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD3B}
states[S_INSTASHIELD3B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD4}
states[S_INSTASHIELD4] = {SPR_TWSP, 3|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD4B}
states[S_INSTASHIELD4B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD5}
states[S_INSTASHIELD5] = {SPR_TWSP, 4|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD5B}
states[S_INSTASHIELD5B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD6}
states[S_INSTASHIELD6] = {SPR_TWSP, 5|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD6B}
states[S_INSTASHIELD6B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD7}
states[S_INSTASHIELD7] = {SPR_TWSP, 5|FF_FULLBRIGHT, 1, A_CapeChase, 0, 0, S_INSTASHIELD7B}
states[S_INSTASHIELD7B] = {SPR_NULL, 0, 1, A_CapeChase, 0, 0, S_INSTASHIELD1}

local DustSpawnCooldown = 0

// Momentum Thrust.
// Based some code off of the Jump Thrust by CobaltBW.
// This is due to the friend group's love for the Jump Thrust.

local SpeedLimit = function(mo,amt)
	if mo.eflags&MFE_UNDERWATER then
		amt = $/2
	end
	
	if mo.player.powers[pw_sneakers]
	or mo.player.powers[pw_super]
		amt = $*2
	end
	
	if FixedHypot(mo.momx,mo.momy) > amt then
		local ang = R_PointToAngle2(0,0,mo.momx,mo.momy)
		mo.momx = P_ReturnThrustX(mo,ang,amt)
		mo.momy = P_ReturnThrustY(mo,ang,amt)
	end
end

local function momentumThok(player)
	if player.mo.skin != "mcsonic" return end
	
	if player.bouncing
		return true
	end
	
	if player.pflags & PF_THOKKED
		if not player.mchyper
			return 
		end
		
		if not (player.pflags & PF_JUMPDOWN)
		and not player.hyperdashed
		and not player.chargewalldash
			player.momentumthokking = false
			player.hyperdash = 3*TICRATE
			player.hyperdashmomz = 0
			player.hyperdashed = true
			player.bouncing = false
			player.mo.spritexscale = FRACUNIT
			player.mo.spriteyscale = FRACUNIT
			P_InstaThrust(player.mo, player.mo.angle, 120*FRACUNIT)
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			S_StartSound(player.mo, sfx_s3k4e)
			P_FlashPal(player, PAL_WHITE, 8)
			player.drawangle = player.mo.angle
			S_StartSound(player.mo, sfx_kc4d)
		end
		
		return
	end
	
	local angle = player.mo.angle
	local actionspd = player.actionspd	
	local xshift = player.cmd.forwardmove*FRACUNIT
	local yshift = player.cmd.sidemove*FRACUNIT
	local x = player.mo.x
	local y = player.mo.y
	local vthrust = 8+(1/2)
	local hthrust = FixedHypot(xshift,yshift)/50
	
	if (player.mo.eflags & MFE_UNDERWATER)
		vthrust = $ * 2 / 3
	end
	
	if player.mchyper
		if hthrust > 0
			vthrust = $ * 3 / 2
		else
			vthrust = $ * 2
		end
	end
	
	if player.dashmode2 >= 2*TICRATE
	or player.powers[pw_super]
	or player.powers[pw_sneakers]
		player.momthincrease = 2
	else
		player.momthincrease = 1
	end
	
	P_Thrust(player.mo, player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT), FixedMul(hthrust, player.actionspd))
	
	SpeedLimit(player.mo,60*player.mo.scale*player.momthincrease)
	
	P_SetObjectMomZ(player.mo, (vthrust*player.jumpfactor), false)
	
	local MCBoost = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_MCBOOST)
	MCBoost.color = player.mo.color
	MCBoost.angle = player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT) + ANGLE_90
	MCBoost.scale = player.mo.scale
	MCBoost.destscale = player.mo.scale
	local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_DUST)
	dust.scale = player.mo.scale
	S_StartSound(player.mo, sfx_thok)
	player.pflags = $|PF_THOKKED
	player.momentumthokking = true
	if player.mchyper
		P_StartQuake(25*FRACUNIT, 16)
		P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
		P_FlashPal(player, PAL_WHITE, 8)
		S_StartSound(player.mo, sfx_s3k4e)
	end
end

addHook("AbilitySpecial", momentumThok)

local function momentumThokeffect(player)
	if player.mo.skin != "mcsonic" return end

	if DustSpawnCooldown > 0
		DustSpawnCooldown = $1 - 1
	end
	
	if player.hyperdash == nil
		player.hyperdash = 0
	end
	
	if player.hyperdash > 0
		player.hyperdash = $ - 1
	end
	
	if player.hyperdashmomz == nil
		player.hyperdashmomz = 0
	end
	
	if player.momentumthokking == nil
		player.momentumthokking = false
	end
	
	if player.momentumthokking
		if (leveltime % 4 == 0)
			local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_DUST)
			dust.momx = cos(player.mo.angle)*-10+(P_RandomRange(-5,5)*FRACUNIT)
			dust.momy = sin(player.mo.angle)*-10+(P_RandomRange(-5,5)*FRACUNIT)
			dust.momz = P_RandomRange(-5,5)*FRACUNIT
			dust.scale = player.mo.scale
		end
		
		local trail = P_SpawnGhostMobj(player.mo)
			
		for i = 0,9
			trail.fuse = 9
			trail.state = S_THOK
			trail.scale = FRACUNIT - (i * FRACUNIT/240)
			trail.destscale = 0
			if player.mchyper
				trail.color = (SKINCOLOR_HYPER1 + abs(((player.powers[pw_super] >> 2) % 9) - 6))
			else
				trail.color = player.mo.color
			end
		end
	end
	
	if player.momentumthokking
		player.thrustfactor = skins[player.mo.skin].thrustfactor * 3 / 2
	elseif not player.momentumthokking
		player.thrustfactor = skins[player.mo.skin].thrustfactor
	end
	
	if player.hyperdash
		if player.cmd.buttons & BT_JUMP
		and not (player.cmd.buttons & BT_SPIN)
			player.hyperdashlean = 1*FRACUNIT
		elseif player.cmd.buttons & BT_SPIN
		and not (player.cmd.buttons & BT_JUMP)
			player.hyperdashlean = -1*FRACUNIT
		elseif not (player.cmd.buttons & BT_SPIN)
		and not (player.cmd.buttons & BT_JUMP)
			player.hyperdashlean = 0
		end
			
		if player.hyperdashlean != 0
			player.hyperdashmomz = $ + player.hyperdashlean
		elseif player.hyperdashmomz > 0
			player.hyperdashmomz = $ - FRACUNIT
		elseif player.hyperdashmomz < 0
			player.hyperdashmomz = $ + FRACUNIT
		end
		P_SetObjectMomZ(player.mo, player.hyperdashmomz, false)
		player.mo.state = S_PLAY_RUN
		player.calculatinghyperdash = true
	end
	
	if not player.hyperdash
	and player.calculatinghyperdash
		S_StartSound(player.mo, sfx_kc65)
		player.mo.state = S_PLAY_FALL
		player.calculatinghyperdash = false
	end
	
	if player.hyperdash
	and P_IsObjectOnGround(player.mo)
		player.hyperdash = 0
	end
	
	if P_IsObjectOnGround(player.mo)
		player.hyperdashed = false
	end
	
	if not (player.pflags & PF_THOKKED)
	or player.hypertrans
	or player.bouncing
	or player.airrolled
	or player.pflags & PF_SHIELDABILITY
		player.momentumthokking = false
	end
end

addHook("PlayerThink", momentumThokeffect)

// Dashmode.
// Based off source code.

local function DoSpindust(player, speed)
	local mo = player.mo
	local prandom = {}
	for i = 0, (leveltime%7) >> 1 // 1, 2, 3 or 4 particles
		local particle = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_SPINDUST)
		
		if mo.eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)
			particle.state = S_SPINDUST_BUBBLE1
		end

		particle.target = mo
		particle.destscale = (mo.destscale << 1)/3
		P_SetScale(particle, particle.destscale)
		if mo.eflags & MFE_VERTICALFLIP // readjust z position if needed
			particle.z = mo.z + mo.height - particle.height
		end
		prandom[0] = P_RandomFixed()<<2 // P_RandomByte()<<10
		prandom[1] = P_RandomRange(-30, 30) // P_RandomRange(-ANG30/FRACUNIT, ANG30/FRACUNIT)*FRACUNIT
		prandom[2] = P_RandomFixed()<<3 // P_RandomByte()<<11
		P_SetObjectMomZ(particle, speed/50 + prandom[0], false)
		P_InstaThrust(particle,
				player.drawangle + prandom[1]*ANG1,
				-FixedMul(speed/12 + FRACUNIT + prandom[2], mo.scale))
		P_TryMove(particle, particle.x + particle.momx, particle.y + particle.momy, true)
	end
end

local function PlayerSpawnClearing(player)
	if not (player.mo and player.mo.valid) return end 
	if player.mo.skin != "mcsonic" return end
	
	player.dashmode2 = 0
	player.normalspeed = skins[player.mo.skin].normalspeed
	player.jumpfactor = skins[player.mo.skin].jumpfactor
	player.charflags = $1 & ~SF_RUNONWATER
end

addHook("PlayerSpawn", PlayerSpawnClearing)

local function Dashmode(player)
	if not (player.mo and player.mo.valid) return end 
	if player.mo.skin != "mcsonic" return end
	
	if player.gotflag
	or player.powers[pw_carry]
	or player.exiting
	or maptol & TOL_NIGHTS
	or not (player.playerstate == PST_LIVE)
		if player.dashmode2 >= TICRATE
			if player.xmomentum
				player.fakenormalspeed = skins[player.mo.skin].normalspeed
			else
				player.normalspeed = skins[player.mo.skin].normalspeed
			end
			player.jumpfactor = skins[player.mo.skin].jumpfactor
			player.charflags = $1 & ~SF_RUNONWATER
			S_StartSound(player.mo, sfx_kc65)
		end
		player.dashmode2 = 0
		return
	end
	
	if player.dashmode2 == nil
		player.dashmode2 = 0
	end
	
	if player.striketimer == nil
		player.striketimer = 0
	end
	
	if player.lastz == nil
		player.lastz = 0
	end
	
	if player.airspeed == nil
		player.airspeed = 0
	end
	
	local prevdashmode = player.dashmode2
	
	if P_PlayerInPain(player)
		if player.dashmode2 >= 2*TICRATE
			player.twirlpain = true
		end
		player.dashmode2 = 0
		if player.xmomentum
			player.fakenormalspeed = skins[player.mo.skin].normalspeed
		else
			player.normalspeed = skins[player.mo.skin].normalspeed
		end // Reset to default if not capable of entering dash mode.
		player.jumpfactor = skins[player.mo.skin].jumpfactor
		player.charflags = $1 & ~SF_RUNONWATER
	end
	
	if player.twirlpain
		player.drawangle = $ + ANG20
		if P_GetPlayerControlDirection(player) == 0
		and player.pflags&PF_DIRECTIONCHAR and player.pflags&PF_ANALOGMODE
			player.mo.angle = player.cmd.angleturn<<16
		end
	end
	
	if not P_PlayerInPain(player)
		player.twirlpain = false
	end
	
	if (player.speed >= FixedMul(player.runspeed, player.mo.scale))
	and P_IsObjectOnGround(player.mo)
	and not player.powers[pw_super]
		if player.dashmode2 > 3*TICRATE
			player.dashmode2 = 3*TICRATE
		end
		
		player.dashmode2 = $ + 1
		
		if player.dashmode2 == 2*TICRATE
			S_StartSound(player.mo, sfx_kc4d)
			local MCBoost = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_MCBOOST)
			MCBoost.color = player.mo.color
			MCBoost.angle = player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT) + ANGLE_90
			MCBoost.scale = player.mo.scale*3/2
			MCBoost.destscale = player.mo.scale*3/2
		end
	elseif not (player.speed >= FixedMul(player.runspeed, player.mo.scale))
	and not (player.pflags & PF_SPINNING)
	and P_IsObjectOnGround(player.mo)
		if player.dashmode2 > 0
			player.dashmode2 = $ - 3
			if (player.dashmode2 + 3) >= 2*TICRATE and player.dashmode2 < 2*TICRATE
				S_StartSound(player.mo, sfx_kc65)
			end	
		elseif player.dashmode2 < 0
			player.dashmode2 = 0
		end
	end
	
	if player.dashmode2 < TICRATE
		if prevdashmode >= TICRATE
			if player.xmomentum
				player.fakenormalspeed = skins[player.mo.skin].normalspeed
			else
				player.normalspeed = skins[player.mo.skin].normalspeed
			end // Reset to default if not capable of entering dash mode.
			player.jumpfactor = skins[player.mo.skin].jumpfactor
			player.charflags = $1 & ~SF_RUNONWATER
		end
	elseif P_IsObjectOnGround(player.mo)
	and player.dashmode2 < 2*TICRATE
		if player.isxmomentum
		and player.fakenormalspeed
			if (player.fakenormalspeed < 45*FRACUNIT and not player.powers[pw_sneakers])
			or (player.fakenormalspeed < 75*FRACUNIT and player.powers[pw_sneakers])
				player.fakenormalspeed = $ + FRACUNIT/2
			end
		else
			if (player.normalspeed < 45*FRACUNIT and not player.powers[pw_sneakers])
			or (player.normalspeed < 75*FRACUNIT and player.powers[pw_sneakers])
				player.normalspeed = $ + FRACUNIT/2
			end
		end
	end
	
	if player.dashmode2 >= TICRATE
	and player.dashmode2 < 2*TICRATE
		if P_IsObjectOnGround(player.mo)
		and (leveltime % 4) == 0
			DoSpindust(player, player.speed*2)
		end
	end
	
	if player.dashmode2 >= 2*TICRATE
		if player.isxmomentum
		and player.fakenormalspeed
			if player.fakenormalspeed > skins[player.mo.skin].normalspeed * 5 / 2
			and not	player.powers[pw_sneakers]
				player.fakenormalspeed = skins[player.mo.skin].normalspeed * 5 / 2
			end
		
			if player.fakenormalspeed > skins[player.mo.skin].normalspeed * 25 / 6
			and player.powers[pw_sneakers]
				player.fakenormalspeed = skins[player.mo.skin].normalspeed * 25 / 6
			end
		else
			if player.normalspeed > skins[player.mo.skin].normalspeed * 5 / 2
			and not	player.powers[pw_sneakers]
				player.normalspeed = skins[player.mo.skin].normalspeed * 5 / 2
			end
		
			if player.normalspeed > skins[player.mo.skin].normalspeed * 25 / 6
			and player.powers[pw_sneakers]
				player.normalspeed = skins[player.mo.skin].normalspeed * 25 / 6
			end
		end
		
		local ghost = P_SpawnGhostMobj(player.mo)
		ghost.fuse = 4
		ghost.colorized = true
		player.charflags = $1|SF_RUNONWATER
		
		if P_IsObjectOnGround(player.mo)
			DoSpindust(player, player.speed*2)
		end
		
		if not P_IsObjectOnGround(player.mo)
			player.airspeed = player.speed
		end
		
		if player.isxmomentum
		and player.fakenormalspeed
			if player.speed > player.fakenormalspeed
			and not (player.pflags & PF_SPINNING)
				if not P_IsObjectOnGround(player.mo)
					player.fakenormalspeed = $ + FRACUNIT/15
				end
			end
		else
			if player.speed > player.normalspeed
			and not (player.pflags & PF_SPINNING)
				if not P_IsObjectOnGround(player.mo)
					player.normalspeed = $ + FRACUNIT/15
				end
			end
		end
		
		if player.isxmomentum
		and player.fakenormalspeed
			if player.airspeed > player.fakenormalspeed
			and (player.mo.eflags & MFE_JUSTHITFLOOR or P_IsObjectOnGround(player.mo))
			and not (player.pflags & PF_SPINNING)
				player.fakenormalspeed = player.airspeed * 85 / 100
				player.airspeed = 0
			end
		else
			if player.airspeed > player.normalspeed
			and (player.mo.eflags & MFE_JUSTHITFLOOR or P_IsObjectOnGround(player.mo))
			and not (player.pflags & PF_SPINNING)
				player.normalspeed = player.airspeed * 85 / 100
				player.airspeed = 0
			end
		end
		
		if player.isxmomentum
		and player.fakenormalspeed
			if player.fakenormalspeed - FRACUNIT > player.speed
			and player.lastz < player.mo.z 
			and P_IsObjectOnGround(player.mo)
				if player.fakenormalspeed >= 70*FRACUNIT
					player.fakenormalspeed = $ - FRACUNIT/15
				else
					player.fakenormalspeed = $ - FRACUNIT/TICRATE
				end
			end
		
			// Referenced XMomentum here, uh oh.
			// Increase normalspeed AND speed as you go down slopes.
	
			if (player.lastz > player.mo.z * P_MobjFlip(player.mo))
			and P_IsObjectOnGround(player.mo)
			and not (player.pflags & PF_SPINNING)
				player.fakenormalspeed = $ + FRACUNIT/8
				P_Thrust(player.mo, player.mo.angle, player.speed/10)
			end
			player.lastz = player.mo.z	
		else
			if player.normalspeed - FRACUNIT > player.speed
			and player.lastz < player.mo.z 
			and P_IsObjectOnGround(player.mo)
				if player.normalspeed >= 70*FRACUNIT
					player.normalspeed = $ - FRACUNIT/15
				else
					player.normalspeed = $ - FRACUNIT/TICRATE
				end
			end
		
			// Referenced XMomentum here, uh oh.
			// Increase normalspeed AND speed as you go down slopes.
	
			if (player.lastz > player.mo.z * P_MobjFlip(player.mo))
			and P_IsObjectOnGround(player.mo)
			and not (player.pflags & PF_SPINNING)
				player.normalspeed = $ + FRACUNIT/8
				P_Thrust(player.mo, player.mo.angle, player.speed/10)
			end
			player.lastz = player.mo.z	
		end
	end
	
	if player.striketimer > 0
		player.striketimer = $ - 1
	end
	
	if player.striketimer
		if leveltime % 5 == 0
			local strikesparkle = P_SpawnMobj(player.mo.x+(P_RandomRange(16,-16)*FRACUNIT),player.mo.y+(P_RandomRange(16,-16)*FRACUNIT),player.mo.z+(P_RandomRange(8,-8)*FRACUNIT),MT_STRIKESPARKLES)
			strikesparkle.color = player.mo.color
			strikesparkle.colorized = true
			strikesparkle.scale = $ * 3 / 2
			
			strikesparkle.momx = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momy = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momz = P_RandomRange(5,-5)*FRACUNIT
		end
	end
end

addHook("PlayerThink", Dashmode)

local function StatesWithDamage(player, badnik)
	if player.mo and player.mo.valid
		if player.striketimer
			return true
		end
	end
end

addHook("PlayerCanDamage", StatesWithDamage)

local function StatesWithInvinc(target, inflictor, source, damage, damagetype)
	if target and target.valid
		if target.player.striketimer
		and not (damagetype & DMG_DEATHMASK)
			return false
		end
	end
end

addHook("ShouldDamage", StatesWithInvinc, MT_PLAYER)
	
// Peelout.
// Reused from MotdSpork.

addHook("ThinkFrame", do
	for player in players.iterate
		if not player.mo return end
			if player.mo and player.mo.skin == "mcsonic"
			//CONS_Printf(player, player.mo.peelouttimer)
				if player.mo.state == S_PLAY_STND
					player.normalspeed = 36*FRACUNIT
				end
				if player.mo.peelouttimer == nil
					player.mo.peelouttimer = 0
				end
				if player.mo.peelout == nil
					player.mo.peelout = 0
				end
				if player.mo.peeloutrun == nil
					player.mo.peeloutrun = 0
				end
				if player.mo.peelsound == nil
					player.mo.peelsound = 0
				end
				if player.mo.peelsound2 == nil
					player.mo.peelsound2 = 0
				end
				if player.pflags & PF_SPINNING
					player.mo.peelouttimer = 0
				end
				if (P_PlayerInPain(player))
				or (player.playerstate == PST_DEAD)
					player.normalspeed = 36*FRACUNIT
					player.mo.peelouttimer = 0
					player.mo.peelout = 0
					player.mo.peeloutrun = 0
					player.mo.peelsound = 0
					player.mo.peeloutsound = 0
				end
				if P_IsObjectOnGround(player.mo)
				and player.speed == 0
				and not (player.pflags & PF_SPINNING)
				and player.cmd.buttons & BT_CUSTOM1
			    and not (player.playerstate == PST_DEAD)
				and not player.bouncing
				and not (player.exiting)
					if player.mo.peelsound == 0
						S_StartSound(player.mo, sfx_pelchr)
						player.mo.peelsound = 1
						
					end
					if player.mo.peelouttimer < 60
						player.mo.peelouttimer = $1 + 1
					end
					player.normalspeed = 0
					player.pflags = $1|PF_STASIS
					if player.drawangle ~= player.mo.angle
						local diff = 0
						local factor = 0
						diff = player.mo.angle - player.drawangle
						factor = 8
						if diff > ANGLE_180
							diff = InvAngle(InvAngle(diff)/(factor or 1))
						else
							diff = $ / (factor or 1)
						end
						player.drawangle = $ + diff
					
					end
					if not (player.mo.eflags & MFE_GOOWATER) then
						for i=(leveltime%7)/2,(leveltime%7) do
							local particle = P_SpawnMobj(player.mo.x, player.mo.y,
							player.mo.z + ((player.mo.eflags & MFE_VERTICALFLIP)/MFE_VERTICALFLIP * (player.mo.height - mobjinfo[MT_SPINDUST].height)),
							MT_SPINDUST)
							if (player.powers[pw_shield] == SH_ELEMENTAL and not (player.mo.eflags & MFE_TOUCHWATER or player.mo.eflags & MFE_UNDERWATER)) then
								particle.state = S_SPINDUST_FIRE1
								particle.bubble = false
							elseif (player.mo.eflags & MFE_TOUCHWATER or player.mo.eflags & MFE_UNDERWATER) then
								particle.state = S_SPINDUST_BUBBLE1
								particle.bubble = true
							else
								particle.state = S_SPINDUST1
								particle.bubble = false
							end
					
							particle.target = player.mo
							particle.eflags = $1 | (player.mo.eflags & MFE_VERTICALFLIP)
							particle.scale = player.mo.scale*2/3
							P_SetObjectMomZ(particle, player.mo.peelouttimer*FRACUNIT/50+P_RandomByte()<<10, false)
							P_InstaThrust(particle, player.mo.angle+(P_RandomRange(-ANG30/FRACUNIT, ANG30/FRACUNIT)*FRACUNIT), FixedMul(-player.mo.peelouttimer*FRACUNIT/12-1*FRACUNIT-P_RandomByte()<<11, player.mo.scale))
							P_TryMove(particle, particle.x+particle.momx, particle.y+particle.momy, true)
						end
					end
				end
			if not (player.cmd.buttons & BT_CUSTOM1) 
				if ((player.mo.peelouttimer >= 1) and (player.mo.peelouttimer <= 35) and not (player.powers[pw_super]))
				or ((player.mo.peelouttimer >= 1) and (player.mo.peelouttimer <= 14) and (player.powers[pw_super]))
					player.normalspeed = 36*FRACUNIT
					P_InstaThrust(player.mo, player.mo.angle, FixedMul(36*FRACUNIT, player.mo.scale))
					player.mo.state = S_PLAY_WALK
					player.mo.peelouttimer = 0
					player.mo.peelsound = 0
				end
			end
			
			if player.powers[pw_super] == 0
				if player.mo.peelouttimer >= 1
				and player.mo.peelouttimer <= 30
					if not (player.mo.state >= S_PLAY_PWALK1)
					and (player.mo.state <= S_PLAY_PWALK16)
						player.mo.state = S_PLAY_PWALK1
					end
				end
				if player.mo.peelouttimer >= 31
				and player.mo.peelouttimer < 50
					if not ((player.mo.state >= S_PLAY_PEEL1) and (player.mo.state <= S_PLAY_PEEL4))
					or ((player.mo.state >= S_PLAY_PWALK1) and (player.mo.state <= S_PLAY_PWALK16))
						player.mo.state = S_PLAY_PEEL1
					end
				end
				if player.mo.peelouttimer >= 50
					if not ((player.mo.state >= S_PLAY_MPEEL1) and (player.mo.state <= S_PLAY_MPEEL4))
					or ((player.mo.state >= S_PLAY_PEEL1) and (player.mo.state <= S_PLAY_PEEL4))
					or ((player.mo.state >= S_PLAY_PWALK1) and (player.mo.state <= S_PLAY_PWALK16))
						player.mo.state = S_PLAY_MPEEL1
					end
				end
			else
				if player.mo.peelouttimer >= 1
				and player.mo.peelouttimer <= 14
					if not (player.mo.state >= S_PLAY_PWALK8)
					and (player.mo.state <= S_PLAY_PWALK16)
						player.mo.state = S_PLAY_PWALK8
					end
				end
				if player.mo.peelouttimer >= 15
					if not (player.mo.state >= S_PLAY_SPEEL1)
					and (player.mo.state <= S_PLAY_SPEEL2)
						player.mo.state = S_PLAY_SPEEL1
					end
				end
			end
			if not (player.cmd.buttons & BT_CUSTOM1)
				if ((player.mo.peelouttimer >= 31) and not (player.powers[pw_super]))
				or ((player.mo.peelouttimer >= 15) and (player.powers[pw_super]))
					if player.mo.peelsound == 1
						player.mo.state = S_PLAY_RUN
						S_StartSound(player.mo, sfx_pelrel)
						player.mo.peelsound = 0
						player.mo.peelout = 1
						if player.mchyper
							P_StartQuake(25*FRACUNIT, 16)
							P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
							P_FlashPal(player, PAL_WHITE, 8)
							S_StartSound(player.mo, sfx_s3k4e)
						end
//						player.norolltimer = TICRATE
						player.striketimer = TICRATE
					end
					P_InstaThrust(player.mo, player.mo.angle, FixedMul(70*FRACUNIT, player.mo.scale))
					if not player.powers[pw_super]
						player.dashmode2 = 3*TICRATE
					end
					player.mo.peelouttimer = 0
					player.drawangle = player.mo.angle
				end
				
			end
			if player.speed > 45*FRACUNIT
			and player.mo.peelout == 1
				player.mo.peeloutrun = 1
				player.normalspeed = 70*FRACUNIT
			end
			if player.speed <= 45*FRACUNIT
			and player.mo.peeloutrun == 1
				player.mo.peelout = 0
				player.mo.peeloutrun = 0
				player.normalspeed = 36*FRACUNIT
			end
			if player.pflags & PF_JUMPED
				if ((player.mo.state >= S_PLAY_PWALK1) and (player.mo.state <= S_PLAY_PWALK16))
				or ((player.mo.state >= S_PLAY_PEEL1) and (player.mo.state <= S_PLAY_PEEL4))
				or ((player.mo.state >= S_PLAY_SPEEL1) and (player.mo.state <= S_PLAY_SPEEL2))
				or ((player.mo.state >= S_PLAY_MPEEL1) and (player.mo.state <= S_PLAY_MPEEL4))
					player.mo.state = S_PLAY_ROLL
				end
				if player.normalspeed == 0*FRACUNIT
					player.normalspeed = 36*FRACUNIT
				end
				player.mo.peelouttimer = 0
				player.mo.peelsound = 0
				player.mo.peelsound2 = 0
			end
		end
	end
end)

//local function NoRollTimer(player)
//	if not player.mo and player.mo.valid return end
	
//	if player.norolltimer == nil
//		player.norolltimer = 0
//	end
	
//	if player.norolltimer > 0
//		player.norolltimer = $ - 1
//		if not player.dropdashed
//			if player.pflags & PF_SPINNING
//				player.pflags = $ & ~PF_SPINNING
//				player.mo.state = S_PLAY_WALK
//			elseif player.pflags & PF_STARTDASH
//				player.pflags = $ & ~PF_STARTDASH
//				player.mo.state = S_PLAY_WALK
//			end
//		end
//	end
	
//	if player.norolltimer
//	and player.dashmode2 < 2*TICRATE
//		player.norolltimer = 0
//	end
//end

//addHook("PlayerThink", NoRollTimer)

// Drop Dash
// Iteration upon an iteration.

local ThokSpawnCooldown = 0

local function DropDash(player)
	if not player.mo and player.mo.valid return end
	
	if player.droptimer == nil
		player.droptimer = 0
	end
	
	if player.walldash == nil
		player.walldash = false
	end
	
	if player.dropdashed == nil
		player.dropdashed = false
	end
	
	if player.dropspeed == nil
		player.dropspeed = 36*FRACUNIT
	end
	
	if (player.mo.skin != "mcsonic") return end
	
	if ThokSpawnCooldown > 0
		ThokSpawnCooldown = $1 - 1
	end
	
	if player.powers[pw_super] == 1
		return
	end
	
	if player.hyperdash
		return
	end
	
	if P_PlayerInPain(player) or not(player.playerstate == PST_LIVE) or player.exiting or player.powers[pw_nocontrol]
		player.triedtodash = false
		player.droptimer = 0
//		player.mo.state = S_PLAY_ROLL
	end
		
	if (player.pflags & PF_JUMPED)
		if (player.triedtodash) then
			player.droptimer = 0
			player.mo.state = S_PLAY_JUMP
		else
			if (player.pflags & PF_USEDOWN) then
				player.droptimer = $1 + 1
				if player.dropspeed < 60*FRACUNIT and not player.mchyper
				or player.mchyper and player.dropspeed < 120*FRACUNIT
					if player.powers[pw_super]
						player.dropspeed = $ + FRACUNIT * 6 / 4
					else
						player.dropspeed = $ + FRACUNIT * 3 / 4
					end
				end
//				print(player.dropspeed/FRACUNIT)
			elseif player.droptimer > 0 then
				player.droptimer = 0
				player.triedtodrop = true
				player.dropspeed = 36*FRACUNIT
			end
		end
	end
	
	if (player.droptimer == 15) then
//	and not player.mo.state == S_DROPDASH
		S_StartSound(player.mo, sfx_drpdsh)
		player.mo.state = S_DROPDASH
	end
	
	if player.mo.state == S_DROPDASH
	and not (player.pflags & PF_USEDOWN)
		player.mo.state = S_PLAY_ROLL
	end
	
	if player.powers[pw_justsprung]
	or P_PlayerInPain(player)
		player.triedtodash = false
		player.droptimer = 0
		player.dropspeed = 36*FRACUNIT
		player.airdashed = false
	end
		
	
	if player.droptimer >= 15
	and player.powers[pw_super] 
	and P_IsObjectOnGround(player.mo)
		P_StartQuake(15*FRACUNIT,6)
	end
			
	if (player.mo.eflags & MFE_JUSTHITFLOOR or P_IsObjectOnGround(player.mo)) then
		if (player.droptimer >= 15) then
			player.mo.state = S_PLAY_ROLL
			player.pflags = $1|PF_SPINNING
			local radius = player.mo.radius >> FRACBITS
			P_SpawnMobjFromMobj(player.mo, P_RandomRange(-radius, radius) << FRACBITS, P_RandomRange(-radius, radius) << FRACBITS, 0, MT_SPINDUST)
			P_SpawnMobjFromMobj(player.mo, P_RandomRange(-radius, radius) << FRACBITS, P_RandomRange(-radius, radius) << FRACBITS, 0, MT_SPINDUST)
			P_SpawnMobjFromMobj(player.mo, P_RandomRange(-radius, radius) << FRACBITS, P_RandomRange(-radius, radius) << FRACBITS, 0, MT_SPINDUST)
			P_SpawnMobjFromMobj(player.mo, P_RandomRange(-radius, radius) << FRACBITS, P_RandomRange(-radius, radius) << FRACBITS, 0, MT_SPINDUST)
			P_InstaThrust(player.mo, player.mo.angle, FixedMul(player.dropspeed+(player.speed/8), player.mo.scale))
			S_StartSound(player.mo, sfx_zoom, player)
			if player.mchyper
				P_StartQuake(25*FRACUNIT, 16)
				P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
				P_FlashPal(player, PAL_WHITE, 8)
				S_StartSound(player.mo, sfx_s3k4e)
			end
			player.dropdashed = true
//			DoSpindust(player, player.dropspeed)
		end
		player.triedtodash = false
		player.droptimer = 0
		player.dropspeed = 36*FRACUNIT
	end
	
	if player.dropdashed
	and player.mo.state != S_PLAY_ROLL
		player.dropdashed = false
	end		
end
				
addHook("PlayerThink", DropDash)
		

local function HyperMCSonic(player)
	if player.mo.skin != "mcsonic" return end
	
	if player.hypertrans == nil
		player.hypertrans = false
	end
	
	if player.sparkleoffset == nil
		player.sparkleoffset = 0
	end
	
	if player.rings >= 100
	and player.powers[pw_super] > 1
		player.hypercapable = true
	elseif (player.rings < 100 and player.powers[pw_super])
	or not player.powers[pw_super]
		player.hypercapable = false
	end
	
	if player.mchyper
	or (mrce_hyperunlocked != nil and mrce_hyperunlocked == false)
		player.hypercapable = false
	end
	
	if player.hypercapable
	and player.powers[pw_super]
	and player.cmd.buttons & BT_CUSTOM3
	and player.pflags & PF_JUMPED
	and not player.mchyper
	and not player.turnedsuper
		player.pflags = $1|PF_THOKKED
		player.mo.state = S_PLAY_SUPER_TRANS1
		player.hypertrans = true
		S_StartSound(player.mo, sfx_s3k9f)
		player.mchyper = true
	end
	
	if player.hypertrans
		player.mo.momx = 0
		player.mo.momy = 0
		player.mo.momz = 0
	end
	
	if player.hypertrans
	and not (player.mo.state >= S_PLAY_SUPER_TRANS1 and player.mo.state <= S_PLAY_SUPER_TRANS6)
		player.hypertrans = false
		player.pflags = $1 & ~PF_THOKKED
		P_StartQuake(25*FRACUNIT, 16)
		P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
		P_FlashPal(player, PAL_WHITE, 8)
		S_StartSound(player.mo, sfx_s3k4e)
	end
	
	if player.mchyper
	and not player.powers[pw_super]
		player.mchyper = false
	end

	if player.mchyper
		player.powers[pw_underwater] = 100*TICRATE
		player.powers[pw_spacetime] = 100*TICRATE
		
		if player.mo.skin == "mcsonic"
			if player.hypertrans
			and player.mo.state != S_PLAY_SUPER_TRANS1
			and player.mo.state != S_PLAY_SUPER_TRANS2
			and player.mo.state != S_PLAY_SUPER_TRANS3
				player.mo.color = SKINCOLOR_HYPER7
			elseif not player.hypertrans
				player.mo.color = (SKINCOLOR_HYPER1 + abs(((player.powers[pw_super] >> 2) % 9) - 6))
			end
		end
		
		if (player.speed > 1*FRACUNIT or player.mo.momz > 1*FRACUNIT or player.mo.momz < -1*FRACUNIT)
		and not player.hypertrans
			local afterimage = P_SpawnGhostMobj(player.mo)
			
			afterimage.color = player.mo.color
			afterimage.colorized = true
		end
		
		player.jumpfactor = skins[player.mo.skin].jumpfactor * 3 / 2
		
		if player.charability2 == CA2_SPINDASH
			player.mindash = 60*FRACUNIT
			player.maxdash = 120*FRACUNIT
		end
		
		if leveltime % 5 == 0
			local strikesparkle = P_SpawnMobj(player.mo.x+(P_RandomRange(16,-16)*FRACUNIT),player.mo.y+(P_RandomRange(16,-16)*FRACUNIT),player.mo.z+(player.mo.height/2)+(P_RandomRange(8,-8)*FRACUNIT),MT_STRIKESPARKLES)
			strikesparkle.color = player.mo.color
			strikesparkle.colorized = true
			strikesparkle.scale = player.mo.scale * 3 / 2
			
			strikesparkle.momx = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momy = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momz = P_RandomRange(5,-5)*FRACUNIT
		end
		
		if (leveltime % 35 == 18) and not player.hypertrans
			P_GivePlayerRings(player, -1)
		end
		
		if not (player.powers[pw_extralife])
			S_ChangeMusic("HYPE", true, player)
		end
		
		if player.cmd.buttons & BT_CUSTOM2
		and player.pflags & PF_JUMPED
		and not player.dehyper
			player.mchyper = false
			player.dehyper = true
			if player.dashmode2 < 2*TICRATE
				player.jumpfactor = skins[player.mo.skin].jumpfactor
			end
			if player.charability2 == CA2_SPINDASH
				player.mindash = skins[player.mo.skin].mindash
				player.maxdash = skins[player.mo.skin].maxdash
			end
			P_RestoreMusic(player)
		end
	else
		if player.dashmode2 > 2*TICRATE
			player.jumpfactor = skins[player.mo.skin].jumpfactor
		end
		if player.charability2 == CA2_SPINDASH
			player.mindash = skins[player.mo.skin].mindash
			player.maxdash = skins[player.mo.skin].maxdash
		end
		if player.sparkleoffset
			player.sparkleoffset = 0
		end
	end
	
	if P_IsObjectOnGround(player.mo)
		player.dehyper = false
	end
	
	if player.powers[pw_super]
	and not player.mchyper
		if player.cmd.buttons & BT_CUSTOM2
		and not player.prejumpcustom2
		and not player.dehyper
		and player.pflags & PF_JUMPED
			player.powers[pw_super] = 0
			P_RestoreMusic(player)
			P_SpawnShieldOrb(player)
			player.mo.color = player.skincolor
		end
	end
		
	if player.cmd.buttons & BT_CUSTOM2
		player.prejumpcustom2 = true
	else
		player.prejumpcustom2 = false
	end
end

addHook("PlayerThink", HyperMCSonic)

local function InstaShield(player)
	if (player.mo) and (player.mo.skin == "mcsonic") //sonic
	and not (player.instashield == 1) and not (player.pflags & PF_THOKKED) and not (player.pflags & PF_USEDOWN) and not player.powers[pw_shield] and not player.powers[pw_invulnerability] and not player.powers[pw_carry] and not player.powers[pw_super]
		player.invisiframe = 1
		S_StartSound(player.mo, sfx_s3k42)
		local instashield = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_INSTASHIELD)
		instashield.target = player.mo
		instashield.scale = player.mo.scale
		instashield.fuse = 12
		player.tracer = instashield
		player.pflags = $1|PF_SHIELDABILITY
		if (player.realmo.eflags & MFE_VERTICALFLIP)
			instashield.eflags = $|MFE_VERTICALFLIP
		end
		player.instashield = 1
		for i = 0, 3
			local strikesparkle = P_SpawnMobj(player.mo.x+(P_RandomRange(16,-16)*FRACUNIT),player.mo.y+(P_RandomRange(16,-16)*FRACUNIT),player.mo.z+(player.mo.height/2)+(P_RandomRange(8,-8)*FRACUNIT),MT_STRIKESPARKLES)
			strikesparkle.color = player.mo.color
			strikesparkle.colorized = true
			strikesparkle.scale = player.mo.scale * 3 / 2
			
			strikesparkle.momx = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momy = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momz = P_RandomRange(5,-5)*FRACUNIT
		end
		return true
	end
end

addHook("PlayerThink", function(player)
	if (player.instashield == 1) and P_IsObjectOnGround(player.mo) //instashield checks
		player.instashield = 0
		if (player.tracer and player.tracer.valid) and player.tracer.type == MT_INSTASHIELD
			P_KillMobj(player.tracer)
		end
	end
	if (player.instashield == 1) and (player.invisiframe == 1)
		player.mo.frame = ($1 & FF_FRAMEMASK)|FF_FULLBRIGHT
		player.mo.colorized = true
	end
	if ((player.invisiframe == 1) and not (player.tracer and player.tracer.valid and player.tracer.type == MT_INSTASHIELD))
		player.invisiframe = nil
		player.mo.colorized = false
	end
end)

addHook("JumpSpinSpecial", InstaShield)

local function InstaCheck(sonic, inflictor, source, damage, element)
	if (sonic.player) and sonic.player.tracer and sonic.player.tracer.valid and sonic.player.tracer.health and sonic.player.tracer.type == MT_INSTASHIELD
		if not (element == DMG_INSTAKILL) and not (element == DMG_DROWNED) and not (element == DMG_SPACEDROWN) and not (element == DMG_DEATHPIT) and not (element == DMG_CRUSHED) and not (element == DMG_SPECTATOR)
			return false  //do not damage player while instashield is active
		end
	end
end

addHook("ShouldDamage", InstaCheck)

local function InstaCollide(insta, mobj) //instashield attacks function as an expanded roll hitbox and can damage things normal spins can't
	if not (insta.z-FixedMul(32*FRACUNIT, insta.scale) > mobj.z+mobj.height)
	and not(mobj.z-FixedMul(32*FRACUNIT, insta.scale) > insta.z+insta.height)
		if (mobj.valid and mobj.health and (mobj.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) or (mobj.flags & MF_PUSHABLE and mobj.flags & MF_SHOOTABLE)))
			P_DamageMobj(mobj, insta, insta.target)
			if ((insta.target.momz < 0) and not (insta.target.eflags & MFE_VERTICALFLIP))
				P_SetObjectMomZ(insta.target, -insta.target.momz)
			elseif ((insta.target.momz > 0) and (insta.target.eflags & MFE_VERTICALFLIP))
				P_SetObjectMomZ(insta.target, insta.target.momz)
			end
			if (mobj.info.spawnhealth > 1)
				P_InstaThrust(insta.target, insta.target.angle, -insta.target.player.speed)
			end
		end
	else
		return nil
	end
end

addHook("MobjMoveCollide", InstaCollide, MT_INSTASHIELD)
addHook("MobjCollide", InstaCollide, MT_INSTASHIELD)

// Victory posing. Originally a S3A 1103 reference.

local function VictoryPose(player)
	if player.mo.skin != "mcsonic" return end
	
	if (player.pflags & PF_FINISHED)
	or (player.exiting)
		if (player.mo.state == S_PLAY_STND or player.mo.state == S_PLAY_WAIT)
		and player.speed == 0
			player.mo.state = S_POSE1
		end
	end
	
	if (player.mo.state >= S_POSE1 and player.mo.state <= S_POSE3)
	and player.speed > 0
		if player.speed < player.runspeed
			player.mo.state = S_PLAY_WALK
		else
			player.mo.state = S_PLAY_RUN
		end
	end
end

addHook("PlayerThink", VictoryPose)

// Burnt death animation
// For example: If the player dies to lava, instead of having the normal death animation, they'll have a grayscale death animation, as a reference to the unused frames from Sonic 1-Sonic 3 & Knuckles.

local function BurntDeath(target, inflictor, source, damage)
	if target
	and target.player
//	and inflictor == nil
//	and source == nil
		if target.skin == "mcsonic"
			if damage == DMG_FIRE
				target.player.burnt = true
				return false
			elseif damage == DMG_ELECTRIC
				target.player.elec = true
				return false
			end
		end
	end
end

addHook("MobjDeath", BurntDeath)

local function BurntUnset(player)
	player.burnt = false
	player.elec = false
end

addHook("PlayerSpawn", BurntUnset)

local function ForceState(player)
	if player.burnt
		if not (leveltime & 3)
			P_SpawnMobj(player.mo.x+(P_RandomRange(-10,10)*FRACUNIT),player.mo.y+(P_RandomRange(-10,10)*FRACUNIT),player.mo.z+(P_RandomRange(-10,10)*FRACUNIT),MT_DUST)
		end
		player.mo.state = S_BURN
	elseif player.elec
		if not (leveltime & 3)
			P_SpawnMobj(player.mo.x+(P_RandomRange(-10,10)*FRACUNIT),player.mo.y+(P_RandomRange(-10,10)*FRACUNIT),player.mo.z+(P_RandomRange(-10,10)*FRACUNIT),MT_DUST)
		end
		if (leveltime & 1) == 0
			player.mo.state = S_ELECTRO1
		else
			player.mo.state = S_ELECTRO2
		end
	end
end

addHook("PlayerThink", ForceState)

// Bounce
// Haha, Blade, shut up.

local function FloorBreak(mo, sec)
	if not (mo and mo.valid) then return end
	
	for x = mo.x, mo.x + mo.radius, mo.radius
		for y = mo.y, mo.y + mo.radius, mo.radius
			for fof in sec.ffloors()
				if (fof.flags & FF_EXISTS) and (fof.flags & FF_BUSTUP)
				and (mo.z + mo.height == fof.bottomheight or mo.z == fof.topheight
				or (mo.eflags & MFE_VERTICALFLIP and (mo.z - mo.height == fof.topheight or mo.z == fof.bottomheight)))
					EV_CrumbleChain(fof)
				end
			end
		end
	end
end

local function Bounce(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.skin != "mcsonic" return end
	
	if not (player.playerstate == PST_LIVE) 
		player.mo.spritexscale = FRACUNIT
		player.mo.spriteyscale = FRACUNIT
		player.bouncing = false
		return 
	end
	
	if player.bouncecount == nil
		player.bouncecount = 0
	end
	
	if player.nobounce == nil
		player.nobounce = false
	end
	
	if player.cmd.buttons & BT_CUSTOM1
	and player.pflags & PF_JUMPED
	and not P_IsObjectOnGround(player.mo)
	and not player.custom1down
	and not player.bouncing
	and not player.hypertrans
	and not player.nobounce
	and not (player.exiting)
	and not player.cling
	and not player.chargewalldash
	and not player.hyperdash
		player.bouncing = true
		P_SetObjectMomZ(player.mo, -32*FRACUNIT, false)
		player.bouncecount = $ + 1
		if player.bouncecount == 3
			for i = 0, 8
				local fa = (i*(ANGLE_180/4))
				local strikesparkle = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z-player.mo.height, MT_STRIKESPARKLES)
				strikesparkle.momx = FixedMul(sin(fa),5*FRACUNIT)
				strikesparkle.momy = FixedMul(cos(fa),5*FRACUNIT)
				strikesparkle.color = player.mo.color
				strikesparkle.scale = player.mo.scale * 3 / 2
				strikesparkle.colorized = true
				P_SetObjectMomZ(strikesparkle, 5*FRACUNIT, false)
			end
		end
	end
	
	if player.bouncing
	and not P_IsObjectOnGround(player.mo)
		if player.mo.spriteyscale < 3*FRACUNIT/2
			player.mo.spriteyscale = $ + FRACUNIT/10
		end
		
		if player.mo.spritexscale > 9*FRACUNIT/10
			player.mo.spritexscale = $ - FRACUNIT/10
		end
	end
	
	if P_IsObjectOnGround(player.mo)
	and not player.bouncing
		player.bouncecount = 0
	end
	
	if P_IsObjectOnGround(player.mo)
	and player.bouncing
		player.bouncing = false
		player.mo.spritexscale = FRACUNIT
		player.mo.spriteyscale = FRACUNIT
		if player.bouncecount < 3
			player.mo.state = S_PLAY_JUMP
			player.pflags = $|PF_JUMPED
			if player.mchyper
				FloorBreak(player.mo, player.mo.subsector.sector)
			end
		else
			if player.mchyper
				player.mo.state = S_PLAY_JUMP
				player.pflags = $|PF_JUMPED
				player.bouncecount = 3
				FloorBreak(player.mo, player.mo.subsector.sector)
			else
				player.mo.state = S_PLAY_SPRING
				FloorBreak(player.mo, player.mo.subsector.sector)
			end
		end
		
		for i = 0, 8
			local fa = (i*(ANGLE_180/4))
			local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
			dust.momx = FixedMul(sin(fa),20*FRACUNIT)
            dust.momy = FixedMul(cos(fa),20*FRACUNIT)
		end
		
		if player.mo.eflags & MFE_UNDERWATER and not player.powers[pw_super]
			P_SetObjectMomZ(player.mo, 10*FRACUNIT+(player.bouncecount*FRACUNIT*2)/2, false)
		elseif player.mo.eflags & MFE_UNDERWATER and player.powers[pw_super]
			P_SetObjectMomZ(player.mo, 10*FRACUNIT+(player.bouncecount*FRACUNIT*2), false)
		elseif player.powers[pw_super] and not (player.mo.eflags & MFE_UNDERWATER)
			P_SetObjectMomZ(player.mo, 10*FRACUNIT+(player.bouncecount*FRACUNIT*2)*2, false)
		else
			P_SetObjectMomZ(player.mo, 10*FRACUNIT+(player.bouncecount*FRACUNIT*2), false)
		end
		
		if player.mchyper
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			P_FlashPal(player, PAL_WHITE, 8)
			S_StartSound(player.mo, sfx_mcbnce)
		end
		
		S_StartSound(player.mo, sfx_mcbnce, player)
		player.pflags = $1 & ~PF_SPINNING
	end	
	
	if player.mo.eflags & MFE_SPRUNG
	and player.bouncing
		player.bouncing = false
		player.mo.spritexscale = FRACUNIT
		player.mo.spriteyscale = FRACUNIT
		player.mo.momz = $*6/5
		player.mo.momx = $*4/3
		player.mo.momy = $*4/3
		for i = 0, 8
			local fa = (i*(ANGLE_180/4))
			local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
			dust.momx = FixedMul(sin(fa),20*FRACUNIT)
            dust.momy = FixedMul(cos(fa),20*FRACUNIT)
		end
		S_StartSound(player.mo, sfx_sprong, player)
	end	
	
	if player.bouncing
		local trail = P_SpawnGhostMobj(player.mo)
			
		for i = 0,9
			trail.fuse = 9
			trail.state = S_THOK
			trail.scale = FRACUNIT - (i * FRACUNIT/240)
			trail.destscale = 0
			if player.mchyper
				trail.color = (SKINCOLOR_HYPER1 + abs(((player.powers[pw_super] >> 2) % 9) - 6))
			else
				trail.color = player.mo.color
			end
		end
	end
	
	if player.bouncing
		if (P_PlayerInPain(player))
		or player.powers[pw_carry]
		or player.cling
			player.bouncing = false
			player.mo.spritexscale = FRACUNIT
			player.mo.spriteyscale = FRACUNIT
		end
	end
	
	if P_IsObjectOnGround(player.mo)
	or (P_PlayerInPain(player))
	or player.powers[pw_carry]
		player.nobounce = false
	end
	
	if player.bouncing
	and P_IsObjectInGoop(player.mo)
		player.bouncing = false
		player.nobounce = true
		player.mo.spritexscale = FRACUNIT
		player.mo.spriteyscale = FRACUNIT
	end
	
	if player.cmd.buttons & BT_CUSTOM1
		player.custom1down = true
	else
		player.custom1down = false
	end
end

addHook("PlayerThink", Bounce)

local function EnemyHurtThwick(enemy, mo)
	if not mo or not mo.valid return end
	if mo.skin != "mcsonic" return end
	local player = mo.player
	if (player == nil) or (player.playerstate != PST_LIVE) return end	
	if not enemy and enemy.valid return end
	
	if (enemy.flags & MF_ENEMY or enemy.flags & MF_BOSS or enemy.flags & MF_MONITOR)
		if player.bouncing
			mo.spritexscale = FRACUNIT
			mo.spriteyscale = FRACUNIT
			P_SetObjectMomZ(player.mo, 12*FRACUNIT+(abs(player.mo.momz)-32*FRACUNIT), false)
			player.bouncecount = $ - 1
			player.bouncing = false
			S_StartSound(mo, sfx_mcbnce, player)
			player.mo.state = S_PLAY_JUMP
		end
	end
end

addHook("MobjDamage", EnemyHurtThwick)

local function MonitorBounce(enemy, mo)
	if not mo or not mo.valid return end
	if mo.skin != "mcsonic" return end
	local player = mo.player
	if (player == nil) or (player.playerstate != PST_LIVE) return end	
	if not enemy and enemy.valid return end
	
	if (enemy.flags & MF_MONITOR)
		if player.bouncing
			mo.spritexscale = FRACUNIT
			mo.spriteyscale = FRACUNIT
			P_SetObjectMomZ(player.mo, 12*FRACUNIT+(abs(player.mo.momz)-32*FRACUNIT), false)
			player.bouncecount = $ - 1
			player.bouncing = false
			S_StartSound(mo, sfx_mcbnce, player)
			player.mo.state = S_PLAY_JUMP
		end
	end
end

addHook("MobjDeath", MonitorBounce)

// Air Roll - If you aren't in a curled-up state, you can curl back up.

local function AirRoll(player)
	if not (player.mo and player.mo.valid) then return end 
	if player.mo.skin != "mcsonic" return end
	
	if not P_IsObjectOnGround(player.mo)
	and not (player.mo.state == S_PLAY_JUMP)
	and not (player.mo.state == S_PLAY_ROLL)
	and not (player.mo.state == S_DROPDASH)
	and player.bouncecount < 3
	and not player.walldashlock
	and not player.hyperdash
	and not (P_PlayerInPain(player))
	and not (player.powers[pw_shield] == SH_WHIRLWIND and player.pflags & PF_SHIELDABILITY)
		player.mo.state = S_PLAY_ROLL
		player.pflags = $1 & ~PF_SPINNING
		player.pflags = $1 | PF_JUMPED
		player.pflags = $1 | PF_THOKKED
		player.airrolled = true
		S_StartSound(player.mo, sfx_airrol)
	end
end
				
addHook("SpinSpecial",AirRoll)

local function RestoreRollControl(player)
	if player.mo.skin != "mcsonic" return end
	
	if player.pflags & PF_SPINNING
	and not P_IsObjectOnGround(player.mo)
		if player.pflags & PF_JUMPED
			player.pflags = $1 & ~PF_SPINNING
		else
			player.pflags = $1 & ~PF_SPINNING
			player.pflags = $1 | PF_JUMPED
			player.pflags = $1 | PF_THOKKED
		end
	end
	
	if P_IsObjectOnGround(player.mo)
	and player.airrolled
		player.airrolled = false
	end
end

addHook("PlayerThink", RestoreRollControl)

local function SpindashAnimation(player)
	if player.mo.skin != "mcsonic" return end
	
	if player.pflags & PF_STARTDASH
	and player.dashspeed < player.mindash + (player.maxdash-player.mindash)/6
		player.mo.state = S_PLAY_ROLL
		P_SpawnThokMobj(player)
	elseif player.mo.state == S_PLAY_ROLL
	and player.dashspeed > player.mindash + (player.maxdash-player.mindash)/6
		player.mo.state = S_PLAY_SPINDASH
	end
end

addHook("PlayerThink", SpindashAnimation)

local function ElementalWaterSkipping(player)
	if player.mo.skin != "mcsonic" return end
	
	if player.powers[pw_shield] == SH_ELEMENTAL
	or player.powers[pw_shield] == SH_BUBBLEWRAP
		if player.mo.eflags & MFE_TOUCHWATER
		and player.mo.momz * P_MobjFlip(player.mo) < 0
		and player.cmd.buttons & BT_JUMP
		and player.pflags & PF_JUMPED
		and player.speed > 15*FRACUNIT
		and not P_PlayerInPain(player)
			P_SetObjectMomZ(player.mo, 15*FRACUNIT/2, false)
			player.mo.state = S_PLAY_JUMP
			player.pflags = $ | PF_JUMPED
			player.pflags = $ | PF_THOKKED
			local splash = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPLISH)
			splash.scale = player.mo.scale * 3 / 2
			player.waterskipping = true
		end
	end

	if P_IsObjectOnGround(player.mo)
	and player.waterskipping
		player.waterskipping = false
	end
	
	if player.waterskipping
		P_SpawnThokMobj(player)
	end
end

addHook("PlayerThink", ElementalWaterSkipping)

local function NoShieldUsage(player)
	if not (player.mo and player.mo.valid) 
		return false
	end
	
	if player.mo.skin != "mcsonic"
		return false
	end
	
	if player.cmd.buttons & BT_JUMP
		if player.waterskipping
			return true
		else
			return false
		end
	else
		return true
	end
end

addHook("ShieldSpecial", NoShieldUsage)

// Here's v2's new co-op feature, Momentum Boost!
// player1 is the MCSonic colliding with the player, player2 is the recipent.
// ChrispyChars used for a reference, but I think I understand this now.

local function MomentumBoost(mo1, mo2)
	if not mo1.valid and mo2.valid return end
	
	local player1 = mo1.player
	local player2 = mo2.player
	
	if not (player1 and player2)
	// REMEMBER! Neither MobjCollide or MobjMoveCollide actually check for z-position. This must be done here!
	or mo1.z > mo2.z + mo2.height 
	or mo2.z > mo1.z + mo1.height
	or player2.powers[pw_carry]
	or player2.playerstate ~= PST_LIVE
	or player2.spectator
	or P_PlayerInPain(player2)
	or mo1.skin ~= "mcsonic"
	or (player1.dashmode2 < 2*TICRATE and not (player1.powers[pw_super]))
	or (player1.powers[pw_super] and player1.speed < player1.runspeed)
		return
	end
	
	if player2.charflags & SF_DASHMODE
		if player2.dashmode < 3*TICRATE
			player2.dashmode = 3*TICRATE
		end
	elseif mo2.skin == "mcsonic"
		if player2.dashmode2 < 2*TICRATE
			player2.dashmode2 = 2*TICRATE
			player2.normalspeed = 45*FRACUNIT
		end
	elseif mo2.skin == "fsonic"
		if player2.sonicdashmode < 3*TICRATE
			player2.sonicdashmode = 3*TICRATE
			player2.sonicdashmodeflag = true	
		end
	elseif mo2.skin == "juniosonic"
		if player2.juniodashmode < 3*TICRATE
			player2.juniodashmode = 3*TICRATE
		end
	else
		if player2.powers[pw_sneakers] == 0
			player2.powers[pw_sneakers] = 20*TICRATE
		end
	end
	
	if player2.speed < player2.normalspeed
		if player2.speed == 0
			player2.cmd.forwardmove = 50
		end
		P_InstaThrust(mo2, mo1.angle, player2.normalspeed)
		local MCBoost = P_SpawnMobj(mo2.x, mo2.y, mo2.z, MT_MCBOOST)
		MCBoost.color = mo2.color
		MCBoost.angle = (player2.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player2.cmd.forwardmove*FRACUNIT, -player2.cmd.sidemove*FRACUNIT)) + ANGLE_90
		MCBoost.scale = mo2.scale
		MCBoost.destscale = mo2.scale
	end
end

addHook("MobjMoveCollide", MomentumBoost)

local function HyperIndicator(player)
	if not (player.mo and player.mo.valid) then return end 
	if player.mo.skin != "mcsonic" return end
	
	if player.powers[pw_super]
	and player.rings >= 100
	and player.pflags & PF_JUMPED
	and not player.mchyper
		if leveltime % 5 == 0
			local strikesparkle = P_SpawnMobj(player.mo.x+(P_RandomRange(16,-16)*FRACUNIT),player.mo.y+(P_RandomRange(16,-16)*FRACUNIT),player.mo.z+(player.mo.height/2)+(P_RandomRange(8,-8)*FRACUNIT),MT_STRIKESPARKLES)
			strikesparkle.color = player.mo.color
			strikesparkle.colorized = true
			strikesparkle.scale = player.mo.scale
			
			strikesparkle.momx = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momy = P_RandomRange(5,-5)*FRACUNIT
			strikesparkle.momz = P_RandomRange(5,-5)*FRACUNIT
		end
	end
end

addHook("PlayerThink", HyperIndicator)

local function WHATISIT(player)
	if not (player.mo and player.mo.valid) then return end 
	if player.mo.skin != "mcsonic" return end
	
	if player.blackpaintimer == nil
		player.blackpaintimer = 0
	end
	
	if P_PlayerInPain(player)
	and player.mo.color == SKINCOLOR_BLACK
	and not player.blackpain
	and P_RandomRange(0,100) < 1
	and player.hurtthisframe
		print("Blade: What is it with this game and making me black?!")
		S_ChangeMusic("MP_MRZ", true, player)
		P_SetupLevelSky(6)
		P_FadeLight(65535, 128, TICRATE, true)
		player.blackpain = true
		player.blackpaintimer = 10*TICRATE
	end
	
	if player.blackpaintimer > 0
		player.blackpaintimer = $ - 1
	end
	
	if player.blackpain
	and leveltime % TICRATE/2 == 0
		local brak = P_SpawnMobj(player.mo.x+(P_RandomRange(-100,100))*FRACUNIT,player.mo.y+(P_RandomRange(-100,100))*FRACUNIT, player.mo.z, MT_CYBRAKDEMON)
		brak.scale = player.mo.scale * 2
	end
	
	if player.blackpain
	and player.blackpaintimer == 0
		for i = 0,32
			local deton = P_SpawnMobj(player.mo.x+(P_RandomRange(-100,100))*FRACUNIT,player.mo.y+(P_RandomRange(-100,100))*FRACUNIT, player.mo.z, MT_DETON)
			deton.scale = player.mo.scale * 2
		end 
		print("Your demise shall soon arrive.")
		player.blackpaintimer = 10*TICRATE
	end
	
	if player.mo.state != S_PLAY_PAIN
		player.hurtthisframe = true
	else
		player.hurtthisframe = false
	end
end

addHook("PlayerThink", WHATISIT)

local function BlackPainClear(player)
	player.blackpain = false
end

addHook("PlayerSpawn", BlackPainClear)

// Based off Reveries code, but I understand it well enough. 
// This DOES reuse newsoup, however.

local function newsoup(player)
	if All7Emeralds(emeralds) and (player.rings > 49) and (player.playerstate == PST_LIVE) and not (player.powers[pw_super]) and not (player.pflags & PF_SPINNING) and not (player.powers[pw_carry]) and not (player.pflags & PF_THOKKED) and not (maptol & TOL_NIGHTS)
	and not (player.panim == PA_ABILITY) and not (player.panim == PA_ABILITY2) and not (player.panim == PA_PAIN) and not (player.panim == PA_ETC)
		return true
	else
		return false
	end
end

local function SuperRemap(player)
	if not (player.mo and player.mo.valid) then return end 
	if player.mo.skin != "mcsonic" return end
	
	if player.cmd.buttons & BT_CUSTOM3
	and newsoup(player)
		player.charflags = $|SF_SUPER
		P_DoSuperTransformation(player)
		player.turnedsuper = true
	elseif (player.charflags & SF_SUPER)
	and not player.powers[pw_super]
	and not (maptol & TOL_NIGHTS)
		player.charflags = $ & ~SF_SUPER
	end
	
	if player.turnedsuper
	and P_IsObjectOnGround(player.mo)
		player.turnedsuper = false
	end
end

addHook("PlayerThink", SuperRemap)

// Wall dash? Reviving this, due to the "Scrunk".
// Knuckles lite? Heh, MCSonic's already sorta that, and this ties RBSonic back in.
// ... just gotta make this good.

local function WallDash(player)
	if not (player.mo and player.mo.valid) then return end 
	if player.mo.skin != "mcsonic" return end	
	
	if player.donotfly == nil
		player.donotfly = false
	end
	
	if player.chargewalldash == nil
		player.chargewalldash = false
	end
	
	if player.walldashlock == nil
		player.walldashlock = false
	end
	
	player.prevpushing = player.powers[pw_pushing]
		
	if lines[player.lastlinehit] and lines[player.lastlinehit].valid
		player.playerline = lines[player.lastlinehit]
		player.bounceside = P_PointOnLineSide(player.mo.x, player.mo.y, player.playerline)
	end
	
//	if lines[player.lastlinehit] and lines[player.lastlinehit].valid
//		player.playerline = lines[player.lastlinehit]
		
//		player.bounceside = P_PointOnLineSide(player.mo.x, player.mo.y, player.playerline)
		
//		if player.playerline.special == 41
//			player.donotfly = true
//		end
		
//		if player.playerline.frontsector and player.playerline.frontsector.valid
//		and player.playerline.frontsector.ceilingpic == F_SKY1
//		and player.mo.z > player.playerline.frontsector.ceilingheight
//			player.donotfly = true
//		end
		
//		if player.playerline.backsector and player.playerline.backsector.valid
//		and player.playerline.backsector.ceilingpic == F_SKY1
//		and player.mo.z > player.playerline.backsector.ceilingheight	
//			player.donotfly = true
//		end
//	end
	
//	if not player.powers[pw_pushing]
//		player.donotfly = false
//	end
	
	if player.droptimer >= 15
	and player.mo.state == S_DROPDASH
	and player.powers[pw_pushing]
	and not player.donotfly
	and not player.chargewalldash
		player.mo.spritexscale = FRACUNIT
		player.mo.spriteyscale = FRACUNIT
		player.bouncing = false
		player.chargewalldash = true
		player.walldashspeed = 12*FRACUNIT
		S_StartSound(player.mo, sfx_spndsh, player)
		player.triedtodash = false
		player.droptimer = 0
		player.dropspeed = 36*FRACUNIT
		player.savednormalspeed = player.normalspeed
	end
	
	if player.chargewalldash
	and player.cmd.buttons & BT_SPIN
		player.mo.momx = $ / 4
		player.mo.momy = $ / 4
		
		if not player.objectcollide
			if player.bounceside == 1
				P_InstaThrust(player.mo,R_PointToAngle2(player.playerline.v1.x, player.playerline.v1.y, player.playerline.v2.x, player.playerline.v2.y)+ANGLE_270, FixedHypot(player.mo.momx, player.mo.momy))
			else
				P_InstaThrust(player.mo,R_PointToAngle2(player.playerline.v1.x, player.playerline.v1.y, player.playerline.v2.x, player.playerline.v2.y)+ANGLE_90, FixedHypot(player.mo.momx, player.mo.momy))
			end
		end
		
		if player.mo.momz * P_MobjFlip(player.mo) < -1*FRACUNIT
			player.mo.momz = P_MobjFlip(player.mo) * -1 * FRACUNIT
		end
		
		player.mo.state = S_PLAY_SPINDASH
		
		if player.walldashspeed < 18*FRACUNIT
			player.walldashspeed = $ + FRACUNIT/8
			if player.walldashspeed == 14*FRACUNIT
			or player.walldashspeed == 16*FRACUNIT
			or player.walldashspeed == 18*FRACUNIT - FRACUNIT/4
				S_StartSound(player.mo, sfx_spndsh, player)
			end
		end
		
		P_SpawnThokMobj(player)
		local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_DUST)
		dust.momx = P_RandomRange(-3, 3) * FRACUNIT
		dust.momy = P_RandomRange(-3, 3) * FRACUNIT
		dust.momz = -player.mo.momz * 3 / 2
	end
	
	if player.chargewalldash
	and not (player.cmd.buttons & BT_SPIN)
		player.walldashlock = true
		if player.mchyper
			if player.mo.eflags & MFE_UNDERWATER
				P_SetObjectMomZ(player.mo, player.walldashspeed, false) // 2/3 * 3/2 = 1
			else
				P_SetObjectMomZ(player.mo, player.walldashspeed*3/2, false)
			end
		else
			if player.mo.eflags & MFE_UNDERWATER
				P_SetObjectMomZ(player.mo, player.walldashspeed*2/3, false)
			else
				P_SetObjectMomZ(player.mo, player.walldashspeed, false)
			end
		end
		
		if player.cmd.forwardmove
		or player.cmd.sidemove
			local directionangle = player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT)
			if P_CheckPosition(player.mo, player.mo.x + cos(directionangle)*15, player.mo.y + sin(directionangle)*15, player.mo.z)
				if player.powers[pw_super]
					P_InstaThrust(player.mo, directionangle, 25*FRACUNIT)
				else
					P_InstaThrust(player.mo, directionangle, 15*FRACUNIT)
				end
			end
		end
		
		player.mo.state = S_PLAY_SPRING
		player.pflags = $ & ~PF_JUMPED
		player.donotfly = false
		player.chargewalldash = false
		S_StartSound(player.mo, sfx_zoom, player)
	end
	
	if player.chargewalldash
		player.normalspeed = 0
	elseif not player.chargewalldash
	and player.normalspeed == 0
		if player.dashmode2 >= TICRATE
			player.normalspeed = player.savednormalspeed
		else
			player.normalspeed = skins[player.mo.skin].normalspeed
		end
	end
	
	if P_PlayerInPain(player)
	or P_IsObjectOnGround(player.mo)
	or player.powers[pw_justsprung]
		player.chargewalldash = false
		player.walldashlock = false
			
		if P_IsObjectOnGround(player.mo)
		and player.chargewalldash
			player.mo.state = S_PLAY_WALK
		end
	end
	
	if not player.powers[pw_pushing]
	and not player.objectcollide
	and player.chargewalldash
	and not P_IsObjectOnGround(player.mo)
		player.chargewalldash = false
		player.mo.state = S_PLAY_JUMP
		player.pflags = $ | PF_JUMPED
		player.walldashlock = false
		player.droptimer = 0
		player.triedtodrop = false
		player.dropspeed = 36*FRACUNIT
		if player.mo.momz * P_MobjFlip(player.mo) > 0
			if player.cmd.buttons & BT_JUMP
				player.walldashlock = true
				if player.mchyper
					if player.mo.eflags & MFE_UNDERWATER
						P_SetObjectMomZ(player.mo, player.walldashspeed, false) // 2/3 * 3/2 = 1
					else
						P_SetObjectMomZ(player.mo, player.walldashspeed*3/2, false)
					end
				else
					if player.mo.eflags & MFE_UNDERWATER
						P_SetObjectMomZ(player.mo, player.walldashspeed*2/3, false)
					else
						P_SetObjectMomZ(player.mo, player.walldashspeed, false)
					end
				end
		
				if player.cmd.forwardmove
				or player.cmd.sidemove
					local directionangle = player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT)
					if P_CheckPosition(player.mo, player.mo.x + cos(directionangle)*15, player.mo.y + sin(directionangle)*15, player.mo.z)
						if player.powers[pw_super]
							P_InstaThrust(player.mo, directionangle, 25*FRACUNIT)
						else
							P_InstaThrust(player.mo, directionangle, 15*FRACUNIT)
						end
					end
				end
				
				player.mo.state = S_PLAY_SPRING
				player.pflags = $ & ~PF_JUMPED
			end
		end
	end
		
	if not player.powers[pw_pushing]
	and player.objectcollide
		player.objectcollide = false
	end
end
	
addHook("PlayerThink", WallDash)

local function WeRamIntoObject(plmo, mo)
	if not (plmo and plmo.valid) return end
	if plmo.skin != ("mcsonic") return end
    if plmo.type ~= MT_PLAYER return end
    if plmo.z + plmo.height < mo.z
	or mo.z + mo.height < plmo.z return end
    local player = plmo.player --plmo is the player mobj, this gets the player
	player.objectcollide = true
end

addHook("MobjMoveCollide", WeRamIntoObject, MT_PLAYER)

// Power Share!
// Press Custom 3 on the ground near other players to give them Super if you are Hyper!
// Even applies to characters that aren't meant to have Super normally.

local function PowerShare(mo1, mo2)
	if not mo1.valid and mo2.valid return end
	
	local player1 = mo1.player
	local player2 = mo2.player
	
	if not (player1 and player2)
	// REMEMBER! Neither MobjCollide or MobjMoveCollide actually check for z-position. This must be done here!
	or mo1.z > mo2.z + mo2.height 
	or mo2.z > mo1.z + mo1.height
	or player2.powers[pw_carry]
	or player2.playerstate ~= PST_LIVE
	or player2.spectator
	or P_PlayerInPain(player2)
	or mo1.skin ~= "mcsonic"
	or not P_IsObjectOnGround(mo1)
	or not P_IsObjectOnGround(mo2)
	or not player1.mchyper
		return
	end
	
	if player1.cmd.buttons & BT_CUSTOM3
	and not player2.powers[pw_super]
		if player2.rings < 50
			player2.rings = 50
		end
		
		if not (player2.charflags & SF_SUPER)
			player2.forcedsuper = true
			player2.charflags = $|SF_SUPER
		end
	
		P_DoSuperTransformation(player2)
		player2.turnedsuper = true
		for i = 0, 8
			local fa = (i*(ANGLE_180/4))
			local strikesparkle = P_SpawnMobj(mo2.x, mo2.y, mo2.z+(mo2.height/2), MT_STRIKESPARKLES)
			strikesparkle.momx = FixedMul(sin(fa),5*FRACUNIT)
			strikesparkle.momy = FixedMul(cos(fa),5*FRACUNIT)
			strikesparkle.color = player1.mo.color
			strikesparkle.scale = player1.mo.scale * 2
			strikesparkle.colorized = true
		end
	end
end

addHook("MobjMoveCollide", PowerShare)

local function IFuckingHateThis(player)
	if not (player.mo and player.mo.valid) then return end 
	
	if not player.powers[pw_super]
		if player.forcedsuper
			player.forcedsuper = false
			player.charflags = $ & ~SF_SUPER
		end
	end
end

addHook("PlayerThink", IFuckingHateThis)

// Source code port time!

local function Ring_FindPlayer(actor)
	local c 
	
	if c == nil
		c = 0
	end
	
	local stop
	local player
	
	// BP: first time init, this allow minimum lastlook changes
	if (actor.lastlook < 0)
		actor.lastlook = P_RandomByte()
	end

	actor.lastlook = $ % 32

	stop = (actor.lastlook - 1) & 31

	while (true)
		// done looking
		if (actor.lastlook == stop)
			return false
		end

//		if not actor.lastlook and actor.lastlook.valid
//			continue
//		end
			
		c = $ + 1
			
		if (c == 2)
			c = 0
			return false
		end
			
		player = players[actor.lastlook]
		
		if player == nil
			return false
		end
		
		if player.powers[pw_shield] == SH_ATTRACT
			return false
		end

		if not player.mo
		or player.mo.health <= 0
			continue // dead
		end

		if player.mchyper
		and (P_AproxDistance(P_AproxDistance(actor.x-player.mo.x, actor.y-player.mo.y), actor.z-player.mo.z) < FixedMul(256*FRACUNIT, player.mo.scale))
			actor.tracer = player.mo

			if (actor.hnext)
				actor.hnext.hprev = actor.hprev
			end
			if (actor.hprev)
				actor.hprev.hnext = actor.hnext
			end
			return true
		end
		actor.lastlook = ((actor.lastlook + 1) & 31)
	end
end	

local function Ring_Attract(source, dest)
	local vangle
	local tx = dest.x
	local ty = dest.y
	local tz = dest.z + (dest.height/2) // Aim for center
	local xydist = P_AproxDistance(tx - source.x, ty - source.y)

	if not dest 
	or dest.health <= 0 
	or not dest.player 
	or not source.tracer
		return
	end

	// change angle
	source.angle = R_PointToAngle2(source.x, source.y, tx, ty)

	// change slope
	local dist = P_AproxDistance(xydist, tz - source.z)

	if (dist < 1)
		dist = 1
	end

	local speedmul = P_AproxDistance(dest.momx, dest.momy) + FixedMul(source.info.speed, source.scale);

	source.momx = FixedMul(FixedDiv(tx - source.x, dist), speedmul)
	source.momy = FixedMul(FixedDiv(ty - source.y, dist), speedmul)
	source.momz = FixedMul(FixedDiv(tz - source.z, dist), speedmul)

	// Instead of just unsetting NOCLIP like an idiot, let's check the distance to our target.
	local ndist = P_AproxDistance(P_AproxDistance(tx - (source.x+source.momx), ty - (source.y+source.momy)), tz - (source.z+source.momz))

	if (ndist > dist) // gone past our target
		// place us on top of them then.
		source.momx = 0
		source.momy = 0
		source.momz = 0
		source.x = tx
		source.y = ty
		source.z = tz
	end
end

local function Ring_AttractChase(actor)
	if (actor.flags2 & MF2_NIGHTSPULL or not actor.health)
	or (actor.tracer and actor.tracer.player and actor.tracer.player.powers[pw_shield] == SH_ATTRACT)
		return
	end

	// Turn flingrings back into regular rings if attracted.
	if (actor.tracer and actor.tracer.player and (actor.info.reactiontime) and actor.type != actor.info.reactiontime)
		local newring = P_SpawnMobj(actor.x, actor.y, actor.z, actor.info.reactiontime)
		newring.momx = actor.momx;
		newring.momy = actor.momy;
		newring.momz = actor.momz;
		P_RemoveMobj(actor)
		return
	end
	
	if Ring_FindPlayer(actor)
		if not (actor.tracer or not actor.tracer.player or not actor.tracer.health or not P_CheckSight(actor, actor.tracer)) // You have to be able to SEE it...sorta
			// Lost attracted rings don't through walls anymore.
			actor.flags = $ & ~MF_NOCLIP
			actor.target = NULL
			return
		end

		// If a FlingRing gets attracted by a shield, change it into a normal ring.
		if (actor.type == actor.info.reactiontime)
			P_SpawnMobj(actor.x, actor.y, actor.z, actor.info.painchance)
			P_RemoveMobj(actor)
			return
		end

		// Keep stuff from going down inside floors and junk
		actor.flags = $ & ~MF_NOCLIPHEIGHT

		// Let attracted rings move through walls and such.
		actor.flags = $ | MF_NOCLIP

		Ring_Attract(actor, actor.tracer)
	end
end

local function HookUpToThinker(mo)
	if not mo and mo.valid return end
	
	Ring_AttractChase(mo)
end

addHook("MobjThinker", HookUpToThinker, MT_RING)
addHook("MobjThinker", HookUpToThinker, MT_FLINGRING)
addHook("MobjThinker", HookUpToThinker, MT_COIN)
addHook("MobjThinker", HookUpToThinker, MT_FLINGCOIN)

local function AltFall(player)
	if not (player.mo and player.mo.valid) then return end 
	if player.mo.skin != "mcsonic" return end
	
	player.prevmostate = player.mo.state
	
//	if player.mo.state == S_PLAY_FALL
//		if player.bouncecount >= 3
//		or player.walldashlock
//			player.mo.state = S_MCALTFALL1
//		end
//	end
	
	if player.mo.state == S_PLAY_WAIT
		if P_RandomRange(0,100) <= 1
		and not player.alreadyhellfire
			player.mo.state = S_HELLFIRE
			S_StartSound(player.mo, sfx_hefire, player)
		end
		player.alreadyhellfire = true
	end
	
	if player.mo.state != S_PLAY_WAIT
		player.alreadyhellfire = false
	end
	
	if player.mo.state == S_HELLFIRE
	and (leveltime % (TICRATE*10)) == 0
		S_StartSound(player.mo, sfx_hefire, player)
	end
end

addHook("PlayerThink", AltFall)

// SuperPhanto already contributed quite a bit to MCSonic v2, so I'll reuse this resource of his and edit it to work.
freeslot("SPR_PEEL")

addHook("ThinkFrame", do
	for player in players.iterate
		if (player.mo.valid) and (player.mo.skin == "mcsonic")
			if player.dashmode2 >= 2*TICRATE
			or player.mo.peelouttimer >= 50
				if player.mo.state == S_PLAY_DASH and not player.powers[pw_super]
				or player.mo.peelouttimer and not player.powers[pw_super]
					local force = FRACUNIT*10
					local angle = ANGLE_90

					local shiftx = FixedMul(cos(player.drawangle + angle), force)
					local shifty = FixedMul(sin(player.drawangle + angle), force)
	
					local shiftx2 = FixedMul(cos(player.drawangle + angle), 8*FRACUNIT)
					local shifty2 = FixedMul(sin(player.drawangle + angle), 8*FRACUNIT)
					
					local shiftx3 = FixedMul(cos(player.drawangle + angle), 9*FRACUNIT)
					local shifty3 = FixedMul(sin(player.drawangle + angle), 9*FRACUNIT)
					
					local shiftx4 = FixedMul(cos(player.drawangle + angle), 7*FRACUNIT)
					local shifty4 = FixedMul(sin(player.drawangle + angle), 7*FRACUNIT)
					
					local shiftx5 = FixedMul(cos(player.drawangle + angle), 10*FRACUNIT)
					local shifty5 = FixedMul(sin(player.drawangle + angle), 10*FRACUNIT)

					player.peelout1 = P_SpawnMobj(player.mo.x - shiftx, player.mo.y - shifty, player.mo.z, MT_THOK)
					player.peelout1.fuse = 1
					player.peelout1.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout1.frame = A
					elseif player.mo.frame == B
						player.peelout1.frame = B
					elseif player.mo.frame == C
						player.peelout1.frame = C
					elseif player.mo.frame == D
						player.peelout1.frame = D
					end
					player.peelout1.renderflags = RF_PAPERSPRITE
					player.peelout1.angle = player.drawangle + ANG15
					player.peelout1.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout1.eflags = $ | MFE_VERTICALFLIP
						player.peelout1.z = player.mo.z + player.mo.height - player.peelout1.height
					end
					
					player.peelout2 = P_SpawnMobj(player.mo.x + shiftx, player.mo.y + shifty, player.mo.z, MT_THOK)
					player.peelout2.fuse = 1
					player.peelout2.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout2.frame = C
					elseif player.mo.frame == B
						player.peelout2.frame = D
					elseif player.mo.frame == C
						player.peelout2.frame = A
					elseif player.mo.frame == D
						player.peelout2.frame = B
					end
					player.peelout2.renderflags = RF_PAPERSPRITE
					player.peelout2.angle = player.drawangle - ANG15
					player.peelout2.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout2.eflags = $ | MFE_VERTICALFLIP
						player.peelout2.z = player.mo.z + player.mo.height - player.peelout2.height
					end
					
					player.peelout3 = P_SpawnMobj(player.mo.x - shiftx2, player.mo.y - shifty2, player.mo.z, MT_THOK)
					player.peelout3.fuse = 1
					player.peelout3.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout3.frame = A
					elseif player.mo.frame == B
						player.peelout3.frame = B
					elseif player.mo.frame == C
						player.peelout3.frame = C
					elseif player.mo.frame == D
						player.peelout3.frame = D
					end
					player.peelout3.renderflags = RF_PAPERSPRITE
					player.peelout3.angle = player.drawangle + ANG15
					player.peelout3.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout3.eflags = $ | MFE_VERTICALFLIP
						player.peelout3.z = player.mo.z + player.mo.height - player.peelout3.height
					end
					
					player.peelout4 = P_SpawnMobj(player.mo.x + shiftx2, player.mo.y + shifty2, player.mo.z, MT_THOK)
					player.peelout4.fuse = 1
					player.peelout4.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout4.frame = C
					elseif player.mo.frame == B
						player.peelout4.frame = D
					elseif player.mo.frame == C
						player.peelout4.frame = A
					elseif player.mo.frame == D
						player.peelout4.frame = B
					end
					player.peelout4.renderflags = RF_PAPERSPRITE
					player.peelout4.angle = player.drawangle - ANG15
					player.peelout4.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout4.eflags = $ | MFE_VERTICALFLIP
						player.peelout4.z = player.mo.z + player.mo.height - player.peelout4.height
					end
					
					player.peelout5 = P_SpawnMobj(player.mo.x - shiftx3, player.mo.y - shifty3, player.mo.z, MT_THOK)
					player.peelout5.fuse = 1
					player.peelout5.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout5.frame = A
					elseif player.mo.frame == B
						player.peelout5.frame = B
					elseif player.mo.frame == C
						player.peelout5.frame = C
					elseif player.mo.frame == D
						player.peelout5.frame = D
					end
					player.peelout5.renderflags = RF_PAPERSPRITE
					player.peelout5.angle = player.drawangle + ANG15
					player.peelout5.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout5.eflags = $ | MFE_VERTICALFLIP
						player.peelout5.z = player.mo.z + player.mo.height - player.peelout5.height
					end
					
					player.peelout6 = P_SpawnMobj(player.mo.x + shiftx3, player.mo.y + shifty3, player.mo.z, MT_THOK)
					player.peelout6.fuse = 1
					player.peelout6.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout6.frame = C
					elseif player.mo.frame == B
						player.peelout6.frame = D
					elseif player.mo.frame == C
						player.peelout6.frame = A
					elseif player.mo.frame == D
						player.peelout6.frame = B
					end
					player.peelout6.renderflags = RF_PAPERSPRITE
					player.peelout6.angle = player.drawangle - ANG15
					player.peelout6.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout6.eflags = $ | MFE_VERTICALFLIP
						player.peelout6.z = player.mo.z + player.mo.height - player.peelout6.height
					end
					
					player.peelout7 = P_SpawnMobj(player.mo.x - shiftx4, player.mo.y - shifty4, player.mo.z, MT_THOK)
					player.peelout7.fuse = 1
					player.peelout7.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout7.frame = A
					elseif player.mo.frame == B
						player.peelout7.frame = B
					elseif player.mo.frame == C
						player.peelout7.frame = C
					elseif player.mo.frame == D
						player.peelout7.frame = D
					end
					player.peelout7.renderflags = RF_PAPERSPRITE
					player.peelout7.angle = player.drawangle + ANG15
					player.peelout7.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout7.eflags = $ | MFE_VERTICALFLIP
						player.peelout7.z = player.mo.z + player.mo.height - player.peelout7.height
					end
					
					player.peelout8 = P_SpawnMobj(player.mo.x + shiftx4, player.mo.y + shifty4, player.mo.z, MT_THOK)
					player.peelout8.fuse = 1
					player.peelout8.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout8.frame = C
					elseif player.mo.frame == B
						player.peelout8.frame = D
					elseif player.mo.frame == C
						player.peelout8.frame = A
					elseif player.mo.frame == D
						player.peelout8.frame = B
					end
					player.peelout8.renderflags = RF_PAPERSPRITE
					player.peelout8.angle = player.drawangle - ANG15
					player.peelout8.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout8.eflags = $ | MFE_VERTICALFLIP
						player.peelout8.z = player.mo.z + player.mo.height - player.peelout8.height
					end
					
					player.peelout9 = P_SpawnMobj(player.mo.x - shiftx5, player.mo.y - shifty5, player.mo.z, MT_THOK)
					player.peelout9.fuse = 1
					player.peelout9.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout9.frame = A
					elseif player.mo.frame == B
						player.peelout9.frame = B
					elseif player.mo.frame == C
						player.peelout9.frame = C
					elseif player.mo.frame == D
						player.peelout9.frame = D
					end
					player.peelout9.renderflags = RF_PAPERSPRITE
					player.peelout9.angle = player.drawangle + ANG15
					player.peelout9.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout9.eflags = $ | MFE_VERTICALFLIP
						player.peelout9.z = player.mo.z + player.mo.height - player.peelout9.height
					end
					
					player.peelout10 = P_SpawnMobj(player.mo.x + shiftx5, player.mo.y + shifty5, player.mo.z, MT_THOK)
					player.peelout10.fuse = 1
					player.peelout10.sprite = SPR_PEEL
					if player.mo.frame == A
						player.peelout10.frame = C
					elseif player.mo.frame == B
						player.peelout10.frame = D
					elseif player.mo.frame == C
						player.peelout10.frame = A
					elseif player.mo.frame == D
						player.peelout10.frame = B
					end
					player.peelout10.renderflags = RF_PAPERSPRITE
					player.peelout10.angle = player.drawangle - ANG15
					player.peelout10.scale = player.mo.scale/2
					if player.mo.eflags & MFE_VERTICALFLIP
						player.peelout10.eflags = $ | MFE_VERTICALFLIP
						player.peelout10.z = player.mo.z + player.mo.height - player.peelout10.height
					end
				end
			end
		end
	end
end)

local function DashSprites(player)
	if not player.mo and player.mo.valid return end
		
	if player.mo.skin != "mcsonic" return end
	
	if player.dashmode2 >= 2*TICRATE
		if player.speed > 36*FRACUNIT
		and player.mo.state == S_PLAY_RUN
			player.mo.state = S_PLAY_DASH
		end
		if player.mo.state == S_PLAY_DASH
		and player.speed <= 36*FRACUNIT
			if player.speed >= player.runspeed
				player.mo.state = S_PLAY_RUN
			else
				player.mo.state = S_PLAY_WALK
			end
		end
	end
	
	if player.mo.state == S_PLAY_DASH
	and player.dashmode2 < 2*TICRATE
		if player.speed >= player.runspeed
			player.mo.state = S_PLAY_RUN
		else
			player.mo.state = S_PLAY_WALK
		end
	end
	
	if player.powers[pw_super]
		if player.speed >= 55 * FRACUNIT
		and player.mo.state == S_PLAY_RUN
			player.mo.state = S_PLAY_DASH
		elseif player.mo.state == S_PLAY_DASH
		and player.speed < 55 * FRACUNIT
			if player.speed >= player.runspeed
				player.mo.state = S_PLAY_RUN
			else
				player.mo.state = S_PLAY_WALK
			end
		end
	end
end

addHook("PlayerThink", DashSprites)