// Max Control Amy! 
// By Inferno
// Time to make MCSonic a series for the entire SRB2 cast!
// NOTE: Not intended as a Rose Gold Amy rip-off, most of these ideas were in place before it had released.

freeslot(
"S_HELIHAMMER",
"SPR_HLHA",
"S_DOUBLEJUMP",
"SPR_HMJP",
"MT_ASH_ORB",
"MT_ASH_ORB2",
"S_ASH_ORB",
"S_ASH_ORB1",
"SPR_ASHS",
"SPR2_TPOS",
"S_TPOSE"
)

states[S_HELIHAMMER] = {
	sprite = SPR_HLHA,
	frame = FF_ANIMATE,
	tics = -1,
	var1 = 7,
	var2 = 1,
	nextstate = S_PLAY_FALL
}

states[S_DOUBLEJUMP] = {
	sprite = SPR_HMJP,
	frame = FF_ANIMATE,
	tics = -1,
	var1 = 7,
	var2 = 2,
	nextstate = S_PLAY_FALL
}

mobjinfo[MT_ASH_ORB] = {
	doomednum = -1,
	spawnstate = S_ASH_ORB,
	spawnhealth = 1000,
	reactiontime = 8,
	radius = 32*FRACUNIT,
	height = 64*FRACUNIT,
	mass = 16,
	dispoffset = 1,
	flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY
}

mobjinfo[MT_ASH_ORB2] = {
	doomednum = -1,
	spawnstate = S_ASH_ORB1,
	spawnhealth = 1000,
	reactiontime = 8,
	radius = 32*FRACUNIT,
	height = 64*FRACUNIT,
	mass = 16,
	dispoffset = 1,
	flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY
}

states[S_ASH_ORB] = {
	sprite = SPR_ASHS,
	frame = A|FF_ANIMATE|TR_TRANS50,
	tics = -1,
	var1 = 15,
	var2 = 2,
	nextstate = S_ASH_ORB
}

states[S_ASH_ORB1] = {
	sprite = SPR_ASHS,
	frame = A|FF_ANIMATE|TR_TRANS70,
	tics = -1,
	var1 = 15,
	var2 = 2,
	nextstate = S_ASH_ORB1
}

states[S_TPOSE] = {
	sprite = SPR_PLAY,
	frame = SPR2_TPOS|A,
	tics = -1,
	nextstate = S_PLAY_WALK
}

sfxinfo[sfx_s1a2] = {
	singular = false,
	caption = "Leaping on ground"
}

local SH_ASH = 14
rawset(_G, "SH_ASH", SH_ASH)

addHook("MobjThinker", function(shield)
	if shield and shield.valid
		if shield.target and shield.target.valid
			P_TeleportMove(shield, shield.target.x, shield.target.y, shield.target.z)
			if shield.target.player.powers[pw_shield] != SH_ASH
				P_RemoveMobj(shield)
			end
		end
	end
end, MT_ASH_ORB)

addHook("MobjThinker", function(shield)
	if shield and shield.valid
		if shield.target and shield.target.valid
			P_TeleportMove(shield, shield.target.x, shield.target.y, shield.target.z-FixedMul(shield.target.player.height/3, shield.scale))
			if shield.target.player.powers[pw_shield] != SH_ASH
				P_RemoveMobj(shield)
			end
			
			if shield.scale <= shield.target.scale * 2
			// Stupid shield nonsense.
			and shield
			and shield.valid
				shield.scale = $ + (FRACUNIT/32)
			else
				P_RemoveMobj(shield)
			end
		end
	end
end, MT_ASH_ORB2)

// Hammer Jump!
// Press Jump during Hammer Lag to fly upwards!

addHook("PlayerThink", function(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.state != S_PLAY_MELEE_LANDING
		player.hammerspeed = player.speed
	end
	
	if player.powers[pw_shield] == SH_ASH
		if leveltime % 70 == 0
			local extraorb = P_SpawnMobj(player.mo.x,player.mo.y,player.mo.z,MT_ASH_ORB2)
			extraorb.target = player.mo		
			extraorb.scale = player.mo.scale
		end
	end
	
	if player.mo.skin != "mcamy" return end
	
	if player.mo.state == S_PLAY_MELEE_LANDING
	and player.cmd.buttons & BT_JUMP
	and not (player.pflags & PF_JUMPDOWN)
		if player.powers[pw_super]
			P_SetObjectMomZ(player.mo, player.hammerspeed/2, false)
		else
			P_SetObjectMomZ(player.mo, player.hammerspeed*7/15, false)
		end
		S_StartSound(player.mo, sfx_sprong, player)
		player.mo.state = S_PLAY_SPRING
		if (player.powers[pw_shield] == SH_ARMAGEDDON)
		and not player.powers[pw_super]
			player.mo.momz = $ * 5 / 4
			P_BlackOw(player)
			local extraorb = P_SpawnMobj(player.mo.x,player.mo.y,player.mo.z,MT_ASH_ORB)
			extraorb.target = player.mo
			P_SwitchShield(player, SH_ASH)
		end
		if player.mchyper
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			if player.mcflashes
				P_FlashPal(player, PAL_WHITE, 8)
			end
			S_StartSound(player.mo, sfx_s3k4e)
			player.mo.momz = $ * 5 / 4
		end
	end
end)

// Break fucking floors.

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)
//				elseif mo.player.stomping
//					mo.player.keepstomp = false
				end
			end
		end
	end
end

// Heli-Hammer

// Press Jump again after a Twinspin to start to hover using your hammer.

addHook("PlayerThink", function(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.skin != "mcamy" return end

	if player.helihammer == nil
		player.helihammer = false
	end
	
	if player.helilag == nil
		player.helilag = false
	end
	
	if player.cyclone == nil
		player.cyclone = false
	end
	
	if P_IsObjectOnGround(player.mo)
	or P_PlayerInPain(player)
	or player.powers[pw_carry]
		player.helihammer = false
		player.helilag = false
		player.doublejumped = false
		player.cyclonelag = false
		player.amyhammered = false
	end
	
	if player.pflags & PF_THOKKED
	and not (player.pflags & PF_JUMPDOWN)
	and not P_IsObjectOnGround(player.mo)
		player.amyhammered = true
	elseif player.amyhammered
	and P_IsObjectOnGround(player.mo)
		player.amyhammered = false
	end
	
	if player.amyhammered
	and player.cmd.buttons & BT_JUMP
	and not player.helihammer
	and not player.helilag
	and not player.cyclone
	and player.mo.state != S_PLAY_MELEE_LANDING
//	and not (player.pflags & PF_JUMPDOWN)
		player.mo.state = S_HELIHAMMER
		player.currentframe = 0
		if player.powers[pw_shield] == SH_FLAMEAURA
			P_InstaThrust(player.mo, player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT), player.speed*13/10)
		else
			P_InstaThrust(player.mo, player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT), player.speed*115/100)
		end
		player.helihammer = true
		if player.mchyper
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			if player.mcflashes
				P_FlashPal(player, PAL_WHITE, 8)
			end
			S_StartSound(player.mo, sfx_s3k4e)
		end
		player.kill = P_LookForEnemies(player, false, false)
	end
	
	if player.helihammer
		if player.pflags & PF_JUMPDOWN
			if leveltime % 7 == 0
				S_StartSound(player.mo, sfx_s3k42, player)
			end
			
			if (P_MobjFlip(player.mo)*player.mo.momz < -gravity*6)
			and not player.mchyper
				player.mo.momz = P_MobjFlip(player.mo)*-gravity*6
			elseif (P_MobjFlip(player.mo)*player.mo.momz < 0)
			and player.mchyper
				player.mo.momz = 0
			end
			
			if leveltime % 2 == 0
				if player.currentframe < 8
					player.currentframe = $ + 2
				else
					player.currentframe = 0
				end
					
				local fa = (player.currentframe*(ANGLE_180/4))
				local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
				dust.momx = FixedMul(sin(fa),15*FRACUNIT)+player.mo.momx
				dust.momy = FixedMul(cos(fa),15*FRACUNIT)+player.mo.momy
				dust.scale = player.mo.scale * 3 / 4
				P_SetObjectMomZ(dust, -10*FRACUNIT, false)
			end
			
			if player.powers[pw_shield] == SH_ATTRACT
				if player.kill and player.kill.valid
					P_HomingAttack(player.mo, player.kill)
				end
			end
		else
			player.helihammer = false
			player.helilag = true
			player.mo.state = S_PLAY_TWINSPIN
			S_StartSound(player.mo, sfx_s3k42, player)
		end
	end
	
	// Double Jump!
	// If you are cancelling the Heli-Hammer and then press Jump, you jump once more!
	
	if not P_IsObjectOnGround(player.mo)
		player.savedmomz = player.mo.momz
	end
	
	if player.helilag
	and player.cmd.buttons & BT_JUMP
	and not player.jumpdown
	and not player.doublejumped
	and not player.cyclone
		P_SetObjectMomZ(player.mo, 12*FRACUNIT, false)
		if player.powers[pw_shield] == SH_WHIRLWIND
		or player.powers[pw_shield] == SH_THUNDERCOIN
		or player.powers[pw_super]
			P_SetObjectMomZ(player.mo, 17*FRACUNIT, false)
		end
		if player.mo.eflags & MFE_UNDERWATER
			player.mo.momz = $ * 3 / 4
		end
		player.mo.state = S_DOUBLEJUMP
		player.pflags = $ & ~PF_JUMPED
		if player.powers[pw_shield] == SH_WHIRLWIND
			S_StartSound(player.mo, sfx_wdjump, player)
		elseif player.powers[pw_shield] == SH_THUNDERCOIN
			S_StartSound(player.mo, sfx_s3k45, player)
		else
			S_StartSound(player.mo, sfx_jump, player)
		end
		for i = 0, 8
			local fa = (i*(ANGLE_180/4))
			local dust
			if player.powers[pw_shield] == SH_THUNDERCOIN
				dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_THUNDERCOIN_SPARK)
				dust.fuse = 18
			else
				dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
			end
			dust.momx = FixedMul(sin(fa),5*FRACUNIT)
			dust.momy = FixedMul(cos(fa),5*FRACUNIT)
			dust.scale = player.mo.scale
			P_SetObjectMomZ(dust, -5*FRACUNIT, false)
		end
		player.doublejumped = true
		if player.mchyper
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			if player.mcflashes
				P_FlashPal(player, PAL_WHITE, 8)
			end
			S_StartSound(player.mo, sfx_s3k4e)
			player.mo.momz = $ * 7 / 4
		end
	end
		
	if player.cyclone
	and P_IsObjectOnGround(player.mo)
		FloorBreak(player.mo, player.mo.subsector.sector)
		player.cyclone = false
		player.cyclonelag = true
		if player.powers[pw_shield] == SH_BUBBLEWRAP
		and not player.powers[pw_super]
			player.mo.momz = FixedMul(-player.savedmomz, 5*FRACUNIT/8) // 2/4, 3/4.
			player.cyclonelag = false
		else
			P_SetObjectMomZ(player.mo, 5*FRACUNIT, false)
		end
		player.mo.state = S_PLAY_JUMP
		player.pflags = $|PF_JUMPED
		player.pflags = $|PF_NOJUMPDAMAGE
		if player.powers[pw_shield] == SH_ELEMENTAL
		and not player.powers[pw_super]
			P_ElementalFire(player, true)
		else
			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),12*FRACUNIT)
				dust.momy = FixedMul(cos(fa),12*FRACUNIT)
				dust.scale = player.mo.scale * 3 / 4
				P_SetObjectMomZ(dust, 3*FRACUNIT, false)
			end
		end
		if player.mchyper
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			if player.mcflashes
				P_FlashPal(player, PAL_WHITE, 8)
			end
			S_StartSound(player.mo, sfx_s3k4e)
		end
	end
	
	if player.cyclone
	and player.mo.eflags & MFE_SPRUNG
		player.mo.momz = $*6/5
		player.mo.momx = $*4/3
		player.mo.momy = $*4/3
		S_StartSound(player.mo, sfx_sprong, player)
		player.cyclone = false
		if player.mchyper
			P_StartQuake(25*FRACUNIT, 16)
			P_NukeEnemies(player.mo, player.mo, 1500*FRACUNIT)
			if player.mcflashes
				P_FlashPal(player, PAL_WHITE, 8)
			end
			S_StartSound(player.mo, sfx_s3k4e)
		end
	end
	
	if player.cyclone
	and P_IsObjectInGoop(player.mo)
		player.cyclone = false
		player.cyclonelag = true
		player.mo.state = S_PLAY_FALL
	end
	
	if player.mo.state == S_DOUBLEJUMP
	and player.mo.momz * P_MobjFlip(player.mo) < 0
		player.mo.state = S_PLAY_FALL
	end
	
	if player.cmd.buttons & BT_JUMP
		player.jumpdown = true
	else
		player.jumpdown = false
	end
end)

local function CycloneDrill(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.skin != "mcamy" return end
	
	if not player.cyclone
	and not player.cyclonelag
	and not player.helihammer
	and not (player.pflags & PF_SPINDOWN)
		player.mo.state = S_HELIHAMMER
		P_SetObjectMomZ(player.mo, -24*FRACUNIT, false)
		S_StartSound(player.mo, sfx_s3k42, player)
		if (player.powers[pw_shield] & SH_FORCE)
			player.mo.momx = 0
			player.mo.momy = 0
		end
		player.cyclone = true
		player.pflags = $|PF_THOKKED
	end
	return true
end

addHook("JumpSpinSpecial", CycloneDrill)

local function EnemyKillDrill(enemy, mo, source, damage)
	if not mo or not mo.valid return end
	if mo.skin != "mcamy" 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|MF_MONITOR)
		if player.cyclone
			player.cyclone = false
			player.cyclonelag = true
			player.helilag = false
			player.mo.state = S_PLAY_JUMP
			player.pflags = $|PF_JUMPED
			player.pflags = $|PF_NOJUMPDAMAGE
			player.pflags = $ & ~PF_THOKKED
		end
	end
end

addHook("MobjDeath", EnemyKillDrill)

local function StatesWithDamage(player, badnik)
	if player.mo and player.mo.valid and player.mo.health
		if player.mo.state == S_HELIHAMMER
			return true
		end
	end
end

addHook("PlayerCanDamage", StatesWithDamage)

// Giant Steps!
// Press Custom 1 on the ground to fly forward at your normalspeed.

addHook("PlayerThink", function(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.skin != "mcamy" return end	
	
	if P_IsObjectOnGround(player.mo)
	and player.cmd.buttons & BT_CUSTOM1
	and not player.custom1held
	and player.mo.state != S_PLAY_MELEE_LANDING
	and not (player.pflags & PF_SLIDING)
	and not player.exiting
		if player.speed == 0
			player.cmd.forwardmove = 50
		end
		P_InstaThrust(player.mo, player.cmd.angleturn << 16 + R_PointToAngle2(0, 0, player.cmd.forwardmove*FRACUNIT, -player.cmd.sidemove*FRACUNIT), player.normalspeed * 5 / 4)
		P_SetObjectMomZ(player.mo, 5*FRACUNIT, false)
		player.mo.state = S_PLAY_RUN
		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)
		S_StartSound(player.mo, sfx_s1a2, player)
	end
		
	if player.cmd.buttons & BT_CUSTOM1
		player.custom1held = true
	else
		player.custom1held = false
	end
end)

addHook("PlayerThink", function(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.skin != "mcamy" return 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)
	end
	
	if P_IsObjectOnGround(player.mo)
		player.wallblock = false
	end
	
	if player.mo.state == S_PLAY_TWINSPIN
	and player.powers[pw_pushing]
	and not player.wallblock
		if player.objectcollide
			P_InstaThrust(player.mo, player.mo.angle + ANGLE_180, skins[player.mo.skin].normalspeed)
		else
			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_90, skins[player.mo.skin].normalspeed)
				player.drawangle = R_PointToAngle2(player.playerline.v1.x, player.playerline.v1.y, player.playerline.v2.x, player.playerline.v2.y)+ANGLE_90
			else
				P_InstaThrust(player.mo,R_PointToAngle2(player.playerline.v1.x, player.playerline.v1.y, player.playerline.v2.x, player.playerline.v2.y)+ANGLE_270, skins[player.mo.skin].normalspeed)
				player.drawangle = R_PointToAngle2(player.playerline.v1.x, player.playerline.v1.y, player.playerline.v2.x, player.playerline.v2.y)+ANGLE_270
			end
		end
		if player.powers[pw_super]
			P_SetObjectMomZ(player.mo, 20*FRACUNIT, false)
		else
			P_SetObjectMomZ(player.mo, 15*FRACUNIT, false)
		end
		if player.mo.eflags & MFE_UNDERWATER
			player.mo.momz = $ * 3 / 4
		end
//		player.mo.state = S_PLAY_JUMP
		if not player.doublejumped
			player.helilag = false
		end
		
		player.wallblock = true
		
		if player.mchyper
			player.amyhammered = false
			player.pflags = $ & ~PF_THOKKED
			player.wallblock = false
		end
		S_StartSound(player.mo, sfx_s3k42, player)
		for i = 0, 4
			local heart = 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_LHRT)
			
			heart.target = player.mo
			
			P_InstaThrust(heart, player.drawangle, P_RandomRange(5,-5)*FRACUNIT)
			P_SetObjectMomZ(heart, P_RandomRange(5,-5)*FRACUNIT, true)
		end
		S_StartSound(player.mo, sfx_s3k8b, player)
	end
	
	if not player.powers[pw_pushing]
	and player.objectcollide
		player.objectcollide = false
	end
end)
			
	
local function WeRamIntoObject(plmo, mo)
	if not (plmo and plmo.valid) return end
	if plmo.skin != ("mcamy") 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)

addHook("ShieldSpecial", function(player)
	if not player.mo and player.mo.valid return end
	
	if player.mo.skin != "mcamy" return end	
	
	return true
end)

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 Dashmode(player)
	if not (player.mo and player.mo.valid) return end 
	if player.mo.skin == "mcamy" 
	or player.mo.skin == "mctails" and player.savedskin != "mcsonic"
		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
				player.normalspeed = skins[player.mo.skin].normalspeed
				player.jumpfactor = skins[player.mo.skin].jumpfactor
				player.charflags = $1 & ~SF_RUNONWATER
				player.airspeed = 0
				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 >= TICRATE
				player.twirlpain = true
			end
			player.dashmode2 = 0
			player.normalspeed = skins[player.mo.skin].normalspeed // Reset to default if not capable of entering dash mode.
			player.jumpfactor = skins[player.mo.skin].jumpfactor
			player.charflags = $1 & ~SF_RUNONWATER
			player.airspeed = 0
		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
			if not player.myheadisspinning
				player.dashmode2 = $ + 1
			end
		
			if player.dashmode2 == TICRATE
				S_StartSound(player.mo, sfx_kc4d)
			end
		elseif not (player.speed >= FixedMul(player.runspeed, player.mo.scale))
		and not (player.pflags & PF_SPINNING)
		and P_IsObjectOnGround(player.mo)
		and not player.myheadisspinning
			if player.dashmode2 > 0
				player.dashmode2 = $ - 3
				if (player.dashmode2 + 3) >= 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
				player.normalspeed = skins[player.mo.skin].normalspeed // Reset to default if not capable of entering dash mode.
				player.jumpfactor = skins[player.mo.skin].jumpfactor
				player.charflags = $1 & ~SF_RUNONWATER
				player.airspeed = 0
			end
		end
	
		if player.dashmode2 >= TICRATE
			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
		
			local ghost = P_SpawnGhostMobj(player.mo)
			ghost.fuse = 4
			ghost.colorized = true
			if ghost.tracer
				ghost.tracer.fuse = 4
				ghost.tracer.colorized = true
			end
			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.speed > player.normalspeed
			and not (player.pflags & PF_SPINNING)
				if not P_IsObjectOnGround(player.mo)
					player.normalspeed = $ + FRACUNIT/15
				end
			end
		
			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
		
			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/60
				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
end

addHook("PlayerThink", Dashmode)

local function InstaCheck2(sonic, inflictor, source, damage, element)
	if (sonic.player) and sonic.state == S_PLAY_TWINSPIN
	and sonic.skin == "mcamy"
		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", InstaCheck2)

local function InstaCheck3(sonic, inflictor, source, damage, element)
	if (sonic.player) and sonic.player.tailswipe
	and sonic.skin == "mctails"
		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", InstaCheck3)

// Fuck you, Rosy.

addHook("MobjThinker", function(mobj)
	if mobj and mobj.valid
		for player in players.iterate
			if player.mo.skin == "mcamy"
				P_RemoveMobj(mobj)
			end
		end
	end
end, MT_ROSY)

addHook("MobjCollide", function(spike, plmo)
    if not (plmo and plmo.valid) return end
	if plmo.skin != ("mcamy") return end
    if plmo.type ~= MT_PLAYER return end
    if plmo.z + plmo.height < spike.z
	or spike.z + spike.height < plmo.z return end
    local player = plmo.player --plmo is the player mobj, this gets the player
	if player.helihammer
	or player.cyclone
        P_KillMobj(spike, plmo, plmo)
    end
end, MT_SPIKE)

local function InstaCheckTresThatIsRightSpanishTime(sonic, inflictor, source, damage, element)
	if (sonic.player)
		if sonic.player.helihammer
			if source.type == MT_SPIKE
				return false  //do not damage player while instashield is active
			end
		end
		
		if sonic.player.cyclone
			if element == DMG_SPIKE
				return false
			end
		end
	end
end

addHook("ShouldDamage", InstaCheckTresThatIsRightSpanishTime)

local function DabOnTheHatersButNotReallyWeAreJustReusingHiddenAmySpritesForComedicGain(player)
	if player.mo.skin != "mcamy" return end
	
	if (player.pflags & PF_FINISHED)
	or (player.exiting)
		if player.cmd.buttons & BT_CUSTOM1
			player.tpose = true
		end
	else
		player.tpose = false
	end
	
	if player.tpose
		if (player.mo.state == S_PLAY_STND or player.mo.state == S_PLAY_WAIT)
		and player.speed == 0
			player.mo.state = S_TPOSE
		end
	end
	
	if player.mo.state == S_TPOSE
		P_StartQuake(25*FRACUNIT, 16)
	end
	
	if player.mo.state == S_TPOSE
	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", DabOnTheHatersButNotReallyWeAreJustReusingHiddenAmySpritesForComedicGain)