// --------------------------------------
// Lua_MetaKnight
// Meta Knight's gameplay!
// --------------------------------------
freeslot(
// Objects
"MT_METASWORD", "S_METASWORD", "MT_METACAPE", "MT_METALOWCAPE", "S_METACAPE_UP", "S_METACAPE_DOWN", "S_METACAPE_FAST", "S_METACAPE_WALK", "S_METACAPE_STND",  "S_METACAPE_WAIT", "S_METACAPE_MINE", "S_METACAPE_DEAD",
"MT_METASHOOT", "MT_METABIGSHOOT", "MT_METASHOOTEFF", "S_METASHOOT", "S_METABIGSHOOT", "S_METASHOOTEFF", "SPR_MTKS",
// States
"S_META_SWORD1", "S_META_SWORD2", "S_META_SWORD3", "S_META_SWORD4",
"S_META_SWORD5", "S_META_SWORD6", "S_META_SWORD7", "S_META_SWORDA1",
"S_META_SWORDA2", "S_META_SWORDA3", "S_META_SWORDA4", "S_META_SWORDA5",
"S_META_SWORDA6", "S_META_SWORDSPIN", "S_META_LUNGE1", "S_META_LUNGE2",
"S_META_DTHRUST1", "S_META_DTHRUST2", "S_META_DTHRUST_END",
"S_META_SWORD_EX_1", "S_META_SWORD_EX_2", "S_META_SWORD_EX_FINAL",
"S_META_FLAP1", "S_META_FLAP2", "S_META_FLAP3", "S_META_FLAP4", "S_META_FLAP5",
"S_META_CHARGE", "S_META_TORNADO")

local metachargetime = TICRATE*3/2
local maxtornadotime = TICRATE*3
local tornadoanim = TICRATE/3

local function RandomOff(num)
	return (P_RandomByte()%num)-(num/2)
end

local function SpawnSparkles(mobj, spriteheight)
	local stype = MT_METASHOOTEFF
	local zoff = 0//8*mobj.scale*P_MobjFlip(mobj)
	local sparkle = P_SpawnMobj(mobj.x+(RandomOff(30)*mobj.scale), mobj.y+(RandomOff(30)*mobj.scale), mobj.z+(mobj.height/2)+(RandomOff(spriteheight)*mobj.scale)+zoff, stype)
	sparkle.scale = mobj.scale
	if(mobj.eflags & MFE_VERTICALFLIP)
		sparkle.eflags = $1|MFE_VERTICALFLIP
	end
end

local function zInWater(mobj, atz)
	local rover
	local topheight
	local bottomheight

	for rover in mobj.subsector.sector.ffloors()
		if not(rover.flags & FF_EXISTS)
			or not(rover.flags & FF_SWIMMABLE)
			continue
		end
		
		topheight = rover.topheight
		bottomheight = rover.bottomheight
		if(rover.t_slope) topheight = P_GetZAt(rover.t_slope, mobj.x, mobj.y) end
		if(rover.b_slope) bottomheight = P_GetZAt(rover.b_slope, mobj.x, mobj.y) end

		if(topheight >= atz and bottomheight <= atz)
			return true
		end
	end
	return false
end

local function spawnTornadoDust(mobj)
	local pos = mobj.floorz
	if(P_MobjFlip(mobj) < 0)
		pos = mobj.ceilingz
	end
	local toscale = FixedDiv(FRACUNIT, abs((mobj.z+(mobj.height/2))-pos)/24)
	if(toscale > mobj.scale)
		toscale = mobj.scale
	end

	local dist = mobj.scale * 14
	local angle1 = FixedAngle((P_RandomByte()*360/256)*FRACUNIT)
	local angle2 = FixedAngle((P_RandomByte()*360/256)*FRACUNIT/2)
	local momx = FixedMul(FixedMul(cos(angle1), cos(angle2)), dist)
	local momy = FixedMul(FixedMul(sin(angle1), cos(angle2)), dist)
	local momz = (abs(FixedMul(dist/2, sin(angle2)))+4*mobj.scale)*P_MobjFlip(mobj)
	momx = FixedMul($1, toscale)
	momy = FixedMul($1, toscale)
	momz = FixedMul($1, toscale)
	local spark = P_SpawnMobj(mobj.x+momx, mobj.y+momy, pos, MT_SPINDUST)
	spark.momx = momx
	spark.momy = momy
	spark.momz = momz
	spark.scale = FixedMul(mobj.scale, toscale)
	if(mobj.eflags & MFE_VERTICALFLIP)
		spark.eflags = $1|MFE_VERTICALFLIP
		spark.flags2 = $1|MF2_OBJECTFLIP
	end
	local watertop = mobj.floorz
	if(spark.eflags & MFE_VERTICALFLIP)
		watertop = mobj.ceilingz
	end
	if(zInWater(spark, watertop))
		spark.state = S_SPINDUST_BUBBLE1
	end
end

// For breaking floors
local function MetaSlopePos(mobj, height, slope)
	if(slope)
		// Get the corner of the object that should be the highest on the slope
		local testx
		local testy
		if(slope.d.x < 0)
			testx = mobj.radius
		else
			testx = -mobj.radius
		end
		
		if(slope.d.y < 0)
			testy = mobj.radius
		else
			testy = -mobj.radius
		end
		
		if(slope.zdelta > 0)
			testx = -testx
			testy = -testy
		end
		
		testx = $1 + mobj.x
		testy = $1 + mobj.y
		return P_GetZAt(slope, testx, testy)
	else
		return height
	end
end

local maxjumps = 5
local metastopspeed = 10
local DASHMODE_THRESHOLD = (3*TICRATE)
rawset(_G, "K_MetaKnight", function(checkflags, destroy, late, player)
	if(checkflags)
		if(player.mkvars.ablstate)
			return AF_NOFLOAT|AF_NODUCK|AF_NOJUMP|AF_NOSWIM|AF_NODROP
		end
		return AF_NOFLOAT|AF_NODROP|AF_NOLOSE
	end
	if(destroy)
		return
	end
	if(late)
		// Don't use these anims
		if(player.metacape and player.metacape == -1)
			if(player.mo.state == S_PLAY_WAIT)
				or(player.mo.state == S_KIRBY_MINECART)
				player.mo.state = S_PLAY_STND
			elseif(player.mo.state == S_PLAY_STND)
				player.mo.tics = states[player.mo.state].tics
			end
		end
		return
	end
	
	// Hurt anim stuff
	if(player.mo.state == S_PLAY_PAIN)
		if(P_PlayerInPain(player))
			or not(player.mo.health)
			player.mo.sprite2 = SPR2_PAIN
		else
			player.mo.sprite2 = SPR2_FLT
		end
	end
	
	// Dashmode cap
	if(player.normalspeed > player.actionspd)
		player.normalspeed = player.actionspd
	end
	
	// Dead...
	if(player.mo.health <= 0)
		return
	end
	
	// Kibito wants this faster
	if(player.mo.state == S_PLAY_FALL)
		and(player.mo.tics > 2)
		player.mo.tics = 2
	end
	
	// Stop dash animation from looping!
	if(player.mo.state == S_PLAY_DASH)
		if(player.mo.tics > 1)	// Also go fast
			player.mo.tics = 1
		end
		if(player.mo.frame & FF_FRAMEMASK > 6)	// Ugly hack, but you can't add FF_SPR2ENDSTATE to the frame to achieve this...
			player.mo.frame = 6|($1 & !FF_FRAMEMASK)
		end
		
		// Don't show the cape...
		if(player.mo.frame & FF_FRAMEMASK < 3)
			and(player.metacape and player.metacape == -1)
			player.mo.frame = 3|($1 & !FF_FRAMEMASK)
		end
	end
	
	// Sword
	if not(player.mkvars.ducking)
		and not(player.mkvars.floating)
		and not(player.pflags & PF_SLIDING)
		and not(player.powers[pw_carry] or player.mkvars.prevcarry)
		and not(player.exiting)
		and not(player.pflags & PF_STASIS)
		and not(player.powers[pw_nocontrol])
		and not(player.mkvars.industdevil)
		and not(player.mkvars.ablstate)
		and not(P_PlayerInPain(player))
		if(player.cmd.buttons & BT_USE)
			and not(player.mkvars.prevcmd & BT_USE)
			if not(P_IsObjectOnGround(player.mo))
				and(player.dashmode > DASHMODE_THRESHOLD)
				and not(player.mkvars.ablcharge)
				player.mo.state = S_META_SWORDSPIN
				S_StartSound(player.mo, sfx_swords)
				player.mkvars.ablstate = 2
				player.mkvars.ablvar1 = 0
			elseif(player.dashmode > DASHMODE_THRESHOLD)
				and not(player.mkvars.ablcharge)
				player.mo.state = S_META_LUNGE1
				player.mkvars.ablstate = 3
				player.mkvars.ablvar1 = 0
				player.mkvars.ablvar2 = FixedDiv(R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy), player.mo.scale)+(FRACUNIT*20)
				player.mkvars.ablvar3 = player.normalspeed
				player.dashmode = 0
			else
				player.mo.state = S_META_SWORD1
				player.mkvars.ablstate = 1
				player.mkvars.ablvar1 = 0
				// Charge sword!
				if(player.mkvars.ablcharge)
					S_StartSound(player.mo, sfx_swords)
					local swrd = P_SPMAngle(player.mo, MT_METABIGSHOOT, player.drawangle, 0)
				end
				player.mkvars.ablcharge = false
			end
		elseif(player.cmd.buttons & BT_CUSTOM2)
			and not(player.mkvars.prevcmd & BT_CUSTOM2)
			and not(P_IsObjectOnGround(player.mo))
			if(player.mo.momz*P_MobjFlip(player.mo) < -10*player.mo.scale)
				player.mo.momz = $1 - 4*player.mo.scale*P_MobjFlip(player.mo)
			else
				player.mo.momz = -10*player.mo.scale*P_MobjFlip(player.mo)
			end
			S_StartSound(player.mo, sfx_sword)
			player.mo.state = S_META_DTHRUST1
			player.mkvars.ablstate = 5
			player.mkvars.ablvar1 = 0
		end
	end
	
	// Force the slash to end when sliding or being carried
	if(player.pflags & PF_SLIDING)
		or(player.powers[pw_carry])
		or(player.mkvars.industdevil)
		or(P_PlayerInPain(player))
		if(player.mkvars.ablstate)
			player.pflags = ($1|PF_NOJUMPDAMAGE) & !PF_JUMPED
		end
		player.mkvars.ablstate = 0
	end
	
	// Start second and third
	if(player.cmd.buttons & BT_USE)
		and not(player.mkvars.prevcmd & BT_USE)
		and not(player.exiting)
		and not(player.pflags & PF_STASIS)
		and not(player.powers[pw_nocontrol])
		and(P_IsObjectOnGround(player.mo))
		and(player.mkvars.ablvar1 > 3)
		if(player.mkvars.ablstate == 1)
			S_StartSound(player.mo, sfx_sword)
			player.mkvars.ablstate = 6
			player.mkvars.ablvar1 = 0
			player.mkvars.ablvar3 = 0
			player.mo.state = S_META_SWORD_EX_1
			P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
			player.mo.momz = 6*player.mo.scale*P_MobjFlip(player.mo)
			if(player.mo.eflags & MFE_UNDERWATER)
				player.mo.momz = $1 / 2
			end
		end
	end
	
	if(player.mkvars.ablstate == 1)
		// Slash
		player.mkvars.ablvar1 = $1 + 1
		if(player.mkvars.ablvar1 > 7)
			player.mkvars.ablstate = 0
			if(player.cmd.buttons & BT_USE)
				and(P_IsObjectOnGround(player.mo))
				player.mkvars.ablstate = 8	// Bad???
				player.mkvars.ablvar1 = 0
				S_StartSound(player.mo, sfx_s3ka4)
			end
		else
			player.mo.state = S_META_SWORD1+player.mkvars.ablvar1-1	// Manual animation yay
			if not(P_IsObjectOnGround(player.mo))		// Air slash
				if(player.mo.state == S_META_SWORD7)
					player.mo.state = S_META_SWORDA6
				else
					player.mo.state = $1 + (S_META_SWORDA1-S_META_SWORD1)
				end
			end
			
			// Set frame properly
			if(player.mo.state >= S_META_SWORD1 and player.mo.state <= S_META_SWORD7)
				player.mo.frame = (player.mo.state-S_META_SWORD1)|($1 & !FF_FRAMEMASK)
			elseif(player.mo.state >= S_META_SWORDA1 and player.mo.state <= S_META_SWORDA3)
				player.mo.frame = (player.mo.state-S_META_SWORDA1)|($1 & !FF_FRAMEMASK)
			elseif(player.mo.state >= S_META_SWORDA4 and player.mo.state <= S_META_SWORDA6)
				player.mo.frame = ((player.mo.state-S_META_SWORDA4)+7)|($1 & !FF_FRAMEMASK)
			end
		end
		P_SPMAngle(player.mo, MT_METASWORD, player.drawangle, 0)
	elseif(player.mkvars.ablstate == 2)
		// Spin
		if(P_IsObjectOnGround(player.mo))
			or(player.mkvars.ablvar1 >= 24)
			player.mkvars.ablstate = 0
		else
			player.mo.state = S_META_SWORDSPIN
			player.mo.frame = (player.mkvars.ablvar1%8)|($1 & !FF_FRAMEMASK)
			player.mkvars.ablvar1 = $1 + 1
		end
	elseif(player.mkvars.ablstate == 3)
		// Lunge
		if not(player.mo.state >= S_META_LUNGE1 and player.mo.state <= S_META_LUNGE2)
			or((player.mkvars.ablvar1 >= states[S_META_LUNGE1].tics) and (R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy) < metastopspeed*player.mo.scale))
			or not(P_IsObjectOnGround(player.mo))
			player.mkvars.ablstate = 0
			player.normalspeed = skins[player.mo.skin].normalspeed
			if(player.mo.state >= S_META_LUNGE1 and player.mo.state <= S_META_LUNGE2)
				if(P_IsObjectOnGround(player.mo))
					player.mo.state = S_PLAY_STND
				else
					player.mo.state = S_PLAY_FALL
				end
			end
		elseif(P_IsObjectOnGround(player.mo))
			and(player.cmd.buttons & BT_JUMP)
			and not(player.mkvars.prevcmd & BT_JUMP)
			and(player.mkvars.ablvar1 > states[S_META_LUNGE1].tics)
			player.mo.state = S_META_SWORDSPIN
			player.mkvars.ablstate = 4
			player.mkvars.ablvar1 = 0
			S_StartSound(player.mo, sfx_swords)
			P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
			player.mo.momz = 7*player.mo.scale
		else
			// Can't move!
			player.powers[pw_nocontrol] = max($1, 1)
			// Use the right frame!
			player.mo.frame = ((player.mo.state-S_META_LUNGE1)+14)+($1 & !FF_FRAMEMASK)
			player.mkvars.ablvar1 = $1 + 1
			// Thrust!
			if(player.mkvars.ablvar1 == states[S_META_LUNGE1].tics)
				S_StartSound(player.mo, sfx_sword)
				P_InstaThrust(player.mo, player.drawangle, FixedMul(player.mkvars.ablvar2, player.mo.scale))
				player.mo.momx = $1 + player.cmomx
				player.mo.momy = $1 + player.cmomy
				player.normalspeed = player.mkvars.ablvar3
				player.dashmode = DASHMODE_THRESHOLD
			elseif(player.mkvars.ablvar1 < states[S_META_LUNGE1].tics)
				player.mo.friction = $1 / 2
			//	player.dashmode = DASHMODE_THRESHOLD
			else
				player.mo.friction = min(FRACUNIT, $1*76/70)
			end
		end
	elseif(player.mkvars.ablstate == 4)
		// Locked spin
		if(P_IsObjectOnGround(player.mo))
			player.mkvars.ablstate = 0
		else
			player.mo.state = S_META_SWORDSPIN
			player.mo.frame = (player.mkvars.ablvar1%8)|($1 & !FF_FRAMEMASK)
			player.mkvars.ablvar1 = $1 + 1
		end
	elseif(player.mkvars.ablstate == 5)
		// Break floors (copied from Sora)
		for rover in player.mo.subsector.sector.ffloors()
			if not(rover.flags & FF_EXISTS)			// Needs to exist!
				or not(rover.flags & FF_BUSTUP)		// And breakable!
				continue
			end
			
			if(MetaSlopePos(player.mo, rover.topheight, rover.t_slope) > player.mo.z+(player.mo.momz))
				and(MetaSlopePos(player.mo, rover.bottomheight, rover.b_slope) < player.mo.z+player.mo.height+(player.mo.momz))
				EV_CrumbleChain(nil, rover)		// Bust it!
				// Run a linedef executor??
				if(rover.master.flags & ML_EFFECT5)
					P_LinedefExecute(P_AproxDistance(rover.master.dx, rover.master.dy)>>FRACBITS, player.mo, player.mo.subsector.sector)
				end
			end
		end
		
		// Downward thrust landing
		if(P_IsObjectOnGround(player.mo))
			player.mkvars.usedjumps = 0	// A
			if not(player.mkvars.ablvar1)
				if(player.dashmode > DASHMODE_THRESHOLD)
				local pspeed = R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)
			//	if(pspeed > FixedMul(player.runspeed, player.mo.scale))
					player.mo.state = S_META_SWORDSPIN
					player.mkvars.ablstate = 2
					player.mkvars.ablvar1 = 0
					S_StartSound(player.mo, sfx_swords)
					S_StartSound(player.mo, sfx_stone)
					P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
					player.mo.momz = 5*player.mo.scale
					P_InstaThrust(player.mo, player.drawangle, pspeed+(2*player.mo.scale))
				else
					player.mo.state = S_META_DTHRUST_END
					player.mo.frame = 2 | ($1 & !FF_FRAMEMASK)
					player.mkvars.ablvar1 = 1
					S_StartSound(player.mo, sfx_stone)
					player.pflags = ($1|PF_NOJUMPDAMAGE) & !PF_JUMPED
				end
			end
		else
			player.pflags = ($1|PF_JUMPED) & !PF_NOJUMPDAMAGE
		end
		
		if(player.mkvars.ablvar1)
			player.mkvars.ablvar1 = $1 + 1
			player.mo.state = S_META_DTHRUST_END
			player.mo.frame = 2 | ($1 & !FF_FRAMEMASK)
			player.powers[pw_nocontrol] = max($1, 1)
		end
		
		// End
		if not(player.mkvars.ablstate == 2)
			if(player.mkvars.ablvar1 > TICRATE/4)
				or not(player.mo.state >= S_META_DTHRUST1 and player.mo.state <= S_META_DTHRUST_END)
				if(player.mo.state >= S_META_DTHRUST1 and player.mo.state <= S_META_DTHRUST_END)
					player.mo.state = S_PLAY_STND
				end
				player.mkvars.ablstate = 0
				player.mkvars.ablvar1 = 0
				player.pflags = ($1|PF_NOJUMPDAMAGE) & !PF_JUMPED	// Paranoid
			end
		end
	elseif(player.mkvars.ablstate == 6)
		// Second swing
		player.mkvars.ablvar1 = $1 + 1
		
		if(player.mkvars.ablvar1 > 1)
			and(player.cmd.buttons & BT_USE)
			and not(player.mkvars.prevcmd & BT_USE)
			player.mkvars.ablvar3 = 1
		end
		
		if(P_IsObjectOnGround(player.mo))
			or(player.mkvars.ablvar1 >= TICRATE*2)
			if(player.mkvars.ablvar3)
				player.mkvars.ablstate = 7
			else
				player.mkvars.ablstate = 0
			end
		elseif(player.mkvars.ablvar1 < TICRATE/2)
			// Not on ground; apply gravity!
			player.mo.momz = $1 + P_GetMobjGravity(player.mo)
		end
		
		if(player.mkvars.ablstate)
			if not(player.mo.state >= S_META_SWORD_EX_1 and player.mo.state <= S_META_SWORD_EX_2)
				player.mo.state = S_META_SWORD_EX_1
			end
			player.mo.frame = (player.mo.state - S_META_SWORD_EX_1 + 11)|($1 & !FF_FRAMEMASK)
		end
		P_SPMAngle(player.mo, MT_METASWORD, player.drawangle, 0)
	elseif(player.mkvars.ablstate == 7)
		// Third swing
		player.mkvars.ablvar1 = $1 + 1
		if(player.mkvars.ablvar1 >= TICRATE*3/2)
			player.mkvars.ablstate = 0
			player.mo.state = S_PLAY_STND
		else
			// Spawn swords!
			if(player.mkvars.ablvar1 & 1)
				S_StartSound(player.mo, sfx_swords)
				local swrd = P_SPMAngle(player.mo, MT_METASHOOT, player.drawangle, 0)
			end
			
		//	player.powers[pw_nocontrol] = max($1, 1)
			player.normalspeed = 0
			player.thrustfactor = 0
			
			player.mo.state = S_META_SWORD_EX_FINAL
			player.mo.frame = 13|($1 & !FF_FRAMEMASK)
		end
		P_SPMAngle(player.mo, MT_METASWORD, player.drawangle, 0)
	elseif(player.mkvars.ablstate == 8)
		// Cancel
		if not(P_IsObjectOnGround(player.mo))
			player.mkvars.ablstate = 0
			if(player.mo.state == S_META_CHARGE)
				player.mo.state = S_PLAY_FALL
			end
		else
			player.powers[pw_nocontrol] = max($1, 1)
			
			// Charging power
			player.mkvars.ablvar1 = $1 + 1
			if(player.mkvars.ablvar1 == metachargetime)
				player.mkvars.ablcharge = true
				while(S_SoundPlaying(player.mo, sfx_s3ka4))
					S_StopSound(player.mo)
				end
				S_StartSound(player.mo, sfx_s3ka8)
			end
			
			// Play animation
			if not(player.mo.state == S_META_CHARGE)
				player.mo.state = S_META_CHARGE
			end
			
			local chargelvl = max(min((player.mkvars.ablvar1*3)/metachargetime, 2), 0)*2
			
			// Frame stuff
			player.mo.frame = (($1&1)+chargelvl)|($1 & !FF_FRAMEMASK)
			if(player.mo.frame & 1)
				and((player.mo.frame & FF_FRAMEMASK) > 2)
				player.mo.frame = $1|FF_FULLBRIGHT
			else
				player.mo.frame = $1 & !FF_FULLBRIGHT
			end
			
			// Tornado! (or exit if not charged...)
			if not(player.cmd.buttons & BT_USE)
				if(player.mkvars.ablvar1 >= metachargetime)
					player.mkvars.ablstate = 9
					player.mkvars.ablvar1 = 0
					player.mkvars.ablvar2 = player.drawangle
					player.mkvars.ablcharge = false
				else
					player.mkvars.ablstate = 0
					player.mo.state = S_PLAY_STND
					while(S_SoundPlaying(player.mo, sfx_s3ka4))
						S_StopSound(player.mo)
					end
				end
			end
			
			// Jumping out
			if(player.cmd.buttons & BT_JUMP)
				and not(player.mkvars.usedjumps & BT_JUMP)
				player.mkvars.ablstate = 0
				// Jump...?
				player.mo.state = S_PLAY_STND
				while(S_SoundPlaying(player.mo, sfx_s3ka4))
					S_StopSound(player.mo)
				end
			end
		end
	elseif(player.mkvars.ablstate == 9)
		player.pflags = ($1|PF_JUMPED) & !PF_NOJUMPDAMAGE
		if(P_IsObjectOnGround(player.mo))
			player.thrustfactor = $1 + 2	// Higher acceleration
		else
			player.thrustfactor = $1 + 6	// Higher acceleration
		end
		// Tornado
		player.mkvars.ablvar1 = $1 + 1
		
		// Animation
		local animvar = min(player.mkvars.ablvar1, maxtornadotime-player.mkvars.ablvar1)
		player.mo.state = S_META_TORNADO
		if(animvar > tornadoanim)
			player.mo.frame = ((player.mkvars.ablvar1%5)+2)|($1 & !FF_FRAMEMASK)
			spawnTornadoDust(player.mo)
			SpawnSparkles(player.mo, 40)
		elseif(animvar > tornadoanim/2)
			player.mo.frame = 1|($1 & !FF_FRAMEMASK)
		else
			player.mo.frame = $1 & !FF_FRAMEMASK
		end
		
		if(player.mkvars.ablvar1%4 == 1)
			S_StartSound(player.mo, sfx_swords)
		end
		
		// SPEEN
		player.mkvars.ablvar2 = $1 + min(animvar, tornadoanim)*FixedAngle(7*FRACUNIT)
		player.drawangle = player.mkvars.ablvar2
		
		// Floating!
		if(player.cmd.buttons & (BT_JUMP|BT_USE))
			player.mo.momz = $1 - P_GetMobjGravity(player.mo)
			player.mo.momz = $1 + player.mo.scale*P_MobjFlip(player.mo)/3
			if(player.mo.momz*P_MobjFlip(player.mo) > 5*player.mo.scale)
				player.mo.momz = 5*player.mo.scale
			end
		elseif not(P_IsObjectOnGround(player.mo))
			player.mo.momz = $1 - P_GetMobjGravity(player.mo)/2
		end
		
		if(player.mkvars.ablvar1 > maxtornadotime)
			player.pflags = ($1 & !PF_JUMPED)|PF_NOJUMPDAMAGE
			player.mkvars.ablstate = 0
			player.mo.state = S_PLAY_FALL
			if(P_IsObjectOnGround(player.mo))
				player.mo.state = S_PLAY_STND
			end
		end
	elseif(player.mo.state == S_META_SWORD7)
		and(player.cmd.forwardmove or player.cmd.sidemove)
		// Reset animation
		if(player.dashmode > DASHMODE_THRESHOLD)
			player.mo.state = S_PLAY_DASH
		elseif(FixedDiv(R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy), player.mo.scale) > player.runspeed)
			player.mo.state = S_PLAY_RUN
		else
			player.mo.state = S_PLAY_WALK
		end
	end
	
	// Slash sound
	if(player.mo.state == S_META_SWORD3)
		or(player.mo.state == S_META_SWORDA3)
		S_StartSound(player.mo, sfx_sword)
	end
	
	// Flying
	if(player.cmd.buttons & BT_JUMP)
		and not(player.mkvars.prevcmd & BT_JUMP)
		and(player.mkvars.usedjumps < maxjumps)
		and not(player.mkvars.ablstate)	// No sword
		and not(player.mo.eflags & MFE_UNDERWATER)
		and not(player.powers[pw_carry])
		and not(P_IsObjectOnGround(player.mo))
		and not(player.mkvars.prevcarry)
		and not(player.mkvars.prevground)
		and not(player.mkvars.ducking)
		and not(player.pflags & PF_SLIDING)
		and not(player.exiting)
		and not(player.pflags & PF_STASIS)
		and not(P_PlayerInPain(player))
		player.mkvars.usedjumps = $1 + 1
		S_StartSound(player.mo, sfx_sword)
		player.mo.momz = 8*player.mo.scale*P_MobjFlip(player.mo)
		player.mo.state = S_META_FLAP1
		player.mo.frame = ($1 & !FF_FRAMEMASK)
		if(player.mkvars.usedjumps >= 3)	// Third jump onwards
			player.mo.momx = $1 - player.cmomx
			player.mo.momy = $1 - player.cmomy
			player.mo.momx = $1 * (9-player.mkvars.usedjumps)/7
			player.mo.momy = $1 * (9-player.mkvars.usedjumps)/7
			if(player.dashmode > DASHMODE_THRESHOLD)
				player.normalspeed = FixedDiv(R_PointToDist2(0, 0, player.mo.momx, player.mo.momy), player.mo.scale)
				if(player.normalspeed < skins[player.mo.skin].normalspeed)
					player.dashmode = 0
					player.normalspeed = skins[player.mo.skin].normalspeed
					S_StartSound(player.mo, sfx_kc65)
				end
			end
			player.mo.momx = $1 + player.cmomx
			player.mo.momy = $1 + player.cmomy
		end
		// Attack enemies because why not
		P_SPMAngle(player.mo, MT_METASWORD, player.drawangle+ANGLE_90, 0)
		P_SPMAngle(player.mo, MT_METASWORD, player.drawangle-ANGLE_90, 0)
	end
end)

// Stab enemies
addHook("TouchSpecial", function(enemy, mobj)
	if(enemy == nil)
		or(mobj == nil)
		or not((enemy.flags & MF_ENEMY) or (enemy.flags & MF_BOSS))
		or not(mobj.player and mobj.player.valid)
		or not(mobj.state >= S_META_SWORDSPIN and mobj.state <= S_META_LUNGE2)
		return
	end
	
	P_DamageMobj(enemy, mobj, mobj)
	if not(mobj.player.powers[pw_flashing])
		mobj.player.powers[pw_flashing] = 1
	end
end)

// Stab monitors
addHook("MobjMoveCollide", function(mobj, enemy)
	if(enemy == nil)
		or(mobj == nil)
		or not(mobj.player and mobj.player.valid)
		or not(mobj.state >= S_META_SWORDSPIN and mobj.state <= S_META_LUNGE2)
		return
	end
	
	// Stab players!
	if(enemy.type == MT_PLAYER)
		and(mobj.z <= enemy.z+enemy.height)
		and(mobj.z+mobj.height >= enemy.z)
		P_DamageMobj(enemy, mobj, mobj)
		if not(mobj.player.powers[pw_flashing])
			mobj.player.powers[pw_flashing] = 1
		end
		return
	end
	
	// Stab monitors
	if(enemy.flags & MF_MONITOR)
		and(mobj.z-(10*enemy.scale) <= enemy.z+enemy.height)
		and(mobj.z+mobj.height >= enemy.z-(10*enemy.scale))
		P_DamageMobj(enemy, mobj, mobj)
		if not(mobj.player.powers[pw_flashing])
			mobj.player.powers[pw_flashing] = 1
		end
	end
end, MT_PLAYER)

// Cape color command
COM_AddCommand("metacape", function(player, color)
	if(color == nil)
		CONS_Printf(player, "Enter a color to use for Meta Knight's cape! default to use the default color, none to remove it")
		return
	end
	
	local lc = string.lower(color)
	if(lc == "default")
		CONS_Printf(player, "Cape reset to default color (player skincolor)")
		player.metacape = nil
		return
	elseif(lc == "none")
		CONS_Printf(player, "Cape removed")
		player.metacape = -1
		return
	end
	
	local endcolor = R_GetColorByName(lc)
	if(endcolor == SKINCOLOR_NONE)	// Not valid???
		endcolor = tonumber(color)
	end
	
	if(endcolor == nil)
		or(endcolor <= SKINCOLOR_NONE)
		or(endcolor >= MAXSKINCOLORS)
		or(R_GetNameByColor(endcolor) == nil)// Hacky but eeehhhh... just ends up erroring too...
		CONS_Printf(player, color+" is not a valid color!")
	end
	
	player.metacape = endcolor
end)

// Cape thinker!
local capeoverlay = 1337+69+420	// Magic number garbage
local capespr2s = {
	[SPR2_STND] = 0,
	[SPR2_WALK] = 0,
	[SPR2_RUN] = 0,
	[SPR2_SPNG] = 4,
	[SPR2_RIDE] = 0,
	[SPR2_FALL] = 6,
	[SPR2_SWIM] = 0,
	[SPR2_GASP] = 0,
	[SPR2_DUCK] = 0,
	[SPR2_SLID] = 0,
	[SPR2_MLEE] = -2,
	[SPR2_MLEL] = -2,
	[SPR2_FIRE] = -2,
	[SPR2_FLT] = 0,
//	[SPR2_CARY] = 0,
	[SPR2_WAIT] = capeoverlay,
	[SPR2_MINE] = capeoverlay,
	[SPR2_PAIN] = capeoverlay,
	[SPR2_DEAD] = capeoverlay,
}

// Works as FollowMobj hook, but that happens before thinkframe...
local uselinkdrawhack = false	// Looks good, doesn't work with shields... :<
local doublecape = true
local function MKCapeThinker(player, mobj)
	mobj.skin = player.mo.skin
	mobj.color = player.mo.color
	mobj.colorized = player.mo.colorized
	mobj.scale = player.mo.scale
	mobj.angle = player.drawangle
	mobj.height = FixedMul(player.mo.scale, skins[player.mo.skin].height)
	local hdif = mobj.height
	mobj.height = $1 * 2
	mobj.eflags = ($1 & !MFE_VERTICALFLIP)|(player.mo.eflags & MFE_VERTICALFLIP)
	mobj.flags2 = ($1 & !(MF2_DONTDRAW|MF2_LINKDRAW))|(player.mo.flags2 & MF2_DONTDRAW)
	
	mobj.plsdie = 2
	if(mobj.double and mobj.double.valid)
		mobj.double.plsdie = 2
	end
	if(mobj.linkto and mobj.linkto.valid)
		mobj.linkto.plsdie = $1 - 1
	end
	
	// Cape color...
	if not(mobj.colorized)
		and not(player.metacape == nil)
		and not(player.metacape == -1)
		mobj.color = player.metacape
	end
	
	// Removed cape
	if(player.metacape)
		and(player.metacape == -1)
		and not(player.mo.sprite2 == SPR2_WAIT)
		and not(player.mo.sprite2 == SPR2_MINE)
		mobj.flags2 = $1|MF2_DONTDRAW
	end
	
	if(capespr2s[player.mo.sprite2] == nil)
		or not(player.mo.sprite == SPR_PLAY)
	//	or(player.mo.eflags & MFE_UNDERWATER)
		or(player.mkvars.usedjumps and player.mo.state == S_PLAY_SPRING)	// Returning to cape after flapping looks weird...
		mobj.flags2 = $1|MF2_DONTDRAW
		if(mobj.double and mobj.double.valid)
			mobj.double.flags2 = $1|MF2_DONTDRAW
		end
		return true
	end
	local anim = S_METACAPE_UP
	local pspeed = FixedDiv(FixedHypot(player.mo.momx, player.mo.momy), player.mo.scale)
	local vspeed = FixedDiv(player.mo.momz, player.mo.scale)*P_MobjFlip(player.mo)
	if(player.mo.sprite2 == SPR2_PAIN or player.mo.sprite2 == SPR2_DEAD)
		anim = S_METACAPE_DEAD
	elseif(player.mo.sprite2 == SPR2_MINE)
		anim = S_METACAPE_MINE
	elseif(player.mo.sprite2 == SPR2_WAIT)
		anim = S_METACAPE_WAIT
	elseif(player.mo.state == S_PLAY_RIDE)
		anim = S_METACAPE_UP
	elseif(player.mo.state == S_PLAY_STND)
		or(player.mo.state == S_PLAY_EDGE)
		anim = S_METACAPE_STND
	elseif(player.mo.state == S_PLAY_WALK)
		anim = S_METACAPE_WALK
	elseif(pspeed > player.runspeed and not(abs(vspeed) > pspeed))
		or(player.mo.state == S_PLAY_RUN)
		or(player.mo.state == S_KIRBY_SLIDE)
		or(player.mo.state == S_KIRBY_SWIM)
		anim = S_METACAPE_FAST
	elseif(vspeed < 0)
		and not(P_IsObjectOnGround(player.mo))
		anim = S_METACAPE_DOWN
	end
	if not(mobj.state == anim)
		mobj.state = anim
	end
	
	// Brute force fixing a bug!
	mobj.sprite2 = states[anim].frame
	mobj.frame = $1 & (3|(!FF_FRAMEMASK))	// frame % 4
	
	// Special condition for walking
	if(anim == S_METACAPE_WALK)
		mobj.frame = player.mo.frame&FF_FRAMEMASK
		mobj.anim_duration = player.mo.anim_duration
		mobj.tics = player.mo.tics
	elseif(player.mo.state == S_KIRBY_DUCK)
		and not(pspeed > FRACUNIT)
		mobj.frame = 3
		mobj.anim_duration = 0
		mobj.tics = 4
	else
		// Anim speed!
		local tspeed = FixedHypot(pspeed, vspeed)
		local maxtics = 4
		if(tspeed > 30*FRACUNIT)
			and not(player.mo.eflags & MFE_UNDERWATER)
			maxtics = 1
		elseif(tspeed > 20*FRACUNIT)
			maxtics = 2
		elseif(tspeed > 10*FRACUNIT)
			maxtics = 3
		end
		if(mobj.tics > maxtics)
			mobj.tics = maxtics
		end
	end
	
	// Offsets
	local capedist = player.mo.scale/2
	local capeheight = capespr2s[player.mo.sprite2]*player.mo.scale*P_MobjFlip(player.mo)
	if(capespr2s[player.mo.sprite2] == capeoverlay)
		capeheight = 0
		capedist = 0
	end
	local xoff = FixedMul(-cos(player.drawangle), capedist)
	local yoff = FixedMul(-sin(player.drawangle), capedist)
	local zstart = player.mo.z+player.mo.height-(mobj.height-hdif)
	if(player.mo.eflags & MFE_VERTICALFLIP)
		zstart = player.mo.z-hdif
	end
	
	if(doublecape)
		xoff = 0
		yoff = 0
	end
	
	// Place the cape!
	if(uselinkdrawhack)
		mobj.flags2 = $1 | MF2_LINKDRAW
		
		if not(mobj.linkto and mobj.linkto.valid)
			mobj.linkto = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_METACAPE)
			mobj.linkto.state = S_KIRBYMONINVIS
		end
		
		// Place MF2_LINKDRAW dummy
		mobj.linkto.skin = mobj.skin
		mobj.linkto.color = mobj.color
		mobj.linkto.colorized = mobj.colorized
		mobj.linkto.scale = mobj.scale
		mobj.linkto.angle = mobj.angle
		mobj.linkto.height = mobj.height
		mobj.linkto.eflags = mobj.eflags
		mobj.linkto.flags2 = mobj.flags2
		P_TeleportMove(mobj.linkto, player.mo.x+xoff, player.mo.y+yoff, zstart+capeheight)
		
		// Setup MF2_LINKDRAW
		mobj.flags2 = $1|MF2_LINKDRAW
		mobj.tracer = mobj.linkto
		
		// Place cape
		P_TeleportMove(mobj, player.mo.x, player.mo.y, zstart+capeheight)
	else
		// Place cape
		mobj.flags2 = $1 & !MF2_LINKDRAW	// Not sure why it isn't disabled from the earlier line, but... Sure, I guess?
		P_TeleportMove(mobj, player.mo.x+xoff, player.mo.y+yoff, zstart+capeheight)
	end
	
	// Double cape
	if(doublecape)
		if(mobj.sprite2 < SPR2_TAL5)	// 5 & 6 are idle, have full rotations
			and not(mobj.sprite2 == SPR2_BNCE)
			if not(mobj.double and mobj.double.valid)
				mobj.double = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_METALOWCAPE)
			end
			
			mobj.double.momx = mobj.momx
			mobj.double.momy = mobj.momy
			mobj.double.momz = mobj.momz
			mobj.double.skin = mobj.skin
			mobj.double.color = mobj.color
			mobj.double.colorized = mobj.colorized
			mobj.double.state = mobj.state
			mobj.double.frame = mobj.frame
			mobj.double.sprite2 = mobj.sprite2 + SPR2_TAL7-SPR2_TAL0	// Use the right sprite!
			mobj.double.scale = mobj.scale
			mobj.double.angle = mobj.angle
			mobj.double.height = mobj.height
			mobj.double.eflags = mobj.eflags
			mobj.double.flags2 = mobj.flags2
			mobj.double.target = mobj.target
			mobj.double.tracer = mobj.tracer
			P_TeleportMove(mobj.double, mobj.x, mobj.y, mobj.z)
		elseif(mobj.double and mobj.double.valid)
			mobj.double.flags2 = $1|MF2_DONTDRAW
		end
	end
	
	return true
end

//addHook("FollowMobj", MKCapeThinker, MT_METACAPE)
addHook("FollowMobj", function(player, mobj)
	player.mkcape = mobj
end, MT_METACAPE)
addHook("PostThinkFrame", function()
	for player in players.iterate
		if(player.mo and player.mo.valid)
			and(player.mkcape and player.mkcape.valid)
			MKCapeThinker(player, player.mkcape)
		end
	end
end)
addHook("MobjThinker", function(ghost)
	if(ghost and ghost.valid and ghost.skin == "metaknight")
		and(ghost.sprite2 >= SPR2_TAL0 and ghost.sprite2 <= SPR2_TALB)
		P_RemoveMobj(ghost)
	end
end, MT_GHOST)
addHook("MobjThinker", function(mobj)
	if not(mobj.plsdie)
		P_RemoveMobj(mobj)
	else
		mobj.plsdie = $1 - 1
	end
end, MT_METACAPE)
addHook("MobjThinker", function(mobj)
	if not(mobj.plsdie)
		P_RemoveMobj(mobj)
	else
		mobj.plsdie = $1 - 1
	end
end, MT_METALOWCAPE)

// Sword breaks walls!
local function bustAtZ(checkz, sector)
	for rover in sector.ffloors()
		if not(rover.flags & FF_EXISTS)
			or not(rover.flags & FF_BUSTUP)
			continue
		end
		
		local topheight = rover.topheight
		local bottomheight = rover.bottomheight
		if(rover.t_slope) topheight = P_GetZAt(rover.t_slope, mobj.x, mobj.y) end
		if(rover.b_slope) bottomheight = P_GetZAt(rover.b_slope, mobj.x, mobj.y) end
		
		if(topheight >= checkz and bottomheight <= checkz)
			// In bustable; break it!
			EV_CrumbleChain(nil, rover)		// Bust it!
			// Run a linedef executor??
			if(rover.master.flags & ML_EFFECT5)
				P_LinedefExecute(P_AproxDistance(rover.master.dx, rover.master.dy)>>FRACBITS, mobj, sector)
			end
		end
	end
end

local function bustWalls(mobj, line)
	bustAtZ(mobj.z+(mobj.height/2), line.frontsector)
	if(line.backsector)
		bustAtZ(mobj.z+(mobj.height/2), line.backsector)
	end
end

addHook("MobjLineCollide", bustWalls, MT_METASWORD)
addHook("MobjLineCollide", bustWalls, MT_METABIGSHOOT)
addHook("MobjLineCollide", function(pmo, line)
	if(pmo.skin == "metaknight")
		and(pmo.player and pmo.player.valid)
		and(pmo.player.mkvars.ablstate == 2 or pmo.player.mkvars.ablstate == 3 or pmo.player.mkvars.ablstate == 4)
		bustWalls(pmo, line)
	end
end, MT_PLAYER)

addHook("MobjThinker", function(mobj)
	if(mobj.shuuttime == nil)
		mobj.shuuttime = TICRATE/3
		// Random movement!
		mobj.momx = $1 + (P_RandomByte()-128)*FRACUNIT/50
		mobj.momy = $1 + (P_RandomByte()-128)*FRACUNIT/50
		mobj.momz = $1 + (P_RandomByte()-128)*FRACUNIT/70
		mobj.angle = R_PointToAngle2(0, 0, mobj.momx, mobj.momy)
	end
	
	if(mobj.shuuttime & 1)
		SpawnSparkles(mobj, 32)
	end
	
	mobj.shuuttime = $1 - 1
	if not(mobj.shuuttime)
		P_RemoveMobj(mobj)
	end
end, MT_METASHOOT)

addHook("MobjThinker", function(mobj)
	SpawnSparkles(mobj, 64)
end, MT_METABIGSHOOT)

// Cape garbage
mobjinfo[MT_ELEMENTAL_ORB].dispoffset = 2
mobjinfo[MT_ATTRACT_ORB].dispoffset = 2
mobjinfo[MT_FORCE_ORB].dispoffset = 2
mobjinfo[MT_ARMAGEDDON_ORB].dispoffset = 2
mobjinfo[MT_WHIRLWIND_ORB].dispoffset = 2
mobjinfo[MT_PITY_ORB].dispoffset = 2
mobjinfo[MT_FLAMEAURA_ORB].dispoffset = 2
mobjinfo[MT_BUBBLEWRAP_ORB].dispoffset = 2
mobjinfo[MT_THUNDERCOIN_ORB].dispoffset = 2
